From dc9a4f41d518e30d72379c6eb1d7602e7f19452d Mon Sep 17 00:00:00 2001 From: Smony Date: Wed, 21 Jun 2017 11:46:40 +0300 Subject: [PATCH 1/6] init new branch --- index.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 index.php diff --git a/index.php b/index.php new file mode 100644 index 0000000..e69de29 From c8eab96d1b16feb95c6bb3ffb90eb57372da73d8 Mon Sep 17 00:00:00 2001 From: Smony Date: Wed, 21 Jun 2017 12:57:47 +0300 Subject: [PATCH 2/6] include RedBeanPHP ORM System --- .htaccess | 5 +- public/rb.php | 13058 ++++++++++++++++++++++++++++++++++++++++++ public/test.php | 5 + tests/.htaccess | 4 + tests/css/style.css | 4 + tests/index.php | 29 + tests/rb.php | 13058 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 26162 insertions(+), 1 deletion(-) create mode 100644 public/rb.php create mode 100644 public/test.php create mode 100644 tests/.htaccess create mode 100644 tests/css/style.css create mode 100644 tests/index.php create mode 100644 tests/rb.php diff --git a/.htaccess b/.htaccess index 64cf18b..ced2c2c 100644 --- a/.htaccess +++ b/.htaccess @@ -1,3 +1,6 @@ AddDefaultCharset utf-8 RewriteEngine On -RewriteRule ^(.*)$ public/$1 +#RewriteRule ^(.*)$ public/$1 + +RewriteRule ^(.*)$ tests/$1 + diff --git a/public/rb.php b/public/rb.php new file mode 100644 index 0000000..ae301ad --- /dev/null +++ b/public/rb.php @@ -0,0 +1,13058 @@ +mode === self::C_LOGGER_ECHO ) { + echo $log; + } else { + $this->logs[] = $log; + } + } else { + if ( $this->mode === self::C_LOGGER_ECHO ) { + echo $argument; + } else { + $this->logs[] = $argument; + } + } + + if ( $this->mode === self::C_LOGGER_ECHO ) echo "
" . PHP_EOL; + } + } + + /** + * Returns the internal log array. + * The internal log array is where all log messages are stored. + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears the internal log array, removing all + * previously stored entries. + * + * @return self + */ + public function clear() + { + $this->logs = array(); + return $this; + } + + /** + * Selects a logging mode. + * There are several options available. + * + * * C_LOGGER_ARRAY - log silently, stores entries in internal log array only + * * C_LOGGER_ECHO - also forward log messages directly to STDOUT + * + * @param integer $mode mode of operation for logging object + * + * @return self + */ + public function setMode( $mode ) + { + if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) { + throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' ); + } + $this->mode = $mode; + return $this; + } + + /** + * Searches for all log entries in internal log array + * for $needle and returns those entries. + * This method will return an array containing all matches for your + * search query. + * + * @param string $needle phrase to look for in internal log array + * + * @return array + */ + public function grep( $needle ) + { + $found = array(); + foreach( $this->logs as $logEntry ) { + if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry; + } + return $found; + } +} +} + +namespace RedBeanPHP\Logger\RDefault { + +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\RedException as RedException; + +/** + * Debug logger. + * A special logger for debugging purposes. + * Provides debugging logging functions for RedBeanPHP. + * + * @file RedBeanPHP/Logger/RDefault/Debug.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Debug extends RDefault implements Logger +{ + /** + * @var integer + */ + private $strLen = 40; + + /** + * Writes a query for logging with all bindings / params filled + * in. + * + * @param string $newSql the query + * @param array $bindings the bindings to process (key-value pairs) + * + * @return string + */ + private function writeQuery( $newSql, $newBindings ) + { + //avoid str_replace collisions: slot1 and slot10 (issue 407). + uksort( $newBindings, function( $a, $b ) { + return ( strlen( $b ) - strlen( $a ) ); + } ); + + $newStr = $newSql; + foreach( $newBindings as $slot => $value ) { + if ( strpos( $slot, ':' ) === 0 ) { + $newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr ); + } + } + return $newStr; + } + + /** + * Fills in a value of a binding and truncates the + * resulting string if necessary. + * + * @param mixed $value bound value + * + * @return string + */ + protected function fillInValue( $value ) + { + if ( is_null( $value ) ) $value = 'NULL'; + + $value = strval( $value ); + if ( strlen( $value ) > ( $this->strLen ) ) { + $value = substr( $value, 0, ( $this->strLen ) ).'... '; + } + + if ( !\RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt( $value ) && $value !== 'NULL') { + $value = '\''.$value.'\''; + } + + return $value; + } + + /** + * Dependending on the current mode of operation, + * this method will either log and output to STDIN or + * just log. + * + * Depending on the value of constant PHP_SAPI this function + * will format output for console or HTML. + * + * @param string $str string to log or output and log + * + * @return void + */ + protected function output( $str ) + { + $this->logs[] = $str; + if ( !$this->mode ) { + $highlight = FALSE; + /* just a quick heuritsic to highlight schema changes */ + if ( strpos( $str, 'CREATE' ) === 0 + || strpos( $str, 'ALTER' ) === 0 + || strpos( $str, 'DROP' ) === 0) { + $highlight = TRUE; + } + if (PHP_SAPI === 'cli') { + if ($highlight) echo "\e[91m"; + echo $str, PHP_EOL; + echo "\e[39m"; + } else { + if ($highlight) { + echo "{$str}"; + } else { + echo $str; + } + echo '
'; + } + } + } + + /** + * Normalizes the slots in an SQL string. + * Replaces question mark slots with :slot1 :slot2 etc. + * + * @param string $sql sql to normalize + * + * @return string + */ + protected function normalizeSlots( $sql ) + { + $newSql = $sql; + $i = 0; + while(strpos($newSql, '?') !== FALSE ){ + $pos = strpos( $newSql, '?' ); + $slot = ':slot'.$i; + $begin = substr( $newSql, 0, $pos ); + $end = substr( $newSql, $pos+1 ); + if (PHP_SAPI === 'cli') { + $newSql = "{$begin}\e[32m{$slot}\e[39m{$end}"; + } else { + $newSql = "{$begin}$slot{$end}"; + } + $i ++; + } + return $newSql; + } + + /** + * Normalizes the bindings. + * Replaces numeric binding keys with :slot1 :slot2 etc. + * + * @param array $bindings bindings to normalize + * + * @return array + */ + protected function normalizeBindings( $bindings ) + { + $i = 0; + $newBindings = array(); + foreach( $bindings as $key => $value ) { + if ( is_numeric($key) ) { + $newKey = ':slot'.$i; + $newBindings[$newKey] = $value; + $i++; + } else { + $newBindings[$key] = $value; + } + } + return $newBindings; + } + + /** + * Logger method. + * + * Takes a number of arguments tries to create + * a proper debug log based on the available data. + * + * @return void + */ + public function log() + { + if ( func_num_args() < 1 ) return; + + $sql = func_get_arg( 0 ); + + if ( func_num_args() < 2) { + $bindings = array(); + } else { + $bindings = func_get_arg( 1 ); + } + + if ( !is_array( $bindings ) ) { + return $this->output( $sql ); + } + + $newSql = $this->normalizeSlots( $sql ); + $newBindings = $this->normalizeBindings( $bindings ); + $newStr = $this->writeQuery( $newSql, $newBindings ); + $this->output( $newStr ); + } + + /** + * Sets the max string length for the parameter output in + * SQL queries. Set this value to a reasonable number to + * keep you SQL queries readable. + * + * @param integer $len string length + * + * @return self + */ + public function setParamStringLength( $len = 20 ) + { + $this->strLen = max(0, $len); + return $this; + } +} +} + +namespace RedBeanPHP { + +/** + * Interface for database drivers. + * The Driver API conforms to the ADODB pseudo standard + * for database drivers. + * + * @file RedBeanPHP/Driver.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Driver +{ + /** + * Runs a query and fetches results as a multi dimensional array. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetAll( $sql, $bindings = array() ); + + /** + * Runs a query and fetches results as a column. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetCol( $sql, $bindings = array() ); + + /** + * Runs a query and returns results as a single cell. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetOne( $sql, $bindings = array() ); + + /** + * Runs a query and returns results as an associative array + * indexed by the first column. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetAssocRow( $sql, $bindings = array() ); + + /** + * Runs a query and returns a flat array containing the values of + * one row. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetRow( $sql, $bindings = array() ); + + /** + * Executes SQL code and allows key-value binding. + * This function allows you to provide an array with values to bind + * to query parameters. For instance you can bind values to question + * marks in the query. Each value in the array corresponds to the + * question mark in the query that matches the position of the value in the + * array. You can also bind values using explicit keys, for instance + * array(":key"=>123) will bind the integer 123 to the key :key in the + * SQL. This method has no return value. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array Affected Rows + */ + public function Execute( $sql, $bindings = array() ); + + /** + * Returns the latest insert ID if driver does support this + * feature. + * + * @return integer + */ + public function GetInsertID(); + + /** + * Returns the number of rows affected by the most recent query + * if the currently selected driver driver supports this feature. + * + * @return integer + */ + public function Affected_Rows(); + + /** + * Returns a cursor-like object from the database. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetCursor( $sql, $bindings = array() ); + + /** + * Toggles debug mode. In debug mode the driver will print all + * SQL to the screen together with some information about the + * results. All SQL code that passes through the driver will be + * passes on to the screen for inspection. + * This method has no return value. + * + * @param boolean $tf TRUE = debug mode ON + * @param Logger $customLogger + * + * @return void + */ + public function setDebugMode( $tf, $customLogger ); + + /** + * Starts a transaction. + * + * @return void + */ + public function CommitTrans(); + + /** + * Commits a transaction. + * + * @return void + */ + public function StartTrans(); + + /** + * Rolls back a transaction. + * + * @return void + */ + public function FailTrans(); + + /** + * Resets the internal Query Counter. + * + * @return self + */ + public function resetCounter(); + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public function getQueryCount(); +} +} + +namespace RedBeanPHP\Driver { + +use RedBeanPHP\Driver as Driver; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\PDOCompatible as PDOCompatible; +use RedBeanPHP\Cursor\PDOCursor as PDOCursor; + +/** + * PDO Driver + * This Driver implements the RedBean Driver API. + * for RedBeanPHP. This is the standard / default database driver + * for RedBeanPHP. + * + * @file RedBeanPHP/PDO.php + * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RPDO implements Driver +{ + /** + * @var integer + */ + protected $max; + + /** + * @var string + */ + protected $dsn; + + /** + * @var boolean + */ + protected $loggingEnabled = FALSE; + + /** + * @var Logger + */ + protected $logger = NULL; + + /** + * @var PDO + */ + protected $pdo; + + /** + * @var integer + */ + protected $affectedRows; + + /** + * @var integer + */ + protected $resultArray; + + /** + * @var array + */ + protected $connectInfo = array(); + + /** + * @var boolean + */ + protected $isConnected = FALSE; + + /** + * @var bool + */ + protected $flagUseStringOnlyBinding = FALSE; + + /** + * @var integer + */ + protected $queryCounter = 0; + + /** + * @var string + */ + protected $mysqlEncoding = ''; + + /** + * Binds parameters. This method binds parameters to a PDOStatement for + * Query Execution. This method binds parameters as NULL, INTEGER or STRING + * and supports both named keys and question mark keys. + * + * @param PDOStatement $statement PDO Statement instance + * @param array $bindings values that need to get bound to the statement + * + * @return void + */ + protected function bindParams( $statement, $bindings ) + { + foreach ( $bindings as $key => &$value ) { + if ( is_integer( $key ) ) { + if ( is_null( $value ) ) { + $statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { + $statement->bindParam( $key + 1, $value, \PDO::PARAM_INT ); + } else { + $statement->bindParam( $key + 1, $value, \PDO::PARAM_STR ); + } + } else { + if ( is_null( $value ) ) { + $statement->bindValue( $key, NULL, \PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { + $statement->bindParam( $key, $value, \PDO::PARAM_INT ); + } else { + $statement->bindParam( $key, $value, \PDO::PARAM_STR ); + } + } + } + } + + /** + * This method runs the actual SQL query and binds a list of parameters to the query. + * slots. The result of the query will be stored in the protected property + * $rs (always array). The number of rows affected (result of rowcount, if supported by database) + * is stored in protected property $affectedRows. If the debug flag is set + * this function will send debugging output to screen buffer. + * + * @param string $sql the SQL string to be send to database server + * @param array $bindings the values that need to get bound to the query slots + * @param array $options + * + * @return mixed + * @throws SQL + */ + protected function runQuery( $sql, $bindings, $options = array() ) + { + $this->connect(); + if ( $this->loggingEnabled && $this->logger ) { + $this->logger->log( $sql, $bindings ); + } + try { + if ( strpos( 'pgsql', $this->dsn ) === 0 ) { + if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) { + $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); + } else { + $statement = $this->pdo->prepare( $sql ); + } + } else { + $statement = $this->pdo->prepare( $sql ); + } + $this->bindParams( $statement, $bindings ); + $statement->execute(); + $this->queryCounter ++; + $this->affectedRows = $statement->rowCount(); + if ( $statement->columnCount() ) { + $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL; + if ( isset( $options['noFetch'] ) && $options['noFetch'] ) { + $this->resultArray = array(); + return $statement; + } + $this->resultArray = $statement->fetchAll( $fetchStyle ); + if ( $this->loggingEnabled && $this->logger ) { + $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' ); + } + } else { + $this->resultArray = array(); + } + } catch ( \PDOException $e ) { + //Unfortunately the code field is supposed to be int by default (php) + //So we need a property to convey the SQL State code. + $err = $e->getMessage(); + if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err ); + $exception = new SQL( $err, 0 ); + $exception->setSQLState( $e->getCode() ); + throw $exception; + } + } + + /** + * Try to fix MySQL character encoding problems. + * MySQL < 5.5 does not support proper 4 byte unicode but they + * seem to have added it with version 5.5 under a different label: utf8mb4. + * We try to select the best possible charset based on your version data. + * + * @return void + */ + protected function setEncoding() + { + $driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME ); + $version = floatval( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) ); + if ($driver === 'mysql') { + $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8'; + $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect + $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection + $this->mysqlEncoding = $encoding; + } + } + + /** + * Constructor. You may either specify dsn, user and password or + * just give an existing PDO connection. + * + * Examples: + * $driver = new RPDO($dsn, $user, $password); + * $driver = new RPDO($existingConnection); + * + * @param string|object $dsn database connection string + * @param string $user optional, usename to sign in + * @param string $pass optional, password for connection login + * + * @return void + */ + public function __construct( $dsn, $user = NULL, $pass = NULL ) + { + if ( is_object( $dsn ) ) { + $this->pdo = $dsn; + $this->isConnected = TRUE; + $this->setEncoding(); + $this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION ); + $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC ); + // make sure that the dsn at least contains the type + $this->dsn = $this->getDatabaseType(); + } else { + $this->dsn = $dsn; + $this->connectInfo = array( 'pass' => $pass, 'user' => $user ); + } + + //PHP 5.3 PDO SQLite has a bug with large numbers: + if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) { + $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis. + } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) { + $this->max = 2147483647; //bindParam in pdo_cubrid also fails... + } else { + $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause) + } + } + + /** + * Returns the best possible encoding for MySQL based on version data. + * + * @return string + */ + public function getMysqlEncoding() + { + return $this->mysqlEncoding; + } + + /** + * Whether to bind all parameters as strings. + * If set to TRUE this will cause all integers to be bound as STRINGS. + * This will NOT affect NULL values. + * + * @param boolean $yesNo pass TRUE to bind all parameters as strings. + * + * @return void + */ + public function setUseStringOnlyBinding( $yesNo ) + { + $this->flagUseStringOnlyBinding = (boolean) $yesNo; + } + + /** + * Sets the maximum value to be bound as integer, normally + * this value equals PHP's MAX INT constant, however sometimes + * PDO driver bindings cannot bind large integers as integers. + * This method allows you to manually set the max integer binding + * value to manage portability/compatibility issues among different + * PHP builds. This method will return the old value. + * + * @param integer $max maximum value for integer bindings + * + * @return integer + */ + public function setMaxIntBind( $max ) + { + if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' ); + $oldMax = $this->max; + $this->max = $max; + return $oldMax; + } + + /** + * Establishes a connection to the database using PHP\PDO + * functionality. If a connection has already been established this + * method will simply return directly. This method also turns on + * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as + * PDO-FETCH-ASSOC. + * + * @return void + */ + public function connect() + { + if ( $this->isConnected ) return; + try { + $user = $this->connectInfo['user']; + $pass = $this->connectInfo['pass']; + $this->pdo = new \PDO( + $this->dsn, + $user, + $pass + ); + $this->setEncoding(); + $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE ); + //cant pass these as argument to constructor, CUBRID driver does not understand... + $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION ); + $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC ); + $this->isConnected = TRUE; + } catch ( \PDOException $exception ) { + $matches = array(); + $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?'; + throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() ); + } + } + + /** + * Directly sets PDO instance into driver. + * This method might improve performance, however since the driver does + * not configure this instance terrible things may happen... only use + * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and + * you know your database server VERY WELL. + * + * @param PDO $pdo PDO instance + * + * @return void + */ + public function setPDO( \PDO $pdo ) { + $this->pdo = $pdo; + } + + /** + * @see Driver::GetAll + */ + public function GetAll( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings ); + return $this->resultArray; + } + + /** + * @see Driver::GetAssocRow + */ + public function GetAssocRow( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings, array( + 'fetchStyle' => \PDO::FETCH_ASSOC + ) + ); + return $this->resultArray; + } + + /** + * @see Driver::GetCol + */ + public function GetCol( $sql, $bindings = array() ) + { + $rows = $this->GetAll( $sql, $bindings ); + $cols = array(); + if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) { + foreach ( $rows as $row ) { + $cols[] = array_shift( $row ); + } + } + + return $cols; + } + + /** + * @see Driver::GetOne + */ + public function GetOne( $sql, $bindings = array() ) + { + $arr = $this->GetAll( $sql, $bindings ); + $res = NULL; + if ( !is_array( $arr ) ) return NULL; + if ( count( $arr ) === 0 ) return NULL; + $row1 = array_shift( $arr ); + if ( !is_array( $row1 ) ) return NULL; + if ( count( $row1 ) === 0 ) return NULL; + $col1 = array_shift( $row1 ); + return $col1; + } + + /** + * Alias for getOne(). + * Backward compatibility. + * + * @param string $sql SQL + * @param array $bindings bindings + * + * @return mixed + */ + public function GetCell( $sql, $bindings = array() ) + { + return $this->GetOne( $sql, $bindings ); + } + + /** + * @see Driver::GetRow + */ + public function GetRow( $sql, $bindings = array() ) + { + $arr = $this->GetAll( $sql, $bindings ); + return array_shift( $arr ); + } + + /** + * @see Driver::Excecute + */ + public function Execute( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings ); + return $this->affectedRows; + } + + /** + * @see Driver::GetInsertID + */ + public function GetInsertID() + { + $this->connect(); + + return (int) $this->pdo->lastInsertId(); + } + + /** + * @see Driver::GetCursor + */ + public function GetCursor( $sql, $bindings = array() ) + { + $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) ); + $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC ); + return $cursor; + } + + /** + * @see Driver::Affected_Rows + */ + public function Affected_Rows() + { + $this->connect(); + return (int) $this->affectedRows; + } + + /** + * Toggles debug mode. In debug mode the driver will print all + * SQL to the screen together with some information about the + * results. + * + * @param boolean $trueFalse turn on/off + * @param Logger $logger logger instance + * + * @return void + */ + public function setDebugMode( $tf, $logger = NULL ) + { + $this->connect(); + $this->loggingEnabled = (bool) $tf; + if ( $this->loggingEnabled and !$logger ) { + $logger = new RDefault(); + } + $this->setLogger( $logger ); + } + + /** + * Injects Logger object. + * Sets the logger instance you wish to use. + * + * @param Logger $logger the logger instance to be used for logging + * + * @return void + */ + public function setLogger( Logger $logger ) + { + $this->logger = $logger; + } + + /** + * Gets Logger object. + * Returns the currently active Logger instance. + * + * @return Logger + */ + public function getLogger() + { + return $this->logger; + } + + /** + * @see Driver::StartTrans + */ + public function StartTrans() + { + $this->connect(); + $this->pdo->beginTransaction(); + } + + /** + * @see Driver::CommitTrans + */ + public function CommitTrans() + { + $this->connect(); + $this->pdo->commit(); + } + + /** + * @see Driver::FailTrans + */ + public function FailTrans() + { + $this->connect(); + $this->pdo->rollback(); + } + + /** + * Returns the name of database driver for PDO. + * Uses the PDO attribute DRIVER NAME to obtain the name of the + * PDO driver. + * + * @return string + */ + public function getDatabaseType() + { + $this->connect(); + + return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); + } + + /** + * Returns the version number of the database. + * + * @return mixed + */ + public function getDatabaseVersion() + { + $this->connect(); + return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION ); + } + + /** + * Returns the underlying PHP PDO instance. + * + * @return PDO + */ + public function getPDO() + { + $this->connect(); + return $this->pdo; + } + + /** + * Closes database connection by destructing PDO. + * + * @return void + */ + public function close() + { + $this->pdo = NULL; + $this->isConnected = FALSE; + } + + /** + * Returns TRUE if the current PDO instance is connected. + * + * @return boolean + */ + public function isConnected() + { + return $this->isConnected && $this->pdo; + } + + /** + * Toggles logging, enables or disables logging. + * + * @param boolean $enable TRUE to enable logging + * + * @return self + */ + public function setEnableLogging( $enable ) + { + $this->loggingEnabled = (boolean) $enable; + } + + /** + * Resets the internal Query Counter. + * + * @return self + */ + public function resetCounter() + { + $this->queryCounter = 0; + return $this; + } + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public function getQueryCount() + { + return $this->queryCounter; + } + + /** + * Returns the maximum value treated as integer parameter + * binding. + * + * This method is mainly for testing purposes but it can help + * you solve some issues relating to integer bindings. + * + * @return integer + */ + public function getIntegerBindingMax() + { + return $this->max; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException as RedException; + +/* PHP 5.3 compatibility */ +if (interface_exists('\JsonSerializable')) { + /* We extend JsonSerializable to avoid namespace conflicts, + can't define interface with special namespace in PHP */ + interface Jsonable extends \JsonSerializable {}; +} else { + interface Jsonable {}; +} + +/** + * OODBBean (Object Oriented DataBase Bean). + * + * to exchange information with the database. A bean represents + * a single table row and offers generic services for interaction + * with databases systems as well as some meta-data. + * + * @file RedBeanPHP/OODBBean.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * @desc OODBBean represents a bean. RedBeanPHP uses beans + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable,Jsonable +{ + /** + * FUSE error modes. + */ + const C_ERR_IGNORE = FALSE; + const C_ERR_LOG = 1; + const C_ERR_NOTICE = 2; + const C_ERR_WARN = 3; + const C_ERR_EXCEPTION = 4; + const C_ERR_FUNC = 5; + const C_ERR_FATAL = 6; + + /** + * @var boolean + */ + protected static $errorHandlingFUSE = FALSE; + + /** + * @var callable|NULL + */ + protected static $errorHandler = NULL; + + /** + * @var array + */ + protected static $aliases = array(); + + /** + * @var boolean + */ + protected static $autoResolve = FALSE; + + /** + * This is where the real properties of the bean live. They are stored and retrieved + * by the magic getter and setter (__get and __set). + * + * @var array $properties + */ + protected $properties = array(); + + /** + * Here we keep the meta data of a bean. + * + * @var array + */ + protected $__info = array(); + + /** + * The BeanHelper allows the bean to access the toolbox objects to implement + * rich functionality, otherwise you would have to do everything with R or + * external objects. + * + * @var BeanHelper + */ + protected $beanHelper = NULL; + + /** + * @var null + */ + protected $fetchType = NULL; + + /** + * @var string + */ + protected $withSql = ''; + + /** + * @var array + */ + protected $withParams = array(); + + /** + * @var string + */ + protected $aliasName = NULL; + + /** + * @var string + */ + protected $via = NULL; + + /** + * @var boolean + */ + protected $noLoad = FALSE; + + /** + * @var boolean + */ + protected $all = FALSE; + + /** + * Sets the error mode for FUSE. + * What to do if a FUSE model method does not exist? + * You can set the following options: + * + * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL + * * OODBBean::C_ERR_LOG, logs the incident using error_log + * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE + * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING + * * OODBBean::C_ERR_EXCEPTION, throws an exception + * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) + * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR + * + * + * Custom handler method signature: handler( array ( + * 'message' => string + * 'bean' => OODBBean + * 'method' => string + * ) ) + * + * + * This method returns the old mode and handler as an array. + * + * @param integer $mode error handling mode + * @param callable|NULL $func custom handler + * + * @return array + */ + public static function setErrorHandlingFUSE($mode, $func = NULL) { + if ( + $mode !== self::C_ERR_IGNORE + && $mode !== self::C_ERR_LOG + && $mode !== self::C_ERR_NOTICE + && $mode !== self::C_ERR_WARN + && $mode !== self::C_ERR_EXCEPTION + && $mode !== self::C_ERR_FUNC + && $mode !== self::C_ERR_FATAL + ) throw new \Exception( 'Invalid error mode selected' ); + + if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) { + throw new \Exception( 'Invalid error handler' ); + } + + $old = array( self::$errorHandlingFUSE, self::$errorHandler ); + self::$errorHandlingFUSE = $mode; + if ( is_callable( $func ) ) { + self::$errorHandler = $func; + } else { + self::$errorHandler = NULL; + } + return $old; + } + + /** + * Sets global aliases. + * Registers a batch of aliases in one go. This works the same as + * fetchAs and setAutoResolve but explicitly. For instance if you register + * the alias 'cover' for 'page' a property containing a reference to a + * page bean called 'cover' will correctly return the page bean and not + * a (non-existant) cover bean. + * + * + * R::aliases( array( 'cover' => 'page' ) ); + * $book = R::dispense( 'book' ); + * $page = R::dispense( 'page' ); + * $book->cover = $page; + * R::store( $book ); + * $book = $book->fresh(); + * $cover = $book->cover; + * echo $cover->getMeta( 'type' ); //page + * + * + * The format of the aliases registration array is: + * + * {alias} => {actual type} + * + * In the example above we use: + * + * cover => page + * + * From that point on, every bean reference to a cover + * will return a 'page' bean. Note that with autoResolve this + * feature along with fetchAs() is no longer very important, although + * relying on explicit aliases can be a bit faster. + * + * @param array $list list of global aliases to use + * + * @return void + */ + public static function aliases( $list ) + { + self::$aliases = $list; + } + + /** + * Enables or disables auto-resolving fetch types. + * Auto-resolving aliased parent beans is convenient but can + * be slower and can create infinite recursion if you + * used aliases to break cyclic relations in your domain. + * + * @param boolean $automatic TRUE to enable automatic resolving aliased parents + * + * @return void + */ + public static function setAutoResolve( $automatic = TRUE ) + { + self::$autoResolve = (boolean) $automatic; + } + + /** + * Sets a meta property for all beans. This is a quicker way to set + * the meta properties for a collection of beans because this method + * can directly access the property arrays of the beans. + * This method returns the beans. + * + * @param array $beans beans to set the meta property of + * @param string $property property to set + * @param mixed $value value + * + * @return array + */ + public static function setMetaAll( $beans, $property, $value ) + { + foreach( $beans as $bean ) { + if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value; + } + + return $beans; + } + + /** + * Parses the join in the with-snippet. + * For instance: + * + * + * $author + * ->withCondition(' @joined.detail.title LIKE ? ') + * ->ownBookList; + * + * + * will automatically join 'detail' on book to + * access the title field. + * + * @note this feature requires Narrow Field Mode and Join Feature + * to be both activated (default). + * + * @param string $type the source type for the join + * + * @return string + */ + private function parseJoin( $type ) + { + $joinSql = ''; + $joins = array(); + if ( strpos($this->withSql, '@joined.' ) !== FALSE ) { + $writer = $this->beanHelper->getToolBox()->getWriter(); + $oldParts = $parts = explode( '@joined.', $this->withSql ); + array_shift( $parts ); + foreach($parts as $part) { + $explosion = explode( '.', $part ); + $joinInfo = array_shift( $explosion ); + //Dont join more than once.. + if ( !isset( $joins[$joinInfo] ) ) { + $joins[ $joinInfo ] = true; + $joinSql .= $writer->writeJoin( $type, $joinInfo, 'LEFT' ); + } + } + $this->withSql = implode( '', $oldParts ); + $joinSql .= ' WHERE '; + } + return $joinSql; + } + + /** + * Internal method. + * Obtains a shared list for a certain type. + * + * @param string $type the name of the list you want to retrieve. + * + * @return array + */ + private function getSharedList( $type, $redbean, $toolbox ) + { + $writer = $toolbox->getWriter(); + + if ( $this->via ) { + $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); + if ( $oldName !== $this->via ) { + //set the new renaming rule + $writer->renameAssocTable( $oldName, $this->via ); + } + $this->via = NULL; + } + + $beans = array(); + if ($this->getID()) { + $type = $this->beau( $type ); + $assocManager = $redbean->getAssociationManager(); + $beans = $assocManager->related( $this, $type, $this->withSql, $this->withParams ); + } + + $this->withSql = ''; + $this->withParams = array(); + + return $beans; + } + + /** + * Internal method. + * Obtains the own list of a certain type. + * + * @param string $type name of the list you want to retrieve + * @param OODB $oodb The RB OODB object database instance + * + * @return array + */ + private function getOwnList( $type, $redbean ) + { + $type = $this->beau( $type ); + + if ( $this->aliasName ) { + $parentField = $this->aliasName; + $myFieldLink = $parentField . '_id'; + + $this->__info['sys.alias.' . $type] = $this->aliasName; + + $this->aliasName = NULL; + } else { + $parentField = $this->__info['type']; + $myFieldLink = $parentField . '_id'; + } + + $beans = array(); + + if ( $this->getID() ) { + + $firstKey = NULL; + if ( count( $this->withParams ) > 0 ) { + reset( $this->withParams ); + + $firstKey = key( $this->withParams ); + } + + $joinSql = $this->parseJoin( $type ); + + if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { + $bindings = $this->withParams; + $bindings[':slot0'] = $this->getID(); + + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); + } else { + $bindings = array_merge( array( $this->getID() ), $this->withParams ); + + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); + } + } + + $this->withSql = ''; + $this->withParams = array(); + + foreach ( $beans as $beanFromList ) { + $beanFromList->__info['sys.parentcache.' . $parentField] = $this; + } + + return $beans; + } + + /** + * Initializes a bean. Used by OODB for dispensing beans. + * It is not recommended to use this method to initialize beans. Instead + * use the OODB object to dispense new beans. You can use this method + * if you build your own bean dispensing mechanism. + * + * @param string $type type of the new bean + * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model + * + * @return void + */ + public function initializeForDispense( $type, BeanHelper $beanhelper ) + { + $this->beanHelper = $beanhelper; + $this->__info['type'] = $type; + $this->__info['sys.id'] = 'id'; + $this->__info['sys.orig'] = array( 'id' => 0 ); + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + $this->properties['id'] = 0; + } + + /** + * Sets the Bean Helper. Normally the Bean Helper is set by OODB. + * Here you can change the Bean Helper. The Bean Helper is an object + * providing access to a toolbox for the bean necessary to retrieve + * nested beans (bean lists: ownBean, sharedBean) without the need to + * rely on static calls to the facade (or make this class dep. on OODB). + * + * @param BeanHelper $helper helper to use for this bean + * + * @return void + */ + public function setBeanHelper( BeanHelper $helper ) + { + $this->beanHelper = $helper; + } + + /** + * Returns an ArrayIterator so you can treat the bean like + * an array with the properties container as its contents. + * This method is meant for PHP and allows you to access beans as if + * they were arrays, i.e. using array notation: + * + * $bean[$key] = $value; + * + * Note that not all PHP functions work with the array interface. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator( $this->properties ); + } + + /** + * Imports all values from an associative array $array. Chainable. + * This method imports the values in the first argument as bean + * propery and value pairs. Use the second parameter to provide a + * selection. If a selection array is passed, only the entries + * having keys mentioned in the selection array will be imported. + * Set the third parameter to TRUE to preserve spaces in selection keys. + * + * @param array $array what you want to import + * @param string|array $selection selection of values + * @param boolean $notrim if TRUE selection keys will NOT be trimmed + * + * @return OODBBean + */ + public function import( $array, $selection = FALSE, $notrim = FALSE ) + { + if ( is_string( $selection ) ) { + $selection = explode( ',', $selection ); + } + + if ( !$notrim && is_array( $selection ) ) { + foreach ( $selection as $key => $selected ) { + $selection[$key] = trim( $selected ); + } + } + + foreach ( $array as $key => $value ) { + if ( $key != '__info' ) { + if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) { + if ( is_array($value ) ) { + if ( isset( $value['_type'] ) ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] ); + unset( $value['_type'] ); + $bean->import($value); + $this->$key = $bean; + } else { + $listBeans = array(); + foreach( $value as $listKey => $listItem ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] ); + unset( $listItem['_type'] ); + $bean->import($listItem); + $list = &$this->$key; + $list[ $listKey ] = $bean; + } + } + } else { + $this->$key = $value; + } + } + } + } + + return $this; + } + + /** + * Fast way to import a row. + * Does not perform any checks. + * + * @param array $row a database row + * + * @return self + */ + public function importRow( $row ) + { + $this->properties = $row; + $this->__info['sys.orig'] = $row; + $this->__info['changed'] = FALSE; + return $this; + } + + /** + * Imports data from another bean. Chainable. + * Copies the properties from the source bean to the internal + * property list. + * + * @param OODBBean $sourceBean the source bean to take properties from + * + * @return OODBBean + */ + public function importFrom( OODBBean $sourceBean ) + { + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + $this->properties = $sourceBean->properties; + + return $this; + } + + /** + * Injects the properties of another bean but keeps the original ID. + * Just like import() but keeps the original ID. + * Chainable. + * + * @param OODBBean $otherBean the bean whose properties you would like to copy + * + * @return OODBBean + */ + public function inject( OODBBean $otherBean ) + { + $myID = $this->properties['id']; + + $this->import( $otherBean->export( FALSE, FALSE, TRUE ) ); + + $this->id = $myID; + + return $this; + } + + /** + * Exports the bean as an array. + * This function exports the contents of a bean to an array and returns + * the resulting array. + * + * @param boolean $meta set to TRUE if you want to export meta data as well + * @param boolean $parents set to TRUE if you want to export parents as well + * @param boolean $onlyMe set to TRUE if you want to export only this bean + * @param array $filters optional whitelist for export + * + * @return array + */ + public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() ) + { + $arr = array(); + + if ( $parents ) { + foreach ( $this as $key => $value ) { + if ( substr( $key, -3 ) != '_id' ) continue; + + $prop = substr( $key, 0, strlen( $key ) - 3 ); + $this->$prop; + } + } + + $hasFilters = is_array( $filters ) && count( $filters ); + + foreach ( $this as $key => $value ) { + if ( !$onlyMe && is_array( $value ) ) { + $vn = array(); + + foreach ( $value as $i => $b ) { + if ( !( $b instanceof OODBBean ) ) continue; + $vn[] = $b->export( $meta, FALSE, FALSE, $filters ); + $value = $vn; + } + } elseif ( $value instanceof OODBBean ) { + if ( $hasFilters ) { + if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue; + } + + $value = $value->export( $meta, $parents, FALSE, $filters ); + } + + $arr[$key] = $value; + } + + if ( $meta ) { + $arr['__info'] = $this->__info; + } + + return $arr; + } + + /** + * Implements isset() function for use as an array. + * + * @param string $property name of the property you want to check + * + * @return boolean + */ + public function __isset( $property ) + { + $property = $this->beau( $property ); + + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + } + return isset( $this->properties[$property] ); + } + + /** + * Returns the ID of the bean no matter what the ID field is. + * + * @return string|null + */ + public function getID() + { + return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL; + } + + /** + * Unsets a property of a bean. + * Magic method, gets called implicitly when performing the unset() operation + * on a bean property. + * + * @param string $property property to unset + * + * @return void + */ + public function __unset( $property ) + { + $property = $this->beau( $property ); + + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + } + + unset( $this->properties[$property] ); + + $shadowKey = 'sys.shadow.'.$property; + if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] ); + + //also clear modifiers + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return; + } + + /** + * Adds WHERE clause conditions to ownList retrieval. + * For instance to get the pages that belong to a book you would + * issue the following command: $book->ownPage + * However, to order these pages by number use: + * + * + * $book->with(' ORDER BY `number` ASC ')->ownPage + * + * + * the additional SQL snippet will be merged into the final + * query. + * + * @param string $sql SQL to be added to retrieval query. + * @param array $bindings array with parameters to bind to SQL snippet + * + * @return OODBBean + */ + public function with( $sql, $bindings = array() ) + { + $this->withSql = $sql; + $this->withParams = $bindings; + return $this; + } + + /** + * Just like with(). Except that this method prepends the SQL query snippet + * with AND which makes it slightly more comfortable to use a conditional + * SQL snippet. For instance to filter an own-list with pages (belonging to + * a book) on specific chapters you can use: + * + * $book->withCondition(' chapter = 3 ')->ownPage + * + * This will return in the own list only the pages having 'chapter == 3'. + * + * @param string $sql SQL to be added to retrieval query (prefixed by AND) + * @param array $bindings array with parameters to bind to SQL snippet + * + * @return OODBBean + */ + public function withCondition( $sql, $bindings = array() ) + { + $this->withSql = ' AND ' . $sql; + $this->withParams = $bindings; + return $this; + } + + /** + * Tells the bean to (re)load the following list without any + * conditions. If you have an ownList or sharedList with a + * condition you can use this method to reload the entire list. + * + * Usage: + * + * + * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3 + * $bean->all()->ownPage; //Reload all pages + * + * + * @return self + */ + public function all() + { + $this->all = TRUE; + return $this; + } + + /** + * Tells the bean to only access the list but not load + * its contents. Use this if you only want to add something to a list + * and you have no interest in retrieving its contents from the database. + * + * @return self + */ + public function noLoad() + { + $this->noLoad = TRUE; + return $this; + } + + /** + * Prepares an own-list to use an alias. This is best explained using + * an example. Imagine a project and a person. The project always involves + * two persons: a teacher and a student. The person beans have been aliased in this + * case, so to the project has a teacher_id pointing to a person, and a student_id + * also pointing to a person. Given a project, we obtain the teacher like this: + * + * + * $project->fetchAs('person')->teacher; + * + * + * Now, if we want all projects of a teacher we cant say: + * + * + * $teacher->ownProject + * + * + * because the $teacher is a bean of type 'person' and no project has been + * assigned to a person. Instead we use the alias() method like this: + * + * + * $teacher->alias('teacher')->ownProject + * + * + * now we get the projects associated with the person bean aliased as + * a teacher. + * + * @param string $aliasName the alias name to use + * + * @return OODBBean + */ + public function alias( $aliasName ) + { + $this->aliasName = $this->beau( $aliasName ); + + return $this; + } + + /** + * Returns properties of bean as an array. + * This method returns the raw internal property list of the + * bean. Only use this method for optimization purposes. Otherwise + * use the export() method to export bean data to arrays. + * + * @return array + */ + public function getProperties() + { + return $this->properties; + } + + /** + * Returns properties of bean as an array. + * This method returns the raw internal property list of the + * bean. Only use this method for optimization purposes. Otherwise + * use the export() method to export bean data to arrays. + * This method returns an array with the properties array and + * the type (string). + * + * @return array + */ + public function getPropertiesAndType() + { + return array( $this->properties, $this->__info['type'] ); + } + + /** + * Turns a camelcase property name into an underscored property name. + * + * Examples: + * + * * oneACLRoute -> one_acl_route + * * camelCase -> camel_case + * + * Also caches the result to improve performance. + * + * @param string $property property to un-beautify + * + * @return string + */ + public function beau( $property ) + { + static $beautifulColumns = array(); + + if ( ctype_lower( $property ) ) return $property; + + if ( + ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) + || ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) + || ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) + ) { + + $property = preg_replace( '/List$/', '', $property ); + return $property; + } + + if ( !isset( $beautifulColumns[$property] ) ) { + $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property ); + } + + return $beautifulColumns[$property]; + } + + /** + * Clears all modifiers. + * + * @return self + */ + public function clearModifiers() + { + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + return $this; + } + + /** + * Determines whether a list is opened in exclusive mode or not. + * If a list has been opened in exclusive mode this method will return TRUE, + * othwerwise it will return FALSE. + * + * @param string $listName name of the list to check + * + * @return boolean + */ + public function isListInExclusiveMode( $listName ) + { + $listName = $this->beau( $listName ); + + if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) { + $listName = substr($listName, 1); + } + + $listName = lcfirst( substr( $listName, 3 ) ); + + return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] ); + } + + /** + * Magic Getter. Gets the value for a specific property in the bean. + * If the property does not exist this getter will make sure no error + * occurs. This is because RedBean allows you to query (probe) for + * properties. If the property can not be found this method will + * return NULL instead. + * + * @param string $property name of the property you wish to obtain the value of + * + * @return mixed + */ + public function &__get( $property ) + { + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; + + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } + } + + $fieldLink = $property . '_id'; + $exists = isset( $this->properties[$property] ); + + //If not exists and no field link and no list, bail out. + if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) { + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + $NULL = NULL; + return $NULL; + } + + $hasAlias = (!is_null($this->aliasName)); + $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $hasAll = (boolean) ($this->all); + + //If exists and no list or exits and list not changed, bail out. + if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) { + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + return $this->properties[$property]; + } + + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + + if ( isset( $this->$fieldLink ) ) { + $this->__info['tainted'] = TRUE; + + if ( isset( $this->__info["sys.parentcache.$property"] ) ) { + $bean = $this->__info["sys.parentcache.$property"]; + } else { + if ( isset( self::$aliases[$property] ) ) { + $type = self::$aliases[$property]; + } elseif ( $this->fetchType ) { + $type = $this->fetchType; + $this->fetchType = NULL; + } else { + $type = $property; + } + $bean = NULL; + if ( !is_null( $this->properties[$fieldLink] ) ) { + $bean = $redbean->load( $type, $this->properties[$fieldLink] ); + //If the IDs dont match, we failed to load, so try autoresolv in that case... + if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) { + $type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property ); + if ( !is_null( $type) ) { + $bean = $redbean->load( $type, $this->properties[$fieldLink] ); + $this->__info["sys.autoresolved.{$property}"] = $type; + } + } + } + } + + $this->properties[$property] = $bean; + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return $this->properties[$property]; + + } + //Implicit: elseif ( $isOwn || $isShared ) { + if ( $this->noLoad ) { + $beans = array(); + } elseif ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); + } + + $this->properties[$property] = $beans; + $this->__info["sys.shadow.$property"] = $beans; + $this->__info['tainted'] = TRUE; + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return $this->properties[$property]; + } + + /** + * Magic Setter. Sets the value for a specific property. + * This setter acts as a hook for OODB to mark beans as tainted. + * The tainted meta property can be retrieved using getMeta("tainted"). + * The tainted meta property indicates whether a bean has been modified and + * can be used in various caching mechanisms. + * + * @param string $property name of the property you wish to assign a value to + * @param mixed $value the value you want to assign + * + * @return void + */ + public function __set( $property, $value ) + { + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; + + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } + } + + $hasAlias = (!is_null($this->aliasName)); + $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $exists = isset( $this->properties[$property] ); + $fieldLink = $property . '_id'; + $isFieldLink = (($pos = strrpos($property, '_id')) !== FALSE) && array_key_exists( ($fieldName = substr($property, 0, $pos)), $this->properties ); + + + if ( ($isOwn || $isShared) && (!$exists || $hasSQL || $differentAlias) ) { + + if ( !$this->noLoad ) { + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + if ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); + } + $this->__info["sys.shadow.$property"] = $beans; + } + } + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + + if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) { + if ( is_null( $value ) || $value === FALSE ) { + + unset( $this->properties[ $property ]); + $this->properties[ $fieldLink ] = NULL; + + return; + } else { + throw new RedException( 'Cannot cast to bean.' ); + } + } + + if ( $isFieldLink ){ + unset( $this->properties[ $fieldName ]); + $this->properties[ $property ] = NULL; + } + + + if ( $value === FALSE ) { + $value = '0'; + } elseif ( $value === TRUE ) { + $value = '1'; + } elseif ( $value instanceof \DateTime ) { + $value = $value->format( 'Y-m-d H:i:s' ); + } + + $this->properties[$property] = $value; + } + + /** + * Sets a property directly, for internal use only. + * + * @param string $property property + * @param mixed $value value + * @param boolean $updateShadow whether you want to update the shadow + * @param boolean $taint whether you want to mark the bean as tainted + * + * @return void + */ + public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE ) + { + $this->properties[$property] = $value; + + if ( $updateShadow ) { + $this->__info['sys.shadow.' . $property] = $value; + } + + if ( $taint ) { + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + } + } + + /** + * Returns the value of a meta property. A meta property + * contains additional information about the bean object that will not + * be stored in the database. Meta information is used to instruct + * RedBeanPHP as well as other systems how to deal with the bean. + * If the property cannot be found this getter will return NULL instead. + * + * Example: + * + * + * $bean->setMeta( 'flush-cache', TRUE ); + * + * + * RedBeanPHP also stores meta data in beans, this meta data uses + * keys prefixed with 'sys.' (system). + * + * @param string $path path to property in meta data + * @param mixed $default default value + * + * @return mixed + */ + public function getMeta( $path, $default = NULL ) + { + return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default; + } + + /** + * Gets and unsets a meta property. + * Moves a meta property out of the bean. + * This is a short-cut method that can be used instead + * of combining a get/unset. + * + * @param string $path path to property in meta data + * @param mixed $default default value + * + * @return mixed + */ + public function moveMeta( $path, $value = NULL ) + { + if ( isset( $this->__info[$path] ) ) { + $value = $this->__info[ $path ]; + unset( $this->__info[ $path ] ); + } + return $value; + } + + /** + * Stores a value in the specified Meta information property. + * The first argument should be the key to store the value under, + * the second argument should be the value. It is common to use + * a path-like notation for meta data in RedBeanPHP like: + * 'my.meta.data', however the dots are purely for readability, the + * meta data methods do not store nested structures or hierarchies. + * + * @param string $path path / key to store value under + * @param mixed $value value to store in bean (not in database) as meta data + * + * @return OODBBean + */ + public function setMeta( $path, $value ) + { + $this->__info[$path] = $value; + + return $this; + } + + /** + * Copies the meta information of the specified bean + * This is a convenience method to enable you to + * exchange meta information easily. + * + * @param OODBBean $bean bean to copy meta data of + * + * @return OODBBean + */ + public function copyMetaFrom( OODBBean $bean ) + { + $this->__info = $bean->__info; + + return $this; + } + + /** + * Sends the call to the registered model. + * This method can also be used to override bean behaviour. + * In that case you don't want an error or exception to be triggered + * if the method does not exist in the model (because it's optional). + * Unfortunately we cannot add an extra argument to __call() for this + * because the signature is fixed. Another option would be to set + * a special flag ( i.e. $this->isOptionalCall ) but that would + * cause additional complexity because we have to deal with extra temporary state. + * So, instead I allowed the method name to be prefixed with '@', in practice + * nobody creates methods like that - however the '@' symbol in PHP is widely known + * to suppress error handling, so we can reuse the semantics of this symbol. + * If a method name gets passed starting with '@' the overrideDontFail variable + * will be set to TRUE and the '@' will be stripped from the function name before + * attempting to invoke the method on the model. This way, we have all the + * logic in one place. + * + * @param string $method name of the method + * @param array $args argument list + * + * @return mixed + */ + public function __call( $method, $args ) + { + $overrideDontFail = FALSE; + if ( strpos( $method, '@' ) === 0 ) { + $method = substr( $method, 1 ); + $overrideDontFail = TRUE; + } + + if ( !isset( $this->__info['model'] ) ) { + $model = $this->beanHelper->getModelForBean( $this ); + + if ( !$model ) { + return NULL; + } + + $this->__info['model'] = $model; + } + if ( !method_exists( $this->__info['model'], $method ) ) { + + if ( self::$errorHandlingFUSE === FALSE || $overrideDontFail ) { + return NULL; + } + + if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) { + return NULL; + } + + $message = "FUSE: method does not exist in model: $method"; + if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) { + error_log( $message ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) { + trigger_error( $message, E_USER_NOTICE ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) { + trigger_error( $message, E_USER_WARNING ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) { + throw new \Exception( $message ); + } elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) { + $func = self::$errorHandler; + return $func(array( + 'message' => $message, + 'method' => $method, + 'args' => $args, + 'bean' => $this + )); + } + trigger_error( $message, E_USER_ERROR ); + return NULL; + } + + return call_user_func_array( array( $this->__info['model'], $method ), $args ); + } + + /** + * Implementation of __toString Method + * Routes call to Model. If the model implements a __toString() method this + * method will be called and the result will be returned. In case of an + * echo-statement this result will be printed. If the model does not + * implement a __toString method, this method will return a JSON + * representation of the current bean. + * + * @return string + */ + public function __toString() + { + $string = $this->__call( '@__toString', array() ); + + if ( $string === NULL ) { + $list = array(); + foreach($this->properties as $property => $value) { + if (is_scalar($value)) { + $list[$property] = $value; + } + } + return json_encode( $list ); + } else { + return $string; + } + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Call gets routed to __set. + * + * @param mixed $offset offset string + * @param mixed $value value + * + * @return void + */ + public function offsetSet( $offset, $value ) + { + $this->__set( $offset, $value ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return boolean + */ + public function offsetExists( $offset ) + { + return $this->__isset( $offset ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Unsets a value from the array/bean. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return void + */ + public function offsetUnset( $offset ) + { + $this->__unset( $offset ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Returns value of a property. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return mixed + */ + public function &offsetGet( $offset ) + { + return $this->__get( $offset ); + } + + /** + * Chainable method to cast a certain ID to a bean; for instance: + * $person = $club->fetchAs('person')->member; + * This will load a bean of type person using member_id as ID. + * + * @param string $type preferred fetch type + * + * @return OODBBean + */ + public function fetchAs( $type ) + { + $this->fetchType = $type; + + return $this; + } + + /** + * For polymorphic bean relations. + * Same as fetchAs but uses a column instead of a direct value. + * + * @param string $field field name to use for mapping + * + * @return OODBBean + */ + public function poly( $field ) + { + return $this->fetchAs( $this->$field ); + } + + /** + * Traverses a bean property with the specified function. + * Recursively iterates through the property invoking the + * function for each bean along the way passing the bean to it. + * + * Can be used together with with, withCondition, alias and fetchAs. + * + * @param string $property property + * @param callable $function function + * @param integer $maxDepth maximum depth for traversal + * + * @return OODBBean + * @throws RedException + */ + public function traverse( $property, $function, $maxDepth = NULL ) + { + $this->via = NULL; + if ( strpos( $property, 'shared' ) !== FALSE ) { + throw new RedException( 'Traverse only works with (x)own-lists.' ); + } + + if ( !is_null( $maxDepth ) ) { + if ( !$maxDepth-- ) return $this; + } + + $oldFetchType = $this->fetchType; + $oldAliasName = $this->aliasName; + $oldWith = $this->withSql; + $oldBindings = $this->withParams; + + $beans = $this->$property; + + if ( $beans === NULL ) return $this; + + if ( !is_array( $beans ) ) $beans = array( $beans ); + + foreach( $beans as $bean ) { + /** @var OODBBean $bean */ + $function( $bean ); + + $bean->fetchType = $oldFetchType; + $bean->aliasName = $oldAliasName; + $bean->withSql = $oldWith; + $bean->withParams = $oldBindings; + + $bean->traverse( $property, $function, $maxDepth ); + } + + return $this; + } + + /** + * Implementation of Countable interface. Makes it possible to use + * count() function on a bean. + * + * @return integer + */ + public function count() + { + return count( $this->properties ); + } + + /** + * Checks whether a bean is empty or not. + * A bean is empty if it has no other properties than the id field OR + * if all the other property are empty(). + * + * @return boolean + */ + public function isEmpty() + { + $empty = TRUE; + foreach ( $this->properties as $key => $value ) { + if ( $key == 'id' ) { + continue; + } + if ( !empty( $value ) ) { + $empty = FALSE; + } + } + + return $empty; + } + + /** + * Chainable setter. + * + * @param string $property the property of the bean + * @param mixed $value the value you want to set + * + * @return OODBBean + */ + public function setAttr( $property, $value ) + { + $this->$property = $value; + + return $this; + } + + /** + * Comfort method. + * Unsets all properties in array. + * + * @param array $properties properties you want to unset. + * + * @return OODBBean + */ + public function unsetAll( $properties ) + { + foreach ( $properties as $prop ) { + if ( isset( $this->properties[$prop] ) ) { + unset( $this->properties[$prop] ); + } + } + + return $this; + } + + /** + * Returns original (old) value of a property. + * You can use this method to see what has changed in a + * bean. + * + * @param string $property name of the property you want the old value of + * + * @return mixed + */ + public function old( $property ) + { + $old = $this->getMeta( 'sys.orig', array() ); + + if ( array_key_exists( $property, $old ) ) { + return $old[$property]; + } + + return NULL; + } + + /** + * Convenience method. + * Returns TRUE if the bean has been changed, or FALSE otherwise. + * Same as $bean->getMeta('tainted'); + * Note that a bean becomes tainted as soon as you retrieve a list from + * the bean. This is because the bean lists are arrays and the bean cannot + * determine whether you have made modifications to a list so RedBeanPHP + * will mark the whole bean as tainted. + * + * @return boolean + */ + public function isTainted() + { + return $this->getMeta( 'tainted' ); + } + + /** + * Returns TRUE if the value of a certain property of the bean has been changed and + * FALSE otherwise. + * + * Note that this method will return TRUE if applied to a loaded list. + * Also note that this method keeps track of the bean's history regardless whether + * it has been stored or not. Storing a bean does not undo it's history, + * to clean the history of a bean use: clearHistory(). + * + * @param string $property name of the property you want the change-status of + * + * @return boolean + */ + public function hasChanged( $property ) + { + return ( array_key_exists( $property, $this->properties ) ) ? + $this->old( $property ) != $this->properties[$property] : FALSE; + } + + /** + * Returns TRUE if the specified list exists, has been loaded and has been changed: + * beans have been added or deleted. This method will not tell you anything about + * the state of the beans in the list. + * + * @param string $property name of the list to check + * + * @return boolean + */ + public function hasListChanged( $property ) + { + if ( !array_key_exists( $property, $this->properties ) ) return FALSE; + $diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] ); + if ( count( $diffAdded ) ) return TRUE; + $diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] ); + if ( count( $diffMissing ) ) return TRUE; + return FALSE; + } + + /** + * Clears (syncs) the history of the bean. + * Resets all shadow values of the bean to their current value. + * + * @return self + */ + public function clearHistory() + { + $this->__info['sys.orig'] = array(); + foreach( $this->properties as $key => $value ) { + if ( is_scalar($value) ) { + $this->__info['sys.orig'][$key] = $value; + } else { + $this->__info['sys.shadow.'.$key] = $value; + } + } + return $this; + } + + /** + * Creates a N-M relation by linking an intermediate bean. + * This method can be used to quickly connect beans using indirect + * relations. For instance, given an album and a song you can connect the two + * using a track with a number like this: + * + * Usage: + * + * + * $album->link('track', array('number'=>1))->song = $song; + * + * + * or: + * + * + * $album->link($trackBean)->song = $song; + * + * + * What this method does is adding the link bean to the own-list, in this case + * ownTrack. If the first argument is a string and the second is an array or + * a JSON string then the linking bean gets dispensed on-the-fly as seen in + * example #1. After preparing the linking bean, the bean is returned thus + * allowing the chained setter: ->song = $song. + * + * @param string|OODBBean $type type of bean to dispense or the full bean + * @param string|array $qualification JSON string or array (optional) + * + * @return OODBBean + */ + public function link( $typeOrBean, $qualification = array() ) + { + if ( is_string( $typeOrBean ) ) { + + $typeOrBean = AQueryWriter::camelsSnake( $typeOrBean ); + + $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean ); + + if ( is_string( $qualification ) ) { + $data = json_decode( $qualification, TRUE ); + } else { + $data = $qualification; + } + + foreach ( $data as $key => $value ) { + $bean->$key = $value; + } + } else { + $bean = $typeOrBean; + } + + $list = 'own' . ucfirst( $bean->getMeta( 'type' ) ); + + array_push( $this->$list, $bean ); + + return $bean; + } + + /** + * Returns a bean of the given type with the same ID of as + * the current one. This only happens in a one-to-one relation. + * This is as far as support for 1-1 goes in RedBeanPHP. This + * method will only return a reference to the bean, changing it + * and storing the bean will not update the related one-bean. + * + * @return OODBBean + */ + public function one( $type ) { + return $this->beanHelper->getToolBox()->getRedBean()->load( $type, $this->id ); + } + + /** + * Returns the same bean freshly loaded from the database. + * + * @return OODBBean + */ + public function fresh() + { + return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] ); + } + + /** + * Registers a association renaming globally. + * + * @param string $via type you wish to use for shared lists + * + * @return OODBBean + */ + public function via( $via ) + { + $this->via = AQueryWriter::camelsSnake( $via ); + + return $this; + } + + /** + * Counts all own beans of type $type. + * Also works with alias(), with() and withCondition(). + * + * @param string $type the type of bean you want to count + * + * @return integer + */ + public function countOwn( $type ) + { + $type = $this->beau( $type ); + + if ( $this->aliasName ) { + $myFieldLink = $this->aliasName . '_id'; + + $this->aliasName = NULL; + } else { + $myFieldLink = $this->__info['type'] . '_id'; + } + + $count = 0; + + if ( $this->getID() ) { + + $firstKey = NULL; + if ( count( $this->withParams ) > 0 ) { + reset( $this->withParams ); + $firstKey = key( $this->withParams ); + } + + $joinSql = $this->parseJoin( $type ); + + if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { + $bindings = $this->withParams; + $bindings[':slot0'] = $this->getID(); + $count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); + } else { + $bindings = array_merge( array( $this->getID() ), $this->withParams ); + $count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); + } + + } + + $this->clearModifiers(); + return (int) $count; + } + + /** + * Counts all shared beans of type $type. + * Also works with via(), with() and withCondition(). + * + * @param string $type type of bean you wish to count + * + * @return integer + */ + public function countShared( $type ) + { + $toolbox = $this->beanHelper->getToolbox(); + $redbean = $toolbox->getRedBean(); + $writer = $toolbox->getWriter(); + + if ( $this->via ) { + $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); + + if ( $oldName !== $this->via ) { + //set the new renaming rule + $writer->renameAssocTable( $oldName, $this->via ); + $this->via = NULL; + } + } + + $type = $this->beau( $type ); + $count = 0; + + if ( $this->getID() ) { + $count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams ); + } + + $this->clearModifiers(); + return (integer) $count; + } + + /** + * Iterates through the specified own-list and + * fetches all properties (with their type) and + * returns the references. + * Use this method to quickly load indirectly related + * beans in an own-list. Whenever you cannot use a + * shared-list this method offers the same convenience + * by aggregating the parent beans of all children in + * the specified own-list. + * + * Example: + * + * + * $quest->aggr( 'xownQuestTarget', 'target', 'quest' ); + * + * + * Loads (in batch) and returns references to all + * quest beans residing in the $questTarget->target properties + * of each element in the xownQuestTargetList. + * + * @param string $list the list you wish to process + * @param string $property the property to load + * @param string $type the type of bean residing in this property (optional) + * + * @return array + */ + public function &aggr( $list, $property, $type = NULL ) + { + $this->via = NULL; + $ids = $beanIndex = $references = array(); + + if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.'); + if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.'); + if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.'); + + if ( is_null( $type ) ) $type = $property; + + foreach( $this->$list as $bean ) { + $field = $property . '_id'; + if ( isset( $bean->$field) ) { + $ids[] = $bean->$field; + $beanIndex[$bean->$field] = $bean; + } + } + + $beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids ); + + //now preload the beans as well + foreach( $beans as $bean ) { + $beanIndex[$bean->id]->setProperty( $property, $bean ); + } + + foreach( $beanIndex as $indexedBean ) { + $references[] = $indexedBean->$property; + } + + return $references; + } + + /** + * Tests whether the database identities of two beans are equal. + * + * @param OODBBean $bean other bean + * + * @return boolean + */ + public function equals(OODBBean $bean) + { + return (bool) ( + ( (string) $this->properties['id'] === (string) $bean->properties['id'] ) + && ( (string) $this->__info['type'] === (string) $bean->__info['type'] ) + ); + } + + /** + * Magic method jsonSerialize, implementation for the \JsonSerializable interface, + * this method gets called by json_encode and facilitates a better JSON representation + * of the bean. Exports the bean on JSON serialization, for the JSON fans. + * + * @see http://php.net/manual/en/class.jsonserializable.php + * + * @return array + */ + public function jsonSerialize() + { + return $this->export(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Observer as Observer; + +/** + * Observable + * Base class for Observables + * + * @file RedBeanPHP/Observable.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Observable { //bracket must be here - otherwise coverage software does not understand. + + /** + * @var array + */ + private $observers = array(); + + /** + * Implementation of the Observer Pattern. + * Adds an event listener to the observable object. + * First argument should be the name of the event you wish to listen for. + * Second argument should be the object that wants to be notified in case + * the event occurs. + * + * @param string $eventname event identifier + * @param Observer $observer observer instance + * + * @return void + */ + public function addEventListener( $eventname, Observer $observer ) + { + if ( !isset( $this->observers[$eventname] ) ) { + $this->observers[$eventname] = array(); + } + + foreach ( $this->observers[$eventname] as $o ) { + if ( $o == $observer ) { + return; + } + } + + $this->observers[$eventname][] = $observer; + } + + /** + * Notifies listeners. + * Sends the signal $eventname, the event identifier and a message object + * to all observers that have been registered to receive notification for + * this event. Part of the observer pattern implementation in RedBeanPHP. + * + * @param string $eventname event you want signal + * @param mixed $info message object to send along + * + * @return void + */ + public function signal( $eventname, $info ) + { + if ( !isset( $this->observers[$eventname] ) ) { + $this->observers[$eventname] = array(); + } + + foreach ( $this->observers[$eventname] as $observer ) { + $observer->onEvent( $eventname, $info ); + } + } +} +} + +namespace RedBeanPHP { + +/** + * Observer. + * + * Interface for Observer object. Implementation of the + * observer pattern. + * + * @file RedBeanPHP/Observer.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * @desc Part of the observer pattern in RedBean + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Observer +{ + /** + * An observer object needs to be capable of receiving + * notifications. Therefore the observer needs to implement the + * onEvent method with two parameters: the event identifier specifying the + * current event and a message object (in RedBeanPHP this can also be a bean). + * + * @param string $eventname event identifier + * @param mixed $bean a message sent along with the notification + * + * @return void + */ + public function onEvent( $eventname, $bean ); +} +} + +namespace RedBeanPHP { + +/** + * Adapter Interface. + * Describes the API for a RedBeanPHP Database Adapter. + * This interface defines the API contract for + * a RedBeanPHP Database Adapter. + * + * @file RedBeanPHP/Adapter.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Adapter +{ + /** + * Should returns a string containing the most recent SQL query + * that has been processed by the adapter. + * + * @return string + */ + public function getSQL(); + + /** + * Executes an SQL Statement using an array of values to bind + * If $noevent is TRUE then this function will not signal its + * observers to notify about the SQL execution; this to prevent + * infinite recursion when using observers. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * @param boolean $noevent no event firing + * + * @return void + */ + public function exec( $sql, $bindings = array(), $noevent = FALSE ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a multi dimensional resultset similar to getAll + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function get( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single row (one array) resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getRow( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single column (one array) resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getCol( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single cell, a scalar value as the resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return string + */ + public function getCell( $sql, $bindings = array() ); + + /** + * Executes the SQL query specified in $sql and takes + * the first two columns of the resultset. This function transforms the + * resultset into an associative array. Values from the the first column will + * serve as keys while the values of the second column will be used as values. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getAssoc( $sql, $bindings = array() ); + + /** + * Executes the SQL query specified in $sql and indexes + * the row by the first column. + * + * @param string $sql Sstring containing SQL code for databaseQL + * @param array $bindings values to bind + * + * @return array + */ + public function getAssocRow( $sql, $bindings = array() ); + + /** + * Returns the latest insert ID. + * + * @return integer + */ + public function getInsertID(); + + /** + * Returns the number of rows that have been + * affected by the last update statement. + * + * @return integer + */ + public function getAffectedRows(); + + /** + * Returns a database agnostic Cursor object. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return Cursor + */ + public function getCursor( $sql, $bindings = array() ); + + /** + * Returns the original database resource. This is useful if you want to + * perform operations on the driver directly instead of working with the + * adapter. RedBean will only access the adapter and never to talk + * directly to the driver though. + * + * @return mixed + */ + public function getDatabase(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Starts a transaction. + * + * @return void + */ + public function startTransaction(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Commits the transaction. + * + * @return void + */ + public function commit(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Rolls back the transaction. + * + * @return void + */ + public function rollback(); + + /** + * Closes database connection. + * + * @return void + */ + public function close(); +} +} + +namespace RedBeanPHP\Adapter { + +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\Driver as Driver; + +/** + * DBAdapter (Database Adapter) + * + * An adapter class to connect various database systems to RedBean + * Database Adapter Class. The task of the database adapter class is to + * communicate with the database driver. You can use all sorts of database + * drivers with RedBeanPHP. The default database drivers that ships with + * the RedBeanPHP library is the RPDO driver ( which uses the PHP Data Objects + * Architecture aka PDO ). + * + * @file RedBeanPHP/Adapter/DBAdapter.php + * @author Gabor de Mooij and the RedBeanPHP Community. + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DBAdapter extends Observable implements Adapter +{ + /** + * @var Driver + */ + private $db = NULL; + + /** + * @var string + */ + private $sql = ''; + + /** + * Constructor. + * + * Creates an instance of the RedBean Adapter Class. + * This class provides an interface for RedBean to work + * with ADO compatible DB instances. + * + * @param Driver $database ADO Compatible DB Instance + */ + public function __construct( $database ) + { + $this->db = $database; + } + + /** + * Returns a string containing the most recent SQL query + * processed by the database adapter, thus conforming to the + * interface: + * + * @see Adapter::getSQL + * + * Methods like get(), getRow() and exec() cause this SQL cache + * to get filled. If no SQL query has been processed yet this function + * will return an empty string. + * + * @return string + */ + public function getSQL() + { + return $this->sql; + } + + /** + * @see Adapter::exec + */ + public function exec( $sql, $bindings = array(), $noevent = FALSE ) + { + if ( !$noevent ) { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + } + + return $this->db->Execute( $sql, $bindings ); + } + + /** + * @see Adapter::get + */ + public function get( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetAll( $sql, $bindings ); + } + + /** + * @see Adapter::getRow + */ + public function getRow( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetRow( $sql, $bindings ); + } + + /** + * @see Adapter::getCol + */ + public function getCol( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetCol( $sql, $bindings ); + } + + /** + * @see Adapter::getAssoc + */ + public function getAssoc( $sql, $bindings = array() ) + { + $this->sql = $sql; + + $this->signal( 'sql_exec', $this ); + + $rows = $this->db->GetAll( $sql, $bindings ); + + $assoc = array(); + if ( !$rows ) { + return $assoc; + } + + foreach ( $rows as $row ) { + if ( empty( $row ) ) continue; + + if ( count( $row ) > 2 ) { + $key = array_shift( $row ); + $value = $row; + } elseif ( count( $row ) > 1 ) { + $key = array_shift( $row ); + $value = array_shift( $row ); + } else { + $key = array_shift( $row ); + $value = $key; + } + + $assoc[$key] = $value; + } + + return $assoc; + } + + /** + * @see Adapter::getAssocRow + */ + public function getAssocRow($sql, $bindings = array()) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetAssocRow( $sql, $bindings ); + } + + /** + * @see Adapter::getCell + */ + public function getCell( $sql, $bindings = array(), $noSignal = NULL ) + { + $this->sql = $sql; + + if ( !$noSignal ) $this->signal( 'sql_exec', $this ); + + return $this->db->GetOne( $sql, $bindings ); + } + + /** + * @see Adapter::getCursor + */ + public function getCursor( $sql, $bindings = array() ) + { + return $this->db->GetCursor( $sql, $bindings ); + } + + /** + * @see Adapter::getInsertID + */ + public function getInsertID() + { + return $this->db->getInsertID(); + } + + /** + * @see Adapter::getAffectedRows + */ + public function getAffectedRows() + { + return $this->db->Affected_Rows(); + } + + /** + * @see Adapter::getDatabase + */ + public function getDatabase() + { + return $this->db; + } + + /** + * @see Adapter::startTransaction + */ + public function startTransaction() + { + $this->db->StartTrans(); + } + + /** + * @see Adapter::commit + */ + public function commit() + { + $this->db->CommitTrans(); + } + + /** + * @see Adapter::rollback + */ + public function rollback() + { + $this->db->FailTrans(); + } + + /** + * @see Adapter::close. + */ + public function close() + { + $this->db->close(); + } +} +} + +namespace RedBeanPHP { + +/** + * Database Cursor Interface. + * A cursor is used by Query Writers to fetch Query Result rows + * one row at a time. This is useful if you expect the result set to + * be quite large. This interface dscribes the API of a database + * cursor. There can be multiple implementations of the Cursor, + * by default RedBeanPHP offers the PDOCursor for drivers shipping + * with RedBeanPHP and the NULLCursor. + * + * @file RedBeanPHP/Cursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Cursor +{ + /** + * Should retrieve the next row of the result set. + * This method is used to iterate over the result set. + * + * @return array + */ + public function getNextItem(); + + /** + * Closes the database cursor. + * Some databases require a cursor to be closed before executing + * another statement/opening a new cursor. + * + * @return void + */ + public function close(); +} +} + +namespace RedBeanPHP\Cursor { + +use RedBeanPHP\Cursor as Cursor; + +/** + * PDO Database Cursor + * Implementation of PDO Database Cursor. + * Used by the BeanCollection to fetch one bean at a time. + * The PDO Cursor is used by Query Writers to support retrieval + * of large bean collections. For instance, this class is used to + * implement the findCollection()/BeanCollection functionality. + * + * @file RedBeanPHP/Cursor/PDOCursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class PDOCursor implements Cursor +{ + /** + * @var PDOStatement + */ + protected $res; + + /** + * @var string + */ + protected $fetchStyle; + + /** + * Constructor, creates a new instance of a PDO Database Cursor. + * + * @param PDOStatement $res the PDO statement + * @param string $fetchStyle fetch style constant to use + * + * @return void + */ + public function __construct( \PDOStatement $res, $fetchStyle ) + { + $this->res = $res; + $this->fetchStyle = $fetchStyle; + } + + /** + * @see Cursor::getNextItem + */ + public function getNextItem() + { + return $this->res->fetch(); + } + + /** + * @see Cursor::close + */ + public function close() + { + $this->res->closeCursor(); + } +} +} + +namespace RedBeanPHP\Cursor { + +use RedBeanPHP\Cursor as Cursor; + +/** + * NULL Database Cursor + * Implementation of the NULL Cursor. + * Used for an empty BeanCollection. This Cursor + * can be used for instance if a query fails but the interface + * demands a cursor to be returned. + * + * @file RedBeanPHP/Cursor/NULLCursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class NullCursor implements Cursor +{ + /** + * @see Cursor::getNextItem + */ + public function getNextItem() + { + return NULL; + } + + /** + * @see Cursor::close + */ + public function close() + { + return NULL; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Cursor as Cursor; +use RedBeanPHP\Repository as Repository; + +/** + * BeanCollection. + * + * The BeanCollection represents a collection of beans and + * makes it possible to use database cursors. The BeanCollection + * has a method next() to obtain the first, next and last bean + * in the collection. The BeanCollection does not implement the array + * interface nor does it try to act like an array because it cannot go + * backward or rewind itself. + * + * Use the BeanCollection for large datasets where skip/limit is not an + * option. Keep in mind that ID-marking (querying a start ID) is a decent + * alternative though. + * + * @file RedBeanPHP/BeanCollection.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class BeanCollection +{ + /** + * @var Cursor + */ + protected $cursor = NULL; + + /** + * @var Repository + */ + protected $repository = NULL; + + /** + * @var string + */ + protected $type = NULL; + + /** + * Constructor, creates a new instance of the BeanCollection. + * + * @param string $type type of beans in this collection + * @param Repository $repository repository to use to generate bean objects + * @param Cursor $cursor cursor object to use + * + * @return void + */ + public function __construct( $type, Repository $repository, Cursor $cursor ) + { + $this->type = $type; + $this->cursor = $cursor; + $this->repository = $repository; + } + + /** + * Returns the next bean in the collection. + * If called the first time, this will return the first bean in the collection. + * If there are no more beans left in the collection, this method + * will return NULL. + * + * @return OODBBean|NULL + */ + public function next() + { + $row = $this->cursor->getNextItem(); + if ( $row ) { + $beans = $this->repository->convertToBeans( $this->type, array( $row ) ); + $bean = array_shift( $beans ); + return $bean; + } + return NULL; + } + + /** + * Closes the underlying cursor (needed for some databases). + * + * @return void + */ + public function close() + { + $this->cursor->close(); + } +} +} + +namespace RedBeanPHP { + +/** + * QueryWriter + * Interface for QueryWriters. + * Describes the API for a QueryWriter. + * + * Terminology: + * + * - beautified property (a camelCased property, has to be converted first) + * - beautified type (a camelCased type, has to be converted first) + * - type (a bean type, corresponds directly to a table) + * - property (a bean property, corresponds directly to a column) + * - table (a checked and quoted type, ready for use in a query) + * - column (a checked and quoted property, ready for use in query) + * - tableNoQ (same as type, but in context of a database operation) + * - columnNoQ (same as property, but in context of a database operation) + * + * @file RedBeanPHP/QueryWriter.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface QueryWriter +{ + /** + * SQL filter constants + */ + const C_SQLFILTER_READ = 'r'; + const C_SQLFILTER_WRITE = 'w'; + + /** + * Query Writer constants. + */ + const C_SQLSTATE_NO_SUCH_TABLE = 1; + const C_SQLSTATE_NO_SUCH_COLUMN = 2; + const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3; + + /** + * Define data type regions + * + * 00 - 80: normal data types + * 80 - 99: special data types, only scan/code if requested + * 99 : specified by user, don't change + */ + const C_DATATYPE_RANGE_SPECIAL = 80; + const C_DATATYPE_RANGE_SPECIFIED = 99; + + /** + * Define GLUE types for use with glueSQLCondition methods. + * Determines how to prefix a snippet of SQL before appending it + * to other SQL (or integrating it, mixing it otherwise). + * + * WHERE - glue as WHERE condition + * AND - glue as AND condition + */ + const C_GLUE_WHERE = 1; + const C_GLUE_AND = 2; + + /** + * Writes an SQL Snippet for a JOIN, returns the + * SQL snippet string. + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $type source type + * @param string $targetType target type (type to join) + * @param string $leftRight type of join (possible: 'LEFT', 'RIGHT' or 'INNER'). + * + * @return string $joinSQLSnippet + */ + public function writeJoin( $type, $targetType, $joinType ); + + /** + * Glues an SQL snippet to the beginning of a WHERE clause. + * This ensures users don't have to add WHERE to their query snippets. + * + * The snippet gets prefixed with WHERE or AND + * if it starts with a condition. + * + * If the snippet does NOT start with a condition (or this function thinks so) + * the snippet is returned as-is. + * + * The GLUE type determines the prefix: + * + * * NONE prefixes with WHERE + * * WHERE prefixes with WHERE and replaces AND if snippets starts with AND + * * AND prefixes with AND + * + * This method will never replace WHERE with AND since a snippet should never + * begin with WHERE in the first place. OR is not supported. + * + * Only a limited set of clauses will be recognized as non-conditions. + * For instance beginning a snippet with complex statements like JOIN or UNION + * will not work. This is too complex for use in a snippet. + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $sql SQL Snippet + * @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND) + * + * @return string + */ + public function glueSQLCondition( $sql, $glue = NULL ); + + /** + * Determines if there is a LIMIT 1 clause in the SQL. + * If not, it will add a LIMIT 1. (used for findOne). + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $sql query to scan and adjust + * + * @return string + */ + public function glueLimitOne( $sql ); + + /** + * Returns the tables that are in the database. + * + * @return array + */ + public function getTables(); + + /** + * This method will create a table for the bean. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type of bean you want to create a table for + * + * @return void + */ + public function createTable( $type ); + + /** + * Returns an array containing all the columns of the specified type. + * The format of the return array looks like this: + * $field => $type where $field is the name of the column and $type + * is a database specific description of the datatype. + * + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type of bean you want to obtain a column list of + * + * @return array + */ + public function getColumns( $type ); + + /** + * Returns the Column Type Code (integer) that corresponds + * to the given value type. This method is used to determine the minimum + * column type required to represent the given value. + * + * @param string $value value + * + * @return integer + */ + public function scanType( $value, $alsoScanSpecialForTypes = FALSE ); + + /** + * This method will add a column to a table. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type name of the table + * @param string $column name of the column + * @param integer $field data type for field + * + * @return void + */ + public function addColumn( $type, $column, $field ); + + /** + * Returns the Type Code for a Column Description. + * Given an SQL column description this method will return the corresponding + * code for the writer. If the include specials flag is set it will also + * return codes for special columns. Otherwise special columns will be identified + * as specified columns. + * + * @param string $typedescription description + * @param boolean $includeSpecials whether you want to get codes for special columns as well + * + * @return integer + */ + public function code( $typedescription, $includeSpecials = FALSE ); + + /** + * This method will widen the column to the specified data type. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type / table that needs to be adjusted + * @param string $column column that needs to be altered + * @param integer $datatype target data type + * + * @return void + */ + public function widenColumn( $type, $column, $datatype ); + + /** + * Selects records from the database. + * This methods selects the records from the database that match the specified + * type, conditions (optional) and additional SQL snippet (optional). + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return array + */ + public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); + + /** + * Selects records from the database and returns a cursor. + * This methods selects the records from the database that match the specified + * type, conditions (optional) and additional SQL snippet (optional). + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return Cursor + */ + public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ); + + /** + * Returns records through an intermediate type. This method is used to obtain records using a link table and + * allows the SQL snippets to reference columns in the link table for additional filtering or ordering. + * + * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side + * @param string $destType destination type, the target type you want to get beans of + * @param mixed $linkID ID to use for the link table + * @param string $addSql Additional SQL snippet + * @param array $bindings Bindings for SQL snippet + * + * @return array + */ + public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ); + + /** + * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation. + * + * @param string $sourceType source type, the first part of the link you're looking for + * @param string $destType destination type, the second part of the link you're looking for + * @param string $sourceID ID for the source + * @param string $destID ID for the destination + * + * @return array|null + */ + public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ); + + /** + * Counts the number of records in the database that match the + * conditions and additional SQL. + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return integer + */ + public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); + + /** + * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings. + * + * @param string $sourceType source type + * @param string $targetType the thing you want to count + * @param mixed $linkID the of the source type + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return integer + */ + public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() ); + + /** + * Returns all rows of specified type that have been tagged with one of the + * strings in the specified tag list array. + * + * Note that the additional SQL snippet can only be used for pagination, + * the SQL snippet will be appended to the end of the query. + * + * @param string $type the bean type you want to query + * @param array $tagList an array of strings, each string containing a tag title + * @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list + * @param string $addSql addition SQL snippet, for pagination + * @param array $bindings parameter bindings for additional SQL snippet + * + * @return array + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ); + + /** + * This method should update (or insert a record), it takes + * a table name, a list of update values ( $field => $value ) and an + * primary key ID (optional). If no primary key ID is provided, an + * INSERT will take place. + * Returns the new ID. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type name of the table to update + * @param array $updatevalues list of update values + * @param integer $id optional primary key ID value + * + * @return integer + */ + public function updateRecord( $type, $updatevalues, $id = NULL ); + + /** + * Deletes records from the database. + * @note $addSql is always prefixed with ' WHERE ' or ' AND .' + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $sql additional SQL + * @param array $bindings bindings + * + * @return void + */ + public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() ); + + /** + * Deletes all links between $sourceType and $destType in an N-M relation. + * + * @param string $sourceType source type + * @param string $destType destination type + * @param string $sourceID source ID + * + * @return void + */ + public function deleteRelations( $sourceType, $destType, $sourceID ); + + /** + * @see QueryWriter::addUniqueConstaint + */ + public function addUniqueIndex( $type, $columns ); + + /** + * This method will add a UNIQUE constraint index to a table on columns $columns. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type target bean type + * @param array $columnsPartOfIndex columns to include in index + * + * @return void + */ + public function addUniqueConstraint( $type, $columns ); + + /** + * This method will check whether the SQL state is in the list of specified states + * and returns TRUE if it does appear in this list or FALSE if it + * does not. The purpose of this method is to translate the database specific state to + * a one of the constants defined in this class and then check whether it is in the list + * of standard states provided. + * + * @param string $state SQL state to consider + * @param array $list list of standardized SQL state constants to check against + * + * @return boolean + */ + public function sqlStateIn( $state, $list ); + + /** + * This method will remove all beans of a certain type. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type bean type + * + * @return void + */ + public function wipe( $type ); + + /** + * This method will add a foreign key from type and field to + * target type and target field. + * The foreign key is created without an action. On delete/update + * no action will be triggered. The FK is only used to allow database + * tools to generate pretty diagrams and to make it easy to add actions + * later on. + * This methods accepts a type and infers the corresponding table name. + * + * + * @param string $type type that will have a foreign key field + * @param string $targetType points to this type + * @param string $property field that contains the foreign key value + * @param string $targetProperty field where the fk points to + * @param string $isDep whether target is dependent and should cascade on update/delete + * + * @return void + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false ); + + /** + * This method will add an index to a type and field with name + * $name. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type to add index to + * @param string $name name of the new index + * @param string $property field to index + * + * @return void + */ + public function addIndex( $type, $name, $property ); + + /** + * Checks and filters a database structure element like a table of column + * for safe use in a query. A database structure has to conform to the + * RedBeanPHP DB security policy which basically means only alphanumeric + * symbols are allowed. This security policy is more strict than conventional + * SQL policies and does therefore not require database specific escaping rules. + * + * @param string $databaseStructure name of the column/table to check + * @param boolean $noQuotes TRUE to NOT put backticks or quotes around the string + * + * @return string + */ + public function esc( $databaseStructure, $dontQuote = FALSE ); + + /** + * Removes all tables and views from the database. + * + * @return void + */ + public function wipeAll(); + + /** + * Renames an association. For instance if you would like to refer to + * album_song as: track you can specify this by calling this method like: + * + * + * renameAssociation('album_song','track') + * + * + * This allows: + * + * + * $album->sharedSong + * + * + * to add/retrieve beans from track instead of album_song. + * Also works for exportAll(). + * + * This method also accepts a single associative array as + * its first argument. + * + * @param string|array $fromType original type name, or array + * @param string $toType new type name (only if 1st argument is string) + * + * @return void + */ + public function renameAssocTable( $fromType, $toType = NULL ); + + /** + * Returns the format for link tables. + * Given an array containing two type names this method returns the + * name of the link table to be used to store and retrieve + * association records. For instance, given two types: person and + * project, the corresponding link table might be: 'person_project'. + * + * @param array $types two types array($type1, $type2) + * + * @return string + */ + public function getAssocTable( $types ); + + /** + * Given a bean type and a property, this method + * tries to infer the fetch type using the foreign key + * definitions in the database. + * For instance: project, student -> person. + * If no fetchType can be inferred, this method will return NULL. + * + * @note QueryWriters do not have to implement this method, + * it's optional. A default version is available in AQueryWriter. + * + * @param $type the source type to fetch a target type for + * @param $property the property to fetch the type of + * + * @return string|NULL + */ + public function inferFetchType( $type, $property ); +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP Abstract Query Writer. + * Represents an abstract Database to RedBean + * To write a driver for a different database for RedBean + * Contains a number of functions all implementors can + * inherit or override. + * + * @file RedBeanPHP/QueryWriter/AQueryWriter.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class AQueryWriter +{ + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var boolean + */ + private static $flagSQLFilterSafeMode = false; + + /** + * @var boolean + */ + private static $flagNarrowFieldMode = true; + + /** + * @var array + */ + public static $renames = array(); + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $defaultValue = 'NULL'; + + /** + * @var string + */ + protected $quoteCharacter = ''; + + /** + * @var boolean + */ + protected $flagUseCache = TRUE; + + /** + * @var array + */ + protected $cache = array(); + + /** + * @var integer + */ + protected $maxCacheSizePerType = 20; + + /** + * @var array + */ + public $typeno_sqltype = array(); + + /** + * Checks whether a number can be treated like an int. + * + * @param string $value string representation of a certain value + * + * @return boolean + */ + public static function canBeTreatedAsInt( $value ) + { + return (bool) ( strval( $value ) === strval( intval( $value ) ) ); + } + + /** + * @see QueryWriter::getAssocTableFormat + */ + public static function getAssocTableFormat( $types ) + { + sort( $types ); + + $assoc = implode( '_', $types ); + + return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc; + } + + /** + * @see QueryWriter::renameAssociation + */ + public static function renameAssociation( $from, $to = NULL ) + { + if ( is_array( $from ) ) { + foreach ( $from as $key => $value ) self::$renames[$key] = $value; + + return; + } + + self::$renames[$from] = $to; + } + + /** + * Globally available service method for RedBeanPHP. + * Converts a camel cased string to a snake cased string. + * + * @param string $camel camelCased string to converty to snake case + * + * @return string + */ + public static function camelsSnake( $camel ) + { + return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) ); + } + + /** + * Clears renames. + * + * @return void + */ + public static function clearRenames() + { + self::$renames = array(); + } + + /** + * Toggles 'Narrow Field Mode'. + * In Narrow Field mode the queryRecord method will + * narrow its selection field to + * + * SELECT {table}.* + * + * instead of + * + * SELECT * + * + * This is a better way of querying because it allows + * more flexibility (for instance joins). However if you need + * the wide selector for backward compatibility; use this method + * to turn OFF Narrow Field Mode by passing FALSE. + * + * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field + * + * @return void + */ + public static function setNarrowFieldMode( $narrowField ) + { + self::$flagNarrowFieldMode = (boolean) $narrowField; + } + + /** + * Sets SQL filters. + * This is a lowlevel method to set the SQL filter array. + * The format of this array is: + * + * + * array( + * '' => array( + * '' => array( + * '' => '' + * ) + * ) + * ) + * + * + * Example: + * + * + * array( + * QueryWriter::C_SQLFILTER_READ => array( + * 'book' => array( + * 'title' => ' LOWER(book.title) ' + * ) + * ) + * + * + * Note that you can use constants instead of magical chars + * as keys for the uppermost array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @param array list of filters to set + * + * @return void + */ + public static function setSQLFilters( $sqlFilters, $safeMode = false ) + { + self::$flagSQLFilterSafeMode = (boolean) $safeMode; + self::$sqlFilters = $sqlFilters; + } + + /** + * Returns current SQL Filters. + * This method returns the raw SQL filter array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @return array + */ + public static function getSQLFilters() + { + return self::$sqlFilters; + } + + /** + * Returns a cache key for the cache values passed. + * This method returns a fingerprint string to be used as a key to store + * data in the writer cache. + * + * @param array $keyValues key-value to generate key for + * + * @return string + */ + private function getCacheKey( $keyValues ) + { + return json_encode( $keyValues ); + } + + /** + * Returns the values associated with the provided cache tag and key. + * + * @param string $cacheTag cache tag to use for lookup + * @param string $key key to use for lookup + * + * @return mixed + */ + private function getCached( $cacheTag, $key ) + { + $sql = $this->adapter->getSQL(); + + if ($this->updateCache()) { + if ( isset( $this->cache[$cacheTag][$key] ) ) { + return $this->cache[$cacheTag][$key]; + } + } + + return NULL; + } + + /** + * Checks if the previous query had a keep-cache tag. + * If so, the cache will persist, otherwise the cache will be flushed. + * + * Returns TRUE if the cache will remain and FALSE if a flush has + * been performed. + * + * @return boolean + */ + private function updateCache() + { + $sql = $this->adapter->getSQL(); + if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) { + // If SQL has been taken place outside of this method then something else then + // a select query might have happened! (or instruct to keep cache) + $this->cache = array(); + return FALSE; + } + return TRUE; + } + + /** + * Stores data from the writer in the cache under a specific key and cache tag. + * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag + * will be the bean type, this makes sure queries associated with a certain reference type will + * never contain conflicting data. + * Why not use the cache tag as a key? Well + * we need to make sure the cache contents fits the key (and key is based on the cache values). + * Otherwise it would be possible to store two different result sets under the same key (the cache tag). + * + * In previous versions you could only store one key-entry, I have changed this to + * improve caching efficiency (issue #400). + * + * @param string $cacheTag cache tag (secondary key) + * @param string $key key to store values under + * @param array $values content to be stored + * + * @return void + */ + private function putResultInCache( $cacheTag, $key, $values ) + { + if ( isset( $this->cache[$cacheTag] ) ) { + if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] ); + } else { + $this->cache[$cacheTag] = array(); + } + + $this->cache[$cacheTag][$key] = $values; + } + + /** + * Creates an SQL snippet from a list of conditions of format: + * + * + * array( + * key => array( + * value1, value2, value3 .... + * ) + * ) + * + * + * @param array $conditions list of conditions + * @param array $bindings parameter bindings for SQL snippet + * @param string $addSql additional SQL snippet to append to result + * + * @return string + */ + private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' ) + { + reset( $bindings ); + $firstKey = key( $bindings ); + $paramTypeIsNum = ( is_numeric( $firstKey ) ); + $counter = 0; + + $sqlConditions = array(); + foreach ( $conditions as $column => $values ) { + if ( !count( $values ) ) continue; + + $sql = $this->esc( $column ); + $sql .= ' IN ( '; + + if ( !is_array( $values ) ) $values = array( $values ); + + if ( $paramTypeIsNum ) { + $sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) '; + + array_unshift($sqlConditions, $sql); + + foreach ( $values as $k => $v ) { + $values[$k] = strval( $v ); + + array_unshift( $bindings, $v ); + } + } else { + + $slots = array(); + + foreach( $values as $k => $v ) { + $slot = ':slot'.$counter++; + $slots[] = $slot; + $bindings[$slot] = strval( $v ); + } + + $sql .= implode( ',', $slots ).' ) '; + $sqlConditions[] = $sql; + } + } + + $sql = ''; + if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) { + $sql = implode( ' AND ', $sqlConditions ); + $sql = " WHERE ( $sql ) "; + + if ( $addSql ) $sql .= $addSql; + } elseif ( $addSql ) { + $sql = $addSql; + } + + return $sql; + } + + /** + * Returns the table names and column names for a relational query. + * + * @param string $sourceType type of the source bean + * @param string $destType type of the bean you want to obtain using the relation + * @param boolean $noQuote TRUE if you want to omit quotes + * + * @return array + */ + private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE ) + { + $linkTable = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote ); + $sourceCol = $this->esc( $sourceType . '_id', $noQuote ); + + if ( $sourceType === $destType ) { + $destCol = $this->esc( $destType . '2_id', $noQuote ); + } else { + $destCol = $this->esc( $destType . '_id', $noQuote ); + } + + $sourceTable = $this->esc( $sourceType, $noQuote ); + $destTable = $this->esc( $destType, $noQuote ); + + return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ); + } + + /** + * Given a type and a property name this method + * returns the foreign key map section associated with this pair. + * + * @param string $type name of the type + * @param string $property name of the property + * + * @return array|NULL + */ + protected function getForeignKeyForTypeProperty( $type, $property ) + { + $property = $this->esc( $property, TRUE ); + + try { + $map = $this->getKeyMapForType( $type ); + } catch ( SQLException $e ) { + return NULL; + } + + foreach( $map as $key ) { + if ( $key['from'] === $property ) return $key; + } + return NULL; + } + + /** + * Returns the foreign key map (FKM) for a type. + * A foreign key map describes the foreign keys in a table. + * A FKM always has the same structure: + * + * + * array( + * 'name' => + * 'from' => + * 'table' => + * 'to' => (most of the time 'id') + * 'on_update' => + * 'on_delete' => + * ) + * + * + * @note the keys in the result array are FKDLs, i.e. descriptive unique + * keys per source table. Also see: AQueryWriter::makeFKLabel for details. + * + * @param string $type the bean type you wish to obtain a key map of + * + * @return array + */ + protected function getKeyMapForType( $type ) + { + return array(); + } + + /** + * This method makes a key for a foreign key description array. + * This key is a readable string unique for every source table. + * This uniform key is called the FKDL Foreign Key Description Label. + * Note that the source table is not part of the FKDL because + * this key is supposed to be 'per source table'. If you wish to + * include a source table, prefix the key with 'on_table__'. + * + * @param string $from the column of the key in the source table + * @param string $type the type (table) where the key points to + * @param string $to the target column of the foreign key (mostly just 'id') + * + * @return string + */ + protected function makeFKLabel($from, $type, $to) + { + return "from_{$from}_to_table_{$type}_col_{$to}"; + } + + /** + * Returns an SQL Filter snippet for reading. + * + * @param string $type type of bean + * + * @return string + */ + protected function getSQLFilterSnippet( $type ) + { + $existingCols = array(); + if (self::$flagSQLFilterSafeMode) { + $existingCols = $this->getColumns( $type ); + } + + $sqlFilters = array(); + if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) { + foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) { + if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) { + $sqlFilters[] = $sqlFilter.' AS '.$property.' '; + } + } + } + $sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : ''; + return $sqlFilterStr; + } + + /** + * Generates a list of parameters (slots) for an SQL snippet. + * This method calculates the correct number of slots to insert in the + * SQL snippet and determines the correct type of slot. If the bindings + * array contains named parameters this method will return named ones and + * update the keys in the value list accordingly (that's why we use the &). + * + * If you pass an offset the bindings will be re-added to the value list. + * Some databases cant handle duplicate parameter names in queries. + * + * @param array &$valueList list of values to generate slots for (gets modified if needed) + * @param array $otherBindings list of additional bindings + * @param integer $offset start counter at... + * + * @return string + */ + protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 ) + { + if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) { + reset( $otherBindings ); + + $key = key( $otherBindings ); + + if ( !is_numeric($key) ) { + $filler = array(); + $newList = (!$offset) ? array() : $valueList; + $counter = $offset; + + foreach( $valueList as $value ) { + $slot = ':slot' . ( $counter++ ); + $filler[] = $slot; + $newList[$slot] = $value; + } + + // Change the keys! + $valueList = $newList; + + return implode( ',', $filler ); + } + } + + return implode( ',', array_fill( 0, count( $valueList ), '?' ) ); + } + + /** + * Adds a data type to the list of data types. + * Use this method to add a new column type definition to the writer. + * Used for UUID support. + * + * @param integer $dataTypeID magic number constant assigned to this data type + * @param string $SQLDefinition SQL column definition (i.e. INT(11)) + * + * @return self + */ + protected function addDataType( $dataTypeID, $SQLDefinition ) + { + $this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition; + $this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID; + return $this; + } + + /** + * Returns the sql that should follow an insert statement. + * + * @param string $table name + * + * @return string + */ + protected function getInsertSuffix( $table ) + { + return ''; + } + + /** + * Checks whether a value starts with zeros. In this case + * the value should probably be stored using a text datatype instead of a + * numerical type in order to preserve the zeros. + * + * @param string $value value to be checked. + * + * @return boolean + */ + protected function startsWithZeros( $value ) + { + $value = strval( $value ); + + if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * Inserts a record into the database using a series of insert columns + * and corresponding insertvalues. Returns the insert id. + * + * @param string $table table to perform query on + * @param array $insertcolumns columns to be inserted + * @param array $insertvalues values to be inserted + * + * @return integer + */ + protected function insertRecord( $type, $insertcolumns, $insertvalues ) + { + $default = $this->defaultValue; + $suffix = $this->getInsertSuffix( $type ); + $table = $this->esc( $type ); + + if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) { + + $insertSlots = array(); + foreach ( $insertcolumns as $k => $v ) { + $insertcolumns[$k] = $this->esc( $v ); + + if (isset(self::$sqlFilters['w'][$type][$v])) { + $insertSlots[] = self::$sqlFilters['w'][$type][$v]; + } else { + $insertSlots[] = '?'; + } + } + + $insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES + ( $default, " . implode( ',', $insertSlots ) . " ) $suffix"; + + $ids = array(); + foreach ( $insertvalues as $i => $insertvalue ) { + $ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i ); + } + + $result = count( $ids ) === 1 ? array_pop( $ids ) : $ids; + } else { + $result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" ); + } + + if ( $suffix ) return $result; + + $last_id = $this->adapter->getInsertID(); + + return $last_id; + } + + /** + * Checks table name or column name. + * + * @param string $table table string + * + * @return string + */ + protected function check( $struct ) + { + if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) { + throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' ); + } + + return $struct; + } + + /** + * Checks whether the specified type (i.e. table) already exists in the database. + * Not part of the Object Database interface! + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + $tables = $this->getTables(); + + return in_array( $table, $tables ); + } + + /** + * @see QueryWriter::glueSQLCondition + */ + public function glueSQLCondition( $sql, $glue = NULL ) + { + static $snippetCache = array(); + + if ( trim( $sql ) === '' ) { + return $sql; + } + + $key = $glue . '|' . $sql; + + if ( isset( $snippetCache[$key] ) ) { + return $snippetCache[$key]; + } + + $lsql = ltrim( $sql ); + + if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) { + if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) { + $snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 ); + } else { + $snippetCache[$key] = $sql; + } + } else { + $snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql; + } + + return $snippetCache[$key]; + } + + /** + * @see QueryWriter::glueLimitOne + */ + public function glueLimitOne( $sql = '') + { + return ( strpos( strtoupper( $sql ), 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql; + } + + /** + * @see QueryWriter::esc + */ + public function esc( $dbStructure, $dontQuote = FALSE ) + { + $this->check( $dbStructure ); + + return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter; + } + + /** + * @see QueryWriter::addColumn + */ + public function addColumn( $type, $column, $field ) + { + $table = $type; + $type = $field; + $table = $this->esc( $table ); + $column = $this->esc( $column ); + + $type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : ''; + + $this->adapter->exec( "ALTER TABLE $table ADD $column $type " ); + } + + /** + * @see QueryWriter::updateRecord + */ + public function updateRecord( $type, $updatevalues, $id = NULL ) + { + $table = $type; + + if ( !$id ) { + $insertcolumns = $insertvalues = array(); + + foreach ( $updatevalues as $pair ) { + $insertcolumns[] = $pair['property']; + $insertvalues[] = $pair['value']; + } + + //Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff) + return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) ); + } + + if ( $id && !count( $updatevalues ) ) { + return $id; + } + + $table = $this->esc( $table ); + $sql = "UPDATE $table SET "; + + $p = $v = array(); + + foreach ( $updatevalues as $uv ) { + + if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) { + $p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']]; + } else { + $p[] = " {$this->esc( $uv["property"] )} = ? "; + } + + $v[] = $uv['value']; + } + + $sql .= implode( ',', $p ) . ' WHERE id = ? '; + + $v[] = $id; + + $this->adapter->exec( $sql, $v ); + + return $id; + } + + /** + * @see QueryWriter::writeJoin + */ + public function writeJoin( $type, $targetType, $leftRight = 'LEFT' ) + { + if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' ) + throw new RedException( 'Invalid JOIN.' ); + + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $field = $this->esc( $targetType, TRUE ); + return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id "; + } + + /** + * @see QueryWriter::queryRecord + */ + public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL ); + + $key = NULL; + if ( $this->flagUseCache ) { + $key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) ); + + if ( $cached = $this->getCached( $type, $key ) ) { + return $cached; + } + } + + $table = $this->esc( $type ); + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $type ); + } + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + + $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*'; + $sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache"; + + $rows = $this->adapter->get( $sql, $bindings ); + + if ( $this->flagUseCache && $key ) { + $this->putResultInCache( $type, $key, $rows ); + } + + return $rows; + } + + /** + * @see QueryWriter::queryRecordWithCursor + */ + public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ) + { + $sql = $this->glueSQLCondition( $addSql, NULL ); + $table = $this->esc( $type ); + $sql = "SELECT {$table}.* FROM {$table} {$sql}"; + return $this->adapter->getCursor( $sql, $bindings ); + } + + /** + * @see QueryWriter::queryRecordRelated + */ + public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE ); + + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) ); + + if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) { + return $cached; + } + + $inClause = $this->getParametersForInClause( $linkIDs, $bindings ); + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + + if ( $sourceType === $destType ) { + $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases + $sql = " + SELECT + {$destTable}.* {$sqlFilterStr} , + COALESCE( + NULLIF({$linkTable}.{$sourceCol}, {$destTable}.id), + NULLIF({$linkTable}.{$destCol}, {$destTable}.id)) AS linked_by + FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) OR + ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} IN ($inClause2) ) + {$addSql} + -- keep-cache"; + + $linkIDs = array_merge( $linkIDs, $linkIDs ); + } else { + $sql = " + SELECT + {$destTable}.* {$sqlFilterStr}, + {$linkTable}.{$sourceCol} AS linked_by + FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) + {$addSql} + -- keep-cache"; + } + + $bindings = array_merge( $linkIDs, $bindings ); + + $rows = $this->adapter->get( $sql, $bindings ); + + $this->putResultInCache( $destType, $key, $rows ); + + return $rows; + } + + /** + * @see QueryWriter::queryRecordLink + */ + public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) ); + + if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) { + return $cached; + } + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + + if ( $sourceTable === $destTable ) { + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} + WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR + ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache"; + $row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) ); + } else { + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} + WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache"; + $row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) ); + } + + $this->putResultInCache( $linkTable, $key, $row ); + + return $row; + } + + /** + * @see QueryWriter::queryTagged + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ) + { + $assocType = $this->getAssocTable( array( $type, 'tag' ) ); + $assocTable = $this->esc( $assocType ); + $assocField = $type . '_id'; + $table = $this->esc( $type ); + $slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) ); + $score = ( $all ) ? count( $tagList ) : 1; + + $sql = " + SELECT {$table}.*, count({$table}.id) FROM {$table} + INNER JOIN {$assocTable} ON {$assocField} = {$table}.id + INNER JOIN tag ON {$assocTable}.tag_id = tag.id + WHERE tag.title IN ({$slots}) + GROUP BY {$table}.id + HAVING count({$table}.id) >= ? + {$addSql} + "; + + $bindings = array_merge( $tagList, array( $score ), $bindings ); + $rows = $this->adapter->get( $sql, $bindings ); + return $rows; + } + + /** + * @see QueryWriter::queryRecordCount + */ + public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql ); + + $table = $this->esc( $type ); + + $this->updateCache(); //check if cache chain has been broken + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + $sql = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache"; + + return (int) $this->adapter->getCell( $sql, $bindings ); + } + + /** + * @see QueryWriter::queryRecordCountRelated + */ + public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $this->updateCache(); //check if cache chain has been broken + + if ( $sourceType === $destType ) { + $sql = " + SELECT COUNT(*) FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR + ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? ) + {$addSql} + -- keep-cache"; + + $bindings = array_merge( array( $linkID, $linkID ), $bindings ); + } else { + $sql = " + SELECT COUNT(*) FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) + {$addSql} + -- keep-cache"; + + $bindings = array_merge( array( $linkID ), $bindings ); + } + + return (int) $this->adapter->getCell( $sql, $bindings ); + } + + /** + * @see QueryWriter::deleteRecord + */ + public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql ); + + $table = $this->esc( $type ); + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + $sql = "DELETE FROM {$table} {$sql}"; + + $this->adapter->exec( $sql, $bindings ); + } + + /** + * @see QueryWriter::deleteRelations + */ + public function deleteRelations( $sourceType, $destType, $sourceID ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + if ( $sourceTable === $destTable ) { + $sql = "DELETE FROM {$linkTable} + WHERE ( {$sourceCol} = ? ) OR + ( {$destCol} = ? ) + "; + + $this->adapter->exec( $sql, array( $sourceID, $sourceID ) ); + } else { + $sql = "DELETE FROM {$linkTable} + WHERE {$sourceCol} = ? "; + + $this->adapter->exec( $sql, array( $sourceID ) ); + } + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $property, $dataType ) + { + if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE; + + $table = $this->esc( $type ); + $column = $this->esc( $property ); + + $newType = $this->typeno_sqltype[$dataType]; + + $this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " ); + + return TRUE; + } + + /** + * @see QueryWriter::wipe + */ + public function wipe( $type ) + { + $table = $this->esc( $type ); + + $this->adapter->exec( "TRUNCATE $table " ); + } + + /** + * @see QueryWriter::renameAssocTable + */ + public function renameAssocTable( $from, $to = NULL ) + { + self::renameAssociation( $from, $to ); + } + + /** + * @see QueryWriter::getAssocTable + */ + public function getAssocTable( $types ) + { + return self::getAssocTableFormat( $types ); + } + + /** + * Turns caching on or off. Default: off. + * If caching is turned on retrieval queries fired after eachother will + * use a result row cache. + * + * @param boolean + * + * @return void + */ + public function setUseCache( $yesNo ) + { + $this->flushCache(); + + $this->flagUseCache = (bool) $yesNo; + } + + /** + * Flushes the Query Writer Cache. + * Clears the internal query cache array and returns its overall + * size. + * + * @return integer + */ + public function flushCache( $newMaxCacheSizePerType = NULL ) + { + if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) { + $this->maxCacheSizePerType = $newMaxCacheSizePerType; + } + $count = count( $this->cache, COUNT_RECURSIVE ); + $this->cache = array(); + return $count; + } + + /** + * @deprecated Use esc() instead. + * + * @param string $column column to be escaped + * @param boolean $noQuotes omit quotes + * + * @return string + */ + public function safeColumn( $column, $noQuotes = FALSE ) + { + return $this->esc( $column, $noQuotes ); + } + + /** + * @deprecated Use esc() instead. + * + * @param string $table table to be escaped + * @param boolean $noQuotes omit quotes + * + * @return string + */ + public function safeTable( $table, $noQuotes = FALSE ) + { + return $this->esc( $table, $noQuotes ); + } + + /** + * @see QueryWriter::inferFetchType + */ + public function inferFetchType( $type, $property ) + { + $type = $this->esc( $type, TRUE ); + $field = $this->esc( $property, TRUE ) . '_id'; + $keys = $this->getKeyMapForType( $type ); + + foreach( $keys as $key ) { + if ( + $key['from'] === $field + ) return $key['table']; + } + return NULL; + } + + /** + * @see QueryWriter::addUniqueConstraint + */ + public function addUniqueIndex( $type, $properties ) + { + return $this->addUniqueConstraint( $type, $properties ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP MySQLWriter. + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the MySQL/MariaDB database platform. + * + * @file RedBeanPHP/QueryWriter/MySQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class MySQL extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_BOOL = 0; + const C_DATATYPE_UINT32 = 2; + const C_DATATYPE_DOUBLE = 3; + const C_DATATYPE_TEXT7 = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible + const C_DATATYPE_TEXT8 = 5; + const C_DATATYPE_TEXT16 = 6; + const C_DATATYPE_TEXT32 = 7; + const C_DATATYPE_SPECIAL_DATE = 80; + const C_DATATYPE_SPECIAL_DATETIME = 81; + const C_DATATYPE_SPECIAL_POINT = 90; + const C_DATATYPE_SPECIAL_LINESTRING = 91; + const C_DATATYPE_SPECIAL_POLYGON = 92; + const C_DATATYPE_SPECIAL_MONEY = 93; + + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '`'; + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $databaseName = $this->adapter->getCell('SELECT DATABASE()'); + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get(' + SELECT + information_schema.key_column_usage.constraint_name AS `name`, + information_schema.key_column_usage.referenced_table_name AS `table`, + information_schema.key_column_usage.column_name AS `from`, + information_schema.key_column_usage.referenced_column_name AS `to`, + information_schema.referential_constraints.update_rule AS `on_update`, + information_schema.referential_constraints.delete_rule AS `on_delete` + FROM information_schema.key_column_usage + INNER JOIN information_schema.referential_constraints + ON information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name + WHERE + information_schema.key_column_usage.table_schema = :database + AND information_schema.referential_constraints.constraint_schema = :database + AND information_schema.key_column_usage.constraint_schema = :database + AND information_schema.key_column_usage.table_name = :table + AND information_schema.key_column_usage.constraint_name != \'PRIMARY\' + AND information_schema.key_column_usage.referenced_table_name IS NOT NULL + ', array( ':database' => $databaseName, ':table' => $table ) ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $k['name'], + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ', + MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ', + MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ', + MySQL::C_DATATYPE_TEXT7 => ' VARCHAR(191) ', + MYSQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ', + MySQL::C_DATATYPE_TEXT16 => ' TEXT ', + MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ', + MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ', + MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', + MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ', + MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ', + MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ', + MySQL::C_DATATYPE_SPECIAL_MONEY => ' DECIMAL(10,2) ' + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[trim( strtolower( $v ) )] = $k; + } + + $this->adapter = $adapter; + + $this->encoding = $this->adapter->getDatabase()->getMysqlEncoding(); + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer + */ + public function getTypeForID() + { + return self::C_DATATYPE_UINT32; + } + + /** + * @see QueryWriter::getTables + */ + public function getTables() + { + return $this->adapter->getCol( 'show tables' ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $encoding = $this->adapter->getDatabase()->getMysqlEncoding(); + $sql = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci "; + + $this->adapter->exec( $sql ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) ); + + $columns = array(); + foreach ( $columnsRaw as $r ) { + $columns[$r['Field']] = $r['Type']; + } + + return $columns; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL; + if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7; + + if ( $flagSpecial ) { + if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_MONEY; + } + if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_DATE; + } + if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_DATETIME; + } + if ( preg_match( '/^POINT\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POINT; + } + if ( preg_match( '/^LINESTRING\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_LINESTRING; + } + if ( preg_match( '/^POLYGON\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POLYGON; + } + } + + //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?). + if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) { + return MySQL::C_DATATYPE_BOOL; + } + + if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; + + if ( !$this->startsWithZeros( $value ) ) { + + if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) { + return MySQL::C_DATATYPE_UINT32; + } + + if ( is_numeric( $value ) ) { + return MySQL::C_DATATYPE_DOUBLE; + } + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) { + return MySQL::C_DATATYPE_TEXT7; + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) { + return MySQL::C_DATATYPE_TEXT8; + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) { + return MySQL::C_DATATYPE_TEXT16; + } + + return MySQL::C_DATATYPE_TEXT32; + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + if ( isset( $this->sqltype_typeno[$typedescription] ) ) { + $r = $this->sqltype_typeno[$typedescription]; + } else { + $r = self::C_DATATYPE_SPECIFIED; + } + + if ( $includeSpecials ) { + return $r; + } + + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + return self::C_DATATYPE_SPECIFIED; + } + + return $r; + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $columns = array(); + foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); + $table = $this->esc( $type ); + sort( $columns ); // Else we get multiple indexes due to order-effects + $name = 'UQ_' . sha1( implode( ',', $columns ) ); + try { + $sql = "ALTER TABLE $table + ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")"; + $this->adapter->exec( $sql ); + } catch ( SQLException $e ) { + //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways! + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $property ) + { + try { + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $property ); + $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::addFK + * @return bool + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE ) + { + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $targetTableNoQ = $this->esc( $targetType, TRUE ); + $field = $this->esc( $property ); + $fieldNoQ = $this->esc( $property, TRUE ); + $targetField = $this->esc( $targetProperty ); + $targetFieldNoQ = $this->esc( $targetProperty, TRUE ); + $tableNoQ = $this->esc( $type, TRUE ); + $fieldNoQ = $this->esc( $property, TRUE ); + if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; + + //Widen the column if it's incapable of representing a foreign key (at least INT). + $columns = $this->getColumns( $tableNoQ ); + $idType = $this->getTypeForID(); + if ( $this->code( $columns[$fieldNoQ] ) !== $idType ) { + $this->widenColumn( $type, $property, $idType ); + } + + $fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ); + $cName = 'c_'.$fkName; + try { + $this->adapter->exec( " + ALTER TABLE {$table} + ADD CONSTRAINT $cName + FOREIGN KEY $fkName ( `{$fieldNoQ}` ) REFERENCES `{$targetTableNoQ}` + (`{$targetFieldNoQ}`) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';'); + } catch ( SQLException $e ) { + // Failure of fk-constraints is not a problem + } + return true; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' ); + + foreach ( $this->getTables() as $t ) { + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + + try { + $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + } + + $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP SQLiteWriter with support for SQLite types + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the SQLite database platform. + * + * @file RedBeanPHP/QueryWriter/SQLiteT.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQLiteT extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_INTEGER = 0; + const C_DATATYPE_NUMERIC = 1; + const C_DATATYPE_TEXT = 2; + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '`'; + + /** + * Gets all information about a table (from a type). + * + * Format: + * array( + * name => name of the table + * columns => array( name => datatype ) + * indexes => array() raw index information rows from PRAGMA query + * keys => array() raw key information rows from PRAGMA query + * ) + * + * @param string $type type you want to get info of + * + * @return array + */ + protected function getTable( $type ) + { + $tableName = $this->esc( $type, TRUE ); + $columns = $this->getColumns( $type ); + $indexes = $this->getIndexes( $type ); + $keys = $this->getKeyMapForType( $type ); + + $table = array( + 'columns' => $columns, + 'indexes' => $indexes, + 'keys' => $keys, + 'name' => $tableName + ); + + $this->tableArchive[$tableName] = $table; + + return $table; + } + + /** + * Puts a table. Updates the table structure. + * In SQLite we can't change columns, drop columns, change or add foreign keys so we + * have a table-rebuild function. You simply load your table with getTable(), modify it and + * then store it with putTable()... + * + * @param array $tableMap information array + * + * @return void + */ + protected function putTable( $tableMap ) + { + $table = $tableMap['name']; + $q = array(); + $q[] = "DROP TABLE IF EXISTS tmp_backup;"; + + $oldColumnNames = array_keys( $this->getColumns( $table ) ); + + foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`"; + + $q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");"; + $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;"; + $q[] = "PRAGMA foreign_keys = 0 "; + $q[] = "DROP TABLE `$table`;"; + + $newTableDefStr = ''; + foreach ( $tableMap['columns'] as $column => $type ) { + if ( $column != 'id' ) { + $newTableDefStr .= ",`$column` $type"; + } + } + + $fkDef = ''; + foreach ( $tableMap['keys'] as $key ) { + $fkDef .= ", FOREIGN KEY(`{$key['from']}`) + REFERENCES `{$key['table']}`(`{$key['to']}`) + ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}"; + } + + $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );"; + + foreach ( $tableMap['indexes'] as $name => $index ) { + if ( strpos( $name, 'UQ_' ) === 0 ) { + $cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) ); + foreach ( $cols as $k => $v ) $cols[$k] = "`$v`"; + $q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")"; + } else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) "; + } + + $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;"; + $q[] = "DROP TABLE tmp_backup;"; + $q[] = "PRAGMA foreign_keys = 1 "; + + foreach ( $q as $sq ) $this->adapter->exec( $sq ); + } + + /** + * Returns the an array describing the indexes for type $type. + * + * @param string $type type to describe indexes of + * + * @return array + */ + protected function getIndexes( $type ) + { + $table = $this->esc( $type, TRUE ); + $indexes = $this->adapter->get( "PRAGMA index_list('$table')" ); + + $indexInfoList = array(); + foreach ( $indexes as $i ) { + $indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " ); + + $indexInfoList[$i['name']]['unique'] = $i['unique']; + } + + return $indexInfoList; + } + + /** + * Adds a foreign key to a type. + * Note: cant put this in try-catch because that can hide the fact + * that database has been damaged. + * + * @param string $type type you want to modify table of + * @param string $targetType target type + * @param string $field field of the type that needs to get the fk + * @param string $targetField field where the fk needs to point to + * @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE + * + * @return boolean + */ + protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE ) + { + $table = $this->esc( $type, TRUE ); + $targetTable = $this->esc( $targetType, TRUE ); + $column = $this->esc( $property, TRUE ); + $targetColumn = $this->esc( $targetProperty, TRUE ); + + $tables = $this->getTables(); + if ( !in_array( $targetTable, $tables ) ) return FALSE; + + if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE; + $t = $this->getTable( $table ); + $consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' ); + $label = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn; + $t['keys'][$label] = array( + 'table' => $targetTable, + 'from' => $column, + 'to' => $targetColumn, + 'on_update' => $consSQL, + 'on_delete' => $consSQL + ); + $this->putTable( $t ); + return TRUE; + } + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get( "PRAGMA foreign_key_list('$table')" ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $label, + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + SQLiteT::C_DATATYPE_INTEGER => 'INTEGER', + SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC', + SQLiteT::C_DATATYPE_TEXT => 'TEXT', + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[$v] = $k; + } + + $this->adapter = $adapter; + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer $const data type to be used for IDS. + */ + public function getTypeForID() + { + return self::C_DATATYPE_INTEGER; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( $value === NULL ) return self::C_DATATYPE_INTEGER; + if ( $value === INF ) return self::C_DATATYPE_TEXT; + + if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; + + if ( $value === TRUE || $value === FALSE ) return self::C_DATATYPE_INTEGER; + + if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER; + + if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648) + || preg_match( '/\d{4}\-\d\d\-\d\d/', $value ) + || preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value ) + ) { + return self::C_DATATYPE_NUMERIC; + } + + return self::C_DATATYPE_TEXT; + } + + /** + * @see QueryWriter::addColumn + */ + public function addColumn( $table, $column, $type ) + { + $column = $this->check( $column ); + $table = $this->check( $table ); + $type = $this->typeno_sqltype[$type]; + + $this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " ); + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 ); + + return $r; + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $column, $datatype ) + { + $t = $this->getTable( $type ); + + $t['columns'][$column] = $this->typeno_sqltype[$datatype]; + + $this->putTable( $t ); + } + + /** + * @see QueryWriter::getTables(); + */ + public function getTables() + { + return $this->adapter->getCol( "SELECT name FROM sqlite_master + WHERE type='table' AND name!='sqlite_sequence';" ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $sql = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) "; + + $this->adapter->exec( $sql ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $table = $this->esc( $table, TRUE ); + + $columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" ); + + $columns = array(); + foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type']; + + return $columns; + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $name = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties ); + $t = $this->getTable( $type ); + $t['indexes'][$name] = array( 'name' => $name ); + try { + $this->putTable( $t ); + } catch( SQLException $e ) { + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + 'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $column ) + { + $columns = $this->getColumns( $type ); + if ( !isset( $columns[$column] ) ) return FALSE; + + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $column, TRUE ); + + try { + $t = $this->getTable( $type ); + $t['indexes'][$name] = array( 'name' => $column ); + $this->putTable( $t ); + return TRUE; + } catch( SQLException $exception ) { + return FALSE; + } + } + + /** + * @see QueryWriter::wipe + */ + public function wipe( $type ) + { + $table = $this->esc( $type ); + + $this->adapter->exec( "DELETE FROM $table " ); + } + + /** + * @see QueryWriter::addFK + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) + { + return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep ); + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'PRAGMA foreign_keys = 0 ' ); + + foreach ( $this->getTables() as $t ) { + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + } + + $this->adapter->exec( 'PRAGMA foreign_keys = 1 ' ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP PostgreSQL Query Writer. + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the PostgreSQL database platform. + * + * @file RedBeanPHP/QueryWriter/PostgreSQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class PostgreSQL extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_INTEGER = 0; + const C_DATATYPE_DOUBLE = 1; + const C_DATATYPE_TEXT = 3; + const C_DATATYPE_SPECIAL_DATE = 80; + const C_DATATYPE_SPECIAL_DATETIME = 81; + const C_DATATYPE_SPECIAL_POINT = 90; + const C_DATATYPE_SPECIAL_LSEG = 91; + const C_DATATYPE_SPECIAL_CIRCLE = 92; + const C_DATATYPE_SPECIAL_MONEY = 93; + const C_DATATYPE_SPECIAL_POLYGON = 94; + const C_DATATYPE_SPECIAL_MONEY2 = 95; //Numbers only money, i.e. fixed point numeric + const C_DATATYPE_SPECIAL_JSON = 96; //JSON support (only manual) + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '"'; + + /** + * @var string + */ + protected $defaultValue = 'DEFAULT'; + + /** + * Returns the insert suffix SQL Snippet + * + * @param string $table table + * + * @return string $sql SQL Snippet + */ + protected function getInsertSuffix( $table ) + { + return 'RETURNING id '; + } + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get( ' + SELECT + information_schema.key_column_usage.constraint_name AS "name", + information_schema.key_column_usage.column_name AS "from", + information_schema.constraint_table_usage.table_name AS "table", + information_schema.constraint_column_usage.column_name AS "to", + information_schema.referential_constraints.update_rule AS "on_update", + information_schema.referential_constraints.delete_rule AS "on_delete" + FROM information_schema.key_column_usage + INNER JOIN information_schema.constraint_table_usage + ON ( + information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog + ) + INNER JOIN information_schema.constraint_column_usage + ON ( + information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog + ) + INNER JOIN information_schema.referential_constraints + ON ( + information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog + ) + WHERE + information_schema.key_column_usage.table_catalog = current_database() + AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) ) + AND information_schema.key_column_usage.table_name = ? + ', array( $type ) ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $k['name'], + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + self::C_DATATYPE_INTEGER => ' integer ', + self::C_DATATYPE_DOUBLE => ' double precision ', + self::C_DATATYPE_TEXT => ' text ', + self::C_DATATYPE_SPECIAL_DATE => ' date ', + self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ', + self::C_DATATYPE_SPECIAL_POINT => ' point ', + self::C_DATATYPE_SPECIAL_LSEG => ' lseg ', + self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ', + self::C_DATATYPE_SPECIAL_MONEY => ' money ', + self::C_DATATYPE_SPECIAL_MONEY2 => ' numeric(10,2) ', + self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ', + self::C_DATATYPE_SPECIAL_JSON => ' json ', + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[trim( strtolower( $v ) )] = $k; + } + + $this->adapter = $adapter; + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer + */ + public function getTypeForID() + { + return self::C_DATATYPE_INTEGER; + } + + /** + * @see QueryWriter::getTables + */ + public function getTables() + { + return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $table = $this->esc( $table, TRUE ); + + $columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table' AND table_schema = ANY( current_schemas( FALSE ) )" ); + + $columns = array(); + foreach ( $columnsRaw as $r ) { + $columns[$r['column_name']] = $r['data_type']; + } + + return $columns; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( $value === INF ) return self::C_DATATYPE_TEXT; + + if ( $flagSpecial && $value ) { + if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_DATE; + } + + if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME; + } + + if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_POINT; + } + + if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_LSEG; + } + + if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE; + } + + if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON; + } + + if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_MONEY; + } + + if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_MONEY2; + } + } + + if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; + + if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; + + if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value ) + && AQueryWriter::canBeTreatedAsInt( $value ) + && $value < 2147483648 + && $value > -2147483648 ) + ) { + return self::C_DATATYPE_INTEGER; + } elseif ( is_numeric( $value ) ) { + return self::C_DATATYPE_DOUBLE; + } else { + return self::C_DATATYPE_TEXT; + } + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + $r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99; + + if ( $includeSpecials ) return $r; + + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + return self::C_DATATYPE_SPECIFIED; + } + + return $r; + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $column, $datatype ) + { + $table = $type; + $type = $datatype; + + $table = $this->esc( $table ); + $column = $this->esc( $column ); + + $newtype = $this->typeno_sqltype[$type]; + + $this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " ); + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $columns = array(); + foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); + $table = $this->esc( $type ); + sort( $columns ); //else we get multiple indexes due to order-effects + $name = "UQ_" . sha1( $table . implode( ',', $columns ) ); + $sql = "ALTER TABLE {$table} + ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")"; + try { + $this->adapter->exec( $sql ); + } catch( SQLException $e ) { + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + '42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $property ) + { + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $property ); + + try { + $this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::addFK + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) + { + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $field = $this->esc( $property ); + $targetField = $this->esc( $targetProperty ); + $tableNoQ = $this->esc( $type, TRUE ); + $fieldNoQ = $this->esc( $property, TRUE ); + if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; + try{ + $delRule = ( $isDep ? 'CASCADE' : 'SET NULL' ); + $this->adapter->exec( "ALTER TABLE {$table} + ADD FOREIGN KEY ( {$field} ) REFERENCES {$targetTable} + ({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' ); + + foreach ( $this->getTables() as $t ) { + $t = $this->esc( $t ); + //Some plugins (PostGIS have unremovable tables/views), avoid exceptions. + try { $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); }catch( \Exception $e ) {} + } + + $this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' ); + } +} +} + +namespace RedBeanPHP { + +/** + * RedBean\Exception Base. + * Represents the base class for RedBeanPHP\Exceptions. + * + * @file RedBeanPHP/Exception.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RedException extends \Exception +{ +} +} + +namespace RedBeanPHP\RedException { + +use RedBeanPHP\RedException as RedException; + +/** + * SQL Exception. + * Represents a generic database exception independent of the underlying driver. + * + * @file RedBeanPHP/RedException/SQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQL extends RedException +{ + /** + * @var string + */ + private $sqlState; + + /** + * Returns an ANSI-92 compliant SQL state. + * + * @return string + */ + public function getSQLState() + { + return $this->sqlState; + } + + /** + * Returns the raw SQL STATE, possibly compliant with + * ANSI SQL error codes - but this depends on database driver. + * + * @param string $sqlState SQL state error code + * + * @return void + */ + public function setSQLState( $sqlState ) + { + $this->sqlState = $sqlState; + } + + /** + * To String prints both code and SQL state. + * + * @return string + */ + public function __toString() + { + return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n". + 'trace: ' . $this->getTraceAsString(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Cursor as Cursor; +use RedBeanPHP\Cursor\NullCursor as NullCursor; + +/** + * Abstract Repository. + * + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Repository +{ + /** + * @var array + */ + protected $stash = NULL; + + /* + * @var integer + */ + protected $nesting = 0; + + /** + * @var DBAdapter + */ + protected $writer; + + /** + * Stores a bean and its lists in one run. + * + * @param OODBBean $bean bean to process + * + * @return void + */ + protected function storeBeanWithLists( OODBBean $bean ) + { + $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups + foreach ( $bean as $property => $value ) { + $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value; + if ( $value instanceof OODBBean ) { + $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); + $bean->setMeta("sys.typeof.{$property}", $value->getMeta('type')); + } elseif ( is_array( $value ) ) { + foreach($value as &$item) { + $item = ( $item instanceof SimpleModel ) ? $item->unbox() : $item; + } + $originals = $bean->moveMeta( 'sys.shadow.' . $property, array() ); + if ( strpos( $property, 'own' ) === 0 ) { + list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); + $listName = lcfirst( substr( $property, 3 ) ); + if ($bean->moveMeta( 'sys.exclusive-'. $listName ) ) { + OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE ); + OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) ); + } + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); + unset( $bean->$property ); + } + } + } + $this->storeBean( $bean ); + $this->processTrashcan( $bean, $ownTrashcan ); + $this->processAdditions( $bean, $ownAdditions ); + $this->processResidue( $ownresidue ); + $this->processSharedTrashcan( $bean, $sharedTrashcan ); + $this->processSharedAdditions( $bean, $sharedAdditions ); + $this->processSharedResidue( $bean, $sharedresidue ); + } + + /** + * Process groups. Internal function. Processes different kind of groups for + * storage function. Given a list of original beans and a list of current beans, + * this function calculates which beans remain in the list (residue), which + * have been deleted (are in the trashcan) and which beans have been added + * (additions). + * + * @param array $originals originals + * @param array $current the current beans + * @param array $additions beans that have been added + * @param array $trashcan beans that have been deleted + * @param array $residue beans that have been left untouched + * + * @return array + */ + protected function processGroups( $originals, $current, $additions, $trashcan, $residue ) + { + return array( + array_merge( $additions, array_diff( $current, $originals ) ), + array_merge( $trashcan, array_diff( $originals, $current ) ), + array_merge( $residue, array_intersect( $current, $originals ) ) + ); + } + + /** + * Processes an embedded bean. + * + * @param OODBBean|SimpleModel $embeddedBean the bean or model + * + * @return integer + */ + protected function prepareEmbeddedBean( $embeddedBean ) + { + if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) { + $this->store( $embeddedBean ); + } + + return $embeddedBean->id; + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles shared addition lists; i.e. the $bean->sharedObject properties. + * + * @param OODBBean $bean the bean + * @param array $sharedAdditions list with shared additions + * + * @return void + */ + protected function processSharedAdditions( $bean, $sharedAdditions ) + { + foreach ( $sharedAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + $this->oodb->getAssociationManager()->associate( $addition, $bean ); + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles own lists; i.e. the $bean->ownObject properties. + * A residue is a bean in an own-list that stays where it is. This method + * checks if there have been any modification to this bean, in that case + * the bean is stored once again, otherwise the bean will be left untouched. + * + * @param OODBBean $bean bean tor process + * @param array $ownresidue list to process + * + * @return void + */ + protected function processResidue( $ownresidue ) + { + foreach ( $ownresidue as $residue ) { + if ( $residue->getMeta( 'tainted' ) ) { + $this->store( $residue ); + } + } + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles own lists; i.e. the $bean->ownObject properties. + * A trash can bean is a bean in an own-list that has been removed + * (when checked with the shadow). This method + * checks if the bean is also in the dependency list. If it is the bean will be removed. + * If not, the connection between the bean and the owner bean will be broken by + * setting the ID to NULL. + * + * @param OODBBean $bean bean to process + * @param array $ownTrashcan list to process + * + * @return void + */ + protected function processTrashcan( $bean, $ownTrashcan ) + { + foreach ( $ownTrashcan as $trash ) { + + $myFieldLink = $bean->getMeta( 'type' ) . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + if ( $trash->getMeta( 'sys.garbage' ) === true ) { + $this->trash( $trash ); + } else { + $trash->$myFieldLink = NULL; + $this->store( $trash ); + } + } + } + + /** + * Unassociates the list items in the trashcan. + * + * @param OODBBean $bean bean to process + * @param array $sharedTrashcan list to process + * + * @return void + */ + protected function processSharedTrashcan( $bean, $sharedTrashcan ) + { + foreach ( $sharedTrashcan as $trash ) { + $this->oodb->getAssociationManager()->unassociate( $trash, $bean ); + } + } + + /** + * Stores all the beans in the residue group. + * + * @param OODBBean $bean bean to process + * @param array $sharedresidue list to process + * + * @return void + */ + protected function processSharedResidue( $bean, $sharedresidue ) + { + foreach ( $sharedresidue as $residue ) { + $this->store( $residue ); + } + } + + /** + * Determines whether the bean has 'loaded lists' or + * 'loaded embedded beans' that need to be processed + * by the store() method. + * + * @param OODBBean $bean bean to be examined + * + * @return boolean + */ + protected function hasListsOrObjects( OODBBean $bean ) + { + $processLists = FALSE; + foreach ( $bean as $value ) { + if ( is_array( $value ) || is_object( $value ) ) { + $processLists = TRUE; + break; + } + } + + return $processLists; + } + + /** + * Converts an embedded bean to an ID, removed the bean property and + * stores the bean in the embedded beans array. + * + * @param array $embeddedBeans destination array for embedded bean + * @param OODBBean $bean target bean to process + * @param string $property property that contains the embedded bean + * @param OODBBean $value embedded bean itself + * + * @return void + */ + protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value ) + { + $linkField = $property . '_id'; + $id = $this->prepareEmbeddedBean( $value ); + if ($bean->$linkField != $id) $bean->$linkField = $id; + $bean->setMeta( 'cast.' . $linkField, 'id' ); + $embeddedBeans[$linkField] = $value; + unset( $bean->$property ); + } + + /** + * Constructor, requires a query writer. + * Creates a new instance of the bean respository class. + * + * @param QueryWriter $writer the Query Writer to use for this repository + * + * @return void + */ + public function __construct( OODB $oodb, QueryWriter $writer ) + { + $this->writer = $writer; + $this->oodb = $oodb; + } + + /** + * Checks whether a OODBBean bean is valid. + * If the type is not valid or the ID is not valid it will + * throw an exception: Security. + * + * @param OODBBean $bean the bean that needs to be checked + * + * @return void + */ + public function check( OODBBean $bean ) + { + //Is all meta information present? + if ( !isset( $bean->id ) ) { + throw new RedException( 'Bean has incomplete Meta Information id ' ); + } + if ( !( $bean->getMeta( 'type' ) ) ) { + throw new RedException( 'Bean has incomplete Meta Information II' ); + } + //Pattern of allowed characters + $pattern = '/[^a-z0-9_]/i'; + //Does the type contain invalid characters? + if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) { + throw new RedException( 'Bean Type is invalid' ); + } + //Are the properties and values valid? + foreach ( $bean as $prop => $value ) { + if ( + is_array( $value ) + || ( is_object( $value ) ) + ) { + throw new RedException( "Invalid Bean value: property $prop" ); + } else if ( + strlen( $prop ) < 1 + || preg_match( $pattern, $prop ) + ) { + throw new RedException( "Invalid Bean property: property $prop" ); + } + } + } + + /** + * Searches the database for a bean that matches conditions $conditions and sql $addSQL + * and returns an array containing all the beans that have been found. + * + * Conditions need to take form: + * + * + * array( + * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) + * 'PROPERTY' => array( POSSIBLE VALUES... ) + * ); + * + * + * All conditions are glued together using the AND-operator, while all value lists + * are glued using IN-operators thus acting as OR-conditions. + * + * Note that you can use property names; the columns will be extracted using the + * appropriate bean formatter. + * + * @param string $type type of beans you are looking for + * @param array $conditions list of conditions + * @param string $addSQL SQL to be used in query + * @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) + * + * @return array + */ + public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) + { + //for backward compatibility, allow mismatch arguments: + if ( is_array( $sql ) ) { + if ( isset( $sql[1] ) ) { + $bindings = $sql[1]; + } + $sql = $sql[0]; + } + try { + $beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) ); + + return $beans; + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + + return array(); + } + + /** + * Finds a BeanCollection. + * + * @param string $type type of beans you are looking for + * @param string $sql SQL to be used in query + * @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) + * + * @return BeanCollection + */ + public function findCollection( $type, $sql, $bindings = array() ) + { + try { + $cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings ); + return new BeanCollection( $type, $this, $cursor ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + return new BeanCollection( $type, $this, new NullCursor ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. We use + * explicit casts instead of functions to preserve performance + * (0.13 vs 0.28 for 10000 iterations on Core i3). + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public function store( $bean ) + { + $processLists = $this->hasListsOrObjects( $bean ); + if ( !$processLists && !$bean->getMeta( 'tainted' ) ) { + return $bean->getID(); //bail out! + } + $this->oodb->signal( 'update', $bean ); + $processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model! + if ( $processLists ) { + $this->storeBeanWithLists( $bean ); + } else { + $this->storeBean( $bean ); + } + $this->oodb->signal( 'after_update', $bean ); + + return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id; + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public function batch( $type, $ids ) + { + if ( !$ids ) { + return array(); + } + $collection = array(); + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) ); + } catch ( SQLException $e ) { + $this->handleException( $e ); + $rows = FALSE; + } + $this->stash[$this->nesting] = array(); + if ( !$rows ) { + return array(); + } + foreach ( $rows as $row ) { + $this->stash[$this->nesting][$row['id']] = $row; + } + foreach ( $ids as $id ) { + $collection[$id] = $this->load( $type, $id ); + } + $this->stash[$this->nesting] = NULL; + + return $collection; + } + + /** + * This is a convenience method; it converts database rows + * (arrays) into beans. Given a type and a set of rows this method + * will return an array of beans of the specified type loaded with + * the data fields provided by the result set from the database. + * + * New in 4.3.2: meta mask. The meta mask is a special mask to send + * data from raw result rows to the meta store of the bean. This is + * useful for bundling additional information with custom queries. + * Values of every column whos name starts with $mask will be + * transferred to the meta section of the bean under key 'data.bundle'. + * + * @param string $type type of beans you would like to have + * @param array $rows rows from the database result + * @param string $mask meta mask to apply (optional) + * + * @return array + */ + public function convertToBeans( $type, $rows, $mask = NULL ) + { + $masklen = 0; + if ( $mask !== NULL ) $masklen = mb_strlen( $mask ); + + $collection = array(); + $this->stash[$this->nesting] = array(); + foreach ( $rows as $row ) { + $meta = array(); + if ( !is_null( $mask ) ) { + foreach( $row as $key => $value ) { + if ( strpos( $key, $mask ) === 0 ) { + unset( $row[$key] ); + $meta[$key] = $value; + } + } + } + + $id = $row['id']; + $this->stash[$this->nesting][$id] = $row; + $collection[$id] = $this->load( $type, $id ); + + if ( $mask !== NULL ) { + $collection[$id]->setMeta( 'data.bundle', $meta ); + } + } + $this->stash[$this->nesting] = NULL; + + return $collection; + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public function count( $type, $addSQL = '', $bindings = array() ) + { + $type = AQueryWriter::camelsSnake( $type ); + if ( count( explode( '_', $type ) ) > 2 ) { + throw new RedException( 'Invalid type for count.' ); + } + + try { + return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings ); + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) { + throw $exception; + } + } + + return 0; + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + */ + public function trash( $bean ) + { + $this->oodb->signal( 'delete', $bean ); + foreach ( $bean as $property => $value ) { + if ( $value instanceof OODBBean ) { + unset( $bean->$property ); + } + if ( is_array( $value ) ) { + if ( strpos( $property, 'own' ) === 0 ) { + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + unset( $bean->$property ); + } + } + } + try { + $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + $bean->id = 0; + $this->oodb->signal( 'after_delete', $bean ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->writer->tableExists( $table ); + } + + /** + * Trash all beans of a given type. Wipes an entire type of bean. + * + * @param string $type type of bean you wish to delete all instances of + * + * @return boolean + */ + public function wipe( $type ) + { + try { + $this->writer->wipe( $type ); + + return TRUE; + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { + throw $exception; + } + + return FALSE; + } + } +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Repository as Repository; + +/** + * Fluid Repository. + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository/Fluid.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Fluid extends Repository +{ + /** + * Figures out the desired type given the cast string ID. + * Given a cast ID, this method will return the associated + * type (INT(10) or VARCHAR for instance). The returned type + * can be processed by the Query Writer to build the specified + * column for you in the database. The Cast ID is actually just + * a superset of the QueryWriter types. In addition to default + * Query Writer column types you can pass the following 'cast types': + * 'id' and 'string'. These will map to Query Writer specific + * column types (probably INT and VARCHAR). + * + * @param string $cast cast identifier + * + * @return integer + */ + private function getTypeFromCast( $cast ) + { + if ( $cast == 'string' ) { + $typeno = $this->writer->scanType( 'STRING' ); + } elseif ( $cast == 'id' ) { + $typeno = $this->writer->getTypeForID(); + } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) { + $typeno = $this->writer->sqltype_typeno[$cast]; + } else { + throw new RedException( 'Invalid Cast' ); + } + + return $typeno; + } + + /** + * Orders the Query Writer to create a table if it does not exist already and + * adds a note in the build report about the creation. + * + * @param OODBBean $bean bean to update report of + * @param string $table table to check and create if not exists + * + * @return void + */ + private function createTableIfNotExists( OODBBean $bean, $table ) + { + //Does table exist? If not, create + if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) { + $this->writer->createTable( $table ); + $bean->setMeta( 'buildreport.flags.created', TRUE ); + } + } + + /** + * Modifies the table to fit the bean data. + * Given a property and a value and the bean, this method will + * adjust the table structure to fit the requirements of the property and value. + * This may include adding a new column or widening an existing column to hold a larger + * or different kind of value. This method employs the writer to adjust the table + * structure in the database. Schema updates are recorded in meta properties of the bean. + * + * This method will also apply indexes, unique constraints and foreign keys. + * + * @param OODBBean $bean bean to get cast data from and store meta in + * @param string $property property to store + * @param mixed $value value to store + * + * @return void + */ + private function modifySchema( OODBBean $bean, $property, $value ) + { + $doFKStuff = FALSE; + $table = $bean->getMeta( 'type' ); + $columns = $this->writer->getColumns( $table ); + $columnNoQ = $this->writer->esc( $property, TRUE ); + if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) { + if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types + $cast = $bean->getMeta( "cast.$property" ); + $typeno = $this->getTypeFromCast( $cast ); + } else { + $cast = FALSE; + $typeno = $this->writer->scanType( $value, TRUE ); + } + if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ? + if ( !$cast ) { //rescan without taking into account special types >80 + $typeno = $this->writer->scanType( $value, FALSE ); + } + $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] ); + if ( $typeno > $sqlt ) { //no, we have to widen the database column type + $this->writer->widenColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.widen', TRUE ); + $doFKStuff = TRUE; + } + } else { + $this->writer->addColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.addcolumn', TRUE ); + $doFKStuff = TRUE; + } + if ($doFKStuff) { + if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) { + $destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3); + $indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}"; + $this->writer->addIndex($table, $indexName, $columnNoQ); + $typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ); + $isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE ); + //Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean + $isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) ); + $result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep ); + //If this is a link bean and all unique columns have been added already, then apply unique constraint + if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) { + $this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') ); + $bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL); + } + } + } + } + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * Note that this method raises a custom exception if the bean + * is not an instance of OODBBean. Therefore it does not use + * a type hint. This allows the user to take action in case + * invalid objects are passed in the list. + * + * @param OODBBean $bean bean to process + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + */ + protected function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + + if ($alias) { + $addition->setMeta( "sys.typeof.{$alias}", $beanType ); + } else { + $addition->setMeta( "sys.typeof.{$beanType}", $beanType ); + } + + $this->store( $addition ); + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + $this->check( $bean ); + $table = $bean->getMeta( 'type' ); + $this->createTableIfNotExists( $bean, $table ); + + $updateValues = array(); + foreach ( $bean as $property => $value ) { + if ( $property !== 'id' ) { + $this->modifySchema( $bean, $property, $value ); + } + if ( $property !== 'id' ) { + $updateValues[] = array( 'property' => $property, 'value' => $value ); + } + } + + $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Handles exceptions. Suppresses exceptions caused by missing structures. + * + * @param Exception $exception exception + * + * @return void + */ + protected function handleException( \Exception $exception ) + { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) + ) { + throw $exception; + } + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->check( $bean ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQLException $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + $rows = 0; + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Repository as Repository; + +/** + * Frozen Repository. + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository/Frozen.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Frozen extends Repository +{ + /** + * Handles exceptions. + * In fluid mode, this suppresses exceptions caused by missing structures. + * However the implementation in frozen mode is rather the opposite, it + * will just re-throw every exception. + * + * @param \Exception $exception exception to handle + * + * @return void + */ + protected function handleException( \Exception $exception ) + { + throw $exception; + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + + list( $properties, $table ) = $bean->getPropertiesAndType(); + $id = $properties['id']; + unset($properties['id']); + $updateValues = array(); + $k1 = 'property'; + $k2 = 'value'; + foreach( $properties as $key => $value ) { + $updateValues[] = array( $k1 => $key, $k2 => $value ); + } + $bean->id = $this->writer->updateRecord( $table, $updateValues, $id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * Note that this method raises a custom exception if the bean + * is not an instance of OODBBean. Therefore it does not use + * a type hint. This allows the user to take action in case + * invalid objects are passed in the list. + * + * @param OODBBean $bean bean to process + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + * @throws RedException + */ + protected function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + $cachedIndex = array(); + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + $this->store( $addition ); + + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param int $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + /** @var \RedBeanPHP\OODBBean $bean */ + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + * @throws SQLException + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQLException $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + throw $exception; //only throw if frozen + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Repository as Repository; +use RedBeanPHP\Repository\Fluid as FluidRepo; +use RedBeanPHP\Repository\Frozen as FrozenRepo; + +/** + * RedBean Object Oriented DataBase. + * + * The RedBean OODB Class is the main class of RedBeanPHP. + * It takes OODBBean objects and stores them to and loads them from the + * database as well as providing other CRUD functions. This class acts as a + * object database. + * + * @file RedBeanPHP/OODB.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODB extends Observable +{ + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var array + */ + protected $chillList = array(); + + /** + * @var array + */ + protected $stash = NULL; + + /* + * @var integer + */ + protected $nesting = 0; + + /** + * @var DBAdapter + */ + protected $writer; + + /** + * @var boolean + */ + protected $isFrozen = FALSE; + + /** + * @var FacadeBeanHelper + */ + protected $beanhelper = NULL; + + /** + * @var AssociationManager + */ + protected $assocManager = NULL; + + /** + * @var Repository + */ + protected $repository = NULL; + + /** + * @var FrozenRepo + */ + protected $frozenRepository = NULL; + + /** + * @var FluidRepo + */ + protected $fluidRepository = NULL; + + /** + * @var boolean + */ + protected static $autoClearHistoryAfterStore = FALSE; + + /** + * If set to TRUE, this method will call clearHistory every time + * the bean gets stored. + * + * @param boolean $autoClear auto clear option + * + * @return void + */ + public static function autoClearHistoryAfterStore( $autoClear = TRUE ) + { + self::$autoClearHistoryAfterStore = (boolean) $autoClear; + } + + /** + * Unboxes a bean from a FUSE model if needed and checks whether the bean is + * an instance of OODBBean. + * + * @param OODBBean $bean bean you wish to unbox + * + * @return OODBBean + */ + protected function unboxIfNeeded( $bean ) + { + if ( $bean instanceof SimpleModel ) { + $bean = $bean->unbox(); + } + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); + } + + return $bean; + } + + /** + * Constructor, requires a query writer. + * + * @param QueryWriter $writer writer + * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled) + */ + public function __construct( QueryWriter $writer, $frozen = FALSE ) + { + if ( $writer instanceof QueryWriter ) { + $this->writer = $writer; + } + + $this->freeze( $frozen ); + } + + /** + * Toggles fluid or frozen mode. In fluid mode the database + * structure is adjusted to accomodate your objects. In frozen mode + * this is not the case. + * + * You can also pass an array containing a selection of frozen types. + * Let's call this chilly mode, it's just like fluid mode except that + * certain types (i.e. tables) aren't touched. + * + * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode + * + * @return void + */ + public function freeze( $toggle ) + { + if ( is_array( $toggle ) ) { + $this->chillList = $toggle; + $this->isFrozen = FALSE; + } else { + $this->isFrozen = (boolean) $toggle; + } + + if ( $this->isFrozen ) { + if ( !$this->frozenRepository ) { + $this->frozenRepository = new FrozenRepo( $this, $this->writer ); + } + + $this->repository = $this->frozenRepository; + + } else { + if ( !$this->fluidRepository ) { + $this->fluidRepository = new FluidRepo( $this, $this->writer ); + } + + $this->repository = $this->fluidRepository; + } + + if ( count( self::$sqlFilters ) ) { + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); + } + + } + + /** + * Returns the current mode of operation of RedBean. + * In fluid mode the database + * structure is adjusted to accomodate your objects. + * In frozen mode + * this is not the case. + * + * @return boolean + */ + public function isFrozen() + { + return (bool) $this->isFrozen; + } + + /** + * Determines whether a type is in the chill list. + * If a type is 'chilled' it's frozen, so its schema cannot be + * changed anymore. However other bean types may still be modified. + * This method is a convenience method for other objects to check if + * the schema of a certain type is locked for modification. + * + * @param string $type the type you wish to check + * + * @return boolean + */ + public function isChilled( $type ) + { + return (boolean) ( in_array( $type, $this->chillList ) ); + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + if ( $number < 1 ) { + if ( $alwaysReturnArray ) return array(); + return NULL; + } + + return $this->repository->dispense( $type, $number, $alwaysReturnArray ); + } + + /** + * Sets bean helper to be given to beans. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @param BeanHelper $beanhelper helper + * + * @return void + */ + public function setBeanHelper( BeanHelper $beanhelper ) + { + $this->beanhelper = $beanhelper; + } + + /** + * Returns the current bean helper. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @return BeanHelper + */ + public function getBeanHelper() + { + return $this->beanhelper; + } + + /** + * Checks whether a OODBBean bean is valid. + * If the type is not valid or the ID is not valid it will + * throw an exception: Security. + * + * @param OODBBean $bean the bean that needs to be checked + * + * @return void + */ + public function check( OODBBean $bean ) + { + $this->repository->check( $bean ); + } + + /** + * Searches the database for a bean that matches conditions $conditions and sql $addSQL + * and returns an array containing all the beans that have been found. + * + * Conditions need to take form: + * + * + * array( + * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) + * 'PROPERTY' => array( POSSIBLE VALUES... ) + * ); + * + * + * All conditions are glued together using the AND-operator, while all value lists + * are glued using IN-operators thus acting as OR-conditions. + * + * Note that you can use property names; the columns will be extracted using the + * appropriate bean formatter. + * + * @param string $type type of beans you are looking for + * @param array $conditions list of conditions + * @param string $addSQL SQL to be used in query + * @param array $bindings a list of values to bind to query parameters + * + * @return array + */ + public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) + { + return $this->repository->find( $type, $conditions, $sql, $bindings ); + } + + /** + * Same as find() but returns a BeanCollection. + * + * @param string $type type of beans you are looking for + * @param string $addSQL SQL to be used in query + * @param array $bindings a list of values to bind to query parameters + * + * @return array + */ + public function findCollection( $type, $sql = NULL, $bindings = array() ) + { + return $this->repository->findCollection( $type, $sql, $bindings ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->repository->tableExists( $table ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. We use + * explicit casts instead of functions to preserve performance + * (0.13 vs 0.28 for 10000 iterations on Core i3). + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public function store( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + $id = $this->repository->store( $bean ); + if ( self::$autoClearHistoryAfterStore ) { + $bean->clearHistory(); + } + return $id; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public function load( $type, $id ) + { + return $this->repository->load( $type, $id ); + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + */ + public function trash( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + return $this->repository->trash( $bean ); + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public function batch( $type, $ids ) + { + return $this->repository->batch( $type, $ids ); + } + + /** + * This is a convenience method; it converts database rows + * (arrays) into beans. Given a type and a set of rows this method + * will return an array of beans of the specified type loaded with + * the data fields provided by the result set from the database. + * + * @param string $type type of beans you would like to have + * @param array $rows rows from the database result + * @param string $mask mask to apply for meta data + * + * @return array + */ + public function convertToBeans( $type, $rows, $mask = NULL ) + { + return $this->repository->convertToBeans( $type, $rows, $mask ); + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public function count( $type, $addSQL = '', $bindings = array() ) + { + return $this->repository->count( $type, $addSQL, $bindings ); + } + + /** + * Trash all beans of a given type. Wipes an entire type of bean. + * + * @param string $type type of bean you wish to delete all instances of + * + * @return boolean + */ + public function wipe( $type ) + { + return $this->repository->wipe( $type ); + } + + /** + * Returns an Association Manager for use with OODB. + * A simple getter function to obtain a reference to the association manager used for + * storage and more. + * + * @return AssociationManager + */ + public function getAssociationManager() + { + if ( !isset( $this->assocManager ) ) { + throw new RedException( 'No association manager available.' ); + } + + return $this->assocManager; + } + + /** + * Sets the association manager instance to be used by this OODB. + * A simple setter function to set the association manager to be used for storage and + * more. + * + * @param AssociationManager $assoc sets the association manager to be used + * + * @return void + */ + public function setAssociationManager( AssociationManager $assocManager ) + { + $this->assocManager = $assocManager; + } + + /** + * Returns the currently used repository instance. + * For testing purposes only. + * + * @return Repository + */ + public function getCurrentRepository() + { + return $this->repository; + } + + /** + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. + * + * @param string $mode mode to set function for, i.e. read or write + * @param string $field field (table.column) to bind SQL function to + * @param string $function SQL function to bind to field + * + * @return void + */ + public function bindFunc( $mode, $field, $function ) + { + list( $type, $property ) = explode( '.', $field ); + $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ; + + if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array(); + if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array(); + + if ( is_null( $function ) ) { + unset( self::$sqlFilters[$mode][$type][$property] ); + } else { + if ($mode === QueryWriter::C_SQLFILTER_WRITE) { + self::$sqlFilters[$mode][$type][$property] = $function.'(?)'; + } else { + self::$sqlFilters[$mode][$type][$property] = $function."($field)"; + } + } + + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * ToolBox. + * + * The toolbox is an integral part of RedBeanPHP providing the basic + * architectural building blocks to manager objects, helpers and additional tools + * like plugins. A toolbox contains the three core components of RedBeanPHP: + * the adapter, the query writer and the core functionality of RedBeanPHP in + * OODB. + * + * @file RedBeanPHP/ToolBox.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class ToolBox +{ + /** + * @var OODB + */ + protected $oodb; + + /** + * @var QueryWriter + */ + protected $writer; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * Constructor. + * The toolbox is an integral part of RedBeanPHP providing the basic + * architectural building blocks to manager objects, helpers and additional tools + * like plugins. A toolbox contains the three core components of RedBeanPHP: + * the adapter, the query writer and the core functionality of RedBeanPHP in + * OODB. + * + * @param OODB $oodb Object Database, OODB + * @param DBAdapter $adapter Database Adapter + * @param QueryWriter $writer Query Writer + */ + public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer ) + { + $this->oodb = $oodb; + $this->adapter = $adapter; + $this->writer = $writer; + return $this; + } + + /** + * Returns the query writer in this toolbox. + * The Query Writer is responsible for building the queries for a + * specific database and executing them through the adapter. + * + * @return QueryWriter + */ + public function getWriter() + { + return $this->writer; + } + + /** + * Returns the OODB instance in this toolbox. + * OODB is responsible for creating, storing, retrieving and deleting + * single beans. Other components rely + * on OODB for their basic functionality. + * + * @return OODB + */ + public function getRedBean() + { + return $this->oodb; + } + + /** + * Returns the database adapter in this toolbox. + * The adapter is responsible for executing the query and binding the values. + * The adapter also takes care of transaction handling. + * + * @return DBAdapter + */ + public function getDatabaseAdapter() + { + return $this->adapter; + } +} +} + +namespace RedBeanPHP { + + +/** + * RedBeanPHP Finder. + * Service class to find beans. For the most part this class + * offers user friendly utility methods for interacting with the + * OODB::find() method, which is rather complex. This class can be + * used to find beans using plain old SQL queries. + * + * @file RedBeanPHP/Finder.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Finder +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var OODB + */ + protected $redbean; + + /** + * Constructor. + * The Finder requires a toolbox. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + } + + /** + * Finds a bean using a type and a where clause (SQL). + * As with most Query tools in RedBean you can provide values to + * be inserted in the SQL statement by populating the value + * array parameter; you can either use the question mark notation + * or the slot-notation (:keyname). + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function find( $type, $sql = NULL, $bindings = array() ) + { + if ( !is_array( $bindings ) ) { + throw new RedException( + 'Expected array, ' . gettype( $bindings ) . ' given.' + ); + } + + return $this->redbean->find( $type, array(), $sql, $bindings ); + } + + /** + * Like find() but also exports the beans as an array. + * This method will perform a find-operation. For every bean + * in the result collection this method will call the export() method. + * This method returns an array containing the array representations + * of every bean in the result set. + * + * @see Finder::find + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findAndExport( $type, $sql = NULL, $bindings = array() ) + { + $arr = array(); + foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) { + $arr[] = $item->export(); + } + + return $arr; + } + + /** + * Like find() but returns just one bean instead of an array of beans. + * This method will return only the first bean of the array. + * If no beans are found, this method will return NULL. + * + * @see Finder::find + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findOne( $type, $sql = NULL, $bindings = array() ) + { + $sql = $this->toolbox->getWriter()->glueLimitOne( $sql ); + + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return reset( $items ); + } + + /** + * Like find() but returns the last bean of the result array. + * Opposite of Finder::findLast(). + * If no beans are found, this method will return NULL. + * + * @see Finder::find + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findLast( $type, $sql = NULL, $bindings = array() ) + { + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return end( $items ); + } + + /** + * Tries to find beans of a certain type, + * if no beans are found, it dispenses a bean of that type. + * Note that this function always returns an array. + * + * @see Finder::find + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findOrDispense( $type, $sql = NULL, $bindings = array() ) + { + $foundBeans = $this->find( $type, $sql, $bindings ); + + if ( empty( $foundBeans ) ) { + return array( $this->redbean->dispense( $type ) ); + } else { + return $foundBeans; + } + } + + /** + * Finds a BeanCollection using the repository. + * A bean collection can be used to retrieve one bean at a time using + * cursors - this is useful for processing large datasets. A bean collection + * will not load all beans into memory all at once, just one at a time. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return BeanCollection + */ + public function findCollection( $type, $sql, $bindings = array() ) + { + return $this->redbean->findCollection( $type, $sql, $bindings ); + } + + /** + * Finds or creates a bean. + * Tries to find a bean with certain properties specified in the second + * parameter ($like). If the bean is found, it will be returned. + * If multiple beans are found, only the first will be returned. + * If no beans match the criteria, a new bean will be dispensed, + * the criteria will be imported as properties and this new bean + * will be stored and returned. + * + * Format of criteria set: property => value + * The criteria set also supports OR-conditions: property => array( value1, orValue2 ) + * + * @param string $type type of bean to search for + * @param array $like criteria set describing bean to search for + * + * @return OODBBean + */ + public function findOrCreate( $type, $like = array() ) + { + $beans = $this->findLike( $type, $like ); + if ( count( $beans ) ) { + $bean = reset( $beans ); + return $bean; + } + + $bean = $this->redbean->dispense( $type ); + $bean->import( $like ); + $this->redbean->store( $bean ); + return $bean; + } + + /** + * Finds beans by its type and a certain criteria set. + * + * Format of criteria set: property => value + * The criteria set also supports OR-conditions: property => array( value1, orValue2 ) + * + * If the additional SQL is a condition, this condition will be glued to the rest + * of the query using an AND operator. Note that this is as far as this method + * can go, there is no way to glue additional SQL using an OR-condition. + * This method provides access to an underlying mechanism in the RedBeanPHP architecture + * to find beans using criteria sets. However, please do not use this method + * for complex queries, use plain SQL instead ( the regular find method ) as it is + * more suitable for the job. This method is + * meant for basic search-by-example operations. + * + * @param string $type type of bean to search for + * @param array $conditions criteria set describing the bean to search for + * @param string $sql additional SQL (for sorting) + * + * @return array + */ + public function findLike( $type, $conditions = array(), $sql = '' ) + { + if ( count( $conditions ) > 0 ) { + foreach( $conditions as $key => $condition ) { + if ( !count( $condition ) ) unset( $conditions[$key] ); + } + } + + return $this->redbean->find( $type, $conditions, $sql ); + } + + /** + * Returns a hashmap with bean arrays keyed by type using an SQL + * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review' + * this method will return movie and review beans. + * + * Example: + * + * + * $stuff = $finder->findMulti('movie,review', ' + * SELECT movie.*, review.* FROM movie + * LEFT JOIN review ON review.movie_id = movie.id'); + * + * + * After this operation, $stuff will contain an entry 'movie' containing all + * movies and an entry named 'review' containing all reviews (all beans). + * You can also pass bindings. + * + * If you want to re-map your beans, so you can use $movie->ownReviewList without + * having RedBeanPHP executing an SQL query you can use the fourth parameter to + * define a selection of remapping closures. + * + * The remapping argument (optional) should contain an array of arrays. + * Each array in the remapping array should contain the following entries: + * + * + * array( + * 'a' => TYPE A + * 'b' => TYPE B + * 'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS + * 'do' => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS + * ) + * + * + * Using this mechanism you can build your own 'preloader' with tiny function + * snippets (and those can be re-used and shared online of course). + * + * Example: + * + * + * array( + * 'a' => 'movie' //define A as movie + * 'b' => 'review' //define B as review + * 'matcher' => function( $a, $b ) { + * return ( $b->movie_id == $a->id ); //Perform action if review.movie_id equals movie.id + * } + * 'do' => function( $a, $b ) { + * $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie + * $a->clearHistory(); //optional, act 'as if these beans have been loaded through ownReviewList'. + * } + * ) + * + * + * The Query Template parameter is optional as well but can be used to + * set a different SQL template (sprintf-style) for processing the original query. + * + * @note the SQL query provided IS NOT THE ONE used internally by this function, + * this function will pre-process the query to get all the data required to find the beans. + * + * @note if you use the 'book.*' notation make SURE you're + * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because + * it's actually an SQL-like template SLOT, not real SQL. + * + * @note instead of an SQL query you can pass a result array as well. + * + * @param string|array $types a list of types (either array or comma separated string) + * @param string|array $sqlOrArr an SQL query or an array of prefetched records + * @param array $bindings optional, bindings for SQL query + * @param array $remappings optional, an array of remapping arrays + * @param string $queryTemplate optional, query template + * + * @return array + */ + public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' ) + { + if ( !is_array( $types ) ) $types = explode( ',', $types ); + if ( !is_array( $sql ) ) { + $writer = $this->toolbox->getWriter(); + $adapter = $this->toolbox->getDatabaseAdapter(); + + //Repair the query, replace book.* with book.id AS book_id etc.. + foreach( $types as $type ) { + $pattern = " {$type}.*"; + if ( strpos( $sql, $pattern ) !== FALSE ) { + $newSelectorArray = array(); + $columns = $writer->getColumns( $type ); + foreach( $columns as $column => $definition ) { + $newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column ); + } + $newSelector = implode( ',', $newSelectorArray ); + $sql = str_replace( $pattern, $newSelector, $sql ); + } + } + + $rows = $adapter->get( $sql, $bindings ); + } else { + $rows = $sql; + } + + //Gather the bean data from the query results using the prefix + $wannaBeans = array(); + foreach( $types as $type ) { + $wannaBeans[$type] = array(); + $prefix = "{$type}__"; + foreach( $rows as $rowkey=>$row ) { + $wannaBean = array(); + foreach( $row as $cell => $value ) { + if ( strpos( $cell, $prefix ) === 0 ) { + $property = substr( $cell, strlen( $prefix ) ); + unset( $rows[$rowkey][$cell] ); + $wannaBean[$property] = $value; + } + } + if ( !isset( $wannaBean['id'] ) ) continue; + if ( is_null( $wannaBean['id'] ) ) continue; + $wannaBeans[$type][$wannaBean['id']] = $wannaBean; + } + } + + //Turn the rows into beans + $beans = array(); + foreach( $wannaBeans as $type => $wannabees ) { + $beans[$type] = $this->redbean->convertToBeans( $type, $wannabees ); + } + + //Apply additional re-mappings + foreach($remappings as $remapping) { + $a = $remapping['a']; + $b = $remapping['b']; + $matcher = $remapping['matcher']; + $do = $remapping['do']; + foreach( $beans[$a] as $bean ) { + foreach( $beans[$b] as $putBean ) { + if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping ); + } + } + } + return $beans; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * Association Manager. + * Manages simple bean associations. + * + * @file RedBeanPHP/AssociationManager.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class AssociationManager extends Observable +{ + /** + * @var OODB + */ + protected $oodb; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var QueryWriter + */ + protected $writer; + + /** + * Handles exceptions. Suppresses exceptions caused by missing structures. + * + * @param \Exception $exception exception to handle + * + * @return void + */ + private function handleException( \Exception $exception ) + { + if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) + ) + ) { + throw $exception; + } + } + + /** + * Internal method. + * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and + * $bindings bindings. If $getLinks is TRUE, link rows are returned instead. + * + * @param OODBBean $bean reference bean instance + * @param string $type target bean type + * @param string $sql additional SQL snippet + * @param array $bindings bindings for query + * + * @return array + */ + private function relatedRows( $bean, $type, $sql = '', $bindings = array() ) + { + $ids = array( $bean->id ); + $sourceType = $bean->getMeta( 'type' ); + try { + return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + return array(); + } + } + + /** + * Associates a pair of beans. This method associates two beans, no matter + * what types. Accepts a base bean that contains data for the linking record. + * This method is used by associate. This method also accepts a base bean to be used + * as the template for the link record in the database. + * + * @param OODBBean $bean1 first bean + * @param OODBBean $bean2 second bean + * @param OODBBean $bean base bean (association record) + * + * @return mixed + */ + protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + $property1 = $bean1->getMeta( 'type' ) . '_id'; + $property2 = $bean2->getMeta( 'type' ) . '_id'; + + if ( $property1 == $property2 ) { + $property2 = $bean2->getMeta( 'type' ) . '2_id'; + } + + $this->oodb->store( $bean1 ); + $this->oodb->store( $bean2 ); + + $bean->setMeta( "cast.$property1", "id" ); + $bean->setMeta( "cast.$property2", "id" ); + $bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) ); + + $bean->$property1 = $bean1->id; + $bean->$property2 = $bean2->id; + + $results = array(); + + try { + $id = $this->oodb->store( $bean ); + $results[] = $id; + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) ) + ) { + throw $exception; + } + } + + return $results; + } + + /** + * Constructor + * + * @param ToolBox $tools toolbox + */ + public function __construct( ToolBox $tools ) + { + $this->oodb = $tools->getRedBean(); + $this->adapter = $tools->getDatabaseAdapter(); + $this->writer = $tools->getWriter(); + $this->toolbox = $tools; + } + + /** + * Creates a table name based on a types array. + * Manages the get the correct name for the linking table for the + * types provided. + * + * @param array $types 2 types as strings + * + * @return string + */ + public function getTable( $types ) + { + return $this->writer->getAssocTable( $types ); + } + + /** + * Associates two beans in a many-to-many relation. + * This method will associate two beans and store the connection between the + * two in a link table. Instead of two single beans this method also accepts + * two sets of beans. Returns the ID or the IDs of the linking beans. + * + * @param OODBBean|array $beans1 one or more beans to form the association + * @param OODBBean|array $beans2 one or more beans to form the association + * + * @return array + */ + public function associate( $beans1, $beans2 ) + { + if ( !is_array( $beans1 ) ) { + $beans1 = array( $beans1 ); + } + + if ( !is_array( $beans2 ) ) { + $beans2 = array( $beans2 ); + } + + $results = array(); + foreach ( $beans1 as $bean1 ) { + foreach ( $beans2 as $bean2 ) { + $table = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) ); + $bean = $this->oodb->dispense( $table ); + $results[] = $this->associateBeans( $bean1, $bean2, $bean ); + } + } + + return ( count( $results ) > 1 ) ? $results : reset( $results ); + } + + /** + * Counts the number of related beans in an N-M relation. + * This method returns the number of beans of type $type associated + * with reference bean(s) $bean. The query can be tuned using an + * SQL snippet for additional filtering. + * + * @param OODBBean|array $bean a bean object or an array of beans + * @param string $type type of bean you're interested in + * @param string $sql SQL snippet (optional) + * @param array $bindings bindings for your SQL string + * + * @return integer + */ + public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() ) + { + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( + 'Expected array or OODBBean but got:' . gettype( $bean ) + ); + } + + if ( !$bean->id ) { + return 0; + } + + $beanType = $bean->getMeta( 'type' ); + + try { + return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + + return 0; + } + } + + /** + * Breaks the association between two beans. This method unassociates two beans. If the + * method succeeds the beans will no longer form an association. In the database + * this means that the association record will be removed. This method uses the + * OODB trash() method to remove the association links, thus giving FUSE models the + * opportunity to hook-in additional business logic. If the $fast parameter is + * set to boolean TRUE this method will remove the beans without their consent, + * bypassing FUSE. This can be used to improve performance. + * + * @param OODBBean $bean1 first bean in target association + * @param OODBBean $bean2 second bean in target association + * @param boolean $fast if TRUE, removes the entries by query without FUSE + * + * @return void + */ + public function unassociate( $beans1, $beans2, $fast = NULL ) + { + $beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1; + $beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2; + + foreach ( $beans1 as $bean1 ) { + foreach ( $beans2 as $bean2 ) { + try { + $this->oodb->store( $bean1 ); + $this->oodb->store( $bean2 ); + + $type1 = $bean1->getMeta( 'type' ); + $type2 = $bean2->getMeta( 'type' ); + + $row = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id ); + $linkType = $this->getTable( array( $type1, $type2 ) ); + + if ( $fast ) { + $this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) ); + + return; + } + + $beans = $this->oodb->convertToBeans( $linkType, array( $row ) ); + + if ( count( $beans ) > 0 ) { + $bean = reset( $beans ); + $this->oodb->trash( $bean ); + } + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + } + } + } + + /** + * Removes all relations for a bean. This method breaks every connection between + * a certain bean $bean and every other bean of type $type. Warning: this method + * is really fast because it uses a direct SQL query however it does not inform the + * models about this. If you want to notify FUSE models about deletion use a foreach-loop + * with unassociate() instead. (that might be slower though) + * + * @param OODBBean $bean reference bean + * @param string $type type of beans that need to be unassociated + * + * @return void + */ + public function clearRelations( OODBBean $bean, $type ) + { + $this->oodb->store( $bean ); + try { + $this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + } + + /** + * Returns all the beans associated with $bean. + * This method will return an array containing all the beans that have + * been associated once with the associate() function and are still + * associated with the bean specified. The type parameter indicates the + * type of beans you are looking for. You can also pass some extra SQL and + * values for that SQL to filter your results after fetching the + * related beans. + * + * Don't try to make use of subqueries, a subquery using IN() seems to + * be slower than two queries! + * + * Since 3.2, you can now also pass an array of beans instead just one + * bean as the first parameter. + * + * @param OODBBean|array $bean the bean you have + * @param string $type the type of beans you want + * @param string $sql SQL snippet for extra filtering + * @param array $bindings values to be inserted in SQL slots + * + * @return array + */ + public function related( $bean, $type, $sql = '', $bindings = array() ) + { + $sql = $this->writer->glueSQLCondition( $sql ); + $rows = $this->relatedRows( $bean, $type, $sql, $bindings ); + $links = array(); + + foreach ( $rows as $key => $row ) { + if ( !isset( $links[$row['id']] ) ) $links[$row['id']] = array(); + $links[$row['id']][] = $row['linked_by']; + unset( $rows[$key]['linked_by'] ); + } + + $beans = $this->oodb->convertToBeans( $type, $rows ); + foreach ( $beans as $bean ) $bean->setMeta( 'sys.belongs-to', $links[$bean->id] ); + + return $beans; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Bean Helper Interface. + * + * Interface for Bean Helper. + * A little bolt that glues the whole machinery together. + * The Bean Helper is passed to the OODB RedBeanPHP Object to + * faciliatte the creation of beans and providing them with + * a toolbox. The Helper also facilitates the FUSE feature, + * determining how beans relate to their models. By overriding + * the getModelForBean method you can tune the FUSEing to + * fit your business application needs. + * + * @file RedBeanPHP/IBeanHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface BeanHelper +{ + /** + * Returns a toolbox to empower the bean. + * This allows beans to perform OODB operations by themselves, + * as such the bean is a proxy for OODB. This allows beans to implement + * their magic getters and setters and return lists. + * + * @return ToolBox + */ + public function getToolbox(); + + /** + * Does approximately the same as getToolbox but also extracts the + * toolbox for you. + * This method returns a list with all toolbox items in Toolbox Constructor order: + * OODB, adapter, writer and finally the toolbox itself!. + * + * @return array + */ + public function getExtractedToolbox(); + + /** + * Given a certain bean this method will + * return the corresponding model. + * If no model is returned (NULL), RedBeanPHP might ask again. + * + * @note You can make RedBeanPHP faster by doing the setup wiring yourself. + * The event listeners take time, so to speed-up RedBeanPHP you can + * drop 'FUSE', if you're not interested in the Models. + * + * @note You can do funny stuff with this method but please be careful. + * You *could* create a model depending on properties of the bean, but + * it's a bit well... adventurous, here is an example: + * + * + * class Book extends RedBeanPHP\SimpleModel {}; + * class Booklet extends RedBeanPHP\SimpleModel {}; + * + * class FlexBeanHelper extends RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper { + * public function getModelForBean( RedBeanPHP\OODBBean $bean ) { + * if (!isset($bean->pages)) return NULL; //will ask again + * if ($bean->pages <= 10) return new Booklet; + * return new Book; + * } + * } + * + * $h = new FlexBeanHelper; + * R::getRedBean()->setBeanHelper($h); + * $book = R::dispense('book'); + * var_dump($book->box()); //NULL cant reach model + * $book->pages = 5; + * var_dump($book->box()); //Booklet + * $book->pages = 15; + * var_dump($book->box()); //still.. Booklet, model has been set + * $book2 = R::dispense('book'); + * $book2->pages = 15; + * var_dump($book2->box()); //Book, more than 10 pages + * + * + * @param OODBBean $bean bean to obtain the corresponding model of + * + * @return SimpleModel|CustomModel|NULL + */ + public function getModelForBean( OODBBean $bean ); +} +} + +namespace RedBeanPHP\BeanHelper { + +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\Facade as Facade; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; + +/** + * Bean Helper. + * + * The Bean helper helps beans to access access the toolbox and + * FUSE models. This Bean Helper makes use of the facade to obtain a + * reference to the toolbox. + * + * @file RedBeanPHP/BeanHelperFacade.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleFacadeBeanHelper implements BeanHelper +{ + /** + * Factory function to create instance of Simple Model, if any. + * + * @var \Closure + */ + private static $factory = null; + + /** + * Factory method using a customizable factory function to create + * the instance of the Simple Model. + * + * @param string $modelClassName name of the class + * + * @return SimpleModel + */ + public static function factory( $modelClassName ) + { + $factory = self::$factory; + return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName(); + } + + /** + * Sets the factory function to create the model when using FUSE + * to connect a bean to a model. + * + * @param \Closure $factory factory function + * + * @return void + */ + public static function setFactoryFunction( $factory ) + { + self::$factory = $factory; + } + + /** + * @see BeanHelper::getToolbox + */ + public function getToolbox() + { + return Facade::getToolBox(); + } + + /** + * @see BeanHelper::getModelForBean + */ + public function getModelForBean( OODBBean $bean ) + { + $model = $bean->getMeta( 'type' ); + $prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_'; + + if ( strpos( $model, '_' ) !== FALSE ) { + $modelParts = explode( '_', $model ); + $modelName = ''; + foreach( $modelParts as $part ) { + $modelName .= ucfirst( $part ); + } + $modelName = $prefix . $modelName; + + if ( !class_exists( $modelName ) ) { + //second try + $modelName = $prefix . ucfirst( $model ); + + if ( !class_exists( $modelName ) ) { + return NULL; + } + } + + } else { + + $modelName = $prefix . ucfirst( $model ); + if ( !class_exists( $modelName ) ) { + return NULL; + } + } + $obj = self::factory( $modelName ); + $obj->loadBean( $bean ); + + return $obj; + } + + /** + * @see BeanHelper::getExtractedToolbox + */ + public function getExtractedToolbox() + { + return Facade::getExtractedToolbox(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\OODBBean as OODBBean; + +/** + * SimpleModel + * Base Model For All RedBeanPHP Models using FUSE. + * + * RedBeanPHP FUSE is a mechanism to connect beans to posthoc + * models. Models are connected to beans by naming conventions. + * Actions on beans will result in actions on models. + * + * @file RedBeanPHP/SimpleModel.php + * @author Gabor de Mooij and the RedBeanPHP Team + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModel +{ + /** + * @var OODBBean + */ + protected $bean; + + /** + * Used by FUSE: the ModelHelper class to connect a bean to a model. + * This method loads a bean in the model. + * + * @param OODBBean $bean bean to load + * + * @return void + */ + public function loadBean( OODBBean $bean ) + { + $this->bean = $bean; + } + + /** + * Magic Getter to make the bean properties available from + * the $this-scope. + * + * @note this method returns a value, not a reference! + * To obtain a reference unbox the bean first! + * + * @param string $prop property to get + * + * @return mixed + */ + public function __get( $prop ) + { + return $this->bean->$prop; + } + + /** + * Magic Setter. + * Sets the value directly as a bean property. + * + * @param string $prop property to set value of + * @param mixed $value value to set + * + * @return void + */ + public function __set( $prop, $value ) + { + $this->bean->$prop = $value; + } + + /** + * Isset implementation. + * Implements the isset function for array-like access. + * + * @param string $key key to check + * + * @return boolean + */ + public function __isset( $key ) + { + return isset( $this->bean->$key ); + } + + /** + * Box the bean using the current model. + * This method wraps the current bean in this model. + * This method can be reached using FUSE through a simple + * OODBBean. The method returns a RedBeanPHP Simple Model. + * This is useful if you would like to rely on PHP type hinting. + * You can box your beans before passing them to functions or methods + * with typed parameters. + * + * @return SimpleModel + */ + public function box() + { + return $this; + } + + /** + * Unbox the bean from the model. + * This method returns the bean inside the model. + * + * @return OODBBean + */ + public function unbox() + { + return $this->bean; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Observer as Observer; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; + +/** + * RedBean Model Helper. + * + * Connects beans to models. + * This is the core of so-called FUSE. + * + * @file RedBeanPHP/ModelHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModelHelper implements Observer +{ + /** + * Gets notified by an observable. + * This method decouples the FUSE system from the actual beans. + * If a FUSE event happens 'update', this method will attempt to + * invoke the corresponding method on the bean. + * + * @param string $eventName i.e. 'delete', 'after_delete' + * @param OODBean $bean affected bean + * + * @return void + */ + public function onEvent( $eventName, $bean ) + { + $bean->$eventName(); + } + + /** + * Attaches the FUSE event listeners. Now the Model Helper will listen for + * CRUD events. If a CRUD event occurs it will send a signal to the model + * that belongs to the CRUD bean and this model will take over control from + * there. This method will attach the following event listeners to the observable: + * + * - 'update' (gets called by R::store, before the records gets inserted / updated) + * - 'after_update' (gets called by R::store, after the records have been inserted / updated) + * - 'open' (gets called by R::load, after the record has been retrieved) + * - 'delete' (gets called by R::trash, before deletion of record) + * - 'after_delete' (gets called by R::trash, after deletion) + * - 'dispense' (gets called by R::dispense) + * + * For every event type, this method will register this helper as a listener. + * The observable will notify the listener (this object) with the event ID and the + * affected bean. This helper will then process the event (onEvent) by invoking + * the event on the bean. If a bean offers a method with the same name as the + * event ID, this method will be invoked. + * + * @param Observable $observable object to observe + * + * @return void + */ + public function attachEventListeners( Observable $observable ) + { + foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) { + $observable->addEventListener( $eventID, $this ); + } + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * RedBeanPHP Tag Manager. + * + * The tag manager offers an easy way to quickly implement basic tagging + * functionality. + * + * Provides methods to tag beans and perform tag-based searches in the + * bean database. + * + * @file RedBeanPHP/TagManager.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class TagManager +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var AssociationManager + */ + protected $associationManager; + + /** + * @var OODBBean + */ + protected $redbean; + + /** + * Checks if the argument is a comma separated string, in this case + * it will split the string into words and return an array instead. + * In case of an array the argument will be returned 'as is'. + * + * @param array|string $tagList list of tags + * + * @return array + */ + private function extractTagsIfNeeded( $tagList ) + { + if ( $tagList !== FALSE && !is_array( $tagList ) ) { + $tags = explode( ',', (string) $tagList ); + } else { + $tags = $tagList; + } + + return $tags; + } + + /** + * Finds a tag bean by it's title. + * Internal method. + * + * @param string $title title to search for + * + * @return OODBBean + */ + protected function findTagByTitle( $title ) + { + $beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) ); + + if ( $beans ) { + $bean = reset( $beans ); + + return $bean; + } + + return NULL; + } + + /** + * Constructor. + * The tag manager offers an easy way to quickly implement basic tagging + * functionality. + * + * @param ToolBox $toolbox toolbox object + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + + $this->associationManager = $this->redbean->getAssociationManager(); + } + + /** + * Tests whether a bean has been associated with one ore more + * of the listed tags. If the third parameter is TRUE this method + * will return TRUE only if all tags that have been specified are indeed + * associated with the given bean, otherwise FALSE. + * If the third parameter is FALSE this + * method will return TRUE if one of the tags matches, FALSE if none + * match. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to check for tags + * @param array|string $tags list of tags + * @param boolean $all whether they must all match or just some + * + * @return boolean + */ + public function hasTag( $bean, $tags, $all = FALSE ) + { + $foundtags = $this->tag( $bean ); + + $tags = $this->extractTagsIfNeeded( $tags ); + $same = array_intersect( $tags, $foundtags ); + + if ( $all ) { + return ( implode( ',', $same ) === implode( ',', $tags ) ); + } + + return (bool) ( count( $same ) > 0 ); + } + + /** + * Removes all sepcified tags from the bean. The tags specified in + * the second parameter will no longer be associated with the bean. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean tagged bean + * @param array|string $tagList list of tags (names) + * + * @return void + */ + public function untag( $bean, $tagList ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + + foreach ( $tags as $tag ) { + if ( $t = $this->findTagByTitle( $tag ) ) { + $this->associationManager->unassociate( $bean, $t ); + } + } + } + + /** + * Tags a bean or returns tags associated with a bean. + * If $tagList is NULL or omitted this method will return a + * comma separated list of tags associated with the bean provided. + * If $tagList is a comma separated list (string) of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to be tagged + * @param array|string $tagList a list of tags + * + * @return array + */ + public function tag( OODBBean $bean, $tagList = NULL ) + { + if ( is_null( $tagList ) ) { + + $tags = $bean->sharedTag; + $foundTags = array(); + + foreach ( $tags as $tag ) { + $foundTags[] = $tag->title; + } + + return $foundTags; + } + + $this->associationManager->clearRelations( $bean, 'tag' ); + $this->addTags( $bean, $tagList ); + + return $tagList; + } + + /** + * Adds tags to a bean. + * If $tagList is a comma separated list of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to add tags to + * @param array|string $tagList list of tags to add to bean + * + * @return void + */ + public function addTags( OODBBean $bean, $tagList ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + + if ( $tagList === FALSE ) { + return; + } + + foreach ( $tags as $tag ) { + if ( !$t = $this->findTagByTitle( $tag ) ) { + $t = $this->redbean->dispense( 'tag' ); + $t->title = $tag; + + $this->redbean->store( $t ); + } + + $this->associationManager->associate( $bean, $t ); + } + } + + /** + * Returns all beans that have been tagged with one or more + * of the specified tags. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param string $beanType type of bean you are looking for + * @param array|string $tagList list of tags to match + * @param string $sql additional SQL (use only for pagination) + * @param array $bindings bindings + * + * @return array + */ + public function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings ); + + return $this->redbean->convertToBeans( $beanType, $records ); + } + + /** + * Returns all beans that have been tagged with ALL of the tags given. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param string $beanType type of bean you are looking for + * @param array|string $tagList list of tags to match + * + * @return array + */ + public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings ); + + return $this->redbean->convertToBeans( $beanType, $records ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Label Maker. + * Makes so-called label beans. + * A label is a bean with only an id, type and name property. + * Labels can be used to create simple entities like categories, tags or enums. + * This service class provides convenience methods to deal with this kind of + * beans. + * + * @file RedBeanPHP/LabelMaker.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class LabelMaker +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * Constructor. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + } + + /** + * A label is a bean with only an id, type and name property. + * This function will dispense beans for all entries in the array. The + * values of the array will be assigned to the name property of each + * individual bean. + * + * + * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] ); + * + * + * @param string $type type of beans you would like to have + * @param array $labels list of labels, names for each bean + * + * @return array + */ + public function dispenseLabels( $type, $labels ) + { + $labelBeans = array(); + foreach ( $labels as $label ) { + $labelBean = $this->toolbox->getRedBean()->dispense( $type ); + $labelBean->name = $label; + $labelBeans[] = $labelBean; + } + + return $labelBeans; + } + + /** + * Gathers labels from beans. This function loops through the beans, + * collects the value of the name property for each individual bean + * and stores the names in a new array. The array then gets sorted using the + * default sort function of PHP (sort). + * + * Usage: + * + * + * $o1->name = 'hamburger'; + * $o2->name = 'pizza'; + * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza + * + * + * Note that the return value is an array of strings, not beans. + * + * @param array $beans list of beans to loop through + * + * @return array + */ + public function gatherLabels( $beans ) + { + $labels = array(); + + foreach ( $beans as $bean ) { + $labels[] = $bean->name; + } + + sort( $labels ); + + return $labels; + } + + /** + * Fetches an ENUM from the database and creates it if necessary. + * An ENUM has the following format: + * + * + * ENUM:VALUE + * + * + * If you pass 'ENUM' only, this method will return an array of its + * values: + * + * + * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA' + * + * + * If you pass 'ENUM:VALUE' this method will return the specified enum bean + * and create it in the database if it does not exist yet: + * + * + * $bananaFlavour = R::enum( 'flavour:banana' ); + * $bananaFlavour->name; + * + * + * So you can use this method to set an ENUM value in a bean: + * + * + * $shake->flavour = R::enum( 'flavour:banana' ); + * + * + * the property flavour now contains the enum bean, a parent bean. + * In the database, flavour_id will point to the flavour record with name 'banana'. + * + * @param string $enum ENUM specification for label + * + * @return array|OODBBean + */ + public function enum( $enum ) + { + $oodb = $this->toolbox->getRedBean(); + + if ( strpos( $enum, ':' ) === FALSE ) { + $type = $enum; + $value = FALSE; + } else { + list( $type, $value ) = explode( ':', $enum ); + $value = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) ); + } + + /** + * We use simply find here, we could use inspect() in fluid mode etc, + * but this would be useless. At first sight it looks clean, you could even + * bake this into find(), however, find not only has to deal with the primary + * search type, people can also include references in the SQL part, so avoiding + * find failures does not matter, this is still the quickest way making use + * of existing functionality. + * + * @note There seems to be a bug in XDebug v2.3.2 causing suppressed + * exceptions like these to surface anyway, to prevent this use: + * + * "xdebug.default_enable = 0" + * + * Also see Github Issue #464 + */ + $values = $oodb->find( $type ); + + if ( $value === FALSE ) { + return $values; + } + + foreach( $values as $enumItem ) { + if ( $enumItem->name === $value ) return $enumItem; + } + + $newEnumItems = $this->dispenseLabels( $type, array( $value ) ); + $newEnumItem = reset( $newEnumItems ); + + $oodb->store( $newEnumItem ); + + return $newEnumItem; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\Logger\RDefault\Debug as Debug; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper; +use RedBeanPHP\Driver\RPDO as RPDO; +use RedBeanPHP\Util\MultiLoader as MultiLoader; +use RedBeanPHP\Util\Transaction as Transaction; +use RedBeanPHP\Util\Dump as Dump; +use RedBeanPHP\Util\DispenseHelper as DispenseHelper; +use RedBeanPHP\Util\ArrayTool as ArrayTool; + +/** + * RedBean Facade + * + * Version Information + * RedBean Version @version 4.3 + * + * This class hides the object landscape of + * RedBeanPHP behind a single letter class providing + * almost all functionality with simple static calls. + * + * @file RedBeanPHP/Facade.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Facade +{ + /** + * RedBeanPHP version constant. + */ + const C_REDBEANPHP_VERSION = '4.3'; + + /** + * @var ToolBox + */ + public static $toolbox; + + /** + * @var OODB + */ + private static $redbean; + + /** + * @var QueryWriter + */ + private static $writer; + + /** + * @var DBAdapter + */ + private static $adapter; + + /** + * @var AssociationManager + */ + private static $associationManager; + + /** + * @var TagManager + */ + private static $tagManager; + + /** + * @var DuplicationManager + */ + private static $duplicationManager; + + /** + * @var LabelMaker + */ + private static $labelMaker; + + /** + * @var Finder + */ + private static $finder; + + /** + * @var Logger + */ + private static $logger; + + /** + * @var array + */ + private static $plugins = array(); + + /** + * @var string + */ + private static $exportCaseStyle = 'default'; + + /** + * Not in use (backward compatibility SQLHelper) + */ + public static $f; + + /** + * @var string + */ + public static $currentDB = ''; + + /** + * @var array + */ + public static $toolboxes = array(); + + /** + * Internal Query function, executes the desired query. Used by + * all facade query functions. This keeps things DRY. + * + * @param string $method desired query method (i.e. 'cell', 'col', 'exec' etc..) + * @param string $sql the sql you want to execute + * @param array $bindings array of values to be bound to query statement + * + * @return array + */ + private static function query( $method, $sql, $bindings ) + { + if ( !self::$redbean->isFrozen() ) { + try { + $rs = Facade::$adapter->$method( $sql, $bindings ); + } catch ( SQLException $exception ) { + if ( self::$writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + return ( $method === 'getCell' ) ? NULL : array(); + } else { + throw $exception; + } + } + + return $rs; + } else { + return Facade::$adapter->$method( $sql, $bindings ); + } + } + + /** + * Returns the RedBeanPHP version string. + * The RedBeanPHP version string always has the same format "X.Y" + * where X is the major version number and Y is the minor version number. + * Point releases are not mentioned in the version string. + * + * @return string + */ + public static function getVersion() + { + return self::C_REDBEANPHP_VERSION; + } + + /** + * Tests the connection. + * Returns TRUE if connection has been established and + * FALSE otherwise. + * + * @return boolean + */ + public static function testConnection() + { + if ( !isset( self::$adapter ) ) return FALSE; + + $database = self::$adapter->getDatabase(); + try { + @$database->connect(); + } catch ( \Exception $e ) {} + return $database->isConnected(); + } + + /** + * Kickstarts redbean for you. This method should be called before you start using + * RedBean. The Setup() method can be called without any arguments, in this case it will + * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems). + * + * @param string $dsn Database connection string + * @param string $username Username for database + * @param string $password Password for database + * @param boolean $frozen TRUE if you want to setup in frozen mode + * + * @return ToolBox + */ + public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE ) + { + if ( is_null( $dsn ) ) { + $dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db'; + } + + self::addDatabase( 'default', $dsn, $username, $password, $frozen ); + self::selectDatabase( 'default' ); + + return self::$toolbox; + } + + /** + * Toggles Narrow Field Mode. + * See documentation in QueryWriter. + * + * @param boolean $mode TRUE = Narrow Field Mode + * + * @return void + */ + public static function setNarrowFieldMode( $mode ) + { + AQueryWriter::setNarrowFieldMode( $mode ); + } + + /** + * Wraps a transaction around a closure or string callback. + * If an Exception is thrown inside, the operation is automatically rolled back. + * If no Exception happens, it commits automatically. + * It also supports (simulated) nested transactions (that is useful when + * you have many methods that needs transactions but are unaware of + * each other). + * + * Example: + * + * + * $from = 1; + * $to = 2; + * $amount = 300; + * + * R::transaction(function() use($from, $to, $amount) + * { + * $accountFrom = R::load('account', $from); + * $accountTo = R::load('account', $to); + * $accountFrom->money -= $amount; + * $accountTo->money += $amount; + * R::store($accountFrom); + * R::store($accountTo); + * }); + * + * + * @param callable $callback Closure (or other callable) with the transaction logic + * + * @return mixed + */ + public static function transaction( $callback ) + { + return Transaction::transaction( self::$adapter, $callback ); + } + + /** + * Adds a database to the facade, afterwards you can select the database using + * selectDatabase($key), where $key is the name you assigned to this database. + * + * Usage: + * + * + * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' ); + * R::selectDatabase( 'database-1' ); //to select database again + * + * + * This method allows you to dynamically add (and select) new databases + * to the facade. Adding a database with the same key will cause an exception. + * + * @param string $key ID for the database + * @param string $dsn DSN for the database + * @param string $user user for connection + * @param NULL|string $pass password for connection + * @param bool $frozen whether this database is frozen or not + * + * @return void + */ + public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE ) + { + if ( isset( self::$toolboxes[$key] ) ) { + throw new RedException( 'A database has already been specified for this key.' ); + } + + if ( is_object($dsn) ) { + $db = new RPDO( $dsn ); + $dbType = $db->getDatabaseType(); + } else { + $db = new RPDO( $dsn, $user, $pass, TRUE ); + $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) ); + } + + $adapter = new DBAdapter( $db ); + + $writers = array( + 'pgsql' => 'PostgreSQL', + 'sqlite' => 'SQLiteT', + 'cubrid' => 'CUBRID', + 'mysql' => 'MySQL', + 'sqlsrv' => 'SQLServer', + ); + + $wkey = trim( strtolower( $dbType ) ); + if ( !isset( $writers[$wkey] ) ) { + $wkey = preg_replace( '/\W/', '' , $wkey ); + throw new RedException( 'Unsupported database ('.$wkey.').' ); + } + $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey]; + $writer = new $writerClass( $adapter ); + $redbean = new OODB( $writer, $frozen ); + + self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer ); + } + + /** + * Determines whether a database identified with the specified key has + * already been added to the facade. This function will return TRUE + * if the database indicated by the key is available and FALSE otherwise. + * + * @param string $key the key/name of the database to check for + * + * @return boolean + */ + public static function hasDatabase( $key ) + { + return ( isset( self::$toolboxes[$key] ) ); + } + + /** + * Selects a different database for the Facade to work with. + * If you use the R::setup() you don't need this method. This method is meant + * for multiple database setups. This method selects the database identified by the + * database ID ($key). Use addDatabase() to add a new database, which in turn + * can be selected using selectDatabase(). If you use R::setup(), the resulting + * database will be stored under key 'default', to switch (back) to this database + * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been + * switched and FALSE otherwise (for instance if you already using the specified database). + * + * @param string $key Key of the database to select + * + * @return boolean + */ + public static function selectDatabase( $key ) + { + if ( self::$currentDB === $key ) { + return FALSE; + } + + if ( !isset( self::$toolboxes[$key] ) ) { + throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' ); + } + + self::configureFacadeWithToolbox( self::$toolboxes[$key] ); + self::$currentDB = $key; + + return TRUE; + } + + /** + * Toggles DEBUG mode. + * In Debug mode all SQL that happens under the hood will + * be printed to the screen and/or logged. + * If no database connection has been configured using R::setup() or + * R::selectDatabase() this method will throw an exception. + * + * There are 2 debug styles: + * + * Classic: separate parameter bindings, explicit and complete but less readable + * Fancy: interpersed bindings, truncates large strings, highlighted schema changes + * + * Fancy style is more readable but sometimes incomplete. + * + * The first parameter turns debugging ON or OFF. + * The second parameter indicates the mode of operation: + * + * 0 Log and write to STDOUT classic style (default) + * 1 Log only, class style + * 2 Log and write to STDOUT fancy style + * 3 Log only, fancy style + * + * This function always returns the logger instance created to generate the + * debug messages. + * + * @param boolean $tf debug mode (TRUE or FALSE) + * @param integer $mode mode of operation + * + * @return RDefault + * @throws RedException + */ + public static function debug( $tf = TRUE, $mode = 0 ) + { + if ($mode > 1) { + $mode -= 2; + $logger = new Debug; + } else { + $logger = new RDefault; + } + + if ( !isset( self::$adapter ) ) { + throw new RedException( 'Use R::setup() first.' ); + } + $logger->setMode($mode); + self::$adapter->getDatabase()->setDebugMode( $tf, $logger ); + + return $logger; + } + + /** + * Turns on the fancy debugger. + * In 'fancy' mode the debugger will output queries with bound + * parameters inside the SQL itself. This method has been added to + * offer a convenient way to activate the fancy debugger system + * in one call. + * + * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode + * + * @return void + */ + public static function fancyDebug( $toggle = TRUE ) + { + self::debug( $toggle, 2 ); + } + + /** + * Inspects the database schema. If you pass the type of a bean this + * method will return the fields of its table in the database. + * The keys of this array will be the field names and the values will be + * the column types used to store their values. + * If no type is passed, this method returns a list of all tables in the database. + * + * @param string $type Type of bean (i.e. table) you want to inspect + * + * @return array + */ + public static function inspect( $type = NULL ) + { + return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public static function store( $bean ) + { + return self::$redbean->store( $bean ); + } + + /** + * Toggles fluid or frozen mode. In fluid mode the database + * structure is adjusted to accomodate your objects. In frozen mode + * this is not the case. + * + * You can also pass an array containing a selection of frozen types. + * Let's call this chilly mode, it's just like fluid mode except that + * certain types (i.e. tables) aren't touched. + * + * @param boolean|array $trueFalse + */ + public static function freeze( $tf = TRUE ) + { + self::$redbean->freeze( $tf ); + } + + /** + * Loads multiple types of beans with the same ID. + * This might look like a strange method, however it can be useful + * for loading a one-to-one relation. + * + * Usage: + * list( $author, $bio ) = R::loadMulti( 'author, bio', $id ); + * + * @param string|array $types the set of types to load at once + * @param mixed $id the common ID + * + * @return OODBBean + */ + public static function loadMulti( $types, $id ) + { + return MultiLoader::load( self::$redbean, $types, $id ); + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public static function load( $type, $id ) + { + return self::$redbean->load( $type, $id ); + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * This facade method also accepts a type-id combination, + * in the latter case this method will attempt to load the specified bean + * and THEN trash it. + * + * @param string|OODBBean|SimpleModel $bean bean you want to remove from database + * @param integer $id ID if the bean to trash (optional, type-id variant only) + * + * @return void + */ + public static function trash( $beanOrType, $id = NULL ) + { + if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) ); + return self::$redbean->trash( $beanOrType ); + } + + /** + * Dispenses a new RedBean OODB Bean for use with + * the rest of the methods. + * + * @param string|array $typeOrBeanArray type or bean array to import + * @param integer $number number of beans to dispense + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return array|OODBBean + */ + public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) + { + return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray ); + } + + /** + * Takes a comma separated list of bean types + * and dispenses these beans. For each type in the list + * you can specify the number of beans to be dispensed. + * + * Usage: + * + * + * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' ); + * + * + * This will dispense a book, a page and a text. This way you can + * quickly dispense beans of various types in just one line of code. + * + * Usage: + * + * + * list($book, $pages) = R::dispenseAll('book,page*100'); + * + * + * This returns an array with a book bean and then another array + * containing 100 page beans. + * + * @param string $order a description of the desired dispense order using the syntax above + * @param boolean $onlyArrays return only arrays even if amount < 2 + * + * @return array + */ + public static function dispenseAll( $order, $onlyArrays = FALSE ) + { + return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays ); + } + + /** + * Convience method. Tries to find beans of a certain type, + * if no beans are found, it dispenses a bean of that type. + * Note that this function always returns an array. + * + * @param string $type type of bean you are looking for + * @param string $sql SQL code for finding the bean + * @param array $bindings parameters to bind to SQL + * + * @return array + */ + public static function findOrDispense( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findOrDispense( $type, $sql, $bindings ); + } + + /** + * Same as findOrDispense but returns just one element. + * + * @param string $type type of bean you are looking for + * @param string $sql SQL code for finding the bean + * @param array $bindings parameters to bind to SQL + * + * @return OODBBean + */ + public static function findOneOrDispense( $type, $sql = NULL, $bindings = array() ) + { + return reset( self::findOrDispense( $type, $sql, $bindings ) ); + } + + /** + * Finds a bean using a type and a where clause (SQL). + * As with most Query tools in RedBean you can provide values to + * be inserted in the SQL statement by populating the value + * array parameter; you can either use the question mark notation + * or the slot-notation (:keyname). + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function find( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->find( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * The findAll() method differs from the find() method in that it does + * not assume a WHERE-clause, so this is valid: + * + * R::findAll('person',' ORDER BY name DESC '); + * + * Your SQL does not have to start with a valid WHERE-clause condition. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function findAll( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->find( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * The variation also exports the beans (i.e. it returns arrays). + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function findAndExport( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findAndExport( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * This variation returns the first bean only. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return OODBBean + */ + public static function findOne( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findOne( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * This variation returns the last bean only. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return OODBBean + */ + public static function findLast( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findLast( $type, $sql, $bindings ); + } + + /** + * Finds a bean collection. + * Use this for large datasets. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return BeanCollection + */ + public static function findCollection( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findCollection( $type, $sql, $bindings ); + } + + /** + * Finds multiple types of beans at once and offers additional + * remapping functionality. This is a very powerful yet complex function. + * For details see Finder::findMulti(). + * + * @see Finder::findMulti() + * + * @param array|string $types a list of bean types to find + * @param string|array $sqlOrArr SQL query string or result set array + * @param array $bindings SQL bindings + * @param array $remappings an array of remapping arrays containing closures + * + * @return array + */ + public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() ) + { + return self::$finder->findMulti( $types, $sql, $bindings, $remappings ); + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public static function batch( $type, $ids ) + { + return self::$redbean->batch( $type, $ids ); + } + + /** + * @see Facade::batch + * + * Alias for batch(). Batch method is older but since we added so-called *All + * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to + * improve the consistency of the Facade API and also add an alias for batch() called + * loadAll. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public static function loadAll( $type, $ids ) + { + return self::$redbean->batch( $type, $ids ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return integer + */ + public static function exec( $sql, $bindings = array() ) + { + return self::query( 'exec', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAll( $sql, $bindings = array() ) + { + return self::query( 'get', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return string + */ + public static function getCell( $sql, $bindings = array() ) + { + return self::query( 'getCell', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getRow( $sql, $bindings = array() ) + { + return self::query( 'getRow', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getCol( $sql, $bindings = array() ) + { + return self::query( 'getCol', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * Results will be returned as an associative array. The first + * column in the select clause will be used for the keys in this array and + * the second column will be used for the values. If only one column is + * selected in the query, both key and value of the array will have the + * value of this field for each row. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAssoc( $sql, $bindings = array() ) + { + return self::query( 'getAssoc', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * Results will be returned as an associative array indexed by the first + * column in the select. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAssocRow( $sql, $bindings = array() ) + { + return self::query( 'getAssocRow', $sql, $bindings ); + } + + /** + * Returns the insert ID for databases that support/require this + * functionality. Alias for R::getAdapter()->getInsertID(). + * + * @return mixed + */ + public static function getInsertID() + { + return self::$adapter->getInsertID(); + } + + /** + * Makes a copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following features. + * - All beans in own-lists will be duplicated as well + * - All references to shared beans will be copied but not the shared beans themselves + * - All references to parent objects (_id fields) will be copied but not the parents themselves + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * @deprecated + * This function is deprecated in favour of R::duplicate(). + * This function has a confusing method signature, the R::duplicate() function + * only accepts two arguments: bean and filters. + * + * @param OODBBean $bean bean to be copied + * @param array $trail for internal usage, pass array() + * @param boolean $pid for internal usage + * @param array $white white list filter with bean types to duplicate + * + * @return array + */ + public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() ) + { + self::$duplicationManager->setFilters( $filters ); + return self::$duplicationManager->dup( $bean, $trail, $pid ); + } + + /** + * Makes a deep copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following: + * + * * All beans in own-lists will be duplicated as well + * * All references to shared beans will be copied but not the shared beans themselves + * * All references to parent objects (_id fields) will be copied but not the parents themselves + * + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * Note: + * This is a simplified version of the deprecated R::dup() function. + * + * @param OODBBean $bean bean to be copied + * @param array $white white list filter with bean types to duplicate + * + * @return array + */ + public static function duplicate( $bean, $filters = array() ) + { + return self::dup( $bean, array(), FALSE, $filters ); + } + + /** + * Exports a collection of beans. Handy for XML/JSON exports with a + * Javascript framework like Dojo or ExtJS. + * What will be exported: + * + * * contents of the bean + * * all own bean lists (recursively) + * * all shared beans (not THEIR own lists) + * + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents whether you want parent beans to be exported + * @param array $filters whitelist of types + * + * @return array + */ + public static function exportAll( $beans, $parents = FALSE, $filters = array()) + { + return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle ); + } + + /** + * Selects case style for export. + * This will determine the case style for the keys of exported beans (see exportAll). + * The following options are accepted: + * + * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid ) + * * 'camel' Camel Case (i.e. bookId isValid ) + * * 'dolphin' Dolphin Case (i.e. bookID isValid ) Like CamelCase but ID is written all uppercase + * + * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different + * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id. + * Due to information loss this cannot be corrected. However if you might try + * DolphinCase for IDs it takes into account the exception concerning IDs. + * + * @param string $caseStyle case style identifier + * + * @return void + */ + public static function useExportCase( $caseStyle = 'default' ) + { + if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' ); + self::$exportCaseStyle = $caseStyle; + } + + /** + * Converts a series of rows to beans. + * This method converts a series of rows to beans. + * The type of the desired output beans can be specified in the + * first parameter. The second parameter is meant for the database + * result rows. + * + * Usage: + * + * + * $rows = R::getAll( 'SELECT * FROM ...' ) + * $beans = R::convertToBeans( $rows ); + * + * + * As of version 4.3.2 you can specify a meta-mask. + * Data from columns with names starting with the value specified in the mask + * will be transferred to the meta section of a bean (under data.bundle). + * + * + * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' ); + * $beans = R::convertToBeans( $rows ); + * $bean = reset( $beans ); + * $data = $bean->getMeta( 'data.bundle' ); + * $extra_count = $data['extra_count']; + * + * + * @param string $type type of beans to produce + * @param array $rows must contain an array of array + * + * @return array + */ + public static function convertToBeans( $type, $rows, $metamask = NULL ) + { + return self::$redbean->convertToBeans( $type, $rows, $metamask ); + } + + /** + * Just like converToBeans, but for one bean. + * @see convertToBeans for more details. + * + * @param string $type type of beans to produce + * @param array $row one row from the database + * + * @return array + */ + public static function convertToBean( $type, $row, $metamask = NULL ) + { + $beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask ); + $bean = reset( $beans ); + return $bean; + } + + /** + * Part of RedBeanPHP Tagging API. + * Tests whether a bean has been associated with one ore more + * of the listed tags. If the third parameter is TRUE this method + * will return TRUE only if all tags that have been specified are indeed + * associated with the given bean, otherwise FALSE. + * If the third parameter is FALSE this + * method will return TRUE if one of the tags matches, FALSE if none + * match. + * + * @param OODBBean $bean bean to check for tags + * @param array $tags list of tags + * @param boolean $all whether they must all match or just some + * + * @return boolean + */ + public static function hasTag( $bean, $tags, $all = FALSE ) + { + return self::$tagManager->hasTag( $bean, $tags, $all ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Removes all specified tags from the bean. The tags specified in + * the second parameter will no longer be associated with the bean. + * + * @param OODBBean $bean tagged bean + * @param array $tagList list of tags (names) + * + * @return void + */ + public static function untag( $bean, $tagList ) + { + self::$tagManager->untag( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Tags a bean or returns tags associated with a bean. + * If $tagList is NULL or omitted this method will return a + * comma separated list of tags associated with the bean provided. + * If $tagList is a comma separated list (string) of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * @param OODBBean $bean bean to tag + * @param mixed $tagList tags to attach to the specified bean + * + * @return string + */ + public static function tag( OODBBean $bean, $tagList = NULL ) + { + return self::$tagManager->tag( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Adds tags to a bean. + * If $tagList is a comma separated list of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * @param OODBBean $bean bean to tag + * @param array $tagList list of tags to add to bean + * + * @return void + */ + public static function addTags( OODBBean $bean, $tagList ) + { + self::$tagManager->addTags( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Returns all beans that have been tagged with one of the tags given. + * + * @param string $beanType type of bean you are looking for + * @param array $tagList list of tags to match + * @param string $sql additional SQL query snippet + * @param array $bindings a list of values to bind to the query parameters + * + * @return array + */ + public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) + { + return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Returns all beans that have been tagged with ALL of the tags given. + * + * @param string $beanType type of bean you are looking for + * @param array $tagList list of tags to match + * @param string $sql additional SQL query snippet + * @param array $bindings a list of values to bind to the query parameters + * + * @return array + */ + public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) + { + return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings ); + } + + /** + * Wipes all beans of type $beanType. + * + * @param string $beanType type of bean you want to destroy entirely + * + * @return boolean + */ + public static function wipe( $beanType ) + { + return Facade::$redbean->wipe( $beanType ); + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public static function count( $type, $addSQL = '', $bindings = array() ) + { + return Facade::$redbean->count( $type, $addSQL, $bindings ); + } + + /** + * Configures the facade, want to have a new Writer? A new Object Database or a new + * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new + * toolbox. + * + * @param ToolBox $tb toolbox to configure facade with + * + * @return ToolBox + */ + public static function configureFacadeWithToolbox( ToolBox $tb ) + { + $oldTools = self::$toolbox; + self::$toolbox = $tb; + self::$writer = self::$toolbox->getWriter(); + self::$adapter = self::$toolbox->getDatabaseAdapter(); + self::$redbean = self::$toolbox->getRedBean(); + self::$finder = new Finder( self::$toolbox ); + self::$associationManager = new AssociationManager( self::$toolbox ); + self::$redbean->setAssociationManager( self::$associationManager ); + self::$labelMaker = new LabelMaker( self::$toolbox ); + $helper = new SimpleModelHelper(); + $helper->attachEventListeners( self::$redbean ); + self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper ); + self::$duplicationManager = new DuplicationManager( self::$toolbox ); + self::$tagManager = new TagManager( self::$toolbox ); + return $oldTools; + } + + /** + * Facade Convience method for adapter transaction system. + * Begins a transaction. + * + * @return bool + */ + public static function begin() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->startTransaction(); + return TRUE; + } + + /** + * Facade Convience method for adapter transaction system. + * Commits a transaction. + * + * @return bool + */ + public static function commit() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->commit(); + return TRUE; + } + + /** + * Facade Convience method for adapter transaction system. + * Rolls back a transaction. + * + * @return bool + */ + public static function rollback() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->rollback(); + return TRUE; + } + + /** + * Returns a list of columns. Format of this array: + * array( fieldname => type ) + * Note that this method only works in fluid mode because it might be + * quite heavy on production servers! + * + * @param string $table name of the table (not type) you want to get columns of + * + * @return array + */ + public static function getColumns( $table ) + { + return self::$writer->getColumns( $table ); + } + + /** + * Generates question mark slots for an array of values. + * + * @param array $array array to generate question mark slots for + * + * @return string + */ + public static function genSlots( $array, $template = NULL ) + { + return ArrayTool::genSlots( $array, $template ); + } + + /** + * Flattens a multi dimensional bindings array for use with genSlots(). + * + * @param array $array array to flatten + * + * @return array + */ + public static function flat( $array, $result = array() ) + { + return ArrayTool::flat( $array, $result ); + } + + /** + * Nukes the entire database. + * This will remove all schema structures from the database. + * Only works in fluid mode. Be careful with this method. + * + * @warning dangerous method, will remove all tables, columns etc. + * + * @return void + */ + public static function nuke() + { + if ( !self::$redbean->isFrozen() ) { + self::$writer->wipeAll(); + } + } + + /** + * Short hand function to store a set of beans at once, IDs will be + * returned as an array. For information please consult the R::store() + * function. + * A loop saver. + * + * @param array $beans list of beans to be stored + * + * @return array + */ + public static function storeAll( $beans ) + { + $ids = array(); + foreach ( $beans as $bean ) { + $ids[] = self::store( $bean ); + } + return $ids; + } + + /** + * Short hand function to trash a set of beans at once. + * For information please consult the R::trash() function. + * A loop saver. + * + * @param array $beans list of beans to be trashed + * + * @return void + */ + public static function trashAll( $beans ) + { + foreach ( $beans as $bean ) { + self::trash( $bean ); + } + } + + /** + * Toggles Writer Cache. + * Turns the Writer Cache on or off. The Writer Cache is a simple + * query based caching system that may improve performance without the need + * for cache management. This caching system will cache non-modifying queries + * that are marked with special SQL comments. As soon as a non-marked query + * gets executed the cache will be flushed. Only non-modifying select queries + * have been marked therefore this mechanism is a rather safe way of caching, requiring + * no explicit flushes or reloads. Of course this does not apply if you intend to test + * or simulate concurrent querying. + * + * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache + * + * @return void + */ + public static function useWriterCache( $yesNo ) + { + self::getWriter()->setUseCache( $yesNo ); + } + + /** + * A label is a bean with only an id, type and name property. + * This function will dispense beans for all entries in the array. The + * values of the array will be assigned to the name property of each + * individual bean. + * + * @param string $type type of beans you would like to have + * @param array $labels list of labels, names for each bean + * + * @return array + */ + public static function dispenseLabels( $type, $labels ) + { + return self::$labelMaker->dispenseLabels( $type, $labels ); + } + + /** + * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs. + * Either returns a (newly created) bean respresenting the desired ENUM + * value or returns a list of all enums for the type. + * + * To obtain (and add if necessary) an ENUM value: + * + * + * $tea->flavour = R::enum( 'flavour:apple' ); + * + * + * Returns a bean of type 'flavour' with name = apple. + * This will add a bean with property name (set to APPLE) to the database + * if it does not exist yet. + * + * To obtain all flavours: + * + * + * R::enum('flavour'); + * + * + * To get a list of all flavour names: + * + * + * R::gatherLabels( R::enum( 'flavour' ) ); + * + * + * @param string $enum either type or type-value + * + * @return array|OODBBean + */ + public static function enum( $enum ) + { + return self::$labelMaker->enum( $enum ); + } + + /** + * Gathers labels from beans. This function loops through the beans, + * collects the values of the name properties of each individual bean + * and stores the names in a new array. The array then gets sorted using the + * default sort function of PHP (sort). + * + * @param array $beans list of beans to loop + * + * @return array + */ + public static function gatherLabels( $beans ) + { + return self::$labelMaker->gatherLabels( $beans ); + } + + /** + * Closes the database connection. + * + * @return void + */ + public static function close() + { + if ( isset( self::$adapter ) ) { + self::$adapter->close(); + } + } + + /** + * Simple convenience function, returns ISO date formatted representation + * of $time. + * + * @param mixed $time UNIX timestamp + * + * @return string + */ + public static function isoDate( $time = NULL ) + { + if ( !$time ) { + $time = time(); + } + + return @date( 'Y-m-d', $time ); + } + + /** + * Simple convenience function, returns ISO date time + * formatted representation + * of $time. + * + * @param mixed $time UNIX timestamp + * + * @return string + */ + public static function isoDateTime( $time = NULL ) + { + if ( !$time ) $time = time(); + return @date( 'Y-m-d H:i:s', $time ); + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param Adapter $adapter Database Adapter for facade to use + * + * @return void + */ + public static function setDatabaseAdapter( Adapter $adapter ) + { + self::$adapter = $adapter; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param QueryWriter $writer Query Writer instance for facade to use + * + * @return void + */ + public static function setWriter( QueryWriter $writer ) + { + self::$writer = $writer; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param OODB $redbean Object Database for facade to use + */ + public static function setRedBean( OODB $redbean ) + { + self::$redbean = $redbean; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return DBAdapter + */ + public static function getDatabaseAdapter() + { + return self::$adapter; + } + + /** + * In case you use PDO (which is recommended and the default but not mandatory, hence + * the database adapter), you can use this method to obtain the PDO object directly. + * This is a convenience method, it will do the same as: + * + * + * R::getDatabaseAdapter()->getDatabase()->getPDO(); + * + * + * If the PDO object could not be found, for whatever reason, this method + * will return NULL instead. + * + * @return NULL|PDO + */ + public static function getPDO() + { + $databaseAdapter = self::getDatabaseAdapter(); + if ( is_null( $databaseAdapter ) ) return NULL; + $database = $databaseAdapter->getDatabase(); + if ( is_null( $database ) ) return NULL; + if ( !method_exists( $database, 'getPDO' ) ) return NULL; + return $database->getPDO(); + } + + /** + * Returns the current duplication manager instance. + * + * @return DuplicationManager + */ + public static function getDuplicationManager() + { + return self::$duplicationManager; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return QueryWriter + */ + public static function getWriter() + { + return self::$writer; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return OODB + */ + public static function getRedBean() + { + return self::$redbean; + } + + /** + * Returns the toolbox currently used by the facade. + * To set the toolbox use R::setup() or R::configureFacadeWithToolbox(). + * To create a toolbox use Setup::kickstart(). Or create a manual + * toolbox using the ToolBox class. + * + * @return ToolBox + */ + public static function getToolBox() + { + return self::$toolbox; + } + + /** + * Mostly for internal use, but might be handy + * for some users. + * This returns all the components of the currently + * selected toolbox. + * + * Returns the components in the following order: + * + * # OODB instance (getRedBean()) + * # Database Adapter + * # Query Writer + * # Toolbox itself + * + * @return array + */ + public static function getExtractedToolbox() + { + return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox ); + } + + /** + * Facade method for AQueryWriter::renameAssociation() + * + * @param string|array $from + * @param string $to + * + * @return void + */ + public static function renameAssociation( $from, $to = NULL ) + { + AQueryWriter::renameAssociation( $from, $to ); + } + + /** + * Little helper method for Resty Bean Can server and others. + * Takes an array of beans and exports each bean. + * Unlike exportAll this method does not recurse into own lists + * and shared lists, the beans are exported as-is, only loaded lists + * are exported. + * + * @param array $beans beans + * + * @return array + */ + public static function beansToArray( $beans ) + { + $list = array(); + foreach( $beans as $bean ) $list[] = $bean->export(); + return $list; + } + + /** + * Sets the error mode for FUSE. + * What to do if a FUSE model method does not exist? + * You can set the following options: + * + * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL + * * OODBBean::C_ERR_LOG, logs the incident using error_log + * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE + * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING + * * OODBBean::C_ERR_EXCEPTION, throws an exception + * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) + * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR + * + * + * Custom handler method signature: handler( array ( + * 'message' => string + * 'bean' => OODBBean + * 'method' => string + * ) ) + * + * + * This method returns the old mode and handler as an array. + * + * @param integer $mode mode, determines how to handle errors + * @param callable|NULL $func custom handler (if applicable) + * + * @return array + */ + public static function setErrorHandlingFUSE( $mode, $func = NULL ) + { + return OODBBean::setErrorHandlingFUSE( $mode, $func ); + } + + /** + * Simple but effective debug function. + * Given a one or more beans this method will + * return an array containing first part of the string + * representation of each item in the array. + * + * @param OODBBean|array $data either a bean or an array of beans + * + * @return array + */ + public static function dump( $data ) + { + return Dump::dump( $data ); + } + + /** + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. + * + * Example: + * + * + * R::bindFunc( 'read', 'location.point', 'asText' ); + * R::bindFunc( 'write', 'location.point', 'GeomFromText' ); + * + * + * Passing NULL as the function will reset (clear) the function + * for this column/mode. + * + * @param string $mode mode for function: i.e. read or write + * @param string $field field (table.column) to bind function to + * @param string $function SQL function to bind to specified column + * + * @return void + */ + public static function bindFunc( $mode, $field, $function ) + { + self::$redbean->bindFunc( $mode, $field, $function ); + } + + /** + * Sets global aliases. + * Registers a batch of aliases in one go. This works the same as + * fetchAs and setAutoResolve but explicitly. For instance if you register + * the alias 'cover' for 'page' a property containing a reference to a + * page bean called 'cover' will correctly return the page bean and not + * a (non-existant) cover bean. + * + * + * R::aliases( array( 'cover' => 'page' ) ); + * $book = R::dispense( 'book' ); + * $page = R::dispense( 'page' ); + * $book->cover = $page; + * R::store( $book ); + * $book = $book->fresh(); + * $cover = $book->cover; + * echo $cover->getMeta( 'type' ); //page + * + * + * The format of the aliases registration array is: + * + * {alias} => {actual type} + * + * In the example above we use: + * + * cover => page + * + * From that point on, every bean reference to a cover + * will return a 'page' bean. Note that with autoResolve this + * feature along with fetchAs() is no longer very important, although + * relying on explicit aliases can be a bit faster. + * + * @param array $list list of global aliases to use + * + * @return void + */ + public static function aliases( $list ) + { + OODBBean::aliases( $list ); + } + + /** + * Tries to find a bean matching a certain type and + * criteria set. If no beans are found a new bean + * will be created, the criteria will be imported into this + * bean and the bean will be stored and returned. + * If multiple beans match the criteria only the first one + * will be returned. + * + * @param string $type type of bean to search for + * @param array $like criteria set describing the bean to search for + * + * @return OODBBean + */ + public static function findOrCreate( $type, $like = array() ) + { + return self::$finder->findOrCreate( $type, $like ); + } + + /** + * Tries to find beans matching the specified type and + * criteria set. + * + * If the optional additional SQL snippet is a condition, it will + * be glued to the rest of the query using the AND operator. + * + * @param string $type type of bean to search for + * @param array $like optional criteria set describing the bean to search for + * @param string $sql optional additional SQL for sorting + * + * @return array + */ + public static function findLike( $type, $like = array(), $sql = '' ) + { + return self::$finder->findLike( $type, $like, $sql ); + } + + /** + * Starts logging queries. + * Use this method to start logging SQL queries being + * executed by the adapter. + * + * @note you cannot use R::debug and R::startLogging + * at the same time because R::debug is essentially a + * special kind of logging. + * + * @return void + */ + public static function startLogging() + { + self::debug( TRUE, RDefault::C_LOGGER_ARRAY ); + } + + /** + * Stops logging, comfortable method to stop logging of queries. + * + * @return void + */ + public static function stopLogging() + { + self::debug( FALSE ); + } + + /** + * Returns the log entries written after the startLogging. + * + * @return array + */ + public static function getLogs() + { + return self::getLogger()->getLogs(); + } + + /** + * Resets the Query counter. + * + * @return integer + */ + public static function resetQueryCount() + { + self::$adapter->getDatabase()->resetCounter(); + } + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public static function getQueryCount() + { + return self::$adapter->getDatabase()->getQueryCount(); + } + + /** + * Returns the current logger instance being used by the + * database object. + * + * @return Logger + */ + public static function getLogger() + { + return self::$adapter->getDatabase()->getLogger(); + } + + /** + * Alias for setAutoResolve() method on OODBBean. + * Enables or disables auto-resolving fetch types. + * Auto-resolving aliased parent beans is convenient but can + * be slower and can create infinite recursion if you + * used aliases to break cyclic relations in your domain. + * + * @param boolean $automatic TRUE to enable automatic resolving aliased parents + * + * @return void + */ + public static function setAutoResolve( $automatic = TRUE ) + { + OODBBean::setAutoResolve( (boolean) $automatic ); + } + + /** + * Dynamically extends the facade with a plugin. + * Using this method you can register your plugin with the facade and then + * use the plugin by invoking the name specified plugin name as a method on + * the facade. + * + * Usage: + * + * + * R::ext( 'makeTea', function() { ... } ); + * + * + * Now you can use your makeTea plugin like this: + * + * + * R::makeTea(); + * + * + * @param string $pluginName name of the method to call the plugin + * @param callable $callable a PHP callable + * + * @return void + */ + public static function ext( $pluginName, $callable ) + { + if ( !ctype_alnum( $pluginName ) ) { + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); + } + self::$plugins[$pluginName] = $callable; + } + + /** + * Call static for use with dynamic plugins. This magic method will + * intercept static calls and route them to the specified plugin. + * + * @param string $pluginName name of the plugin + * @param array $params list of arguments to pass to plugin method + * + * @return mixed + */ + public static function __callStatic( $pluginName, $params ) + { + if ( !ctype_alnum( $pluginName) ) { + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); + } + if ( !isset( self::$plugins[$pluginName] ) ) { + throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' ); + } + return call_user_func_array( self::$plugins[$pluginName], $params ); + } +} + +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; + +/** + * Duplication Manager + * The Duplication Manager creates deep copies from beans, this means + * it can duplicate an entire bean hierarchy. You can use this feature to + * implement versioning for instance. Because duplication and exporting are + * closely related this class is also used to export beans recursively + * (i.e. we make a duplicate and then convert to array). This class allows + * you to tune the duplication process by specifying filters determining + * which relations to take into account and by specifying tables + * (in which case no reflective queries have to be issued thus improving + * performance). This class also hosts the Camelfy function used to + * reformat the keys of an array, this method is publicly available and + * used internally by exportAll(). + * + * @file RedBeanPHP/DuplicationManager.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DuplicationManager +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var AssociationManager + */ + protected $associationManager; + + /** + * @var OODB + */ + protected $redbean; + + /** + * @var array + */ + protected $tables = array(); + + /** + * @var array + */ + protected $columns = array(); + + /** + * @var array + */ + protected $filters = array(); + + /** + * @var array + */ + protected $cacheTables = FALSE; + + /** + * Copies the shared beans in a bean, i.e. all the sharedBean-lists. + * + * @param OODBBean $copy target bean to copy lists to + * @param string $shared name of the shared list + * @param array $beans array with shared beans to copy + * + * @return void + */ + private function copySharedBeans( OODBBean $copy, $shared, $beans ) + { + $copy->$shared = array(); + + foreach ( $beans as $subBean ) { + array_push( $copy->$shared, $subBean ); + } + } + + /** + * Copies the own beans in a bean, i.e. all the ownBean-lists. + * Each bean in the own-list belongs exclusively to its owner so + * we need to invoke the duplicate method again to duplicate each bean here. + * + * @param OODBBean $copy target bean to copy lists to + * @param string $owned name of the own list + * @param array $beans array with shared beans to copy + * @param array $trail array with former beans to detect recursion + * @param boolean $preserveIDs TRUE means preserve IDs, for export only + * + * @return void + */ + private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs ) + { + $copy->$owned = array(); + foreach ( $beans as $subBean ) { + array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) ); + } + } + + /** + * Creates a copy of bean $bean and copies all primitive properties (not lists) + * and the parents beans to the newly created bean. Also sets the ID of the bean + * to 0. + * + * @param OODBBean $bean bean to copy + * + * @return OODBBean + */ + private function createCopy( OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + + $copy = $this->redbean->dispense( $type ); + $copy->setMeta( 'sys.dup-from-id', $bean->id ); + $copy->setMeta( 'sys.old-id', $bean->id ); + $copy->importFrom( $bean ); + $copy->id = 0; + + return $copy; + } + + /** + * Generates a key from the bean type and its ID and determines if the bean + * occurs in the trail, if not the bean will be added to the trail. + * Returns TRUE if the bean occurs in the trail and FALSE otherwise. + * + * @param array $trail list of former beans + * @param OODBBean $bean currently selected bean + * + * @return boolean + */ + private function inTrailOrAdd( &$trail, OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + $key = $type . $bean->getID(); + + if ( isset( $trail[$key] ) ) { + return TRUE; + } + + $trail[$key] = $bean; + + return FALSE; + } + + /** + * Given the type name of a bean this method returns the canonical names + * of the own-list and the shared-list properties respectively. + * Returns a list with two elements: name of the own-list, and name + * of the shared list. + * + * @param string $typeName bean type name + * + * @return array + */ + private function getListNames( $typeName ) + { + $owned = 'own' . ucfirst( $typeName ); + $shared = 'shared' . ucfirst( $typeName ); + + return array( $owned, $shared ); + } + + /** + * Determines whether the bean has an own list based on + * schema inspection from realtime schema or cache. + * + * @param string $type bean type to get list for + * @param string $target type of list you want to detect + * + * @return boolean + */ + protected function hasOwnList( $type, $target ) + { + return isset( $this->columns[$target][$type . '_id'] ); + } + + /** + * Determines whether the bea has a shared list based on + * schema inspection from realtime schema or cache. + * + * @param string $type bean type to get list for + * @param string $target type of list you are looking for + * + * @return boolean + */ + protected function hasSharedList( $type, $target ) + { + return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables ); + } + + /** + * @see DuplicationManager::dup + * + * @param OODBBean $bean bean to be copied + * @param array $trail trail to prevent infinite loops + * @param boolean $preserveIDs preserve IDs + * + * @return OODBBean + */ + protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + { + if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean; + + $type = $bean->getMeta( 'type' ); + + $copy = $this->createCopy( $bean ); + foreach ( $this->tables as $table ) { + + if ( !empty( $this->filters ) ) { + if ( !in_array( $table, $this->filters ) ) continue; + } + + list( $owned, $shared ) = $this->getListNames( $table ); + + if ( $this->hasSharedList( $type, $table ) ) { + if ( $beans = $bean->$shared ) { + $this->copySharedBeans( $copy, $shared, $beans ); + } + } elseif ( $this->hasOwnList( $type, $table ) ) { + if ( $beans = $bean->$owned ) { + $this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs ); + } + + $copy->setMeta( 'sys.shadow.' . $owned, NULL ); + } + + $copy->setMeta( 'sys.shadow.' . $shared, NULL ); + } + + $copy->id = ( $preserveIDs ) ? $bean->id : $copy->id; + + return $copy; + } + + /** + * Constructor, + * creates a new instance of DupManager. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + $this->associationManager = $this->redbean->getAssociationManager(); + } + + /** + * Recursively turns the keys of an array into + * camelCase. + * + * @param array $array array to camelize + * @param boolean $dolphinMode whether you want the exception for IDs. + * + * @return array + */ + public function camelfy( $array, $dolphinMode = false ) { + $newArray = array(); + foreach( $array as $key => $element ) { + $newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){ + return strtoupper( $matches[1] ); + }, $key); + + if ( $dolphinMode ) { + $newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey ); + } + + $newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element; + } + return $newArray; + } + + /** + * For better performance you can pass the tables in an array to this method. + * If the tables are available the duplication manager will not query them so + * this might be beneficial for performance. + * + * This method allows two array formats: + * + * + * array( TABLE1, TABLE2 ... ) + * + * + * or + * + * + * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... ) + * + * + * @param array $tables a table cache array + * + * @return void + */ + public function setTables( $tables ) + { + foreach ( $tables as $key => $value ) { + if ( is_numeric( $key ) ) { + $this->tables[] = $value; + } else { + $this->tables[] = $key; + $this->columns[$key] = $value; + } + } + + $this->cacheTables = TRUE; + } + + /** + * Returns a schema array for cache. + * You can use the return value of this method as a cache, + * store it in RAM or on disk and pass it to setTables later. + * + * @return array + */ + public function getSchema() + { + return $this->columns; + } + + /** + * Indicates whether you want the duplication manager to cache the database schema. + * If this flag is set to TRUE the duplication manager will query the database schema + * only once. Otherwise the duplicationmanager will, by default, query the schema + * every time a duplication action is performed (dup()). + * + * @param boolean $yesNo TRUE to use caching, FALSE otherwise + */ + public function setCacheTables( $yesNo ) + { + $this->cacheTables = $yesNo; + } + + /** + * A filter array is an array with table names. + * By setting a table filter you can make the duplication manager only take into account + * certain bean types. Other bean types will be ignored when exporting or making a + * deep copy. If no filters are set all types will be taking into account, this is + * the default behavior. + * + * @param array $filters list of tables to be filtered + * + * @return void + */ + public function setFilters( $filters ) + { + if ( !is_array( $filters ) ) { + $filters = array( $filters ); + } + + $this->filters = $filters; + } + + /** + * Makes a copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following features. + * - All beans in own-lists will be duplicated as well + * - All references to shared beans will be copied but not the shared beans themselves + * - All references to parent objects (_id fields) will be copied but not the parents themselves + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * Note: + * this function actually passes the arguments to a protected function called + * duplicate() that does all the work. This method takes care of creating a clone + * of the bean to avoid the bean getting tainted (triggering saving when storing it). + * + * @param OODBBean $bean bean to be copied + * @param array $trail for internal usage, pass array() + * @param boolean $preserveIDs for internal usage + * + * @return OODBBean + */ + public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + { + if ( !count( $this->tables ) ) { + $this->tables = $this->toolbox->getWriter()->getTables(); + } + + if ( !count( $this->columns ) ) { + foreach ( $this->tables as $table ) { + $this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table ); + } + } + + $rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs ); + + if ( !$this->cacheTables ) { + $this->tables = array(); + $this->columns = array(); + } + + return $rs; + } + + /** + * Exports a collection of beans recursively. + * This method will export an array of beans in the first argument to a + * set of arrays. This can be used to send JSON or XML representations + * of bean hierarchies to the client. + * + * For every bean in the array this method will export: + * + * - contents of the bean + * - all own bean lists (recursively) + * - all shared beans (but not THEIR own lists) + * + * If the second parameter is set to TRUE the parents of the beans in the + * array will be exported as well (but not THEIR parents). + * + * The third parameter can be used to provide a white-list array + * for filtering. This is an array of strings representing type names, + * only the type names in the filter list will be exported. + * + * The fourth parameter can be used to change the keys of the resulting + * export arrays. The default mode is 'snake case' but this leaves the + * keys as-is, because 'snake' is the default case style used by + * RedBeanPHP in the database. You can set this to 'camel' for + * camel cased keys or 'dolphin' (same as camelcase but id will be + * converted to ID instead of Id). + * + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents also export parents + * @param array $filters only these types (whitelist) + * @param string $caseStyle case style identifier + * + * @return array + */ + public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake') + { + $array = array(); + + if ( !is_array( $beans ) ) { + $beans = array( $beans ); + } + + foreach ( $beans as $bean ) { + $this->setFilters( $filters ); + + $duplicate = $this->dup( $bean, array(), TRUE ); + + $array[] = $duplicate->export( FALSE, $parents, FALSE, $filters ); + } + + if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array ); + if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true ); + + return $array; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException as RedException; + +/** + * Array Tool Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * This is a helper or service class containing frequently used + * array functions for dealing with SQL queries. + * + * @file RedBeanPHP/Util/ArrayTool.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class ArrayTool +{ + /** + * Generates question mark slots for an array of values. + * + * @param array $array array to generate question mark slots for + * + * @return string + */ + public static function genSlots( $array, $template = NULL ) + { + $str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : ''; + return ( is_null( $template ) || $str === '' ) ? $str : sprintf( $template, $str ); + } + + /** + * Flattens a multi dimensional bindings array for use with genSlots(). + * + * @param array $array array to flatten + * @param array $result result array parameter (for recursion) + * + * @return array + */ + public static function flat( $array, $result = array() ) + { + foreach( $array as $value ) { + if ( is_array( $value ) ) $result = self::flat( $value, $result ); + else $result[] = $value; + } + return $result; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\RedException as RedException; + +/** + * Dispense Helper + * + * A helper class containing a dispense utility. + * + * @file RedBeanPHP/Util/DispenseHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DispenseHelper +{ + /** + * Dispenses a new RedBean OODB Bean for use with + * the rest of the methods. + * + * @param OODB $oodb OODB + * @param string|array $typeOrBeanArray type or bean array to import + * @param integer $number number of beans to dispense + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return array|OODBBean + */ + public static function dispense( OODB $oodb, $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) { + + if ( is_array($typeOrBeanArray) ) { + + if ( !isset( $typeOrBeanArray['_type'] ) ) { + $list = array(); + foreach( $typeOrBeanArray as $beanArray ) { + if ( + !( is_array( $beanArray ) + && isset( $beanArray['_type'] ) ) ) { + throw new RedException( 'Invalid Array Bean' ); + } + } + foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $oodb, $beanArray ); + return $list; + } + + $import = $typeOrBeanArray; + $type = $import['_type']; + unset( $import['_type'] ); + } else { + $type = $typeOrBeanArray; + } + + if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) { + throw new RedException( 'Invalid type: ' . $type ); + } + + $beanOrBeans = $oodb->dispense( $type, $num, $alwaysReturnArray ); + + if ( isset( $import ) ) { + $beanOrBeans->import( $import ); + } + + return $beanOrBeans; + } + + + /** + * Takes a comma separated list of bean types + * and dispenses these beans. For each type in the list + * you can specify the number of beans to be dispensed. + * + * Usage: + * + * + * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' ); + * + * + * This will dispense a book, a page and a text. This way you can + * quickly dispense beans of various types in just one line of code. + * + * Usage: + * + * + * list($book, $pages) = R::dispenseAll('book,page*100'); + * + * + * This returns an array with a book bean and then another array + * containing 100 page beans. + * + * @param OODB $oodb OODB + * @param string $order a description of the desired dispense order using the syntax above + * @param boolean $onlyArrays return only arrays even if amount < 2 + * + * @return array + */ + public static function dispenseAll( OODB $oodb, $order, $onlyArrays = FALSE ) + { + $list = array(); + + foreach( explode( ',', $order ) as $order ) { + if ( strpos( $order, '*' ) !== FALSE ) { + list( $type, $amount ) = explode( '*', $order ); + } else { + $type = $order; + $amount = 1; + } + + $list[] = self::dispense( $oodb, $type, $amount, $onlyArrays ); + } + + return $list; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Dump helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * Dumps the contents of a bean in an array for + * debugging purposes. + * + * @file RedBeanPHP/Util/Dump.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Dump +{ + /** + * Simple but effective debug function. + * Given a one or more beans this method will + * return an array containing first part of the string + * representation of each item in the array. + * + * @param OODBBean|array $data either a bean or an array of beans + * + * @return array + */ + public static function dump( $data ) + { + $array = array(); + + if ( $data instanceof OODBBean ) { + $str = strval( $data ); + if (strlen($str) > 35) { + $beanStr = substr( $str, 0, 35 ).'... '; + } else { + $beanStr = $str; + } + return $beanStr; + } + + if ( is_array( $data ) ) { + foreach( $data as $key => $item ) { + $array[$key] = self::dump( $item ); + } + } + + return $array; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; + +/** + * Multi Bean Loader Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * This helper class offers limited support for one-to-one + * relations by providing a service to load a set of beans + * with differnt types and a common ID. + * + * @file RedBeanPHP/Util/MultiLoader.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class MultiLoader +{ + /** + * Loads multiple types of beans with the same ID. + * This might look like a strange method, however it can be useful + * for loading a one-to-one relation. + * + * @param OODB $oodb OODB object + * @param string|array $types the set of types to load at once + * @param mixed $id the common ID + * + * @return OODBBean + */ + public static function load( OODB $oodb, $types, $id ) + { + if ( is_string( $types ) ) { + $types = explode( ',', $types ); + } + + if ( !is_array( $types ) ) { + return array(); + } + + foreach ( $types as $k => $typeItem ) { + $types[$k] = $oodb->load( $typeItem, $id ); + } + + return $types; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\Adapter as Adapter; + +/** + * Transaction Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * Database transaction helper. This is a convenience class + * to perform a callback in a database transaction. This class + * contains a method to wrap your callback in a transaction. + * + * @file RedBeanPHP/Util/Transaction.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Transaction +{ + /** + * Wraps a transaction around a closure or string callback. + * If an Exception is thrown inside, the operation is automatically rolled back. + * If no Exception happens, it commits automatically. + * It also supports (simulated) nested transactions (that is useful when + * you have many methods that needs transactions but are unaware of + * each other). + * + * Example: + * + * + * $from = 1; + * $to = 2; + * $amount = 300; + * + * R::transaction(function() use($from, $to, $amount) + * { + * $accountFrom = R::load('account', $from); + * $accountTo = R::load('account', $to); + * $accountFrom->money -= $amount; + * $accountTo->money += $amount; + * R::store($accountFrom); + * R::store($accountTo); + * }); + * + * + * @param Adapter $adapter Database Adapter providing transaction mechanisms. + * @param callable $callback Closure (or other callable) with the transaction logic + * + * @return mixed + */ + public static function transaction( Adapter $adapter, $callback ) + { + if ( !is_callable( $callback ) ) { + throw new RedException( 'R::transaction needs a valid callback.' ); + } + + static $depth = 0; + $result = null; + try { + if ( $depth == 0 ) { + $adapter->startTransaction(); + } + $depth++; + $result = call_user_func( $callback ); //maintain 5.2 compatibility + $depth--; + if ( $depth == 0 ) { + $adapter->commit(); + } + } catch ( \Exception $exception ) { + $depth--; + if ( $depth == 0 ) { + $adapter->rollback(); + } + throw $exception; + } + return $result; + } +} +} + +namespace RedBeanPHP { + +/** + * RedBean Plugin. + * Marker interface for plugins. + * Use this interface when defining new plugins, it's an + * easy way for the rest of the application to recognize your + * plugin. This plugin interface does not require you to + * implement a specific API. + * + * @file RedBean/Plugin.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Plugin +{ +} + +; +} +namespace { + +//make some classes available for backward compatibility +class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {}; + +if (!class_exists('R')) { + class R extends \RedBeanPHP\Facade{}; +} + + + +/** + * Support functions for RedBeanPHP. + * Additional convenience shortcut functions for RedBeanPHP. + * + * @file RedBeanPHP/Functions.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ + +/** + * Convenience function for ENUM short syntax in queries. + * + * Usage: + * + * + * R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] ); + * + * + * If a function called EID() already exists you'll have to write this + * wrapper yourself ;) + * + * @param string $enumName enum code as you would pass to R::enum() + * + * @return mixed + */ +if (!function_exists('EID')) { + + function EID($enumName) + { + return \RedBeanPHP\Facade::enum( $enumName )->id; + } + +} + +/** + * Prints the result of R::dump() to the screen using + * print_r. + * + * @param mixed $data data to dump + * + * @return void + */ +if ( !function_exists( 'dmp' ) ) { + + function dmp( $list ) + { + print_r( \RedBeanPHP\Facade::dump( $list ) ); + } +} + +/** + * Function alias for R::genSlots(). + */ +if ( !function_exists( 'genslots' ) ) { + + function genslots( $slots, $tpl = NULL ) + { + return \RedBeanPHP\Facade::genSlots( $slots, $tpl ); + } +} + +/** + * Function alias for R::flat(). + */ +if ( !function_exists( 'array_flatten' ) ) { + + function array_flatten( $array ) + { + return \RedBeanPHP\Facade::flat( $array ); + } +} + + +} diff --git a/public/test.php b/public/test.php new file mode 100644 index 0000000..df24537 --- /dev/null +++ b/public/test.php @@ -0,0 +1,5 @@ +title = 'Name'; + $category->text = 'Text'; + + $res = R::store($category); + // $category = R::load('category', 9); + + + \ No newline at end of file diff --git a/tests/rb.php b/tests/rb.php new file mode 100644 index 0000000..ae301ad --- /dev/null +++ b/tests/rb.php @@ -0,0 +1,13058 @@ +mode === self::C_LOGGER_ECHO ) { + echo $log; + } else { + $this->logs[] = $log; + } + } else { + if ( $this->mode === self::C_LOGGER_ECHO ) { + echo $argument; + } else { + $this->logs[] = $argument; + } + } + + if ( $this->mode === self::C_LOGGER_ECHO ) echo "
" . PHP_EOL; + } + } + + /** + * Returns the internal log array. + * The internal log array is where all log messages are stored. + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears the internal log array, removing all + * previously stored entries. + * + * @return self + */ + public function clear() + { + $this->logs = array(); + return $this; + } + + /** + * Selects a logging mode. + * There are several options available. + * + * * C_LOGGER_ARRAY - log silently, stores entries in internal log array only + * * C_LOGGER_ECHO - also forward log messages directly to STDOUT + * + * @param integer $mode mode of operation for logging object + * + * @return self + */ + public function setMode( $mode ) + { + if ($mode !== self::C_LOGGER_ARRAY && $mode !== self::C_LOGGER_ECHO ) { + throw new RedException( 'Invalid mode selected for logger, use C_LOGGER_ARRAY or C_LOGGER_ECHO.' ); + } + $this->mode = $mode; + return $this; + } + + /** + * Searches for all log entries in internal log array + * for $needle and returns those entries. + * This method will return an array containing all matches for your + * search query. + * + * @param string $needle phrase to look for in internal log array + * + * @return array + */ + public function grep( $needle ) + { + $found = array(); + foreach( $this->logs as $logEntry ) { + if ( strpos( $logEntry, $needle ) !== FALSE ) $found[] = $logEntry; + } + return $found; + } +} +} + +namespace RedBeanPHP\Logger\RDefault { + +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\RedException as RedException; + +/** + * Debug logger. + * A special logger for debugging purposes. + * Provides debugging logging functions for RedBeanPHP. + * + * @file RedBeanPHP/Logger/RDefault/Debug.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Debug extends RDefault implements Logger +{ + /** + * @var integer + */ + private $strLen = 40; + + /** + * Writes a query for logging with all bindings / params filled + * in. + * + * @param string $newSql the query + * @param array $bindings the bindings to process (key-value pairs) + * + * @return string + */ + private function writeQuery( $newSql, $newBindings ) + { + //avoid str_replace collisions: slot1 and slot10 (issue 407). + uksort( $newBindings, function( $a, $b ) { + return ( strlen( $b ) - strlen( $a ) ); + } ); + + $newStr = $newSql; + foreach( $newBindings as $slot => $value ) { + if ( strpos( $slot, ':' ) === 0 ) { + $newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr ); + } + } + return $newStr; + } + + /** + * Fills in a value of a binding and truncates the + * resulting string if necessary. + * + * @param mixed $value bound value + * + * @return string + */ + protected function fillInValue( $value ) + { + if ( is_null( $value ) ) $value = 'NULL'; + + $value = strval( $value ); + if ( strlen( $value ) > ( $this->strLen ) ) { + $value = substr( $value, 0, ( $this->strLen ) ).'... '; + } + + if ( !\RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt( $value ) && $value !== 'NULL') { + $value = '\''.$value.'\''; + } + + return $value; + } + + /** + * Dependending on the current mode of operation, + * this method will either log and output to STDIN or + * just log. + * + * Depending on the value of constant PHP_SAPI this function + * will format output for console or HTML. + * + * @param string $str string to log or output and log + * + * @return void + */ + protected function output( $str ) + { + $this->logs[] = $str; + if ( !$this->mode ) { + $highlight = FALSE; + /* just a quick heuritsic to highlight schema changes */ + if ( strpos( $str, 'CREATE' ) === 0 + || strpos( $str, 'ALTER' ) === 0 + || strpos( $str, 'DROP' ) === 0) { + $highlight = TRUE; + } + if (PHP_SAPI === 'cli') { + if ($highlight) echo "\e[91m"; + echo $str, PHP_EOL; + echo "\e[39m"; + } else { + if ($highlight) { + echo "{$str}"; + } else { + echo $str; + } + echo '
'; + } + } + } + + /** + * Normalizes the slots in an SQL string. + * Replaces question mark slots with :slot1 :slot2 etc. + * + * @param string $sql sql to normalize + * + * @return string + */ + protected function normalizeSlots( $sql ) + { + $newSql = $sql; + $i = 0; + while(strpos($newSql, '?') !== FALSE ){ + $pos = strpos( $newSql, '?' ); + $slot = ':slot'.$i; + $begin = substr( $newSql, 0, $pos ); + $end = substr( $newSql, $pos+1 ); + if (PHP_SAPI === 'cli') { + $newSql = "{$begin}\e[32m{$slot}\e[39m{$end}"; + } else { + $newSql = "{$begin}$slot{$end}"; + } + $i ++; + } + return $newSql; + } + + /** + * Normalizes the bindings. + * Replaces numeric binding keys with :slot1 :slot2 etc. + * + * @param array $bindings bindings to normalize + * + * @return array + */ + protected function normalizeBindings( $bindings ) + { + $i = 0; + $newBindings = array(); + foreach( $bindings as $key => $value ) { + if ( is_numeric($key) ) { + $newKey = ':slot'.$i; + $newBindings[$newKey] = $value; + $i++; + } else { + $newBindings[$key] = $value; + } + } + return $newBindings; + } + + /** + * Logger method. + * + * Takes a number of arguments tries to create + * a proper debug log based on the available data. + * + * @return void + */ + public function log() + { + if ( func_num_args() < 1 ) return; + + $sql = func_get_arg( 0 ); + + if ( func_num_args() < 2) { + $bindings = array(); + } else { + $bindings = func_get_arg( 1 ); + } + + if ( !is_array( $bindings ) ) { + return $this->output( $sql ); + } + + $newSql = $this->normalizeSlots( $sql ); + $newBindings = $this->normalizeBindings( $bindings ); + $newStr = $this->writeQuery( $newSql, $newBindings ); + $this->output( $newStr ); + } + + /** + * Sets the max string length for the parameter output in + * SQL queries. Set this value to a reasonable number to + * keep you SQL queries readable. + * + * @param integer $len string length + * + * @return self + */ + public function setParamStringLength( $len = 20 ) + { + $this->strLen = max(0, $len); + return $this; + } +} +} + +namespace RedBeanPHP { + +/** + * Interface for database drivers. + * The Driver API conforms to the ADODB pseudo standard + * for database drivers. + * + * @file RedBeanPHP/Driver.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Driver +{ + /** + * Runs a query and fetches results as a multi dimensional array. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetAll( $sql, $bindings = array() ); + + /** + * Runs a query and fetches results as a column. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetCol( $sql, $bindings = array() ); + + /** + * Runs a query and returns results as a single cell. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetOne( $sql, $bindings = array() ); + + /** + * Runs a query and returns results as an associative array + * indexed by the first column. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetAssocRow( $sql, $bindings = array() ); + + /** + * Runs a query and returns a flat array containing the values of + * one row. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array + */ + public function GetRow( $sql, $bindings = array() ); + + /** + * Executes SQL code and allows key-value binding. + * This function allows you to provide an array with values to bind + * to query parameters. For instance you can bind values to question + * marks in the query. Each value in the array corresponds to the + * question mark in the query that matches the position of the value in the + * array. You can also bind values using explicit keys, for instance + * array(":key"=>123) will bind the integer 123 to the key :key in the + * SQL. This method has no return value. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return array Affected Rows + */ + public function Execute( $sql, $bindings = array() ); + + /** + * Returns the latest insert ID if driver does support this + * feature. + * + * @return integer + */ + public function GetInsertID(); + + /** + * Returns the number of rows affected by the most recent query + * if the currently selected driver driver supports this feature. + * + * @return integer + */ + public function Affected_Rows(); + + /** + * Returns a cursor-like object from the database. + * + * @param string $sql SQL query to execute + * @param array $bindings list of values to bind to SQL snippet + * + * @return mixed + */ + public function GetCursor( $sql, $bindings = array() ); + + /** + * Toggles debug mode. In debug mode the driver will print all + * SQL to the screen together with some information about the + * results. All SQL code that passes through the driver will be + * passes on to the screen for inspection. + * This method has no return value. + * + * @param boolean $tf TRUE = debug mode ON + * @param Logger $customLogger + * + * @return void + */ + public function setDebugMode( $tf, $customLogger ); + + /** + * Starts a transaction. + * + * @return void + */ + public function CommitTrans(); + + /** + * Commits a transaction. + * + * @return void + */ + public function StartTrans(); + + /** + * Rolls back a transaction. + * + * @return void + */ + public function FailTrans(); + + /** + * Resets the internal Query Counter. + * + * @return self + */ + public function resetCounter(); + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public function getQueryCount(); +} +} + +namespace RedBeanPHP\Driver { + +use RedBeanPHP\Driver as Driver; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\PDOCompatible as PDOCompatible; +use RedBeanPHP\Cursor\PDOCursor as PDOCursor; + +/** + * PDO Driver + * This Driver implements the RedBean Driver API. + * for RedBeanPHP. This is the standard / default database driver + * for RedBeanPHP. + * + * @file RedBeanPHP/PDO.php + * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) Desfrenes & Gabor de Mooij and the RedBeanPHP community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RPDO implements Driver +{ + /** + * @var integer + */ + protected $max; + + /** + * @var string + */ + protected $dsn; + + /** + * @var boolean + */ + protected $loggingEnabled = FALSE; + + /** + * @var Logger + */ + protected $logger = NULL; + + /** + * @var PDO + */ + protected $pdo; + + /** + * @var integer + */ + protected $affectedRows; + + /** + * @var integer + */ + protected $resultArray; + + /** + * @var array + */ + protected $connectInfo = array(); + + /** + * @var boolean + */ + protected $isConnected = FALSE; + + /** + * @var bool + */ + protected $flagUseStringOnlyBinding = FALSE; + + /** + * @var integer + */ + protected $queryCounter = 0; + + /** + * @var string + */ + protected $mysqlEncoding = ''; + + /** + * Binds parameters. This method binds parameters to a PDOStatement for + * Query Execution. This method binds parameters as NULL, INTEGER or STRING + * and supports both named keys and question mark keys. + * + * @param PDOStatement $statement PDO Statement instance + * @param array $bindings values that need to get bound to the statement + * + * @return void + */ + protected function bindParams( $statement, $bindings ) + { + foreach ( $bindings as $key => &$value ) { + if ( is_integer( $key ) ) { + if ( is_null( $value ) ) { + $statement->bindValue( $key + 1, NULL, \PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { + $statement->bindParam( $key + 1, $value, \PDO::PARAM_INT ); + } else { + $statement->bindParam( $key + 1, $value, \PDO::PARAM_STR ); + } + } else { + if ( is_null( $value ) ) { + $statement->bindValue( $key, NULL, \PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && abs( $value ) <= $this->max ) { + $statement->bindParam( $key, $value, \PDO::PARAM_INT ); + } else { + $statement->bindParam( $key, $value, \PDO::PARAM_STR ); + } + } + } + } + + /** + * This method runs the actual SQL query and binds a list of parameters to the query. + * slots. The result of the query will be stored in the protected property + * $rs (always array). The number of rows affected (result of rowcount, if supported by database) + * is stored in protected property $affectedRows. If the debug flag is set + * this function will send debugging output to screen buffer. + * + * @param string $sql the SQL string to be send to database server + * @param array $bindings the values that need to get bound to the query slots + * @param array $options + * + * @return mixed + * @throws SQL + */ + protected function runQuery( $sql, $bindings, $options = array() ) + { + $this->connect(); + if ( $this->loggingEnabled && $this->logger ) { + $this->logger->log( $sql, $bindings ); + } + try { + if ( strpos( 'pgsql', $this->dsn ) === 0 ) { + if ( defined( '\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT' ) ) { + $statement = $this->pdo->prepare( $sql, array( \PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); + } else { + $statement = $this->pdo->prepare( $sql ); + } + } else { + $statement = $this->pdo->prepare( $sql ); + } + $this->bindParams( $statement, $bindings ); + $statement->execute(); + $this->queryCounter ++; + $this->affectedRows = $statement->rowCount(); + if ( $statement->columnCount() ) { + $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL; + if ( isset( $options['noFetch'] ) && $options['noFetch'] ) { + $this->resultArray = array(); + return $statement; + } + $this->resultArray = $statement->fetchAll( $fetchStyle ); + if ( $this->loggingEnabled && $this->logger ) { + $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' ); + } + } else { + $this->resultArray = array(); + } + } catch ( \PDOException $e ) { + //Unfortunately the code field is supposed to be int by default (php) + //So we need a property to convey the SQL State code. + $err = $e->getMessage(); + if ( $this->loggingEnabled && $this->logger ) $this->logger->log( 'An error occurred: ' . $err ); + $exception = new SQL( $err, 0 ); + $exception->setSQLState( $e->getCode() ); + throw $exception; + } + } + + /** + * Try to fix MySQL character encoding problems. + * MySQL < 5.5 does not support proper 4 byte unicode but they + * seem to have added it with version 5.5 under a different label: utf8mb4. + * We try to select the best possible charset based on your version data. + * + * @return void + */ + protected function setEncoding() + { + $driver = $this->pdo->getAttribute( \PDO::ATTR_DRIVER_NAME ); + $version = floatval( $this->pdo->getAttribute( \PDO::ATTR_SERVER_VERSION ) ); + if ($driver === 'mysql') { + $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8'; + $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect + $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection + $this->mysqlEncoding = $encoding; + } + } + + /** + * Constructor. You may either specify dsn, user and password or + * just give an existing PDO connection. + * + * Examples: + * $driver = new RPDO($dsn, $user, $password); + * $driver = new RPDO($existingConnection); + * + * @param string|object $dsn database connection string + * @param string $user optional, usename to sign in + * @param string $pass optional, password for connection login + * + * @return void + */ + public function __construct( $dsn, $user = NULL, $pass = NULL ) + { + if ( is_object( $dsn ) ) { + $this->pdo = $dsn; + $this->isConnected = TRUE; + $this->setEncoding(); + $this->pdo->setAttribute( \PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION ); + $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC ); + // make sure that the dsn at least contains the type + $this->dsn = $this->getDatabaseType(); + } else { + $this->dsn = $dsn; + $this->connectInfo = array( 'pass' => $pass, 'user' => $user ); + } + + //PHP 5.3 PDO SQLite has a bug with large numbers: + if ( ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3 ) || defined('HHVM_VERSION') || $this->dsn === 'test-sqlite-53' ) { + $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis. + } elseif ( strpos( $this->dsn, 'cubrid' ) === 0 ) { + $this->max = 2147483647; //bindParam in pdo_cubrid also fails... + } else { + $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause) + } + } + + /** + * Returns the best possible encoding for MySQL based on version data. + * + * @return string + */ + public function getMysqlEncoding() + { + return $this->mysqlEncoding; + } + + /** + * Whether to bind all parameters as strings. + * If set to TRUE this will cause all integers to be bound as STRINGS. + * This will NOT affect NULL values. + * + * @param boolean $yesNo pass TRUE to bind all parameters as strings. + * + * @return void + */ + public function setUseStringOnlyBinding( $yesNo ) + { + $this->flagUseStringOnlyBinding = (boolean) $yesNo; + } + + /** + * Sets the maximum value to be bound as integer, normally + * this value equals PHP's MAX INT constant, however sometimes + * PDO driver bindings cannot bind large integers as integers. + * This method allows you to manually set the max integer binding + * value to manage portability/compatibility issues among different + * PHP builds. This method will return the old value. + * + * @param integer $max maximum value for integer bindings + * + * @return integer + */ + public function setMaxIntBind( $max ) + { + if ( !is_integer( $max ) ) throw new RedException( 'Parameter has to be integer.' ); + $oldMax = $this->max; + $this->max = $max; + return $oldMax; + } + + /** + * Establishes a connection to the database using PHP\PDO + * functionality. If a connection has already been established this + * method will simply return directly. This method also turns on + * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as + * PDO-FETCH-ASSOC. + * + * @return void + */ + public function connect() + { + if ( $this->isConnected ) return; + try { + $user = $this->connectInfo['user']; + $pass = $this->connectInfo['pass']; + $this->pdo = new \PDO( + $this->dsn, + $user, + $pass + ); + $this->setEncoding(); + $this->pdo->setAttribute( \PDO::ATTR_STRINGIFY_FETCHES, TRUE ); + //cant pass these as argument to constructor, CUBRID driver does not understand... + $this->pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION ); + $this->pdo->setAttribute( \PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC ); + $this->isConnected = TRUE; + } catch ( \PDOException $exception ) { + $matches = array(); + $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?'; + throw new \PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() ); + } + } + + /** + * Directly sets PDO instance into driver. + * This method might improve performance, however since the driver does + * not configure this instance terrible things may happen... only use + * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and + * you know your database server VERY WELL. + * + * @param PDO $pdo PDO instance + * + * @return void + */ + public function setPDO( \PDO $pdo ) { + $this->pdo = $pdo; + } + + /** + * @see Driver::GetAll + */ + public function GetAll( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings ); + return $this->resultArray; + } + + /** + * @see Driver::GetAssocRow + */ + public function GetAssocRow( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings, array( + 'fetchStyle' => \PDO::FETCH_ASSOC + ) + ); + return $this->resultArray; + } + + /** + * @see Driver::GetCol + */ + public function GetCol( $sql, $bindings = array() ) + { + $rows = $this->GetAll( $sql, $bindings ); + $cols = array(); + if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) { + foreach ( $rows as $row ) { + $cols[] = array_shift( $row ); + } + } + + return $cols; + } + + /** + * @see Driver::GetOne + */ + public function GetOne( $sql, $bindings = array() ) + { + $arr = $this->GetAll( $sql, $bindings ); + $res = NULL; + if ( !is_array( $arr ) ) return NULL; + if ( count( $arr ) === 0 ) return NULL; + $row1 = array_shift( $arr ); + if ( !is_array( $row1 ) ) return NULL; + if ( count( $row1 ) === 0 ) return NULL; + $col1 = array_shift( $row1 ); + return $col1; + } + + /** + * Alias for getOne(). + * Backward compatibility. + * + * @param string $sql SQL + * @param array $bindings bindings + * + * @return mixed + */ + public function GetCell( $sql, $bindings = array() ) + { + return $this->GetOne( $sql, $bindings ); + } + + /** + * @see Driver::GetRow + */ + public function GetRow( $sql, $bindings = array() ) + { + $arr = $this->GetAll( $sql, $bindings ); + return array_shift( $arr ); + } + + /** + * @see Driver::Excecute + */ + public function Execute( $sql, $bindings = array() ) + { + $this->runQuery( $sql, $bindings ); + return $this->affectedRows; + } + + /** + * @see Driver::GetInsertID + */ + public function GetInsertID() + { + $this->connect(); + + return (int) $this->pdo->lastInsertId(); + } + + /** + * @see Driver::GetCursor + */ + public function GetCursor( $sql, $bindings = array() ) + { + $statement = $this->runQuery( $sql, $bindings, array( 'noFetch' => TRUE ) ); + $cursor = new PDOCursor( $statement, \PDO::FETCH_ASSOC ); + return $cursor; + } + + /** + * @see Driver::Affected_Rows + */ + public function Affected_Rows() + { + $this->connect(); + return (int) $this->affectedRows; + } + + /** + * Toggles debug mode. In debug mode the driver will print all + * SQL to the screen together with some information about the + * results. + * + * @param boolean $trueFalse turn on/off + * @param Logger $logger logger instance + * + * @return void + */ + public function setDebugMode( $tf, $logger = NULL ) + { + $this->connect(); + $this->loggingEnabled = (bool) $tf; + if ( $this->loggingEnabled and !$logger ) { + $logger = new RDefault(); + } + $this->setLogger( $logger ); + } + + /** + * Injects Logger object. + * Sets the logger instance you wish to use. + * + * @param Logger $logger the logger instance to be used for logging + * + * @return void + */ + public function setLogger( Logger $logger ) + { + $this->logger = $logger; + } + + /** + * Gets Logger object. + * Returns the currently active Logger instance. + * + * @return Logger + */ + public function getLogger() + { + return $this->logger; + } + + /** + * @see Driver::StartTrans + */ + public function StartTrans() + { + $this->connect(); + $this->pdo->beginTransaction(); + } + + /** + * @see Driver::CommitTrans + */ + public function CommitTrans() + { + $this->connect(); + $this->pdo->commit(); + } + + /** + * @see Driver::FailTrans + */ + public function FailTrans() + { + $this->connect(); + $this->pdo->rollback(); + } + + /** + * Returns the name of database driver for PDO. + * Uses the PDO attribute DRIVER NAME to obtain the name of the + * PDO driver. + * + * @return string + */ + public function getDatabaseType() + { + $this->connect(); + + return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); + } + + /** + * Returns the version number of the database. + * + * @return mixed + */ + public function getDatabaseVersion() + { + $this->connect(); + return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION ); + } + + /** + * Returns the underlying PHP PDO instance. + * + * @return PDO + */ + public function getPDO() + { + $this->connect(); + return $this->pdo; + } + + /** + * Closes database connection by destructing PDO. + * + * @return void + */ + public function close() + { + $this->pdo = NULL; + $this->isConnected = FALSE; + } + + /** + * Returns TRUE if the current PDO instance is connected. + * + * @return boolean + */ + public function isConnected() + { + return $this->isConnected && $this->pdo; + } + + /** + * Toggles logging, enables or disables logging. + * + * @param boolean $enable TRUE to enable logging + * + * @return self + */ + public function setEnableLogging( $enable ) + { + $this->loggingEnabled = (boolean) $enable; + } + + /** + * Resets the internal Query Counter. + * + * @return self + */ + public function resetCounter() + { + $this->queryCounter = 0; + return $this; + } + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public function getQueryCount() + { + return $this->queryCounter; + } + + /** + * Returns the maximum value treated as integer parameter + * binding. + * + * This method is mainly for testing purposes but it can help + * you solve some issues relating to integer bindings. + * + * @return integer + */ + public function getIntegerBindingMax() + { + return $this->max; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException as RedException; + +/* PHP 5.3 compatibility */ +if (interface_exists('\JsonSerializable')) { + /* We extend JsonSerializable to avoid namespace conflicts, + can't define interface with special namespace in PHP */ + interface Jsonable extends \JsonSerializable {}; +} else { + interface Jsonable {}; +} + +/** + * OODBBean (Object Oriented DataBase Bean). + * + * to exchange information with the database. A bean represents + * a single table row and offers generic services for interaction + * with databases systems as well as some meta-data. + * + * @file RedBeanPHP/OODBBean.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * @desc OODBBean represents a bean. RedBeanPHP uses beans + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable,Jsonable +{ + /** + * FUSE error modes. + */ + const C_ERR_IGNORE = FALSE; + const C_ERR_LOG = 1; + const C_ERR_NOTICE = 2; + const C_ERR_WARN = 3; + const C_ERR_EXCEPTION = 4; + const C_ERR_FUNC = 5; + const C_ERR_FATAL = 6; + + /** + * @var boolean + */ + protected static $errorHandlingFUSE = FALSE; + + /** + * @var callable|NULL + */ + protected static $errorHandler = NULL; + + /** + * @var array + */ + protected static $aliases = array(); + + /** + * @var boolean + */ + protected static $autoResolve = FALSE; + + /** + * This is where the real properties of the bean live. They are stored and retrieved + * by the magic getter and setter (__get and __set). + * + * @var array $properties + */ + protected $properties = array(); + + /** + * Here we keep the meta data of a bean. + * + * @var array + */ + protected $__info = array(); + + /** + * The BeanHelper allows the bean to access the toolbox objects to implement + * rich functionality, otherwise you would have to do everything with R or + * external objects. + * + * @var BeanHelper + */ + protected $beanHelper = NULL; + + /** + * @var null + */ + protected $fetchType = NULL; + + /** + * @var string + */ + protected $withSql = ''; + + /** + * @var array + */ + protected $withParams = array(); + + /** + * @var string + */ + protected $aliasName = NULL; + + /** + * @var string + */ + protected $via = NULL; + + /** + * @var boolean + */ + protected $noLoad = FALSE; + + /** + * @var boolean + */ + protected $all = FALSE; + + /** + * Sets the error mode for FUSE. + * What to do if a FUSE model method does not exist? + * You can set the following options: + * + * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL + * * OODBBean::C_ERR_LOG, logs the incident using error_log + * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE + * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING + * * OODBBean::C_ERR_EXCEPTION, throws an exception + * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) + * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR + * + * + * Custom handler method signature: handler( array ( + * 'message' => string + * 'bean' => OODBBean + * 'method' => string + * ) ) + * + * + * This method returns the old mode and handler as an array. + * + * @param integer $mode error handling mode + * @param callable|NULL $func custom handler + * + * @return array + */ + public static function setErrorHandlingFUSE($mode, $func = NULL) { + if ( + $mode !== self::C_ERR_IGNORE + && $mode !== self::C_ERR_LOG + && $mode !== self::C_ERR_NOTICE + && $mode !== self::C_ERR_WARN + && $mode !== self::C_ERR_EXCEPTION + && $mode !== self::C_ERR_FUNC + && $mode !== self::C_ERR_FATAL + ) throw new \Exception( 'Invalid error mode selected' ); + + if ( $mode === self::C_ERR_FUNC && !is_callable( $func ) ) { + throw new \Exception( 'Invalid error handler' ); + } + + $old = array( self::$errorHandlingFUSE, self::$errorHandler ); + self::$errorHandlingFUSE = $mode; + if ( is_callable( $func ) ) { + self::$errorHandler = $func; + } else { + self::$errorHandler = NULL; + } + return $old; + } + + /** + * Sets global aliases. + * Registers a batch of aliases in one go. This works the same as + * fetchAs and setAutoResolve but explicitly. For instance if you register + * the alias 'cover' for 'page' a property containing a reference to a + * page bean called 'cover' will correctly return the page bean and not + * a (non-existant) cover bean. + * + * + * R::aliases( array( 'cover' => 'page' ) ); + * $book = R::dispense( 'book' ); + * $page = R::dispense( 'page' ); + * $book->cover = $page; + * R::store( $book ); + * $book = $book->fresh(); + * $cover = $book->cover; + * echo $cover->getMeta( 'type' ); //page + * + * + * The format of the aliases registration array is: + * + * {alias} => {actual type} + * + * In the example above we use: + * + * cover => page + * + * From that point on, every bean reference to a cover + * will return a 'page' bean. Note that with autoResolve this + * feature along with fetchAs() is no longer very important, although + * relying on explicit aliases can be a bit faster. + * + * @param array $list list of global aliases to use + * + * @return void + */ + public static function aliases( $list ) + { + self::$aliases = $list; + } + + /** + * Enables or disables auto-resolving fetch types. + * Auto-resolving aliased parent beans is convenient but can + * be slower and can create infinite recursion if you + * used aliases to break cyclic relations in your domain. + * + * @param boolean $automatic TRUE to enable automatic resolving aliased parents + * + * @return void + */ + public static function setAutoResolve( $automatic = TRUE ) + { + self::$autoResolve = (boolean) $automatic; + } + + /** + * Sets a meta property for all beans. This is a quicker way to set + * the meta properties for a collection of beans because this method + * can directly access the property arrays of the beans. + * This method returns the beans. + * + * @param array $beans beans to set the meta property of + * @param string $property property to set + * @param mixed $value value + * + * @return array + */ + public static function setMetaAll( $beans, $property, $value ) + { + foreach( $beans as $bean ) { + if ( $bean instanceof OODBBean ) $bean->__info[ $property ] = $value; + } + + return $beans; + } + + /** + * Parses the join in the with-snippet. + * For instance: + * + * + * $author + * ->withCondition(' @joined.detail.title LIKE ? ') + * ->ownBookList; + * + * + * will automatically join 'detail' on book to + * access the title field. + * + * @note this feature requires Narrow Field Mode and Join Feature + * to be both activated (default). + * + * @param string $type the source type for the join + * + * @return string + */ + private function parseJoin( $type ) + { + $joinSql = ''; + $joins = array(); + if ( strpos($this->withSql, '@joined.' ) !== FALSE ) { + $writer = $this->beanHelper->getToolBox()->getWriter(); + $oldParts = $parts = explode( '@joined.', $this->withSql ); + array_shift( $parts ); + foreach($parts as $part) { + $explosion = explode( '.', $part ); + $joinInfo = array_shift( $explosion ); + //Dont join more than once.. + if ( !isset( $joins[$joinInfo] ) ) { + $joins[ $joinInfo ] = true; + $joinSql .= $writer->writeJoin( $type, $joinInfo, 'LEFT' ); + } + } + $this->withSql = implode( '', $oldParts ); + $joinSql .= ' WHERE '; + } + return $joinSql; + } + + /** + * Internal method. + * Obtains a shared list for a certain type. + * + * @param string $type the name of the list you want to retrieve. + * + * @return array + */ + private function getSharedList( $type, $redbean, $toolbox ) + { + $writer = $toolbox->getWriter(); + + if ( $this->via ) { + $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); + if ( $oldName !== $this->via ) { + //set the new renaming rule + $writer->renameAssocTable( $oldName, $this->via ); + } + $this->via = NULL; + } + + $beans = array(); + if ($this->getID()) { + $type = $this->beau( $type ); + $assocManager = $redbean->getAssociationManager(); + $beans = $assocManager->related( $this, $type, $this->withSql, $this->withParams ); + } + + $this->withSql = ''; + $this->withParams = array(); + + return $beans; + } + + /** + * Internal method. + * Obtains the own list of a certain type. + * + * @param string $type name of the list you want to retrieve + * @param OODB $oodb The RB OODB object database instance + * + * @return array + */ + private function getOwnList( $type, $redbean ) + { + $type = $this->beau( $type ); + + if ( $this->aliasName ) { + $parentField = $this->aliasName; + $myFieldLink = $parentField . '_id'; + + $this->__info['sys.alias.' . $type] = $this->aliasName; + + $this->aliasName = NULL; + } else { + $parentField = $this->__info['type']; + $myFieldLink = $parentField . '_id'; + } + + $beans = array(); + + if ( $this->getID() ) { + + $firstKey = NULL; + if ( count( $this->withParams ) > 0 ) { + reset( $this->withParams ); + + $firstKey = key( $this->withParams ); + } + + $joinSql = $this->parseJoin( $type ); + + if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { + $bindings = $this->withParams; + $bindings[':slot0'] = $this->getID(); + + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); + } else { + $bindings = array_merge( array( $this->getID() ), $this->withParams ); + + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); + } + } + + $this->withSql = ''; + $this->withParams = array(); + + foreach ( $beans as $beanFromList ) { + $beanFromList->__info['sys.parentcache.' . $parentField] = $this; + } + + return $beans; + } + + /** + * Initializes a bean. Used by OODB for dispensing beans. + * It is not recommended to use this method to initialize beans. Instead + * use the OODB object to dispense new beans. You can use this method + * if you build your own bean dispensing mechanism. + * + * @param string $type type of the new bean + * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model + * + * @return void + */ + public function initializeForDispense( $type, BeanHelper $beanhelper ) + { + $this->beanHelper = $beanhelper; + $this->__info['type'] = $type; + $this->__info['sys.id'] = 'id'; + $this->__info['sys.orig'] = array( 'id' => 0 ); + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + $this->properties['id'] = 0; + } + + /** + * Sets the Bean Helper. Normally the Bean Helper is set by OODB. + * Here you can change the Bean Helper. The Bean Helper is an object + * providing access to a toolbox for the bean necessary to retrieve + * nested beans (bean lists: ownBean, sharedBean) without the need to + * rely on static calls to the facade (or make this class dep. on OODB). + * + * @param BeanHelper $helper helper to use for this bean + * + * @return void + */ + public function setBeanHelper( BeanHelper $helper ) + { + $this->beanHelper = $helper; + } + + /** + * Returns an ArrayIterator so you can treat the bean like + * an array with the properties container as its contents. + * This method is meant for PHP and allows you to access beans as if + * they were arrays, i.e. using array notation: + * + * $bean[$key] = $value; + * + * Note that not all PHP functions work with the array interface. + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator( $this->properties ); + } + + /** + * Imports all values from an associative array $array. Chainable. + * This method imports the values in the first argument as bean + * propery and value pairs. Use the second parameter to provide a + * selection. If a selection array is passed, only the entries + * having keys mentioned in the selection array will be imported. + * Set the third parameter to TRUE to preserve spaces in selection keys. + * + * @param array $array what you want to import + * @param string|array $selection selection of values + * @param boolean $notrim if TRUE selection keys will NOT be trimmed + * + * @return OODBBean + */ + public function import( $array, $selection = FALSE, $notrim = FALSE ) + { + if ( is_string( $selection ) ) { + $selection = explode( ',', $selection ); + } + + if ( !$notrim && is_array( $selection ) ) { + foreach ( $selection as $key => $selected ) { + $selection[$key] = trim( $selected ); + } + } + + foreach ( $array as $key => $value ) { + if ( $key != '__info' ) { + if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) { + if ( is_array($value ) ) { + if ( isset( $value['_type'] ) ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] ); + unset( $value['_type'] ); + $bean->import($value); + $this->$key = $bean; + } else { + $listBeans = array(); + foreach( $value as $listKey => $listItem ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] ); + unset( $listItem['_type'] ); + $bean->import($listItem); + $list = &$this->$key; + $list[ $listKey ] = $bean; + } + } + } else { + $this->$key = $value; + } + } + } + } + + return $this; + } + + /** + * Fast way to import a row. + * Does not perform any checks. + * + * @param array $row a database row + * + * @return self + */ + public function importRow( $row ) + { + $this->properties = $row; + $this->__info['sys.orig'] = $row; + $this->__info['changed'] = FALSE; + return $this; + } + + /** + * Imports data from another bean. Chainable. + * Copies the properties from the source bean to the internal + * property list. + * + * @param OODBBean $sourceBean the source bean to take properties from + * + * @return OODBBean + */ + public function importFrom( OODBBean $sourceBean ) + { + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + $this->properties = $sourceBean->properties; + + return $this; + } + + /** + * Injects the properties of another bean but keeps the original ID. + * Just like import() but keeps the original ID. + * Chainable. + * + * @param OODBBean $otherBean the bean whose properties you would like to copy + * + * @return OODBBean + */ + public function inject( OODBBean $otherBean ) + { + $myID = $this->properties['id']; + + $this->import( $otherBean->export( FALSE, FALSE, TRUE ) ); + + $this->id = $myID; + + return $this; + } + + /** + * Exports the bean as an array. + * This function exports the contents of a bean to an array and returns + * the resulting array. + * + * @param boolean $meta set to TRUE if you want to export meta data as well + * @param boolean $parents set to TRUE if you want to export parents as well + * @param boolean $onlyMe set to TRUE if you want to export only this bean + * @param array $filters optional whitelist for export + * + * @return array + */ + public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() ) + { + $arr = array(); + + if ( $parents ) { + foreach ( $this as $key => $value ) { + if ( substr( $key, -3 ) != '_id' ) continue; + + $prop = substr( $key, 0, strlen( $key ) - 3 ); + $this->$prop; + } + } + + $hasFilters = is_array( $filters ) && count( $filters ); + + foreach ( $this as $key => $value ) { + if ( !$onlyMe && is_array( $value ) ) { + $vn = array(); + + foreach ( $value as $i => $b ) { + if ( !( $b instanceof OODBBean ) ) continue; + $vn[] = $b->export( $meta, FALSE, FALSE, $filters ); + $value = $vn; + } + } elseif ( $value instanceof OODBBean ) { + if ( $hasFilters ) { + if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue; + } + + $value = $value->export( $meta, $parents, FALSE, $filters ); + } + + $arr[$key] = $value; + } + + if ( $meta ) { + $arr['__info'] = $this->__info; + } + + return $arr; + } + + /** + * Implements isset() function for use as an array. + * + * @param string $property name of the property you want to check + * + * @return boolean + */ + public function __isset( $property ) + { + $property = $this->beau( $property ); + + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + } + return isset( $this->properties[$property] ); + } + + /** + * Returns the ID of the bean no matter what the ID field is. + * + * @return string|null + */ + public function getID() + { + return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL; + } + + /** + * Unsets a property of a bean. + * Magic method, gets called implicitly when performing the unset() operation + * on a bean property. + * + * @param string $property property to unset + * + * @return void + */ + public function __unset( $property ) + { + $property = $this->beau( $property ); + + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + } + + unset( $this->properties[$property] ); + + $shadowKey = 'sys.shadow.'.$property; + if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] ); + + //also clear modifiers + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return; + } + + /** + * Adds WHERE clause conditions to ownList retrieval. + * For instance to get the pages that belong to a book you would + * issue the following command: $book->ownPage + * However, to order these pages by number use: + * + * + * $book->with(' ORDER BY `number` ASC ')->ownPage + * + * + * the additional SQL snippet will be merged into the final + * query. + * + * @param string $sql SQL to be added to retrieval query. + * @param array $bindings array with parameters to bind to SQL snippet + * + * @return OODBBean + */ + public function with( $sql, $bindings = array() ) + { + $this->withSql = $sql; + $this->withParams = $bindings; + return $this; + } + + /** + * Just like with(). Except that this method prepends the SQL query snippet + * with AND which makes it slightly more comfortable to use a conditional + * SQL snippet. For instance to filter an own-list with pages (belonging to + * a book) on specific chapters you can use: + * + * $book->withCondition(' chapter = 3 ')->ownPage + * + * This will return in the own list only the pages having 'chapter == 3'. + * + * @param string $sql SQL to be added to retrieval query (prefixed by AND) + * @param array $bindings array with parameters to bind to SQL snippet + * + * @return OODBBean + */ + public function withCondition( $sql, $bindings = array() ) + { + $this->withSql = ' AND ' . $sql; + $this->withParams = $bindings; + return $this; + } + + /** + * Tells the bean to (re)load the following list without any + * conditions. If you have an ownList or sharedList with a + * condition you can use this method to reload the entire list. + * + * Usage: + * + * + * $bean->with( ' LIMIT 3 ' )->ownPage; //Just 3 + * $bean->all()->ownPage; //Reload all pages + * + * + * @return self + */ + public function all() + { + $this->all = TRUE; + return $this; + } + + /** + * Tells the bean to only access the list but not load + * its contents. Use this if you only want to add something to a list + * and you have no interest in retrieving its contents from the database. + * + * @return self + */ + public function noLoad() + { + $this->noLoad = TRUE; + return $this; + } + + /** + * Prepares an own-list to use an alias. This is best explained using + * an example. Imagine a project and a person. The project always involves + * two persons: a teacher and a student. The person beans have been aliased in this + * case, so to the project has a teacher_id pointing to a person, and a student_id + * also pointing to a person. Given a project, we obtain the teacher like this: + * + * + * $project->fetchAs('person')->teacher; + * + * + * Now, if we want all projects of a teacher we cant say: + * + * + * $teacher->ownProject + * + * + * because the $teacher is a bean of type 'person' and no project has been + * assigned to a person. Instead we use the alias() method like this: + * + * + * $teacher->alias('teacher')->ownProject + * + * + * now we get the projects associated with the person bean aliased as + * a teacher. + * + * @param string $aliasName the alias name to use + * + * @return OODBBean + */ + public function alias( $aliasName ) + { + $this->aliasName = $this->beau( $aliasName ); + + return $this; + } + + /** + * Returns properties of bean as an array. + * This method returns the raw internal property list of the + * bean. Only use this method for optimization purposes. Otherwise + * use the export() method to export bean data to arrays. + * + * @return array + */ + public function getProperties() + { + return $this->properties; + } + + /** + * Returns properties of bean as an array. + * This method returns the raw internal property list of the + * bean. Only use this method for optimization purposes. Otherwise + * use the export() method to export bean data to arrays. + * This method returns an array with the properties array and + * the type (string). + * + * @return array + */ + public function getPropertiesAndType() + { + return array( $this->properties, $this->__info['type'] ); + } + + /** + * Turns a camelcase property name into an underscored property name. + * + * Examples: + * + * * oneACLRoute -> one_acl_route + * * camelCase -> camel_case + * + * Also caches the result to improve performance. + * + * @param string $property property to un-beautify + * + * @return string + */ + public function beau( $property ) + { + static $beautifulColumns = array(); + + if ( ctype_lower( $property ) ) return $property; + + if ( + ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) + || ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) + || ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) + ) { + + $property = preg_replace( '/List$/', '', $property ); + return $property; + } + + if ( !isset( $beautifulColumns[$property] ) ) { + $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property ); + } + + return $beautifulColumns[$property]; + } + + /** + * Clears all modifiers. + * + * @return self + */ + public function clearModifiers() + { + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + return $this; + } + + /** + * Determines whether a list is opened in exclusive mode or not. + * If a list has been opened in exclusive mode this method will return TRUE, + * othwerwise it will return FALSE. + * + * @param string $listName name of the list to check + * + * @return boolean + */ + public function isListInExclusiveMode( $listName ) + { + $listName = $this->beau( $listName ); + + if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) { + $listName = substr($listName, 1); + } + + $listName = lcfirst( substr( $listName, 3 ) ); + + return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] ); + } + + /** + * Magic Getter. Gets the value for a specific property in the bean. + * If the property does not exist this getter will make sure no error + * occurs. This is because RedBean allows you to query (probe) for + * properties. If the property can not be found this method will + * return NULL instead. + * + * @param string $property name of the property you wish to obtain the value of + * + * @return mixed + */ + public function &__get( $property ) + { + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; + + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } + } + + $fieldLink = $property . '_id'; + $exists = isset( $this->properties[$property] ); + + //If not exists and no field link and no list, bail out. + if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) { + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + $NULL = NULL; + return $NULL; + } + + $hasAlias = (!is_null($this->aliasName)); + $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $hasAll = (boolean) ($this->all); + + //If exists and no list or exits and list not changed, bail out. + if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) { + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + return $this->properties[$property]; + } + + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + + if ( isset( $this->$fieldLink ) ) { + $this->__info['tainted'] = TRUE; + + if ( isset( $this->__info["sys.parentcache.$property"] ) ) { + $bean = $this->__info["sys.parentcache.$property"]; + } else { + if ( isset( self::$aliases[$property] ) ) { + $type = self::$aliases[$property]; + } elseif ( $this->fetchType ) { + $type = $this->fetchType; + $this->fetchType = NULL; + } else { + $type = $property; + } + $bean = NULL; + if ( !is_null( $this->properties[$fieldLink] ) ) { + $bean = $redbean->load( $type, $this->properties[$fieldLink] ); + //If the IDs dont match, we failed to load, so try autoresolv in that case... + if ( $bean->id !== $this->properties[$fieldLink] && self::$autoResolve ) { + $type = $this->beanHelper->getToolbox()->getWriter()->inferFetchType( $this->__info['type'], $property ); + if ( !is_null( $type) ) { + $bean = $redbean->load( $type, $this->properties[$fieldLink] ); + $this->__info["sys.autoresolved.{$property}"] = $type; + } + } + } + } + + $this->properties[$property] = $bean; + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return $this->properties[$property]; + + } + //Implicit: elseif ( $isOwn || $isShared ) { + if ( $this->noLoad ) { + $beans = array(); + } elseif ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); + } + + $this->properties[$property] = $beans; + $this->__info["sys.shadow.$property"] = $beans; + $this->__info['tainted'] = TRUE; + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return $this->properties[$property]; + } + + /** + * Magic Setter. Sets the value for a specific property. + * This setter acts as a hook for OODB to mark beans as tainted. + * The tainted meta property can be retrieved using getMeta("tainted"). + * The tainted meta property indicates whether a bean has been modified and + * can be used in various caching mechanisms. + * + * @param string $property name of the property you wish to assign a value to + * @param mixed $value the value you want to assign + * + * @return void + */ + public function __set( $property, $value ) + { + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; + + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } + } + + $hasAlias = (!is_null($this->aliasName)); + $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $exists = isset( $this->properties[$property] ); + $fieldLink = $property . '_id'; + $isFieldLink = (($pos = strrpos($property, '_id')) !== FALSE) && array_key_exists( ($fieldName = substr($property, 0, $pos)), $this->properties ); + + + if ( ($isOwn || $isShared) && (!$exists || $hasSQL || $differentAlias) ) { + + if ( !$this->noLoad ) { + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + if ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); + } + $this->__info["sys.shadow.$property"] = $beans; + } + } + + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + + if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) { + if ( is_null( $value ) || $value === FALSE ) { + + unset( $this->properties[ $property ]); + $this->properties[ $fieldLink ] = NULL; + + return; + } else { + throw new RedException( 'Cannot cast to bean.' ); + } + } + + if ( $isFieldLink ){ + unset( $this->properties[ $fieldName ]); + $this->properties[ $property ] = NULL; + } + + + if ( $value === FALSE ) { + $value = '0'; + } elseif ( $value === TRUE ) { + $value = '1'; + } elseif ( $value instanceof \DateTime ) { + $value = $value->format( 'Y-m-d H:i:s' ); + } + + $this->properties[$property] = $value; + } + + /** + * Sets a property directly, for internal use only. + * + * @param string $property property + * @param mixed $value value + * @param boolean $updateShadow whether you want to update the shadow + * @param boolean $taint whether you want to mark the bean as tainted + * + * @return void + */ + public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE ) + { + $this->properties[$property] = $value; + + if ( $updateShadow ) { + $this->__info['sys.shadow.' . $property] = $value; + } + + if ( $taint ) { + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + } + } + + /** + * Returns the value of a meta property. A meta property + * contains additional information about the bean object that will not + * be stored in the database. Meta information is used to instruct + * RedBeanPHP as well as other systems how to deal with the bean. + * If the property cannot be found this getter will return NULL instead. + * + * Example: + * + * + * $bean->setMeta( 'flush-cache', TRUE ); + * + * + * RedBeanPHP also stores meta data in beans, this meta data uses + * keys prefixed with 'sys.' (system). + * + * @param string $path path to property in meta data + * @param mixed $default default value + * + * @return mixed + */ + public function getMeta( $path, $default = NULL ) + { + return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default; + } + + /** + * Gets and unsets a meta property. + * Moves a meta property out of the bean. + * This is a short-cut method that can be used instead + * of combining a get/unset. + * + * @param string $path path to property in meta data + * @param mixed $default default value + * + * @return mixed + */ + public function moveMeta( $path, $value = NULL ) + { + if ( isset( $this->__info[$path] ) ) { + $value = $this->__info[ $path ]; + unset( $this->__info[ $path ] ); + } + return $value; + } + + /** + * Stores a value in the specified Meta information property. + * The first argument should be the key to store the value under, + * the second argument should be the value. It is common to use + * a path-like notation for meta data in RedBeanPHP like: + * 'my.meta.data', however the dots are purely for readability, the + * meta data methods do not store nested structures or hierarchies. + * + * @param string $path path / key to store value under + * @param mixed $value value to store in bean (not in database) as meta data + * + * @return OODBBean + */ + public function setMeta( $path, $value ) + { + $this->__info[$path] = $value; + + return $this; + } + + /** + * Copies the meta information of the specified bean + * This is a convenience method to enable you to + * exchange meta information easily. + * + * @param OODBBean $bean bean to copy meta data of + * + * @return OODBBean + */ + public function copyMetaFrom( OODBBean $bean ) + { + $this->__info = $bean->__info; + + return $this; + } + + /** + * Sends the call to the registered model. + * This method can also be used to override bean behaviour. + * In that case you don't want an error or exception to be triggered + * if the method does not exist in the model (because it's optional). + * Unfortunately we cannot add an extra argument to __call() for this + * because the signature is fixed. Another option would be to set + * a special flag ( i.e. $this->isOptionalCall ) but that would + * cause additional complexity because we have to deal with extra temporary state. + * So, instead I allowed the method name to be prefixed with '@', in practice + * nobody creates methods like that - however the '@' symbol in PHP is widely known + * to suppress error handling, so we can reuse the semantics of this symbol. + * If a method name gets passed starting with '@' the overrideDontFail variable + * will be set to TRUE and the '@' will be stripped from the function name before + * attempting to invoke the method on the model. This way, we have all the + * logic in one place. + * + * @param string $method name of the method + * @param array $args argument list + * + * @return mixed + */ + public function __call( $method, $args ) + { + $overrideDontFail = FALSE; + if ( strpos( $method, '@' ) === 0 ) { + $method = substr( $method, 1 ); + $overrideDontFail = TRUE; + } + + if ( !isset( $this->__info['model'] ) ) { + $model = $this->beanHelper->getModelForBean( $this ); + + if ( !$model ) { + return NULL; + } + + $this->__info['model'] = $model; + } + if ( !method_exists( $this->__info['model'], $method ) ) { + + if ( self::$errorHandlingFUSE === FALSE || $overrideDontFail ) { + return NULL; + } + + if ( in_array( $method, array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ), TRUE ) ) { + return NULL; + } + + $message = "FUSE: method does not exist in model: $method"; + if ( self::$errorHandlingFUSE === self::C_ERR_LOG ) { + error_log( $message ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_NOTICE ) { + trigger_error( $message, E_USER_NOTICE ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_WARN ) { + trigger_error( $message, E_USER_WARNING ); + return NULL; + } elseif ( self::$errorHandlingFUSE === self::C_ERR_EXCEPTION ) { + throw new \Exception( $message ); + } elseif ( self::$errorHandlingFUSE === self::C_ERR_FUNC ) { + $func = self::$errorHandler; + return $func(array( + 'message' => $message, + 'method' => $method, + 'args' => $args, + 'bean' => $this + )); + } + trigger_error( $message, E_USER_ERROR ); + return NULL; + } + + return call_user_func_array( array( $this->__info['model'], $method ), $args ); + } + + /** + * Implementation of __toString Method + * Routes call to Model. If the model implements a __toString() method this + * method will be called and the result will be returned. In case of an + * echo-statement this result will be printed. If the model does not + * implement a __toString method, this method will return a JSON + * representation of the current bean. + * + * @return string + */ + public function __toString() + { + $string = $this->__call( '@__toString', array() ); + + if ( $string === NULL ) { + $list = array(); + foreach($this->properties as $property => $value) { + if (is_scalar($value)) { + $list[$property] = $value; + } + } + return json_encode( $list ); + } else { + return $string; + } + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Call gets routed to __set. + * + * @param mixed $offset offset string + * @param mixed $value value + * + * @return void + */ + public function offsetSet( $offset, $value ) + { + $this->__set( $offset, $value ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return boolean + */ + public function offsetExists( $offset ) + { + return $this->__isset( $offset ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Unsets a value from the array/bean. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return void + */ + public function offsetUnset( $offset ) + { + $this->__unset( $offset ); + } + + /** + * Implementation of Array Access Interface, you can access bean objects + * like an array. + * Returns value of a property. + * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * + * @param mixed $offset property + * + * @return mixed + */ + public function &offsetGet( $offset ) + { + return $this->__get( $offset ); + } + + /** + * Chainable method to cast a certain ID to a bean; for instance: + * $person = $club->fetchAs('person')->member; + * This will load a bean of type person using member_id as ID. + * + * @param string $type preferred fetch type + * + * @return OODBBean + */ + public function fetchAs( $type ) + { + $this->fetchType = $type; + + return $this; + } + + /** + * For polymorphic bean relations. + * Same as fetchAs but uses a column instead of a direct value. + * + * @param string $field field name to use for mapping + * + * @return OODBBean + */ + public function poly( $field ) + { + return $this->fetchAs( $this->$field ); + } + + /** + * Traverses a bean property with the specified function. + * Recursively iterates through the property invoking the + * function for each bean along the way passing the bean to it. + * + * Can be used together with with, withCondition, alias and fetchAs. + * + * @param string $property property + * @param callable $function function + * @param integer $maxDepth maximum depth for traversal + * + * @return OODBBean + * @throws RedException + */ + public function traverse( $property, $function, $maxDepth = NULL ) + { + $this->via = NULL; + if ( strpos( $property, 'shared' ) !== FALSE ) { + throw new RedException( 'Traverse only works with (x)own-lists.' ); + } + + if ( !is_null( $maxDepth ) ) { + if ( !$maxDepth-- ) return $this; + } + + $oldFetchType = $this->fetchType; + $oldAliasName = $this->aliasName; + $oldWith = $this->withSql; + $oldBindings = $this->withParams; + + $beans = $this->$property; + + if ( $beans === NULL ) return $this; + + if ( !is_array( $beans ) ) $beans = array( $beans ); + + foreach( $beans as $bean ) { + /** @var OODBBean $bean */ + $function( $bean ); + + $bean->fetchType = $oldFetchType; + $bean->aliasName = $oldAliasName; + $bean->withSql = $oldWith; + $bean->withParams = $oldBindings; + + $bean->traverse( $property, $function, $maxDepth ); + } + + return $this; + } + + /** + * Implementation of Countable interface. Makes it possible to use + * count() function on a bean. + * + * @return integer + */ + public function count() + { + return count( $this->properties ); + } + + /** + * Checks whether a bean is empty or not. + * A bean is empty if it has no other properties than the id field OR + * if all the other property are empty(). + * + * @return boolean + */ + public function isEmpty() + { + $empty = TRUE; + foreach ( $this->properties as $key => $value ) { + if ( $key == 'id' ) { + continue; + } + if ( !empty( $value ) ) { + $empty = FALSE; + } + } + + return $empty; + } + + /** + * Chainable setter. + * + * @param string $property the property of the bean + * @param mixed $value the value you want to set + * + * @return OODBBean + */ + public function setAttr( $property, $value ) + { + $this->$property = $value; + + return $this; + } + + /** + * Comfort method. + * Unsets all properties in array. + * + * @param array $properties properties you want to unset. + * + * @return OODBBean + */ + public function unsetAll( $properties ) + { + foreach ( $properties as $prop ) { + if ( isset( $this->properties[$prop] ) ) { + unset( $this->properties[$prop] ); + } + } + + return $this; + } + + /** + * Returns original (old) value of a property. + * You can use this method to see what has changed in a + * bean. + * + * @param string $property name of the property you want the old value of + * + * @return mixed + */ + public function old( $property ) + { + $old = $this->getMeta( 'sys.orig', array() ); + + if ( array_key_exists( $property, $old ) ) { + return $old[$property]; + } + + return NULL; + } + + /** + * Convenience method. + * Returns TRUE if the bean has been changed, or FALSE otherwise. + * Same as $bean->getMeta('tainted'); + * Note that a bean becomes tainted as soon as you retrieve a list from + * the bean. This is because the bean lists are arrays and the bean cannot + * determine whether you have made modifications to a list so RedBeanPHP + * will mark the whole bean as tainted. + * + * @return boolean + */ + public function isTainted() + { + return $this->getMeta( 'tainted' ); + } + + /** + * Returns TRUE if the value of a certain property of the bean has been changed and + * FALSE otherwise. + * + * Note that this method will return TRUE if applied to a loaded list. + * Also note that this method keeps track of the bean's history regardless whether + * it has been stored or not. Storing a bean does not undo it's history, + * to clean the history of a bean use: clearHistory(). + * + * @param string $property name of the property you want the change-status of + * + * @return boolean + */ + public function hasChanged( $property ) + { + return ( array_key_exists( $property, $this->properties ) ) ? + $this->old( $property ) != $this->properties[$property] : FALSE; + } + + /** + * Returns TRUE if the specified list exists, has been loaded and has been changed: + * beans have been added or deleted. This method will not tell you anything about + * the state of the beans in the list. + * + * @param string $property name of the list to check + * + * @return boolean + */ + public function hasListChanged( $property ) + { + if ( !array_key_exists( $property, $this->properties ) ) return FALSE; + $diffAdded = array_diff_assoc( $this->properties[$property], $this->__info['sys.shadow.'.$property] ); + if ( count( $diffAdded ) ) return TRUE; + $diffMissing = array_diff_assoc( $this->__info['sys.shadow.'.$property], $this->properties[$property] ); + if ( count( $diffMissing ) ) return TRUE; + return FALSE; + } + + /** + * Clears (syncs) the history of the bean. + * Resets all shadow values of the bean to their current value. + * + * @return self + */ + public function clearHistory() + { + $this->__info['sys.orig'] = array(); + foreach( $this->properties as $key => $value ) { + if ( is_scalar($value) ) { + $this->__info['sys.orig'][$key] = $value; + } else { + $this->__info['sys.shadow.'.$key] = $value; + } + } + return $this; + } + + /** + * Creates a N-M relation by linking an intermediate bean. + * This method can be used to quickly connect beans using indirect + * relations. For instance, given an album and a song you can connect the two + * using a track with a number like this: + * + * Usage: + * + * + * $album->link('track', array('number'=>1))->song = $song; + * + * + * or: + * + * + * $album->link($trackBean)->song = $song; + * + * + * What this method does is adding the link bean to the own-list, in this case + * ownTrack. If the first argument is a string and the second is an array or + * a JSON string then the linking bean gets dispensed on-the-fly as seen in + * example #1. After preparing the linking bean, the bean is returned thus + * allowing the chained setter: ->song = $song. + * + * @param string|OODBBean $type type of bean to dispense or the full bean + * @param string|array $qualification JSON string or array (optional) + * + * @return OODBBean + */ + public function link( $typeOrBean, $qualification = array() ) + { + if ( is_string( $typeOrBean ) ) { + + $typeOrBean = AQueryWriter::camelsSnake( $typeOrBean ); + + $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean ); + + if ( is_string( $qualification ) ) { + $data = json_decode( $qualification, TRUE ); + } else { + $data = $qualification; + } + + foreach ( $data as $key => $value ) { + $bean->$key = $value; + } + } else { + $bean = $typeOrBean; + } + + $list = 'own' . ucfirst( $bean->getMeta( 'type' ) ); + + array_push( $this->$list, $bean ); + + return $bean; + } + + /** + * Returns a bean of the given type with the same ID of as + * the current one. This only happens in a one-to-one relation. + * This is as far as support for 1-1 goes in RedBeanPHP. This + * method will only return a reference to the bean, changing it + * and storing the bean will not update the related one-bean. + * + * @return OODBBean + */ + public function one( $type ) { + return $this->beanHelper->getToolBox()->getRedBean()->load( $type, $this->id ); + } + + /** + * Returns the same bean freshly loaded from the database. + * + * @return OODBBean + */ + public function fresh() + { + return $this->beanHelper->getToolbox()->getRedBean()->load( $this->getMeta( 'type' ), $this->properties['id'] ); + } + + /** + * Registers a association renaming globally. + * + * @param string $via type you wish to use for shared lists + * + * @return OODBBean + */ + public function via( $via ) + { + $this->via = AQueryWriter::camelsSnake( $via ); + + return $this; + } + + /** + * Counts all own beans of type $type. + * Also works with alias(), with() and withCondition(). + * + * @param string $type the type of bean you want to count + * + * @return integer + */ + public function countOwn( $type ) + { + $type = $this->beau( $type ); + + if ( $this->aliasName ) { + $myFieldLink = $this->aliasName . '_id'; + + $this->aliasName = NULL; + } else { + $myFieldLink = $this->__info['type'] . '_id'; + } + + $count = 0; + + if ( $this->getID() ) { + + $firstKey = NULL; + if ( count( $this->withParams ) > 0 ) { + reset( $this->withParams ); + $firstKey = key( $this->withParams ); + } + + $joinSql = $this->parseJoin( $type ); + + if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { + $bindings = $this->withParams; + $bindings[':slot0'] = $this->getID(); + $count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); + } else { + $bindings = array_merge( array( $this->getID() ), $this->withParams ); + $count = $this->beanHelper->getToolbox()->getWriter()->queryRecordCount( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); + } + + } + + $this->clearModifiers(); + return (int) $count; + } + + /** + * Counts all shared beans of type $type. + * Also works with via(), with() and withCondition(). + * + * @param string $type type of bean you wish to count + * + * @return integer + */ + public function countShared( $type ) + { + $toolbox = $this->beanHelper->getToolbox(); + $redbean = $toolbox->getRedBean(); + $writer = $toolbox->getWriter(); + + if ( $this->via ) { + $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) ); + + if ( $oldName !== $this->via ) { + //set the new renaming rule + $writer->renameAssocTable( $oldName, $this->via ); + $this->via = NULL; + } + } + + $type = $this->beau( $type ); + $count = 0; + + if ( $this->getID() ) { + $count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams ); + } + + $this->clearModifiers(); + return (integer) $count; + } + + /** + * Iterates through the specified own-list and + * fetches all properties (with their type) and + * returns the references. + * Use this method to quickly load indirectly related + * beans in an own-list. Whenever you cannot use a + * shared-list this method offers the same convenience + * by aggregating the parent beans of all children in + * the specified own-list. + * + * Example: + * + * + * $quest->aggr( 'xownQuestTarget', 'target', 'quest' ); + * + * + * Loads (in batch) and returns references to all + * quest beans residing in the $questTarget->target properties + * of each element in the xownQuestTargetList. + * + * @param string $list the list you wish to process + * @param string $property the property to load + * @param string $type the type of bean residing in this property (optional) + * + * @return array + */ + public function &aggr( $list, $property, $type = NULL ) + { + $this->via = NULL; + $ids = $beanIndex = $references = array(); + + if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.'); + if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.'); + if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.'); + + if ( is_null( $type ) ) $type = $property; + + foreach( $this->$list as $bean ) { + $field = $property . '_id'; + if ( isset( $bean->$field) ) { + $ids[] = $bean->$field; + $beanIndex[$bean->$field] = $bean; + } + } + + $beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids ); + + //now preload the beans as well + foreach( $beans as $bean ) { + $beanIndex[$bean->id]->setProperty( $property, $bean ); + } + + foreach( $beanIndex as $indexedBean ) { + $references[] = $indexedBean->$property; + } + + return $references; + } + + /** + * Tests whether the database identities of two beans are equal. + * + * @param OODBBean $bean other bean + * + * @return boolean + */ + public function equals(OODBBean $bean) + { + return (bool) ( + ( (string) $this->properties['id'] === (string) $bean->properties['id'] ) + && ( (string) $this->__info['type'] === (string) $bean->__info['type'] ) + ); + } + + /** + * Magic method jsonSerialize, implementation for the \JsonSerializable interface, + * this method gets called by json_encode and facilitates a better JSON representation + * of the bean. Exports the bean on JSON serialization, for the JSON fans. + * + * @see http://php.net/manual/en/class.jsonserializable.php + * + * @return array + */ + public function jsonSerialize() + { + return $this->export(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Observer as Observer; + +/** + * Observable + * Base class for Observables + * + * @file RedBeanPHP/Observable.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Observable { //bracket must be here - otherwise coverage software does not understand. + + /** + * @var array + */ + private $observers = array(); + + /** + * Implementation of the Observer Pattern. + * Adds an event listener to the observable object. + * First argument should be the name of the event you wish to listen for. + * Second argument should be the object that wants to be notified in case + * the event occurs. + * + * @param string $eventname event identifier + * @param Observer $observer observer instance + * + * @return void + */ + public function addEventListener( $eventname, Observer $observer ) + { + if ( !isset( $this->observers[$eventname] ) ) { + $this->observers[$eventname] = array(); + } + + foreach ( $this->observers[$eventname] as $o ) { + if ( $o == $observer ) { + return; + } + } + + $this->observers[$eventname][] = $observer; + } + + /** + * Notifies listeners. + * Sends the signal $eventname, the event identifier and a message object + * to all observers that have been registered to receive notification for + * this event. Part of the observer pattern implementation in RedBeanPHP. + * + * @param string $eventname event you want signal + * @param mixed $info message object to send along + * + * @return void + */ + public function signal( $eventname, $info ) + { + if ( !isset( $this->observers[$eventname] ) ) { + $this->observers[$eventname] = array(); + } + + foreach ( $this->observers[$eventname] as $observer ) { + $observer->onEvent( $eventname, $info ); + } + } +} +} + +namespace RedBeanPHP { + +/** + * Observer. + * + * Interface for Observer object. Implementation of the + * observer pattern. + * + * @file RedBeanPHP/Observer.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * @desc Part of the observer pattern in RedBean + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Observer +{ + /** + * An observer object needs to be capable of receiving + * notifications. Therefore the observer needs to implement the + * onEvent method with two parameters: the event identifier specifying the + * current event and a message object (in RedBeanPHP this can also be a bean). + * + * @param string $eventname event identifier + * @param mixed $bean a message sent along with the notification + * + * @return void + */ + public function onEvent( $eventname, $bean ); +} +} + +namespace RedBeanPHP { + +/** + * Adapter Interface. + * Describes the API for a RedBeanPHP Database Adapter. + * This interface defines the API contract for + * a RedBeanPHP Database Adapter. + * + * @file RedBeanPHP/Adapter.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Adapter +{ + /** + * Should returns a string containing the most recent SQL query + * that has been processed by the adapter. + * + * @return string + */ + public function getSQL(); + + /** + * Executes an SQL Statement using an array of values to bind + * If $noevent is TRUE then this function will not signal its + * observers to notify about the SQL execution; this to prevent + * infinite recursion when using observers. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * @param boolean $noevent no event firing + * + * @return void + */ + public function exec( $sql, $bindings = array(), $noevent = FALSE ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a multi dimensional resultset similar to getAll + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function get( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single row (one array) resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getRow( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single column (one array) resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getCol( $sql, $bindings = array() ); + + /** + * Executes an SQL Query and returns a resultset. + * This method returns a single cell, a scalar value as the resultset. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return string + */ + public function getCell( $sql, $bindings = array() ); + + /** + * Executes the SQL query specified in $sql and takes + * the first two columns of the resultset. This function transforms the + * resultset into an associative array. Values from the the first column will + * serve as keys while the values of the second column will be used as values. + * The values array can be used to bind values to the place holders in the + * SQL query. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return array + */ + public function getAssoc( $sql, $bindings = array() ); + + /** + * Executes the SQL query specified in $sql and indexes + * the row by the first column. + * + * @param string $sql Sstring containing SQL code for databaseQL + * @param array $bindings values to bind + * + * @return array + */ + public function getAssocRow( $sql, $bindings = array() ); + + /** + * Returns the latest insert ID. + * + * @return integer + */ + public function getInsertID(); + + /** + * Returns the number of rows that have been + * affected by the last update statement. + * + * @return integer + */ + public function getAffectedRows(); + + /** + * Returns a database agnostic Cursor object. + * + * @param string $sql string containing SQL code for database + * @param array $bindings array of values to bind to parameters in query string + * + * @return Cursor + */ + public function getCursor( $sql, $bindings = array() ); + + /** + * Returns the original database resource. This is useful if you want to + * perform operations on the driver directly instead of working with the + * adapter. RedBean will only access the adapter and never to talk + * directly to the driver though. + * + * @return mixed + */ + public function getDatabase(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Starts a transaction. + * + * @return void + */ + public function startTransaction(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Commits the transaction. + * + * @return void + */ + public function commit(); + + /** + * This method is part of the RedBean Transaction Management + * mechanisms. + * Rolls back the transaction. + * + * @return void + */ + public function rollback(); + + /** + * Closes database connection. + * + * @return void + */ + public function close(); +} +} + +namespace RedBeanPHP\Adapter { + +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\Driver as Driver; + +/** + * DBAdapter (Database Adapter) + * + * An adapter class to connect various database systems to RedBean + * Database Adapter Class. The task of the database adapter class is to + * communicate with the database driver. You can use all sorts of database + * drivers with RedBeanPHP. The default database drivers that ships with + * the RedBeanPHP library is the RPDO driver ( which uses the PHP Data Objects + * Architecture aka PDO ). + * + * @file RedBeanPHP/Adapter/DBAdapter.php + * @author Gabor de Mooij and the RedBeanPHP Community. + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DBAdapter extends Observable implements Adapter +{ + /** + * @var Driver + */ + private $db = NULL; + + /** + * @var string + */ + private $sql = ''; + + /** + * Constructor. + * + * Creates an instance of the RedBean Adapter Class. + * This class provides an interface for RedBean to work + * with ADO compatible DB instances. + * + * @param Driver $database ADO Compatible DB Instance + */ + public function __construct( $database ) + { + $this->db = $database; + } + + /** + * Returns a string containing the most recent SQL query + * processed by the database adapter, thus conforming to the + * interface: + * + * @see Adapter::getSQL + * + * Methods like get(), getRow() and exec() cause this SQL cache + * to get filled. If no SQL query has been processed yet this function + * will return an empty string. + * + * @return string + */ + public function getSQL() + { + return $this->sql; + } + + /** + * @see Adapter::exec + */ + public function exec( $sql, $bindings = array(), $noevent = FALSE ) + { + if ( !$noevent ) { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + } + + return $this->db->Execute( $sql, $bindings ); + } + + /** + * @see Adapter::get + */ + public function get( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetAll( $sql, $bindings ); + } + + /** + * @see Adapter::getRow + */ + public function getRow( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetRow( $sql, $bindings ); + } + + /** + * @see Adapter::getCol + */ + public function getCol( $sql, $bindings = array() ) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetCol( $sql, $bindings ); + } + + /** + * @see Adapter::getAssoc + */ + public function getAssoc( $sql, $bindings = array() ) + { + $this->sql = $sql; + + $this->signal( 'sql_exec', $this ); + + $rows = $this->db->GetAll( $sql, $bindings ); + + $assoc = array(); + if ( !$rows ) { + return $assoc; + } + + foreach ( $rows as $row ) { + if ( empty( $row ) ) continue; + + if ( count( $row ) > 2 ) { + $key = array_shift( $row ); + $value = $row; + } elseif ( count( $row ) > 1 ) { + $key = array_shift( $row ); + $value = array_shift( $row ); + } else { + $key = array_shift( $row ); + $value = $key; + } + + $assoc[$key] = $value; + } + + return $assoc; + } + + /** + * @see Adapter::getAssocRow + */ + public function getAssocRow($sql, $bindings = array()) + { + $this->sql = $sql; + $this->signal( 'sql_exec', $this ); + + return $this->db->GetAssocRow( $sql, $bindings ); + } + + /** + * @see Adapter::getCell + */ + public function getCell( $sql, $bindings = array(), $noSignal = NULL ) + { + $this->sql = $sql; + + if ( !$noSignal ) $this->signal( 'sql_exec', $this ); + + return $this->db->GetOne( $sql, $bindings ); + } + + /** + * @see Adapter::getCursor + */ + public function getCursor( $sql, $bindings = array() ) + { + return $this->db->GetCursor( $sql, $bindings ); + } + + /** + * @see Adapter::getInsertID + */ + public function getInsertID() + { + return $this->db->getInsertID(); + } + + /** + * @see Adapter::getAffectedRows + */ + public function getAffectedRows() + { + return $this->db->Affected_Rows(); + } + + /** + * @see Adapter::getDatabase + */ + public function getDatabase() + { + return $this->db; + } + + /** + * @see Adapter::startTransaction + */ + public function startTransaction() + { + $this->db->StartTrans(); + } + + /** + * @see Adapter::commit + */ + public function commit() + { + $this->db->CommitTrans(); + } + + /** + * @see Adapter::rollback + */ + public function rollback() + { + $this->db->FailTrans(); + } + + /** + * @see Adapter::close. + */ + public function close() + { + $this->db->close(); + } +} +} + +namespace RedBeanPHP { + +/** + * Database Cursor Interface. + * A cursor is used by Query Writers to fetch Query Result rows + * one row at a time. This is useful if you expect the result set to + * be quite large. This interface dscribes the API of a database + * cursor. There can be multiple implementations of the Cursor, + * by default RedBeanPHP offers the PDOCursor for drivers shipping + * with RedBeanPHP and the NULLCursor. + * + * @file RedBeanPHP/Cursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Cursor +{ + /** + * Should retrieve the next row of the result set. + * This method is used to iterate over the result set. + * + * @return array + */ + public function getNextItem(); + + /** + * Closes the database cursor. + * Some databases require a cursor to be closed before executing + * another statement/opening a new cursor. + * + * @return void + */ + public function close(); +} +} + +namespace RedBeanPHP\Cursor { + +use RedBeanPHP\Cursor as Cursor; + +/** + * PDO Database Cursor + * Implementation of PDO Database Cursor. + * Used by the BeanCollection to fetch one bean at a time. + * The PDO Cursor is used by Query Writers to support retrieval + * of large bean collections. For instance, this class is used to + * implement the findCollection()/BeanCollection functionality. + * + * @file RedBeanPHP/Cursor/PDOCursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class PDOCursor implements Cursor +{ + /** + * @var PDOStatement + */ + protected $res; + + /** + * @var string + */ + protected $fetchStyle; + + /** + * Constructor, creates a new instance of a PDO Database Cursor. + * + * @param PDOStatement $res the PDO statement + * @param string $fetchStyle fetch style constant to use + * + * @return void + */ + public function __construct( \PDOStatement $res, $fetchStyle ) + { + $this->res = $res; + $this->fetchStyle = $fetchStyle; + } + + /** + * @see Cursor::getNextItem + */ + public function getNextItem() + { + return $this->res->fetch(); + } + + /** + * @see Cursor::close + */ + public function close() + { + $this->res->closeCursor(); + } +} +} + +namespace RedBeanPHP\Cursor { + +use RedBeanPHP\Cursor as Cursor; + +/** + * NULL Database Cursor + * Implementation of the NULL Cursor. + * Used for an empty BeanCollection. This Cursor + * can be used for instance if a query fails but the interface + * demands a cursor to be returned. + * + * @file RedBeanPHP/Cursor/NULLCursor.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class NullCursor implements Cursor +{ + /** + * @see Cursor::getNextItem + */ + public function getNextItem() + { + return NULL; + } + + /** + * @see Cursor::close + */ + public function close() + { + return NULL; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Cursor as Cursor; +use RedBeanPHP\Repository as Repository; + +/** + * BeanCollection. + * + * The BeanCollection represents a collection of beans and + * makes it possible to use database cursors. The BeanCollection + * has a method next() to obtain the first, next and last bean + * in the collection. The BeanCollection does not implement the array + * interface nor does it try to act like an array because it cannot go + * backward or rewind itself. + * + * Use the BeanCollection for large datasets where skip/limit is not an + * option. Keep in mind that ID-marking (querying a start ID) is a decent + * alternative though. + * + * @file RedBeanPHP/BeanCollection.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class BeanCollection +{ + /** + * @var Cursor + */ + protected $cursor = NULL; + + /** + * @var Repository + */ + protected $repository = NULL; + + /** + * @var string + */ + protected $type = NULL; + + /** + * Constructor, creates a new instance of the BeanCollection. + * + * @param string $type type of beans in this collection + * @param Repository $repository repository to use to generate bean objects + * @param Cursor $cursor cursor object to use + * + * @return void + */ + public function __construct( $type, Repository $repository, Cursor $cursor ) + { + $this->type = $type; + $this->cursor = $cursor; + $this->repository = $repository; + } + + /** + * Returns the next bean in the collection. + * If called the first time, this will return the first bean in the collection. + * If there are no more beans left in the collection, this method + * will return NULL. + * + * @return OODBBean|NULL + */ + public function next() + { + $row = $this->cursor->getNextItem(); + if ( $row ) { + $beans = $this->repository->convertToBeans( $this->type, array( $row ) ); + $bean = array_shift( $beans ); + return $bean; + } + return NULL; + } + + /** + * Closes the underlying cursor (needed for some databases). + * + * @return void + */ + public function close() + { + $this->cursor->close(); + } +} +} + +namespace RedBeanPHP { + +/** + * QueryWriter + * Interface for QueryWriters. + * Describes the API for a QueryWriter. + * + * Terminology: + * + * - beautified property (a camelCased property, has to be converted first) + * - beautified type (a camelCased type, has to be converted first) + * - type (a bean type, corresponds directly to a table) + * - property (a bean property, corresponds directly to a column) + * - table (a checked and quoted type, ready for use in a query) + * - column (a checked and quoted property, ready for use in query) + * - tableNoQ (same as type, but in context of a database operation) + * - columnNoQ (same as property, but in context of a database operation) + * + * @file RedBeanPHP/QueryWriter.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface QueryWriter +{ + /** + * SQL filter constants + */ + const C_SQLFILTER_READ = 'r'; + const C_SQLFILTER_WRITE = 'w'; + + /** + * Query Writer constants. + */ + const C_SQLSTATE_NO_SUCH_TABLE = 1; + const C_SQLSTATE_NO_SUCH_COLUMN = 2; + const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3; + + /** + * Define data type regions + * + * 00 - 80: normal data types + * 80 - 99: special data types, only scan/code if requested + * 99 : specified by user, don't change + */ + const C_DATATYPE_RANGE_SPECIAL = 80; + const C_DATATYPE_RANGE_SPECIFIED = 99; + + /** + * Define GLUE types for use with glueSQLCondition methods. + * Determines how to prefix a snippet of SQL before appending it + * to other SQL (or integrating it, mixing it otherwise). + * + * WHERE - glue as WHERE condition + * AND - glue as AND condition + */ + const C_GLUE_WHERE = 1; + const C_GLUE_AND = 2; + + /** + * Writes an SQL Snippet for a JOIN, returns the + * SQL snippet string. + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $type source type + * @param string $targetType target type (type to join) + * @param string $leftRight type of join (possible: 'LEFT', 'RIGHT' or 'INNER'). + * + * @return string $joinSQLSnippet + */ + public function writeJoin( $type, $targetType, $joinType ); + + /** + * Glues an SQL snippet to the beginning of a WHERE clause. + * This ensures users don't have to add WHERE to their query snippets. + * + * The snippet gets prefixed with WHERE or AND + * if it starts with a condition. + * + * If the snippet does NOT start with a condition (or this function thinks so) + * the snippet is returned as-is. + * + * The GLUE type determines the prefix: + * + * * NONE prefixes with WHERE + * * WHERE prefixes with WHERE and replaces AND if snippets starts with AND + * * AND prefixes with AND + * + * This method will never replace WHERE with AND since a snippet should never + * begin with WHERE in the first place. OR is not supported. + * + * Only a limited set of clauses will be recognized as non-conditions. + * For instance beginning a snippet with complex statements like JOIN or UNION + * will not work. This is too complex for use in a snippet. + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $sql SQL Snippet + * @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND) + * + * @return string + */ + public function glueSQLCondition( $sql, $glue = NULL ); + + /** + * Determines if there is a LIMIT 1 clause in the SQL. + * If not, it will add a LIMIT 1. (used for findOne). + * + * @note A default implementation is available in AQueryWriter + * unless a database uses very different SQL this should suffice. + * + * @param string $sql query to scan and adjust + * + * @return string + */ + public function glueLimitOne( $sql ); + + /** + * Returns the tables that are in the database. + * + * @return array + */ + public function getTables(); + + /** + * This method will create a table for the bean. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type of bean you want to create a table for + * + * @return void + */ + public function createTable( $type ); + + /** + * Returns an array containing all the columns of the specified type. + * The format of the return array looks like this: + * $field => $type where $field is the name of the column and $type + * is a database specific description of the datatype. + * + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type of bean you want to obtain a column list of + * + * @return array + */ + public function getColumns( $type ); + + /** + * Returns the Column Type Code (integer) that corresponds + * to the given value type. This method is used to determine the minimum + * column type required to represent the given value. + * + * @param string $value value + * + * @return integer + */ + public function scanType( $value, $alsoScanSpecialForTypes = FALSE ); + + /** + * This method will add a column to a table. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type name of the table + * @param string $column name of the column + * @param integer $field data type for field + * + * @return void + */ + public function addColumn( $type, $column, $field ); + + /** + * Returns the Type Code for a Column Description. + * Given an SQL column description this method will return the corresponding + * code for the writer. If the include specials flag is set it will also + * return codes for special columns. Otherwise special columns will be identified + * as specified columns. + * + * @param string $typedescription description + * @param boolean $includeSpecials whether you want to get codes for special columns as well + * + * @return integer + */ + public function code( $typedescription, $includeSpecials = FALSE ); + + /** + * This method will widen the column to the specified data type. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type / table that needs to be adjusted + * @param string $column column that needs to be altered + * @param integer $datatype target data type + * + * @return void + */ + public function widenColumn( $type, $column, $datatype ); + + /** + * Selects records from the database. + * This methods selects the records from the database that match the specified + * type, conditions (optional) and additional SQL snippet (optional). + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return array + */ + public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); + + /** + * Selects records from the database and returns a cursor. + * This methods selects the records from the database that match the specified + * type, conditions (optional) and additional SQL snippet (optional). + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return Cursor + */ + public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ); + + /** + * Returns records through an intermediate type. This method is used to obtain records using a link table and + * allows the SQL snippets to reference columns in the link table for additional filtering or ordering. + * + * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side + * @param string $destType destination type, the target type you want to get beans of + * @param mixed $linkID ID to use for the link table + * @param string $addSql Additional SQL snippet + * @param array $bindings Bindings for SQL snippet + * + * @return array + */ + public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ); + + /** + * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation. + * + * @param string $sourceType source type, the first part of the link you're looking for + * @param string $destType destination type, the second part of the link you're looking for + * @param string $sourceID ID for the source + * @param string $destID ID for the destination + * + * @return array|null + */ + public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ); + + /** + * Counts the number of records in the database that match the + * conditions and additional SQL. + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return integer + */ + public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ); + + /** + * Returns the number of records linked through $linkType and satisfying the SQL in $addSQL/$bindings. + * + * @param string $sourceType source type + * @param string $targetType the thing you want to count + * @param mixed $linkID the of the source type + * @param string $addSQL additional SQL snippet + * @param array $bindings bindings for SQL snippet + * + * @return integer + */ + public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() ); + + /** + * Returns all rows of specified type that have been tagged with one of the + * strings in the specified tag list array. + * + * Note that the additional SQL snippet can only be used for pagination, + * the SQL snippet will be appended to the end of the query. + * + * @param string $type the bean type you want to query + * @param array $tagList an array of strings, each string containing a tag title + * @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list + * @param string $addSql addition SQL snippet, for pagination + * @param array $bindings parameter bindings for additional SQL snippet + * + * @return array + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ); + + /** + * This method should update (or insert a record), it takes + * a table name, a list of update values ( $field => $value ) and an + * primary key ID (optional). If no primary key ID is provided, an + * INSERT will take place. + * Returns the new ID. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type name of the table to update + * @param array $updatevalues list of update values + * @param integer $id optional primary key ID value + * + * @return integer + */ + public function updateRecord( $type, $updatevalues, $id = NULL ); + + /** + * Deletes records from the database. + * @note $addSql is always prefixed with ' WHERE ' or ' AND .' + * + * @param string $type name of the table you want to query + * @param array $conditions criteria ( $column => array( $values ) ) + * @param string $sql additional SQL + * @param array $bindings bindings + * + * @return void + */ + public function deleteRecord( $type, $conditions = array(), $addSql = '', $bindings = array() ); + + /** + * Deletes all links between $sourceType and $destType in an N-M relation. + * + * @param string $sourceType source type + * @param string $destType destination type + * @param string $sourceID source ID + * + * @return void + */ + public function deleteRelations( $sourceType, $destType, $sourceID ); + + /** + * @see QueryWriter::addUniqueConstaint + */ + public function addUniqueIndex( $type, $columns ); + + /** + * This method will add a UNIQUE constraint index to a table on columns $columns. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type target bean type + * @param array $columnsPartOfIndex columns to include in index + * + * @return void + */ + public function addUniqueConstraint( $type, $columns ); + + /** + * This method will check whether the SQL state is in the list of specified states + * and returns TRUE if it does appear in this list or FALSE if it + * does not. The purpose of this method is to translate the database specific state to + * a one of the constants defined in this class and then check whether it is in the list + * of standard states provided. + * + * @param string $state SQL state to consider + * @param array $list list of standardized SQL state constants to check against + * + * @return boolean + */ + public function sqlStateIn( $state, $list ); + + /** + * This method will remove all beans of a certain type. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type bean type + * + * @return void + */ + public function wipe( $type ); + + /** + * This method will add a foreign key from type and field to + * target type and target field. + * The foreign key is created without an action. On delete/update + * no action will be triggered. The FK is only used to allow database + * tools to generate pretty diagrams and to make it easy to add actions + * later on. + * This methods accepts a type and infers the corresponding table name. + * + * + * @param string $type type that will have a foreign key field + * @param string $targetType points to this type + * @param string $property field that contains the foreign key value + * @param string $targetProperty field where the fk points to + * @param string $isDep whether target is dependent and should cascade on update/delete + * + * @return void + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = false ); + + /** + * This method will add an index to a type and field with name + * $name. + * This methods accepts a type and infers the corresponding table name. + * + * @param string $type type to add index to + * @param string $name name of the new index + * @param string $property field to index + * + * @return void + */ + public function addIndex( $type, $name, $property ); + + /** + * Checks and filters a database structure element like a table of column + * for safe use in a query. A database structure has to conform to the + * RedBeanPHP DB security policy which basically means only alphanumeric + * symbols are allowed. This security policy is more strict than conventional + * SQL policies and does therefore not require database specific escaping rules. + * + * @param string $databaseStructure name of the column/table to check + * @param boolean $noQuotes TRUE to NOT put backticks or quotes around the string + * + * @return string + */ + public function esc( $databaseStructure, $dontQuote = FALSE ); + + /** + * Removes all tables and views from the database. + * + * @return void + */ + public function wipeAll(); + + /** + * Renames an association. For instance if you would like to refer to + * album_song as: track you can specify this by calling this method like: + * + * + * renameAssociation('album_song','track') + * + * + * This allows: + * + * + * $album->sharedSong + * + * + * to add/retrieve beans from track instead of album_song. + * Also works for exportAll(). + * + * This method also accepts a single associative array as + * its first argument. + * + * @param string|array $fromType original type name, or array + * @param string $toType new type name (only if 1st argument is string) + * + * @return void + */ + public function renameAssocTable( $fromType, $toType = NULL ); + + /** + * Returns the format for link tables. + * Given an array containing two type names this method returns the + * name of the link table to be used to store and retrieve + * association records. For instance, given two types: person and + * project, the corresponding link table might be: 'person_project'. + * + * @param array $types two types array($type1, $type2) + * + * @return string + */ + public function getAssocTable( $types ); + + /** + * Given a bean type and a property, this method + * tries to infer the fetch type using the foreign key + * definitions in the database. + * For instance: project, student -> person. + * If no fetchType can be inferred, this method will return NULL. + * + * @note QueryWriters do not have to implement this method, + * it's optional. A default version is available in AQueryWriter. + * + * @param $type the source type to fetch a target type for + * @param $property the property to fetch the type of + * + * @return string|NULL + */ + public function inferFetchType( $type, $property ); +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP Abstract Query Writer. + * Represents an abstract Database to RedBean + * To write a driver for a different database for RedBean + * Contains a number of functions all implementors can + * inherit or override. + * + * @file RedBeanPHP/QueryWriter/AQueryWriter.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class AQueryWriter +{ + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var boolean + */ + private static $flagSQLFilterSafeMode = false; + + /** + * @var boolean + */ + private static $flagNarrowFieldMode = true; + + /** + * @var array + */ + public static $renames = array(); + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $defaultValue = 'NULL'; + + /** + * @var string + */ + protected $quoteCharacter = ''; + + /** + * @var boolean + */ + protected $flagUseCache = TRUE; + + /** + * @var array + */ + protected $cache = array(); + + /** + * @var integer + */ + protected $maxCacheSizePerType = 20; + + /** + * @var array + */ + public $typeno_sqltype = array(); + + /** + * Checks whether a number can be treated like an int. + * + * @param string $value string representation of a certain value + * + * @return boolean + */ + public static function canBeTreatedAsInt( $value ) + { + return (bool) ( strval( $value ) === strval( intval( $value ) ) ); + } + + /** + * @see QueryWriter::getAssocTableFormat + */ + public static function getAssocTableFormat( $types ) + { + sort( $types ); + + $assoc = implode( '_', $types ); + + return ( isset( self::$renames[$assoc] ) ) ? self::$renames[$assoc] : $assoc; + } + + /** + * @see QueryWriter::renameAssociation + */ + public static function renameAssociation( $from, $to = NULL ) + { + if ( is_array( $from ) ) { + foreach ( $from as $key => $value ) self::$renames[$key] = $value; + + return; + } + + self::$renames[$from] = $to; + } + + /** + * Globally available service method for RedBeanPHP. + * Converts a camel cased string to a snake cased string. + * + * @param string $camel camelCased string to converty to snake case + * + * @return string + */ + public static function camelsSnake( $camel ) + { + return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) ); + } + + /** + * Clears renames. + * + * @return void + */ + public static function clearRenames() + { + self::$renames = array(); + } + + /** + * Toggles 'Narrow Field Mode'. + * In Narrow Field mode the queryRecord method will + * narrow its selection field to + * + * SELECT {table}.* + * + * instead of + * + * SELECT * + * + * This is a better way of querying because it allows + * more flexibility (for instance joins). However if you need + * the wide selector for backward compatibility; use this method + * to turn OFF Narrow Field Mode by passing FALSE. + * + * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field + * + * @return void + */ + public static function setNarrowFieldMode( $narrowField ) + { + self::$flagNarrowFieldMode = (boolean) $narrowField; + } + + /** + * Sets SQL filters. + * This is a lowlevel method to set the SQL filter array. + * The format of this array is: + * + * + * array( + * '' => array( + * '
' => array( + * '' => '' + * ) + * ) + * ) + * + * + * Example: + * + * + * array( + * QueryWriter::C_SQLFILTER_READ => array( + * 'book' => array( + * 'title' => ' LOWER(book.title) ' + * ) + * ) + * + * + * Note that you can use constants instead of magical chars + * as keys for the uppermost array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @param array list of filters to set + * + * @return void + */ + public static function setSQLFilters( $sqlFilters, $safeMode = false ) + { + self::$flagSQLFilterSafeMode = (boolean) $safeMode; + self::$sqlFilters = $sqlFilters; + } + + /** + * Returns current SQL Filters. + * This method returns the raw SQL filter array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @return array + */ + public static function getSQLFilters() + { + return self::$sqlFilters; + } + + /** + * Returns a cache key for the cache values passed. + * This method returns a fingerprint string to be used as a key to store + * data in the writer cache. + * + * @param array $keyValues key-value to generate key for + * + * @return string + */ + private function getCacheKey( $keyValues ) + { + return json_encode( $keyValues ); + } + + /** + * Returns the values associated with the provided cache tag and key. + * + * @param string $cacheTag cache tag to use for lookup + * @param string $key key to use for lookup + * + * @return mixed + */ + private function getCached( $cacheTag, $key ) + { + $sql = $this->adapter->getSQL(); + + if ($this->updateCache()) { + if ( isset( $this->cache[$cacheTag][$key] ) ) { + return $this->cache[$cacheTag][$key]; + } + } + + return NULL; + } + + /** + * Checks if the previous query had a keep-cache tag. + * If so, the cache will persist, otherwise the cache will be flushed. + * + * Returns TRUE if the cache will remain and FALSE if a flush has + * been performed. + * + * @return boolean + */ + private function updateCache() + { + $sql = $this->adapter->getSQL(); + if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) { + // If SQL has been taken place outside of this method then something else then + // a select query might have happened! (or instruct to keep cache) + $this->cache = array(); + return FALSE; + } + return TRUE; + } + + /** + * Stores data from the writer in the cache under a specific key and cache tag. + * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag + * will be the bean type, this makes sure queries associated with a certain reference type will + * never contain conflicting data. + * Why not use the cache tag as a key? Well + * we need to make sure the cache contents fits the key (and key is based on the cache values). + * Otherwise it would be possible to store two different result sets under the same key (the cache tag). + * + * In previous versions you could only store one key-entry, I have changed this to + * improve caching efficiency (issue #400). + * + * @param string $cacheTag cache tag (secondary key) + * @param string $key key to store values under + * @param array $values content to be stored + * + * @return void + */ + private function putResultInCache( $cacheTag, $key, $values ) + { + if ( isset( $this->cache[$cacheTag] ) ) { + if ( count( $this->cache[$cacheTag] ) > $this->maxCacheSizePerType ) array_shift( $this->cache[$cacheTag] ); + } else { + $this->cache[$cacheTag] = array(); + } + + $this->cache[$cacheTag][$key] = $values; + } + + /** + * Creates an SQL snippet from a list of conditions of format: + * + * + * array( + * key => array( + * value1, value2, value3 .... + * ) + * ) + * + * + * @param array $conditions list of conditions + * @param array $bindings parameter bindings for SQL snippet + * @param string $addSql additional SQL snippet to append to result + * + * @return string + */ + private function makeSQLFromConditions( $conditions, &$bindings, $addSql = '' ) + { + reset( $bindings ); + $firstKey = key( $bindings ); + $paramTypeIsNum = ( is_numeric( $firstKey ) ); + $counter = 0; + + $sqlConditions = array(); + foreach ( $conditions as $column => $values ) { + if ( !count( $values ) ) continue; + + $sql = $this->esc( $column ); + $sql .= ' IN ( '; + + if ( !is_array( $values ) ) $values = array( $values ); + + if ( $paramTypeIsNum ) { + $sql .= implode( ',', array_fill( 0, count( $values ), '?' ) ) . ' ) '; + + array_unshift($sqlConditions, $sql); + + foreach ( $values as $k => $v ) { + $values[$k] = strval( $v ); + + array_unshift( $bindings, $v ); + } + } else { + + $slots = array(); + + foreach( $values as $k => $v ) { + $slot = ':slot'.$counter++; + $slots[] = $slot; + $bindings[$slot] = strval( $v ); + } + + $sql .= implode( ',', $slots ).' ) '; + $sqlConditions[] = $sql; + } + } + + $sql = ''; + if ( is_array( $sqlConditions ) && count( $sqlConditions ) > 0 ) { + $sql = implode( ' AND ', $sqlConditions ); + $sql = " WHERE ( $sql ) "; + + if ( $addSql ) $sql .= $addSql; + } elseif ( $addSql ) { + $sql = $addSql; + } + + return $sql; + } + + /** + * Returns the table names and column names for a relational query. + * + * @param string $sourceType type of the source bean + * @param string $destType type of the bean you want to obtain using the relation + * @param boolean $noQuote TRUE if you want to omit quotes + * + * @return array + */ + private function getRelationalTablesAndColumns( $sourceType, $destType, $noQuote = FALSE ) + { + $linkTable = $this->esc( $this->getAssocTable( array( $sourceType, $destType ) ), $noQuote ); + $sourceCol = $this->esc( $sourceType . '_id', $noQuote ); + + if ( $sourceType === $destType ) { + $destCol = $this->esc( $destType . '2_id', $noQuote ); + } else { + $destCol = $this->esc( $destType . '_id', $noQuote ); + } + + $sourceTable = $this->esc( $sourceType, $noQuote ); + $destTable = $this->esc( $destType, $noQuote ); + + return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ); + } + + /** + * Given a type and a property name this method + * returns the foreign key map section associated with this pair. + * + * @param string $type name of the type + * @param string $property name of the property + * + * @return array|NULL + */ + protected function getForeignKeyForTypeProperty( $type, $property ) + { + $property = $this->esc( $property, TRUE ); + + try { + $map = $this->getKeyMapForType( $type ); + } catch ( SQLException $e ) { + return NULL; + } + + foreach( $map as $key ) { + if ( $key['from'] === $property ) return $key; + } + return NULL; + } + + /** + * Returns the foreign key map (FKM) for a type. + * A foreign key map describes the foreign keys in a table. + * A FKM always has the same structure: + * + * + * array( + * 'name' => + * 'from' => + * 'table' => + * 'to' => (most of the time 'id') + * 'on_update' => + * 'on_delete' => + * ) + * + * + * @note the keys in the result array are FKDLs, i.e. descriptive unique + * keys per source table. Also see: AQueryWriter::makeFKLabel for details. + * + * @param string $type the bean type you wish to obtain a key map of + * + * @return array + */ + protected function getKeyMapForType( $type ) + { + return array(); + } + + /** + * This method makes a key for a foreign key description array. + * This key is a readable string unique for every source table. + * This uniform key is called the FKDL Foreign Key Description Label. + * Note that the source table is not part of the FKDL because + * this key is supposed to be 'per source table'. If you wish to + * include a source table, prefix the key with 'on_table__'. + * + * @param string $from the column of the key in the source table + * @param string $type the type (table) where the key points to + * @param string $to the target column of the foreign key (mostly just 'id') + * + * @return string + */ + protected function makeFKLabel($from, $type, $to) + { + return "from_{$from}_to_table_{$type}_col_{$to}"; + } + + /** + * Returns an SQL Filter snippet for reading. + * + * @param string $type type of bean + * + * @return string + */ + protected function getSQLFilterSnippet( $type ) + { + $existingCols = array(); + if (self::$flagSQLFilterSafeMode) { + $existingCols = $this->getColumns( $type ); + } + + $sqlFilters = array(); + if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) { + foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) { + if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) { + $sqlFilters[] = $sqlFilter.' AS '.$property.' '; + } + } + } + $sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : ''; + return $sqlFilterStr; + } + + /** + * Generates a list of parameters (slots) for an SQL snippet. + * This method calculates the correct number of slots to insert in the + * SQL snippet and determines the correct type of slot. If the bindings + * array contains named parameters this method will return named ones and + * update the keys in the value list accordingly (that's why we use the &). + * + * If you pass an offset the bindings will be re-added to the value list. + * Some databases cant handle duplicate parameter names in queries. + * + * @param array &$valueList list of values to generate slots for (gets modified if needed) + * @param array $otherBindings list of additional bindings + * @param integer $offset start counter at... + * + * @return string + */ + protected function getParametersForInClause( &$valueList, $otherBindings, $offset = 0 ) + { + if ( is_array( $otherBindings ) && count( $otherBindings ) > 0 ) { + reset( $otherBindings ); + + $key = key( $otherBindings ); + + if ( !is_numeric($key) ) { + $filler = array(); + $newList = (!$offset) ? array() : $valueList; + $counter = $offset; + + foreach( $valueList as $value ) { + $slot = ':slot' . ( $counter++ ); + $filler[] = $slot; + $newList[$slot] = $value; + } + + // Change the keys! + $valueList = $newList; + + return implode( ',', $filler ); + } + } + + return implode( ',', array_fill( 0, count( $valueList ), '?' ) ); + } + + /** + * Adds a data type to the list of data types. + * Use this method to add a new column type definition to the writer. + * Used for UUID support. + * + * @param integer $dataTypeID magic number constant assigned to this data type + * @param string $SQLDefinition SQL column definition (i.e. INT(11)) + * + * @return self + */ + protected function addDataType( $dataTypeID, $SQLDefinition ) + { + $this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition; + $this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID; + return $this; + } + + /** + * Returns the sql that should follow an insert statement. + * + * @param string $table name + * + * @return string + */ + protected function getInsertSuffix( $table ) + { + return ''; + } + + /** + * Checks whether a value starts with zeros. In this case + * the value should probably be stored using a text datatype instead of a + * numerical type in order to preserve the zeros. + * + * @param string $value value to be checked. + * + * @return boolean + */ + protected function startsWithZeros( $value ) + { + $value = strval( $value ); + + if ( strlen( $value ) > 1 && strpos( $value, '0' ) === 0 && strpos( $value, '0.' ) !== 0 ) { + return TRUE; + } else { + return FALSE; + } + } + + /** + * Inserts a record into the database using a series of insert columns + * and corresponding insertvalues. Returns the insert id. + * + * @param string $table table to perform query on + * @param array $insertcolumns columns to be inserted + * @param array $insertvalues values to be inserted + * + * @return integer + */ + protected function insertRecord( $type, $insertcolumns, $insertvalues ) + { + $default = $this->defaultValue; + $suffix = $this->getInsertSuffix( $type ); + $table = $this->esc( $type ); + + if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) { + + $insertSlots = array(); + foreach ( $insertcolumns as $k => $v ) { + $insertcolumns[$k] = $this->esc( $v ); + + if (isset(self::$sqlFilters['w'][$type][$v])) { + $insertSlots[] = self::$sqlFilters['w'][$type][$v]; + } else { + $insertSlots[] = '?'; + } + } + + $insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES + ( $default, " . implode( ',', $insertSlots ) . " ) $suffix"; + + $ids = array(); + foreach ( $insertvalues as $i => $insertvalue ) { + $ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i ); + } + + $result = count( $ids ) === 1 ? array_pop( $ids ) : $ids; + } else { + $result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix" ); + } + + if ( $suffix ) return $result; + + $last_id = $this->adapter->getInsertID(); + + return $last_id; + } + + /** + * Checks table name or column name. + * + * @param string $table table string + * + * @return string + */ + protected function check( $struct ) + { + if ( !is_string( $struct ) || !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) { + throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' ); + } + + return $struct; + } + + /** + * Checks whether the specified type (i.e. table) already exists in the database. + * Not part of the Object Database interface! + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + $tables = $this->getTables(); + + return in_array( $table, $tables ); + } + + /** + * @see QueryWriter::glueSQLCondition + */ + public function glueSQLCondition( $sql, $glue = NULL ) + { + static $snippetCache = array(); + + if ( trim( $sql ) === '' ) { + return $sql; + } + + $key = $glue . '|' . $sql; + + if ( isset( $snippetCache[$key] ) ) { + return $snippetCache[$key]; + } + + $lsql = ltrim( $sql ); + + if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) { + if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) { + $snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 ); + } else { + $snippetCache[$key] = $sql; + } + } else { + $snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql; + } + + return $snippetCache[$key]; + } + + /** + * @see QueryWriter::glueLimitOne + */ + public function glueLimitOne( $sql = '') + { + return ( strpos( strtoupper( $sql ), 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql; + } + + /** + * @see QueryWriter::esc + */ + public function esc( $dbStructure, $dontQuote = FALSE ) + { + $this->check( $dbStructure ); + + return ( $dontQuote ) ? $dbStructure : $this->quoteCharacter . $dbStructure . $this->quoteCharacter; + } + + /** + * @see QueryWriter::addColumn + */ + public function addColumn( $type, $column, $field ) + { + $table = $type; + $type = $field; + $table = $this->esc( $table ); + $column = $this->esc( $column ); + + $type = ( isset( $this->typeno_sqltype[$type] ) ) ? $this->typeno_sqltype[$type] : ''; + + $this->adapter->exec( "ALTER TABLE $table ADD $column $type " ); + } + + /** + * @see QueryWriter::updateRecord + */ + public function updateRecord( $type, $updatevalues, $id = NULL ) + { + $table = $type; + + if ( !$id ) { + $insertcolumns = $insertvalues = array(); + + foreach ( $updatevalues as $pair ) { + $insertcolumns[] = $pair['property']; + $insertvalues[] = $pair['value']; + } + + //Otherwise psql returns string while MySQL/SQLite return numeric causing problems with additions (array_diff) + return (string) $this->insertRecord( $table, $insertcolumns, array( $insertvalues ) ); + } + + if ( $id && !count( $updatevalues ) ) { + return $id; + } + + $table = $this->esc( $table ); + $sql = "UPDATE $table SET "; + + $p = $v = array(); + + foreach ( $updatevalues as $uv ) { + + if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) { + $p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']]; + } else { + $p[] = " {$this->esc( $uv["property"] )} = ? "; + } + + $v[] = $uv['value']; + } + + $sql .= implode( ',', $p ) . ' WHERE id = ? '; + + $v[] = $id; + + $this->adapter->exec( $sql, $v ); + + return $id; + } + + /** + * @see QueryWriter::writeJoin + */ + public function writeJoin( $type, $targetType, $leftRight = 'LEFT' ) + { + if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' ) + throw new RedException( 'Invalid JOIN.' ); + + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $field = $this->esc( $targetType, TRUE ); + return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id "; + } + + /** + * @see QueryWriter::queryRecord + */ + public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL ); + + $key = NULL; + if ( $this->flagUseCache ) { + $key = $this->getCacheKey( array( $conditions, $addSql, $bindings, 'select' ) ); + + if ( $cached = $this->getCached( $type, $key ) ) { + return $cached; + } + } + + $table = $this->esc( $type ); + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $type ); + } + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + + $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*'; + $sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache"; + + $rows = $this->adapter->get( $sql, $bindings ); + + if ( $this->flagUseCache && $key ) { + $this->putResultInCache( $type, $key, $rows ); + } + + return $rows; + } + + /** + * @see QueryWriter::queryRecordWithCursor + */ + public function queryRecordWithCursor( $type, $addSql = NULL, $bindings = array() ) + { + $sql = $this->glueSQLCondition( $addSql, NULL ); + $table = $this->esc( $type ); + $sql = "SELECT {$table}.* FROM {$table} {$sql}"; + return $this->adapter->getCursor( $sql, $bindings ); + } + + /** + * @see QueryWriter::queryRecordRelated + */ + public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE ); + + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) ); + + if ( $this->flagUseCache && $cached = $this->getCached( $destType, $key ) ) { + return $cached; + } + + $inClause = $this->getParametersForInClause( $linkIDs, $bindings ); + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + + if ( $sourceType === $destType ) { + $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases + $sql = " + SELECT + {$destTable}.* {$sqlFilterStr} , + COALESCE( + NULLIF({$linkTable}.{$sourceCol}, {$destTable}.id), + NULLIF({$linkTable}.{$destCol}, {$destTable}.id)) AS linked_by + FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) OR + ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} IN ($inClause2) ) + {$addSql} + -- keep-cache"; + + $linkIDs = array_merge( $linkIDs, $linkIDs ); + } else { + $sql = " + SELECT + {$destTable}.* {$sqlFilterStr}, + {$linkTable}.{$sourceCol} AS linked_by + FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) + {$addSql} + -- keep-cache"; + } + + $bindings = array_merge( $linkIDs, $bindings ); + + $rows = $this->adapter->get( $sql, $bindings ); + + $this->putResultInCache( $destType, $key, $rows ); + + return $rows; + } + + /** + * @see QueryWriter::queryRecordLink + */ + public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $key = $this->getCacheKey( array( $sourceType, $destType, $sourceID, $destID ) ); + + if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) { + return $cached; + } + + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + + if ( $sourceTable === $destTable ) { + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} + WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR + ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache"; + $row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) ); + } else { + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} + WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache"; + $row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) ); + } + + $this->putResultInCache( $linkTable, $key, $row ); + + return $row; + } + + /** + * @see QueryWriter::queryTagged + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ) + { + $assocType = $this->getAssocTable( array( $type, 'tag' ) ); + $assocTable = $this->esc( $assocType ); + $assocField = $type . '_id'; + $table = $this->esc( $type ); + $slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) ); + $score = ( $all ) ? count( $tagList ) : 1; + + $sql = " + SELECT {$table}.*, count({$table}.id) FROM {$table} + INNER JOIN {$assocTable} ON {$assocField} = {$table}.id + INNER JOIN tag ON {$assocTable}.tag_id = tag.id + WHERE tag.title IN ({$slots}) + GROUP BY {$table}.id + HAVING count({$table}.id) >= ? + {$addSql} + "; + + $bindings = array_merge( $tagList, array( $score ), $bindings ); + $rows = $this->adapter->get( $sql, $bindings ); + return $rows; + } + + /** + * @see QueryWriter::queryRecordCount + */ + public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql ); + + $table = $this->esc( $type ); + + $this->updateCache(); //check if cache chain has been broken + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + $sql = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache"; + + return (int) $this->adapter->getCell( $sql, $bindings ); + } + + /** + * @see QueryWriter::queryRecordCountRelated + */ + public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + $this->updateCache(); //check if cache chain has been broken + + if ( $sourceType === $destType ) { + $sql = " + SELECT COUNT(*) FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR + ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? ) + {$addSql} + -- keep-cache"; + + $bindings = array_merge( array( $linkID, $linkID ), $bindings ); + } else { + $sql = " + SELECT COUNT(*) FROM {$linkTable} + INNER JOIN {$destTable} ON + ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) + {$addSql} + -- keep-cache"; + + $bindings = array_merge( array( $linkID ), $bindings ); + } + + return (int) $this->adapter->getCell( $sql, $bindings ); + } + + /** + * @see QueryWriter::deleteRecord + */ + public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) + { + $addSql = $this->glueSQLCondition( $addSql ); + + $table = $this->esc( $type ); + + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); + $sql = "DELETE FROM {$table} {$sql}"; + + $this->adapter->exec( $sql, $bindings ); + } + + /** + * @see QueryWriter::deleteRelations + */ + public function deleteRelations( $sourceType, $destType, $sourceID ) + { + list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + + if ( $sourceTable === $destTable ) { + $sql = "DELETE FROM {$linkTable} + WHERE ( {$sourceCol} = ? ) OR + ( {$destCol} = ? ) + "; + + $this->adapter->exec( $sql, array( $sourceID, $sourceID ) ); + } else { + $sql = "DELETE FROM {$linkTable} + WHERE {$sourceCol} = ? "; + + $this->adapter->exec( $sql, array( $sourceID ) ); + } + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $property, $dataType ) + { + if ( !isset($this->typeno_sqltype[$dataType]) ) return FALSE; + + $table = $this->esc( $type ); + $column = $this->esc( $property ); + + $newType = $this->typeno_sqltype[$dataType]; + + $this->adapter->exec( "ALTER TABLE $table CHANGE $column $column $newType " ); + + return TRUE; + } + + /** + * @see QueryWriter::wipe + */ + public function wipe( $type ) + { + $table = $this->esc( $type ); + + $this->adapter->exec( "TRUNCATE $table " ); + } + + /** + * @see QueryWriter::renameAssocTable + */ + public function renameAssocTable( $from, $to = NULL ) + { + self::renameAssociation( $from, $to ); + } + + /** + * @see QueryWriter::getAssocTable + */ + public function getAssocTable( $types ) + { + return self::getAssocTableFormat( $types ); + } + + /** + * Turns caching on or off. Default: off. + * If caching is turned on retrieval queries fired after eachother will + * use a result row cache. + * + * @param boolean + * + * @return void + */ + public function setUseCache( $yesNo ) + { + $this->flushCache(); + + $this->flagUseCache = (bool) $yesNo; + } + + /** + * Flushes the Query Writer Cache. + * Clears the internal query cache array and returns its overall + * size. + * + * @return integer + */ + public function flushCache( $newMaxCacheSizePerType = NULL ) + { + if ( !is_null( $newMaxCacheSizePerType ) && $newMaxCacheSizePerType > 0 ) { + $this->maxCacheSizePerType = $newMaxCacheSizePerType; + } + $count = count( $this->cache, COUNT_RECURSIVE ); + $this->cache = array(); + return $count; + } + + /** + * @deprecated Use esc() instead. + * + * @param string $column column to be escaped + * @param boolean $noQuotes omit quotes + * + * @return string + */ + public function safeColumn( $column, $noQuotes = FALSE ) + { + return $this->esc( $column, $noQuotes ); + } + + /** + * @deprecated Use esc() instead. + * + * @param string $table table to be escaped + * @param boolean $noQuotes omit quotes + * + * @return string + */ + public function safeTable( $table, $noQuotes = FALSE ) + { + return $this->esc( $table, $noQuotes ); + } + + /** + * @see QueryWriter::inferFetchType + */ + public function inferFetchType( $type, $property ) + { + $type = $this->esc( $type, TRUE ); + $field = $this->esc( $property, TRUE ) . '_id'; + $keys = $this->getKeyMapForType( $type ); + + foreach( $keys as $key ) { + if ( + $key['from'] === $field + ) return $key['table']; + } + return NULL; + } + + /** + * @see QueryWriter::addUniqueConstraint + */ + public function addUniqueIndex( $type, $properties ) + { + return $this->addUniqueConstraint( $type, $properties ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP MySQLWriter. + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the MySQL/MariaDB database platform. + * + * @file RedBeanPHP/QueryWriter/MySQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class MySQL extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_BOOL = 0; + const C_DATATYPE_UINT32 = 2; + const C_DATATYPE_DOUBLE = 3; + const C_DATATYPE_TEXT7 = 4; //InnoDB cant index varchar(255) utf8mb4 - so keep 191 as long as possible + const C_DATATYPE_TEXT8 = 5; + const C_DATATYPE_TEXT16 = 6; + const C_DATATYPE_TEXT32 = 7; + const C_DATATYPE_SPECIAL_DATE = 80; + const C_DATATYPE_SPECIAL_DATETIME = 81; + const C_DATATYPE_SPECIAL_POINT = 90; + const C_DATATYPE_SPECIAL_LINESTRING = 91; + const C_DATATYPE_SPECIAL_POLYGON = 92; + const C_DATATYPE_SPECIAL_MONEY = 93; + + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '`'; + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $databaseName = $this->adapter->getCell('SELECT DATABASE()'); + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get(' + SELECT + information_schema.key_column_usage.constraint_name AS `name`, + information_schema.key_column_usage.referenced_table_name AS `table`, + information_schema.key_column_usage.column_name AS `from`, + information_schema.key_column_usage.referenced_column_name AS `to`, + information_schema.referential_constraints.update_rule AS `on_update`, + information_schema.referential_constraints.delete_rule AS `on_delete` + FROM information_schema.key_column_usage + INNER JOIN information_schema.referential_constraints + ON information_schema.referential_constraints.constraint_name = information_schema.key_column_usage.constraint_name + WHERE + information_schema.key_column_usage.table_schema = :database + AND information_schema.referential_constraints.constraint_schema = :database + AND information_schema.key_column_usage.constraint_schema = :database + AND information_schema.key_column_usage.table_name = :table + AND information_schema.key_column_usage.constraint_name != \'PRIMARY\' + AND information_schema.key_column_usage.referenced_table_name IS NOT NULL + ', array( ':database' => $databaseName, ':table' => $table ) ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $k['name'], + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ', + MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ', + MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ', + MySQL::C_DATATYPE_TEXT7 => ' VARCHAR(191) ', + MYSQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ', + MySQL::C_DATATYPE_TEXT16 => ' TEXT ', + MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ', + MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ', + MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', + MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ', + MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ', + MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ', + MySQL::C_DATATYPE_SPECIAL_MONEY => ' DECIMAL(10,2) ' + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[trim( strtolower( $v ) )] = $k; + } + + $this->adapter = $adapter; + + $this->encoding = $this->adapter->getDatabase()->getMysqlEncoding(); + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer + */ + public function getTypeForID() + { + return self::C_DATATYPE_UINT32; + } + + /** + * @see QueryWriter::getTables + */ + public function getTables() + { + return $this->adapter->getCol( 'show tables' ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $encoding = $this->adapter->getDatabase()->getMysqlEncoding(); + $sql = "CREATE TABLE $table (id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY ( id )) ENGINE = InnoDB DEFAULT CHARSET={$encoding} COLLATE={$encoding}_unicode_ci "; + + $this->adapter->exec( $sql ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $columnsRaw = $this->adapter->get( "DESCRIBE " . $this->esc( $table ) ); + + $columns = array(); + foreach ( $columnsRaw as $r ) { + $columns[$r['Field']] = $r['Type']; + } + + return $columns; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL; + if ( $value === INF ) return MySQL::C_DATATYPE_TEXT7; + + if ( $flagSpecial ) { + if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_MONEY; + } + if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_DATE; + } + if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_DATETIME; + } + if ( preg_match( '/^POINT\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POINT; + } + if ( preg_match( '/^LINESTRING\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_LINESTRING; + } + if ( preg_match( '/^POLYGON\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POLYGON; + } + } + + //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?). + if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) { + return MySQL::C_DATATYPE_BOOL; + } + + if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; + + if ( !$this->startsWithZeros( $value ) ) { + + if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) { + return MySQL::C_DATATYPE_UINT32; + } + + if ( is_numeric( $value ) ) { + return MySQL::C_DATATYPE_DOUBLE; + } + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 191 ) { + return MySQL::C_DATATYPE_TEXT7; + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) { + return MySQL::C_DATATYPE_TEXT8; + } + + if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) { + return MySQL::C_DATATYPE_TEXT16; + } + + return MySQL::C_DATATYPE_TEXT32; + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + if ( isset( $this->sqltype_typeno[$typedescription] ) ) { + $r = $this->sqltype_typeno[$typedescription]; + } else { + $r = self::C_DATATYPE_SPECIFIED; + } + + if ( $includeSpecials ) { + return $r; + } + + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + return self::C_DATATYPE_SPECIFIED; + } + + return $r; + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $columns = array(); + foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); + $table = $this->esc( $type ); + sort( $columns ); // Else we get multiple indexes due to order-effects + $name = 'UQ_' . sha1( implode( ',', $columns ) ); + try { + $sql = "ALTER TABLE $table + ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")"; + $this->adapter->exec( $sql ); + } catch ( SQLException $e ) { + //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways! + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $property ) + { + try { + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $property ); + $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::addFK + * @return bool + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDependent = FALSE ) + { + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $targetTableNoQ = $this->esc( $targetType, TRUE ); + $field = $this->esc( $property ); + $fieldNoQ = $this->esc( $property, TRUE ); + $targetField = $this->esc( $targetProperty ); + $targetFieldNoQ = $this->esc( $targetProperty, TRUE ); + $tableNoQ = $this->esc( $type, TRUE ); + $fieldNoQ = $this->esc( $property, TRUE ); + if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; + + //Widen the column if it's incapable of representing a foreign key (at least INT). + $columns = $this->getColumns( $tableNoQ ); + $idType = $this->getTypeForID(); + if ( $this->code( $columns[$fieldNoQ] ) !== $idType ) { + $this->widenColumn( $type, $property, $idType ); + } + + $fkName = 'fk_'.($tableNoQ.'_'.$fieldNoQ); + $cName = 'c_'.$fkName; + try { + $this->adapter->exec( " + ALTER TABLE {$table} + ADD CONSTRAINT $cName + FOREIGN KEY $fkName ( `{$fieldNoQ}` ) REFERENCES `{$targetTableNoQ}` + (`{$targetFieldNoQ}`) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';'); + } catch ( SQLException $e ) { + // Failure of fk-constraints is not a problem + } + return true; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 0;' ); + + foreach ( $this->getTables() as $t ) { + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + + try { + $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + } + + $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP SQLiteWriter with support for SQLite types + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the SQLite database platform. + * + * @file RedBeanPHP/QueryWriter/SQLiteT.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQLiteT extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_INTEGER = 0; + const C_DATATYPE_NUMERIC = 1; + const C_DATATYPE_TEXT = 2; + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '`'; + + /** + * Gets all information about a table (from a type). + * + * Format: + * array( + * name => name of the table + * columns => array( name => datatype ) + * indexes => array() raw index information rows from PRAGMA query + * keys => array() raw key information rows from PRAGMA query + * ) + * + * @param string $type type you want to get info of + * + * @return array + */ + protected function getTable( $type ) + { + $tableName = $this->esc( $type, TRUE ); + $columns = $this->getColumns( $type ); + $indexes = $this->getIndexes( $type ); + $keys = $this->getKeyMapForType( $type ); + + $table = array( + 'columns' => $columns, + 'indexes' => $indexes, + 'keys' => $keys, + 'name' => $tableName + ); + + $this->tableArchive[$tableName] = $table; + + return $table; + } + + /** + * Puts a table. Updates the table structure. + * In SQLite we can't change columns, drop columns, change or add foreign keys so we + * have a table-rebuild function. You simply load your table with getTable(), modify it and + * then store it with putTable()... + * + * @param array $tableMap information array + * + * @return void + */ + protected function putTable( $tableMap ) + { + $table = $tableMap['name']; + $q = array(); + $q[] = "DROP TABLE IF EXISTS tmp_backup;"; + + $oldColumnNames = array_keys( $this->getColumns( $table ) ); + + foreach ( $oldColumnNames as $k => $v ) $oldColumnNames[$k] = "`$v`"; + + $q[] = "CREATE TEMPORARY TABLE tmp_backup(" . implode( ",", $oldColumnNames ) . ");"; + $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;"; + $q[] = "PRAGMA foreign_keys = 0 "; + $q[] = "DROP TABLE `$table`;"; + + $newTableDefStr = ''; + foreach ( $tableMap['columns'] as $column => $type ) { + if ( $column != 'id' ) { + $newTableDefStr .= ",`$column` $type"; + } + } + + $fkDef = ''; + foreach ( $tableMap['keys'] as $key ) { + $fkDef .= ", FOREIGN KEY(`{$key['from']}`) + REFERENCES `{$key['table']}`(`{$key['to']}`) + ON DELETE {$key['on_delete']} ON UPDATE {$key['on_update']}"; + } + + $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );"; + + foreach ( $tableMap['indexes'] as $name => $index ) { + if ( strpos( $name, 'UQ_' ) === 0 ) { + $cols = explode( '__', substr( $name, strlen( 'UQ_' . $table ) ) ); + foreach ( $cols as $k => $v ) $cols[$k] = "`$v`"; + $q[] = "CREATE UNIQUE INDEX $name ON `$table` (" . implode( ',', $cols ) . ")"; + } else $q[] = "CREATE INDEX $name ON `$table` ({$index['name']}) "; + } + + $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;"; + $q[] = "DROP TABLE tmp_backup;"; + $q[] = "PRAGMA foreign_keys = 1 "; + + foreach ( $q as $sq ) $this->adapter->exec( $sq ); + } + + /** + * Returns the an array describing the indexes for type $type. + * + * @param string $type type to describe indexes of + * + * @return array + */ + protected function getIndexes( $type ) + { + $table = $this->esc( $type, TRUE ); + $indexes = $this->adapter->get( "PRAGMA index_list('$table')" ); + + $indexInfoList = array(); + foreach ( $indexes as $i ) { + $indexInfoList[$i['name']] = $this->adapter->getRow( "PRAGMA index_info('{$i['name']}') " ); + + $indexInfoList[$i['name']]['unique'] = $i['unique']; + } + + return $indexInfoList; + } + + /** + * Adds a foreign key to a type. + * Note: cant put this in try-catch because that can hide the fact + * that database has been damaged. + * + * @param string $type type you want to modify table of + * @param string $targetType target type + * @param string $field field of the type that needs to get the fk + * @param string $targetField field where the fk needs to point to + * @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE + * + * @return boolean + */ + protected function buildFK( $type, $targetType, $property, $targetProperty, $constraint = FALSE ) + { + $table = $this->esc( $type, TRUE ); + $targetTable = $this->esc( $targetType, TRUE ); + $column = $this->esc( $property, TRUE ); + $targetColumn = $this->esc( $targetProperty, TRUE ); + + $tables = $this->getTables(); + if ( !in_array( $targetTable, $tables ) ) return FALSE; + + if ( !is_null( $this->getForeignKeyForTypeProperty( $table, $column ) ) ) return FALSE; + $t = $this->getTable( $table ); + $consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' ); + $label = 'from_' . $column . '_to_table_' . $targetTable . '_col_' . $targetColumn; + $t['keys'][$label] = array( + 'table' => $targetTable, + 'from' => $column, + 'to' => $targetColumn, + 'on_update' => $consSQL, + 'on_delete' => $consSQL + ); + $this->putTable( $t ); + return TRUE; + } + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get( "PRAGMA foreign_key_list('$table')" ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $label, + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + SQLiteT::C_DATATYPE_INTEGER => 'INTEGER', + SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC', + SQLiteT::C_DATATYPE_TEXT => 'TEXT', + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[$v] = $k; + } + + $this->adapter = $adapter; + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer $const data type to be used for IDS. + */ + public function getTypeForID() + { + return self::C_DATATYPE_INTEGER; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( $value === NULL ) return self::C_DATATYPE_INTEGER; + if ( $value === INF ) return self::C_DATATYPE_TEXT; + + if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; + + if ( $value === TRUE || $value === FALSE ) return self::C_DATATYPE_INTEGER; + + if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER; + + if ( ( is_numeric( $value ) && $value < 2147483648 && $value > -2147483648) + || preg_match( '/\d{4}\-\d\d\-\d\d/', $value ) + || preg_match( '/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/', $value ) + ) { + return self::C_DATATYPE_NUMERIC; + } + + return self::C_DATATYPE_TEXT; + } + + /** + * @see QueryWriter::addColumn + */ + public function addColumn( $table, $column, $type ) + { + $column = $this->check( $column ); + $table = $this->check( $table ); + $type = $this->typeno_sqltype[$type]; + + $this->adapter->exec( "ALTER TABLE `$table` ADD `$column` $type " ); + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99 ); + + return $r; + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $column, $datatype ) + { + $t = $this->getTable( $type ); + + $t['columns'][$column] = $this->typeno_sqltype[$datatype]; + + $this->putTable( $t ); + } + + /** + * @see QueryWriter::getTables(); + */ + public function getTables() + { + return $this->adapter->getCol( "SELECT name FROM sqlite_master + WHERE type='table' AND name!='sqlite_sequence';" ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $sql = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) "; + + $this->adapter->exec( $sql ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $table = $this->esc( $table, TRUE ); + + $columnsRaw = $this->adapter->get( "PRAGMA table_info('$table')" ); + + $columns = array(); + foreach ( $columnsRaw as $r ) $columns[$r['name']] = $r['type']; + + return $columns; + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $name = 'UQ_' . $this->esc( $type, TRUE ) . implode( '__', $properties ); + $t = $this->getTable( $type ); + $t['indexes'][$name] = array( 'name' => $name ); + try { + $this->putTable( $t ); + } catch( SQLException $e ) { + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + 'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $column ) + { + $columns = $this->getColumns( $type ); + if ( !isset( $columns[$column] ) ) return FALSE; + + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $column, TRUE ); + + try { + $t = $this->getTable( $type ); + $t['indexes'][$name] = array( 'name' => $column ); + $this->putTable( $t ); + return TRUE; + } catch( SQLException $exception ) { + return FALSE; + } + } + + /** + * @see QueryWriter::wipe + */ + public function wipe( $type ) + { + $table = $this->esc( $type ); + + $this->adapter->exec( "DELETE FROM $table " ); + } + + /** + * @see QueryWriter::addFK + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) + { + return $this->buildFK( $type, $targetType, $property, $targetProperty, $isDep ); + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'PRAGMA foreign_keys = 0 ' ); + + foreach ( $this->getTables() as $t ) { + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + + try { + $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); + } catch ( SQLException $e ) { + } + } + + $this->adapter->exec( 'PRAGMA foreign_keys = 1 ' ); + } +} +} + +namespace RedBeanPHP\QueryWriter { + +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * RedBeanPHP PostgreSQL Query Writer. + * This is a QueryWriter class for RedBeanPHP. + * This QueryWriter provides support for the PostgreSQL database platform. + * + * @file RedBeanPHP/QueryWriter/PostgreSQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class PostgreSQL extends AQueryWriter implements QueryWriter +{ + /** + * Data types + */ + const C_DATATYPE_INTEGER = 0; + const C_DATATYPE_DOUBLE = 1; + const C_DATATYPE_TEXT = 3; + const C_DATATYPE_SPECIAL_DATE = 80; + const C_DATATYPE_SPECIAL_DATETIME = 81; + const C_DATATYPE_SPECIAL_POINT = 90; + const C_DATATYPE_SPECIAL_LSEG = 91; + const C_DATATYPE_SPECIAL_CIRCLE = 92; + const C_DATATYPE_SPECIAL_MONEY = 93; + const C_DATATYPE_SPECIAL_POLYGON = 94; + const C_DATATYPE_SPECIAL_MONEY2 = 95; //Numbers only money, i.e. fixed point numeric + const C_DATATYPE_SPECIAL_JSON = 96; //JSON support (only manual) + const C_DATATYPE_SPECIFIED = 99; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var string + */ + protected $quoteCharacter = '"'; + + /** + * @var string + */ + protected $defaultValue = 'DEFAULT'; + + /** + * Returns the insert suffix SQL Snippet + * + * @param string $table table + * + * @return string $sql SQL Snippet + */ + protected function getInsertSuffix( $table ) + { + return 'RETURNING id '; + } + + /** + * @see AQueryWriter::getKeyMapForType + */ + protected function getKeyMapForType( $type ) + { + $table = $this->esc( $type, TRUE ); + $keys = $this->adapter->get( ' + SELECT + information_schema.key_column_usage.constraint_name AS "name", + information_schema.key_column_usage.column_name AS "from", + information_schema.constraint_table_usage.table_name AS "table", + information_schema.constraint_column_usage.column_name AS "to", + information_schema.referential_constraints.update_rule AS "on_update", + information_schema.referential_constraints.delete_rule AS "on_delete" + FROM information_schema.key_column_usage + INNER JOIN information_schema.constraint_table_usage + ON ( + information_schema.key_column_usage.constraint_name = information_schema.constraint_table_usage.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_table_usage.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_table_usage.constraint_catalog + ) + INNER JOIN information_schema.constraint_column_usage + ON ( + information_schema.key_column_usage.constraint_name = information_schema.constraint_column_usage.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.constraint_column_usage.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.constraint_column_usage.constraint_catalog + ) + INNER JOIN information_schema.referential_constraints + ON ( + information_schema.key_column_usage.constraint_name = information_schema.referential_constraints.constraint_name + AND information_schema.key_column_usage.constraint_schema = information_schema.referential_constraints.constraint_schema + AND information_schema.key_column_usage.constraint_catalog = information_schema.referential_constraints.constraint_catalog + ) + WHERE + information_schema.key_column_usage.table_catalog = current_database() + AND information_schema.key_column_usage.table_schema = ANY( current_schemas( FALSE ) ) + AND information_schema.key_column_usage.table_name = ? + ', array( $type ) ); + $keyInfoList = array(); + foreach ( $keys as $k ) { + $label = $this->makeFKLabel( $k['from'], $k['table'], $k['to'] ); + $keyInfoList[$label] = array( + 'name' => $k['name'], + 'from' => $k['from'], + 'table' => $k['table'], + 'to' => $k['to'], + 'on_update' => $k['on_update'], + 'on_delete' => $k['on_delete'] + ); + } + return $keyInfoList; + } + + /** + * Constructor + * + * @param Adapter $adapter Database Adapter + */ + public function __construct( Adapter $adapter ) + { + $this->typeno_sqltype = array( + self::C_DATATYPE_INTEGER => ' integer ', + self::C_DATATYPE_DOUBLE => ' double precision ', + self::C_DATATYPE_TEXT => ' text ', + self::C_DATATYPE_SPECIAL_DATE => ' date ', + self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ', + self::C_DATATYPE_SPECIAL_POINT => ' point ', + self::C_DATATYPE_SPECIAL_LSEG => ' lseg ', + self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ', + self::C_DATATYPE_SPECIAL_MONEY => ' money ', + self::C_DATATYPE_SPECIAL_MONEY2 => ' numeric(10,2) ', + self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ', + self::C_DATATYPE_SPECIAL_JSON => ' json ', + ); + + $this->sqltype_typeno = array(); + + foreach ( $this->typeno_sqltype as $k => $v ) { + $this->sqltype_typeno[trim( strtolower( $v ) )] = $k; + } + + $this->adapter = $adapter; + } + + /** + * This method returns the datatype to be used for primary key IDS and + * foreign keys. Returns one if the data type constants. + * + * @return integer + */ + public function getTypeForID() + { + return self::C_DATATYPE_INTEGER; + } + + /** + * @see QueryWriter::getTables + */ + public function getTables() + { + return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' ); + } + + /** + * @see QueryWriter::createTable + */ + public function createTable( $table ) + { + $table = $this->esc( $table ); + + $this->adapter->exec( " CREATE TABLE $table (id SERIAL PRIMARY KEY); " ); + } + + /** + * @see QueryWriter::getColumns + */ + public function getColumns( $table ) + { + $table = $this->esc( $table, TRUE ); + + $columnsRaw = $this->adapter->get( "SELECT column_name, data_type FROM information_schema.columns WHERE table_name='$table' AND table_schema = ANY( current_schemas( FALSE ) )" ); + + $columns = array(); + foreach ( $columnsRaw as $r ) { + $columns[$r['column_name']] = $r['data_type']; + } + + return $columns; + } + + /** + * @see QueryWriter::scanType + */ + public function scanType( $value, $flagSpecial = FALSE ) + { + $this->svalue = $value; + + if ( $value === INF ) return self::C_DATATYPE_TEXT; + + if ( $flagSpecial && $value ) { + if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_DATE; + } + + if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME; + } + + if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_POINT; + } + + if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_LSEG; + } + + if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE; + } + + if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON; + } + + if ( preg_match( '/^\-?(\$|€|¥|£)[\d,\.]+$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_MONEY; + } + + if ( preg_match( '/^-?\d+\.\d{2}$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_MONEY2; + } + } + + if ( is_float( $value ) ) return self::C_DATATYPE_DOUBLE; + + if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; + + if ( $value === FALSE || $value === TRUE || $value === NULL || ( is_numeric( $value ) + && AQueryWriter::canBeTreatedAsInt( $value ) + && $value < 2147483648 + && $value > -2147483648 ) + ) { + return self::C_DATATYPE_INTEGER; + } elseif ( is_numeric( $value ) ) { + return self::C_DATATYPE_DOUBLE; + } else { + return self::C_DATATYPE_TEXT; + } + } + + /** + * @see QueryWriter::code + */ + public function code( $typedescription, $includeSpecials = FALSE ) + { + $r = ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : 99; + + if ( $includeSpecials ) return $r; + + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + return self::C_DATATYPE_SPECIFIED; + } + + return $r; + } + + /** + * @see QueryWriter::widenColumn + */ + public function widenColumn( $type, $column, $datatype ) + { + $table = $type; + $type = $datatype; + + $table = $this->esc( $table ); + $column = $this->esc( $column ); + + $newtype = $this->typeno_sqltype[$type]; + + $this->adapter->exec( "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype " ); + } + + /** + * @see QueryWriter::addUniqueIndex + */ + public function addUniqueConstraint( $type, $properties ) + { + $tableNoQ = $this->esc( $type, TRUE ); + $columns = array(); + foreach( $properties as $key => $column ) $columns[$key] = $this->esc( $column ); + $table = $this->esc( $type ); + sort( $columns ); //else we get multiple indexes due to order-effects + $name = "UQ_" . sha1( $table . implode( ',', $columns ) ); + $sql = "ALTER TABLE {$table} + ADD CONSTRAINT $name UNIQUE (" . implode( ',', $columns ) . ")"; + try { + $this->adapter->exec( $sql ); + } catch( SQLException $e ) { + return FALSE; + } + return TRUE; + } + + /** + * @see QueryWriter::sqlStateIn + */ + public function sqlStateIn( $state, $list ) + { + $stateMap = array( + '42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + ); + + return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); + } + + /** + * @see QueryWriter::addIndex + */ + public function addIndex( $type, $name, $property ) + { + $table = $this->esc( $type ); + $name = preg_replace( '/\W/', '', $name ); + $column = $this->esc( $property ); + + try { + $this->adapter->exec( "CREATE INDEX {$name} ON $table ({$column}) " ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::addFK + */ + public function addFK( $type, $targetType, $property, $targetProperty, $isDep = FALSE ) + { + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $field = $this->esc( $property ); + $targetField = $this->esc( $targetProperty ); + $tableNoQ = $this->esc( $type, TRUE ); + $fieldNoQ = $this->esc( $property, TRUE ); + if ( !is_null( $this->getForeignKeyForTypeProperty( $tableNoQ, $fieldNoQ ) ) ) return FALSE; + try{ + $delRule = ( $isDep ? 'CASCADE' : 'SET NULL' ); + $this->adapter->exec( "ALTER TABLE {$table} + ADD FOREIGN KEY ( {$field} ) REFERENCES {$targetTable} + ({$targetField}) ON DELETE {$delRule} ON UPDATE {$delRule} DEFERRABLE ;" ); + return TRUE; + } catch ( SQLException $e ) { + return FALSE; + } + } + + /** + * @see QueryWriter::wipeAll + */ + public function wipeAll() + { + $this->adapter->exec( 'SET CONSTRAINTS ALL DEFERRED' ); + + foreach ( $this->getTables() as $t ) { + $t = $this->esc( $t ); + //Some plugins (PostGIS have unremovable tables/views), avoid exceptions. + try { $this->adapter->exec( "DROP TABLE IF EXISTS $t CASCADE " ); }catch( \Exception $e ) {} + } + + $this->adapter->exec( 'SET CONSTRAINTS ALL IMMEDIATE' ); + } +} +} + +namespace RedBeanPHP { + +/** + * RedBean\Exception Base. + * Represents the base class for RedBeanPHP\Exceptions. + * + * @file RedBeanPHP/Exception.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RedException extends \Exception +{ +} +} + +namespace RedBeanPHP\RedException { + +use RedBeanPHP\RedException as RedException; + +/** + * SQL Exception. + * Represents a generic database exception independent of the underlying driver. + * + * @file RedBeanPHP/RedException/SQL.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQL extends RedException +{ + /** + * @var string + */ + private $sqlState; + + /** + * Returns an ANSI-92 compliant SQL state. + * + * @return string + */ + public function getSQLState() + { + return $this->sqlState; + } + + /** + * Returns the raw SQL STATE, possibly compliant with + * ANSI SQL error codes - but this depends on database driver. + * + * @param string $sqlState SQL state error code + * + * @return void + */ + public function setSQLState( $sqlState ) + { + $this->sqlState = $sqlState; + } + + /** + * To String prints both code and SQL state. + * + * @return string + */ + public function __toString() + { + return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n". + 'trace: ' . $this->getTraceAsString(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Cursor as Cursor; +use RedBeanPHP\Cursor\NullCursor as NullCursor; + +/** + * Abstract Repository. + * + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Repository +{ + /** + * @var array + */ + protected $stash = NULL; + + /* + * @var integer + */ + protected $nesting = 0; + + /** + * @var DBAdapter + */ + protected $writer; + + /** + * Stores a bean and its lists in one run. + * + * @param OODBBean $bean bean to process + * + * @return void + */ + protected function storeBeanWithLists( OODBBean $bean ) + { + $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups + foreach ( $bean as $property => $value ) { + $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value; + if ( $value instanceof OODBBean ) { + $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); + $bean->setMeta("sys.typeof.{$property}", $value->getMeta('type')); + } elseif ( is_array( $value ) ) { + foreach($value as &$item) { + $item = ( $item instanceof SimpleModel ) ? $item->unbox() : $item; + } + $originals = $bean->moveMeta( 'sys.shadow.' . $property, array() ); + if ( strpos( $property, 'own' ) === 0 ) { + list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); + $listName = lcfirst( substr( $property, 3 ) ); + if ($bean->moveMeta( 'sys.exclusive-'. $listName ) ) { + OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE ); + OODBBean::setMetaAll( $ownAdditions, 'sys.buildcommand.fkdependson', $bean->getMeta( 'type' ) ); + } + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); + unset( $bean->$property ); + } + } + } + $this->storeBean( $bean ); + $this->processTrashcan( $bean, $ownTrashcan ); + $this->processAdditions( $bean, $ownAdditions ); + $this->processResidue( $ownresidue ); + $this->processSharedTrashcan( $bean, $sharedTrashcan ); + $this->processSharedAdditions( $bean, $sharedAdditions ); + $this->processSharedResidue( $bean, $sharedresidue ); + } + + /** + * Process groups. Internal function. Processes different kind of groups for + * storage function. Given a list of original beans and a list of current beans, + * this function calculates which beans remain in the list (residue), which + * have been deleted (are in the trashcan) and which beans have been added + * (additions). + * + * @param array $originals originals + * @param array $current the current beans + * @param array $additions beans that have been added + * @param array $trashcan beans that have been deleted + * @param array $residue beans that have been left untouched + * + * @return array + */ + protected function processGroups( $originals, $current, $additions, $trashcan, $residue ) + { + return array( + array_merge( $additions, array_diff( $current, $originals ) ), + array_merge( $trashcan, array_diff( $originals, $current ) ), + array_merge( $residue, array_intersect( $current, $originals ) ) + ); + } + + /** + * Processes an embedded bean. + * + * @param OODBBean|SimpleModel $embeddedBean the bean or model + * + * @return integer + */ + protected function prepareEmbeddedBean( $embeddedBean ) + { + if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) { + $this->store( $embeddedBean ); + } + + return $embeddedBean->id; + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles shared addition lists; i.e. the $bean->sharedObject properties. + * + * @param OODBBean $bean the bean + * @param array $sharedAdditions list with shared additions + * + * @return void + */ + protected function processSharedAdditions( $bean, $sharedAdditions ) + { + foreach ( $sharedAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + $this->oodb->getAssociationManager()->associate( $addition, $bean ); + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles own lists; i.e. the $bean->ownObject properties. + * A residue is a bean in an own-list that stays where it is. This method + * checks if there have been any modification to this bean, in that case + * the bean is stored once again, otherwise the bean will be left untouched. + * + * @param OODBBean $bean bean tor process + * @param array $ownresidue list to process + * + * @return void + */ + protected function processResidue( $ownresidue ) + { + foreach ( $ownresidue as $residue ) { + if ( $residue->getMeta( 'tainted' ) ) { + $this->store( $residue ); + } + } + } + + /** + * Processes a list of beans from a bean. A bean may contain lists. This + * method handles own lists; i.e. the $bean->ownObject properties. + * A trash can bean is a bean in an own-list that has been removed + * (when checked with the shadow). This method + * checks if the bean is also in the dependency list. If it is the bean will be removed. + * If not, the connection between the bean and the owner bean will be broken by + * setting the ID to NULL. + * + * @param OODBBean $bean bean to process + * @param array $ownTrashcan list to process + * + * @return void + */ + protected function processTrashcan( $bean, $ownTrashcan ) + { + foreach ( $ownTrashcan as $trash ) { + + $myFieldLink = $bean->getMeta( 'type' ) . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + if ( $trash->getMeta( 'sys.garbage' ) === true ) { + $this->trash( $trash ); + } else { + $trash->$myFieldLink = NULL; + $this->store( $trash ); + } + } + } + + /** + * Unassociates the list items in the trashcan. + * + * @param OODBBean $bean bean to process + * @param array $sharedTrashcan list to process + * + * @return void + */ + protected function processSharedTrashcan( $bean, $sharedTrashcan ) + { + foreach ( $sharedTrashcan as $trash ) { + $this->oodb->getAssociationManager()->unassociate( $trash, $bean ); + } + } + + /** + * Stores all the beans in the residue group. + * + * @param OODBBean $bean bean to process + * @param array $sharedresidue list to process + * + * @return void + */ + protected function processSharedResidue( $bean, $sharedresidue ) + { + foreach ( $sharedresidue as $residue ) { + $this->store( $residue ); + } + } + + /** + * Determines whether the bean has 'loaded lists' or + * 'loaded embedded beans' that need to be processed + * by the store() method. + * + * @param OODBBean $bean bean to be examined + * + * @return boolean + */ + protected function hasListsOrObjects( OODBBean $bean ) + { + $processLists = FALSE; + foreach ( $bean as $value ) { + if ( is_array( $value ) || is_object( $value ) ) { + $processLists = TRUE; + break; + } + } + + return $processLists; + } + + /** + * Converts an embedded bean to an ID, removed the bean property and + * stores the bean in the embedded beans array. + * + * @param array $embeddedBeans destination array for embedded bean + * @param OODBBean $bean target bean to process + * @param string $property property that contains the embedded bean + * @param OODBBean $value embedded bean itself + * + * @return void + */ + protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value ) + { + $linkField = $property . '_id'; + $id = $this->prepareEmbeddedBean( $value ); + if ($bean->$linkField != $id) $bean->$linkField = $id; + $bean->setMeta( 'cast.' . $linkField, 'id' ); + $embeddedBeans[$linkField] = $value; + unset( $bean->$property ); + } + + /** + * Constructor, requires a query writer. + * Creates a new instance of the bean respository class. + * + * @param QueryWriter $writer the Query Writer to use for this repository + * + * @return void + */ + public function __construct( OODB $oodb, QueryWriter $writer ) + { + $this->writer = $writer; + $this->oodb = $oodb; + } + + /** + * Checks whether a OODBBean bean is valid. + * If the type is not valid or the ID is not valid it will + * throw an exception: Security. + * + * @param OODBBean $bean the bean that needs to be checked + * + * @return void + */ + public function check( OODBBean $bean ) + { + //Is all meta information present? + if ( !isset( $bean->id ) ) { + throw new RedException( 'Bean has incomplete Meta Information id ' ); + } + if ( !( $bean->getMeta( 'type' ) ) ) { + throw new RedException( 'Bean has incomplete Meta Information II' ); + } + //Pattern of allowed characters + $pattern = '/[^a-z0-9_]/i'; + //Does the type contain invalid characters? + if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) { + throw new RedException( 'Bean Type is invalid' ); + } + //Are the properties and values valid? + foreach ( $bean as $prop => $value ) { + if ( + is_array( $value ) + || ( is_object( $value ) ) + ) { + throw new RedException( "Invalid Bean value: property $prop" ); + } else if ( + strlen( $prop ) < 1 + || preg_match( $pattern, $prop ) + ) { + throw new RedException( "Invalid Bean property: property $prop" ); + } + } + } + + /** + * Searches the database for a bean that matches conditions $conditions and sql $addSQL + * and returns an array containing all the beans that have been found. + * + * Conditions need to take form: + * + * + * array( + * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) + * 'PROPERTY' => array( POSSIBLE VALUES... ) + * ); + * + * + * All conditions are glued together using the AND-operator, while all value lists + * are glued using IN-operators thus acting as OR-conditions. + * + * Note that you can use property names; the columns will be extracted using the + * appropriate bean formatter. + * + * @param string $type type of beans you are looking for + * @param array $conditions list of conditions + * @param string $addSQL SQL to be used in query + * @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) + * + * @return array + */ + public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) + { + //for backward compatibility, allow mismatch arguments: + if ( is_array( $sql ) ) { + if ( isset( $sql[1] ) ) { + $bindings = $sql[1]; + } + $sql = $sql[0]; + } + try { + $beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) ); + + return $beans; + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + + return array(); + } + + /** + * Finds a BeanCollection. + * + * @param string $type type of beans you are looking for + * @param string $sql SQL to be used in query + * @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) + * + * @return BeanCollection + */ + public function findCollection( $type, $sql, $bindings = array() ) + { + try { + $cursor = $this->writer->queryRecordWithCursor( $type, $sql, $bindings ); + return new BeanCollection( $type, $this, $cursor ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + return new BeanCollection( $type, $this, new NullCursor ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. We use + * explicit casts instead of functions to preserve performance + * (0.13 vs 0.28 for 10000 iterations on Core i3). + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public function store( $bean ) + { + $processLists = $this->hasListsOrObjects( $bean ); + if ( !$processLists && !$bean->getMeta( 'tainted' ) ) { + return $bean->getID(); //bail out! + } + $this->oodb->signal( 'update', $bean ); + $processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model! + if ( $processLists ) { + $this->storeBeanWithLists( $bean ); + } else { + $this->storeBean( $bean ); + } + $this->oodb->signal( 'after_update', $bean ); + + return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id; + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public function batch( $type, $ids ) + { + if ( !$ids ) { + return array(); + } + $collection = array(); + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) ); + } catch ( SQLException $e ) { + $this->handleException( $e ); + $rows = FALSE; + } + $this->stash[$this->nesting] = array(); + if ( !$rows ) { + return array(); + } + foreach ( $rows as $row ) { + $this->stash[$this->nesting][$row['id']] = $row; + } + foreach ( $ids as $id ) { + $collection[$id] = $this->load( $type, $id ); + } + $this->stash[$this->nesting] = NULL; + + return $collection; + } + + /** + * This is a convenience method; it converts database rows + * (arrays) into beans. Given a type and a set of rows this method + * will return an array of beans of the specified type loaded with + * the data fields provided by the result set from the database. + * + * New in 4.3.2: meta mask. The meta mask is a special mask to send + * data from raw result rows to the meta store of the bean. This is + * useful for bundling additional information with custom queries. + * Values of every column whos name starts with $mask will be + * transferred to the meta section of the bean under key 'data.bundle'. + * + * @param string $type type of beans you would like to have + * @param array $rows rows from the database result + * @param string $mask meta mask to apply (optional) + * + * @return array + */ + public function convertToBeans( $type, $rows, $mask = NULL ) + { + $masklen = 0; + if ( $mask !== NULL ) $masklen = mb_strlen( $mask ); + + $collection = array(); + $this->stash[$this->nesting] = array(); + foreach ( $rows as $row ) { + $meta = array(); + if ( !is_null( $mask ) ) { + foreach( $row as $key => $value ) { + if ( strpos( $key, $mask ) === 0 ) { + unset( $row[$key] ); + $meta[$key] = $value; + } + } + } + + $id = $row['id']; + $this->stash[$this->nesting][$id] = $row; + $collection[$id] = $this->load( $type, $id ); + + if ( $mask !== NULL ) { + $collection[$id]->setMeta( 'data.bundle', $meta ); + } + } + $this->stash[$this->nesting] = NULL; + + return $collection; + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public function count( $type, $addSQL = '', $bindings = array() ) + { + $type = AQueryWriter::camelsSnake( $type ); + if ( count( explode( '_', $type ) ) > 2 ) { + throw new RedException( 'Invalid type for count.' ); + } + + try { + return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings ); + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) { + throw $exception; + } + } + + return 0; + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + */ + public function trash( $bean ) + { + $this->oodb->signal( 'delete', $bean ); + foreach ( $bean as $property => $value ) { + if ( $value instanceof OODBBean ) { + unset( $bean->$property ); + } + if ( is_array( $value ) ) { + if ( strpos( $property, 'own' ) === 0 ) { + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + unset( $bean->$property ); + } + } + } + try { + $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + $bean->id = 0; + $this->oodb->signal( 'after_delete', $bean ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->writer->tableExists( $table ); + } + + /** + * Trash all beans of a given type. Wipes an entire type of bean. + * + * @param string $type type of bean you wish to delete all instances of + * + * @return boolean + */ + public function wipe( $type ) + { + try { + $this->writer->wipe( $type ); + + return TRUE; + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { + throw $exception; + } + + return FALSE; + } + } +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Repository as Repository; + +/** + * Fluid Repository. + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository/Fluid.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Fluid extends Repository +{ + /** + * Figures out the desired type given the cast string ID. + * Given a cast ID, this method will return the associated + * type (INT(10) or VARCHAR for instance). The returned type + * can be processed by the Query Writer to build the specified + * column for you in the database. The Cast ID is actually just + * a superset of the QueryWriter types. In addition to default + * Query Writer column types you can pass the following 'cast types': + * 'id' and 'string'. These will map to Query Writer specific + * column types (probably INT and VARCHAR). + * + * @param string $cast cast identifier + * + * @return integer + */ + private function getTypeFromCast( $cast ) + { + if ( $cast == 'string' ) { + $typeno = $this->writer->scanType( 'STRING' ); + } elseif ( $cast == 'id' ) { + $typeno = $this->writer->getTypeForID(); + } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) { + $typeno = $this->writer->sqltype_typeno[$cast]; + } else { + throw new RedException( 'Invalid Cast' ); + } + + return $typeno; + } + + /** + * Orders the Query Writer to create a table if it does not exist already and + * adds a note in the build report about the creation. + * + * @param OODBBean $bean bean to update report of + * @param string $table table to check and create if not exists + * + * @return void + */ + private function createTableIfNotExists( OODBBean $bean, $table ) + { + //Does table exist? If not, create + if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) { + $this->writer->createTable( $table ); + $bean->setMeta( 'buildreport.flags.created', TRUE ); + } + } + + /** + * Modifies the table to fit the bean data. + * Given a property and a value and the bean, this method will + * adjust the table structure to fit the requirements of the property and value. + * This may include adding a new column or widening an existing column to hold a larger + * or different kind of value. This method employs the writer to adjust the table + * structure in the database. Schema updates are recorded in meta properties of the bean. + * + * This method will also apply indexes, unique constraints and foreign keys. + * + * @param OODBBean $bean bean to get cast data from and store meta in + * @param string $property property to store + * @param mixed $value value to store + * + * @return void + */ + private function modifySchema( OODBBean $bean, $property, $value ) + { + $doFKStuff = FALSE; + $table = $bean->getMeta( 'type' ); + $columns = $this->writer->getColumns( $table ); + $columnNoQ = $this->writer->esc( $property, TRUE ); + if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) { + if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types + $cast = $bean->getMeta( "cast.$property" ); + $typeno = $this->getTypeFromCast( $cast ); + } else { + $cast = FALSE; + $typeno = $this->writer->scanType( $value, TRUE ); + } + if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ? + if ( !$cast ) { //rescan without taking into account special types >80 + $typeno = $this->writer->scanType( $value, FALSE ); + } + $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] ); + if ( $typeno > $sqlt ) { //no, we have to widen the database column type + $this->writer->widenColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.widen', TRUE ); + $doFKStuff = TRUE; + } + } else { + $this->writer->addColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.addcolumn', TRUE ); + $doFKStuff = TRUE; + } + if ($doFKStuff) { + if (strrpos($columnNoQ, '_id')===(strlen($columnNoQ)-3)) { + $destinationColumnNoQ = substr($columnNoQ, 0, strlen($columnNoQ)-3); + $indexName = "index_foreignkey_{$table}_{$destinationColumnNoQ}"; + $this->writer->addIndex($table, $indexName, $columnNoQ); + $typeof = $bean->getMeta("sys.typeof.{$destinationColumnNoQ}", $destinationColumnNoQ); + $isLink = $bean->getMeta( 'sys.buildcommand.unique', FALSE ); + //Make FK CASCADING if part of exclusive list (dependson=typeof) or if link bean + $isDep = ( $bean->moveMeta( 'sys.buildcommand.fkdependson' ) === $typeof || is_array( $isLink ) ); + $result = $this->writer->addFK( $table, $typeof, $columnNoQ, 'id', $isDep ); + //If this is a link bean and all unique columns have been added already, then apply unique constraint + if ( is_array( $isLink ) && !count( array_diff( $isLink, array_keys( $this->writer->getColumns( $table ) ) ) ) ) { + $this->writer->addUniqueConstraint( $table, $bean->moveMeta('sys.buildcommand.unique') ); + $bean->setMeta("sys.typeof.{$destinationColumnNoQ}", NULL); + } + } + } + } + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * Note that this method raises a custom exception if the bean + * is not an instance of OODBBean. Therefore it does not use + * a type hint. This allows the user to take action in case + * invalid objects are passed in the list. + * + * @param OODBBean $bean bean to process + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + */ + protected function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + + if ($alias) { + $addition->setMeta( "sys.typeof.{$alias}", $beanType ); + } else { + $addition->setMeta( "sys.typeof.{$beanType}", $beanType ); + } + + $this->store( $addition ); + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + $this->check( $bean ); + $table = $bean->getMeta( 'type' ); + $this->createTableIfNotExists( $bean, $table ); + + $updateValues = array(); + foreach ( $bean as $property => $value ) { + if ( $property !== 'id' ) { + $this->modifySchema( $bean, $property, $value ); + } + if ( $property !== 'id' ) { + $updateValues[] = array( 'property' => $property, 'value' => $value ); + } + } + + $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Handles exceptions. Suppresses exceptions caused by missing structures. + * + * @param Exception $exception exception + * + * @return void + */ + protected function handleException( \Exception $exception ) + { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) + ) { + throw $exception; + } + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->check( $bean ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQLException $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + $rows = 0; + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Repository as Repository; + +/** + * Frozen Repository. + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * @file RedBeanPHP/Repository/Frozen.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Frozen extends Repository +{ + /** + * Handles exceptions. + * In fluid mode, this suppresses exceptions caused by missing structures. + * However the implementation in frozen mode is rather the opposite, it + * will just re-throw every exception. + * + * @param \Exception $exception exception to handle + * + * @return void + */ + protected function handleException( \Exception $exception ) + { + throw $exception; + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + + list( $properties, $table ) = $bean->getPropertiesAndType(); + $id = $properties['id']; + unset($properties['id']); + $updateValues = array(); + $k1 = 'property'; + $k2 = 'value'; + foreach( $properties as $key => $value ) { + $updateValues[] = array( $k1 => $key, $k2 => $value ); + } + $bean->id = $this->writer->updateRecord( $table, $updateValues, $id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * Note that this method raises a custom exception if the bean + * is not an instance of OODBBean. Therefore it does not use + * a type hint. This allows the user to take action in case + * invalid objects are passed in the list. + * + * @param OODBBean $bean bean to process + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + * @throws RedException + */ + protected function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + $cachedIndex = array(); + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + $this->store( $addition ); + + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param int $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + /** @var \RedBeanPHP\OODBBean $bean */ + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + * @throws SQLException + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQLException $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + throw $exception; //only throw if frozen + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Repository as Repository; +use RedBeanPHP\Repository\Fluid as FluidRepo; +use RedBeanPHP\Repository\Frozen as FrozenRepo; + +/** + * RedBean Object Oriented DataBase. + * + * The RedBean OODB Class is the main class of RedBeanPHP. + * It takes OODBBean objects and stores them to and loads them from the + * database as well as providing other CRUD functions. This class acts as a + * object database. + * + * @file RedBeanPHP/OODB.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODB extends Observable +{ + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var array + */ + protected $chillList = array(); + + /** + * @var array + */ + protected $stash = NULL; + + /* + * @var integer + */ + protected $nesting = 0; + + /** + * @var DBAdapter + */ + protected $writer; + + /** + * @var boolean + */ + protected $isFrozen = FALSE; + + /** + * @var FacadeBeanHelper + */ + protected $beanhelper = NULL; + + /** + * @var AssociationManager + */ + protected $assocManager = NULL; + + /** + * @var Repository + */ + protected $repository = NULL; + + /** + * @var FrozenRepo + */ + protected $frozenRepository = NULL; + + /** + * @var FluidRepo + */ + protected $fluidRepository = NULL; + + /** + * @var boolean + */ + protected static $autoClearHistoryAfterStore = FALSE; + + /** + * If set to TRUE, this method will call clearHistory every time + * the bean gets stored. + * + * @param boolean $autoClear auto clear option + * + * @return void + */ + public static function autoClearHistoryAfterStore( $autoClear = TRUE ) + { + self::$autoClearHistoryAfterStore = (boolean) $autoClear; + } + + /** + * Unboxes a bean from a FUSE model if needed and checks whether the bean is + * an instance of OODBBean. + * + * @param OODBBean $bean bean you wish to unbox + * + * @return OODBBean + */ + protected function unboxIfNeeded( $bean ) + { + if ( $bean instanceof SimpleModel ) { + $bean = $bean->unbox(); + } + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); + } + + return $bean; + } + + /** + * Constructor, requires a query writer. + * + * @param QueryWriter $writer writer + * @param array|boolean $frozen mode of operation: TRUE (frozen), FALSE (default, fluid) or ARRAY (chilled) + */ + public function __construct( QueryWriter $writer, $frozen = FALSE ) + { + if ( $writer instanceof QueryWriter ) { + $this->writer = $writer; + } + + $this->freeze( $frozen ); + } + + /** + * Toggles fluid or frozen mode. In fluid mode the database + * structure is adjusted to accomodate your objects. In frozen mode + * this is not the case. + * + * You can also pass an array containing a selection of frozen types. + * Let's call this chilly mode, it's just like fluid mode except that + * certain types (i.e. tables) aren't touched. + * + * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode + * + * @return void + */ + public function freeze( $toggle ) + { + if ( is_array( $toggle ) ) { + $this->chillList = $toggle; + $this->isFrozen = FALSE; + } else { + $this->isFrozen = (boolean) $toggle; + } + + if ( $this->isFrozen ) { + if ( !$this->frozenRepository ) { + $this->frozenRepository = new FrozenRepo( $this, $this->writer ); + } + + $this->repository = $this->frozenRepository; + + } else { + if ( !$this->fluidRepository ) { + $this->fluidRepository = new FluidRepo( $this, $this->writer ); + } + + $this->repository = $this->fluidRepository; + } + + if ( count( self::$sqlFilters ) ) { + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); + } + + } + + /** + * Returns the current mode of operation of RedBean. + * In fluid mode the database + * structure is adjusted to accomodate your objects. + * In frozen mode + * this is not the case. + * + * @return boolean + */ + public function isFrozen() + { + return (bool) $this->isFrozen; + } + + /** + * Determines whether a type is in the chill list. + * If a type is 'chilled' it's frozen, so its schema cannot be + * changed anymore. However other bean types may still be modified. + * This method is a convenience method for other objects to check if + * the schema of a certain type is locked for modification. + * + * @param string $type the type you wish to check + * + * @return boolean + */ + public function isChilled( $type ) + { + return (boolean) ( in_array( $type, $this->chillList ) ); + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + if ( $number < 1 ) { + if ( $alwaysReturnArray ) return array(); + return NULL; + } + + return $this->repository->dispense( $type, $number, $alwaysReturnArray ); + } + + /** + * Sets bean helper to be given to beans. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @param BeanHelper $beanhelper helper + * + * @return void + */ + public function setBeanHelper( BeanHelper $beanhelper ) + { + $this->beanhelper = $beanhelper; + } + + /** + * Returns the current bean helper. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @return BeanHelper + */ + public function getBeanHelper() + { + return $this->beanhelper; + } + + /** + * Checks whether a OODBBean bean is valid. + * If the type is not valid or the ID is not valid it will + * throw an exception: Security. + * + * @param OODBBean $bean the bean that needs to be checked + * + * @return void + */ + public function check( OODBBean $bean ) + { + $this->repository->check( $bean ); + } + + /** + * Searches the database for a bean that matches conditions $conditions and sql $addSQL + * and returns an array containing all the beans that have been found. + * + * Conditions need to take form: + * + * + * array( + * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) + * 'PROPERTY' => array( POSSIBLE VALUES... ) + * ); + * + * + * All conditions are glued together using the AND-operator, while all value lists + * are glued using IN-operators thus acting as OR-conditions. + * + * Note that you can use property names; the columns will be extracted using the + * appropriate bean formatter. + * + * @param string $type type of beans you are looking for + * @param array $conditions list of conditions + * @param string $addSQL SQL to be used in query + * @param array $bindings a list of values to bind to query parameters + * + * @return array + */ + public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) + { + return $this->repository->find( $type, $conditions, $sql, $bindings ); + } + + /** + * Same as find() but returns a BeanCollection. + * + * @param string $type type of beans you are looking for + * @param string $addSQL SQL to be used in query + * @param array $bindings a list of values to bind to query parameters + * + * @return array + */ + public function findCollection( $type, $sql = NULL, $bindings = array() ) + { + return $this->repository->findCollection( $type, $sql, $bindings ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->repository->tableExists( $table ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. We use + * explicit casts instead of functions to preserve performance + * (0.13 vs 0.28 for 10000 iterations on Core i3). + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public function store( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + $id = $this->repository->store( $bean ); + if ( self::$autoClearHistoryAfterStore ) { + $bean->clearHistory(); + } + return $id; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public function load( $type, $id ) + { + return $this->repository->load( $type, $id ); + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + */ + public function trash( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + return $this->repository->trash( $bean ); + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public function batch( $type, $ids ) + { + return $this->repository->batch( $type, $ids ); + } + + /** + * This is a convenience method; it converts database rows + * (arrays) into beans. Given a type and a set of rows this method + * will return an array of beans of the specified type loaded with + * the data fields provided by the result set from the database. + * + * @param string $type type of beans you would like to have + * @param array $rows rows from the database result + * @param string $mask mask to apply for meta data + * + * @return array + */ + public function convertToBeans( $type, $rows, $mask = NULL ) + { + return $this->repository->convertToBeans( $type, $rows, $mask ); + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public function count( $type, $addSQL = '', $bindings = array() ) + { + return $this->repository->count( $type, $addSQL, $bindings ); + } + + /** + * Trash all beans of a given type. Wipes an entire type of bean. + * + * @param string $type type of bean you wish to delete all instances of + * + * @return boolean + */ + public function wipe( $type ) + { + return $this->repository->wipe( $type ); + } + + /** + * Returns an Association Manager for use with OODB. + * A simple getter function to obtain a reference to the association manager used for + * storage and more. + * + * @return AssociationManager + */ + public function getAssociationManager() + { + if ( !isset( $this->assocManager ) ) { + throw new RedException( 'No association manager available.' ); + } + + return $this->assocManager; + } + + /** + * Sets the association manager instance to be used by this OODB. + * A simple setter function to set the association manager to be used for storage and + * more. + * + * @param AssociationManager $assoc sets the association manager to be used + * + * @return void + */ + public function setAssociationManager( AssociationManager $assocManager ) + { + $this->assocManager = $assocManager; + } + + /** + * Returns the currently used repository instance. + * For testing purposes only. + * + * @return Repository + */ + public function getCurrentRepository() + { + return $this->repository; + } + + /** + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. + * + * @param string $mode mode to set function for, i.e. read or write + * @param string $field field (table.column) to bind SQL function to + * @param string $function SQL function to bind to field + * + * @return void + */ + public function bindFunc( $mode, $field, $function ) + { + list( $type, $property ) = explode( '.', $field ); + $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ; + + if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array(); + if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array(); + + if ( is_null( $function ) ) { + unset( self::$sqlFilters[$mode][$type][$property] ); + } else { + if ($mode === QueryWriter::C_SQLFILTER_WRITE) { + self::$sqlFilters[$mode][$type][$property] = $function.'(?)'; + } else { + self::$sqlFilters[$mode][$type][$property] = $function."($field)"; + } + } + + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * ToolBox. + * + * The toolbox is an integral part of RedBeanPHP providing the basic + * architectural building blocks to manager objects, helpers and additional tools + * like plugins. A toolbox contains the three core components of RedBeanPHP: + * the adapter, the query writer and the core functionality of RedBeanPHP in + * OODB. + * + * @file RedBeanPHP/ToolBox.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class ToolBox +{ + /** + * @var OODB + */ + protected $oodb; + + /** + * @var QueryWriter + */ + protected $writer; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * Constructor. + * The toolbox is an integral part of RedBeanPHP providing the basic + * architectural building blocks to manager objects, helpers and additional tools + * like plugins. A toolbox contains the three core components of RedBeanPHP: + * the adapter, the query writer and the core functionality of RedBeanPHP in + * OODB. + * + * @param OODB $oodb Object Database, OODB + * @param DBAdapter $adapter Database Adapter + * @param QueryWriter $writer Query Writer + */ + public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer ) + { + $this->oodb = $oodb; + $this->adapter = $adapter; + $this->writer = $writer; + return $this; + } + + /** + * Returns the query writer in this toolbox. + * The Query Writer is responsible for building the queries for a + * specific database and executing them through the adapter. + * + * @return QueryWriter + */ + public function getWriter() + { + return $this->writer; + } + + /** + * Returns the OODB instance in this toolbox. + * OODB is responsible for creating, storing, retrieving and deleting + * single beans. Other components rely + * on OODB for their basic functionality. + * + * @return OODB + */ + public function getRedBean() + { + return $this->oodb; + } + + /** + * Returns the database adapter in this toolbox. + * The adapter is responsible for executing the query and binding the values. + * The adapter also takes care of transaction handling. + * + * @return DBAdapter + */ + public function getDatabaseAdapter() + { + return $this->adapter; + } +} +} + +namespace RedBeanPHP { + + +/** + * RedBeanPHP Finder. + * Service class to find beans. For the most part this class + * offers user friendly utility methods for interacting with the + * OODB::find() method, which is rather complex. This class can be + * used to find beans using plain old SQL queries. + * + * @file RedBeanPHP/Finder.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Finder +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var OODB + */ + protected $redbean; + + /** + * Constructor. + * The Finder requires a toolbox. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + } + + /** + * Finds a bean using a type and a where clause (SQL). + * As with most Query tools in RedBean you can provide values to + * be inserted in the SQL statement by populating the value + * array parameter; you can either use the question mark notation + * or the slot-notation (:keyname). + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function find( $type, $sql = NULL, $bindings = array() ) + { + if ( !is_array( $bindings ) ) { + throw new RedException( + 'Expected array, ' . gettype( $bindings ) . ' given.' + ); + } + + return $this->redbean->find( $type, array(), $sql, $bindings ); + } + + /** + * Like find() but also exports the beans as an array. + * This method will perform a find-operation. For every bean + * in the result collection this method will call the export() method. + * This method returns an array containing the array representations + * of every bean in the result set. + * + * @see Finder::find + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findAndExport( $type, $sql = NULL, $bindings = array() ) + { + $arr = array(); + foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) { + $arr[] = $item->export(); + } + + return $arr; + } + + /** + * Like find() but returns just one bean instead of an array of beans. + * This method will return only the first bean of the array. + * If no beans are found, this method will return NULL. + * + * @see Finder::find + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findOne( $type, $sql = NULL, $bindings = array() ) + { + $sql = $this->toolbox->getWriter()->glueLimitOne( $sql ); + + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return reset( $items ); + } + + /** + * Like find() but returns the last bean of the result array. + * Opposite of Finder::findLast(). + * If no beans are found, this method will return NULL. + * + * @see Finder::find + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findLast( $type, $sql = NULL, $bindings = array() ) + { + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return end( $items ); + } + + /** + * Tries to find beans of a certain type, + * if no beans are found, it dispenses a bean of that type. + * Note that this function always returns an array. + * + * @see Finder::find + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findOrDispense( $type, $sql = NULL, $bindings = array() ) + { + $foundBeans = $this->find( $type, $sql, $bindings ); + + if ( empty( $foundBeans ) ) { + return array( $this->redbean->dispense( $type ) ); + } else { + return $foundBeans; + } + } + + /** + * Finds a BeanCollection using the repository. + * A bean collection can be used to retrieve one bean at a time using + * cursors - this is useful for processing large datasets. A bean collection + * will not load all beans into memory all at once, just one at a time. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return BeanCollection + */ + public function findCollection( $type, $sql, $bindings = array() ) + { + return $this->redbean->findCollection( $type, $sql, $bindings ); + } + + /** + * Finds or creates a bean. + * Tries to find a bean with certain properties specified in the second + * parameter ($like). If the bean is found, it will be returned. + * If multiple beans are found, only the first will be returned. + * If no beans match the criteria, a new bean will be dispensed, + * the criteria will be imported as properties and this new bean + * will be stored and returned. + * + * Format of criteria set: property => value + * The criteria set also supports OR-conditions: property => array( value1, orValue2 ) + * + * @param string $type type of bean to search for + * @param array $like criteria set describing bean to search for + * + * @return OODBBean + */ + public function findOrCreate( $type, $like = array() ) + { + $beans = $this->findLike( $type, $like ); + if ( count( $beans ) ) { + $bean = reset( $beans ); + return $bean; + } + + $bean = $this->redbean->dispense( $type ); + $bean->import( $like ); + $this->redbean->store( $bean ); + return $bean; + } + + /** + * Finds beans by its type and a certain criteria set. + * + * Format of criteria set: property => value + * The criteria set also supports OR-conditions: property => array( value1, orValue2 ) + * + * If the additional SQL is a condition, this condition will be glued to the rest + * of the query using an AND operator. Note that this is as far as this method + * can go, there is no way to glue additional SQL using an OR-condition. + * This method provides access to an underlying mechanism in the RedBeanPHP architecture + * to find beans using criteria sets. However, please do not use this method + * for complex queries, use plain SQL instead ( the regular find method ) as it is + * more suitable for the job. This method is + * meant for basic search-by-example operations. + * + * @param string $type type of bean to search for + * @param array $conditions criteria set describing the bean to search for + * @param string $sql additional SQL (for sorting) + * + * @return array + */ + public function findLike( $type, $conditions = array(), $sql = '' ) + { + if ( count( $conditions ) > 0 ) { + foreach( $conditions as $key => $condition ) { + if ( !count( $condition ) ) unset( $conditions[$key] ); + } + } + + return $this->redbean->find( $type, $conditions, $sql ); + } + + /** + * Returns a hashmap with bean arrays keyed by type using an SQL + * query as its resource. Given an SQL query like 'SELECT movie.*, review.* FROM movie... JOIN review' + * this method will return movie and review beans. + * + * Example: + * + * + * $stuff = $finder->findMulti('movie,review', ' + * SELECT movie.*, review.* FROM movie + * LEFT JOIN review ON review.movie_id = movie.id'); + * + * + * After this operation, $stuff will contain an entry 'movie' containing all + * movies and an entry named 'review' containing all reviews (all beans). + * You can also pass bindings. + * + * If you want to re-map your beans, so you can use $movie->ownReviewList without + * having RedBeanPHP executing an SQL query you can use the fourth parameter to + * define a selection of remapping closures. + * + * The remapping argument (optional) should contain an array of arrays. + * Each array in the remapping array should contain the following entries: + * + * + * array( + * 'a' => TYPE A + * 'b' => TYPE B + * 'matcher' => MATCHING FUNCTION ACCEPTING A, B and ALL BEANS + * 'do' => OPERATION FUNCTION ACCEPTING A, B, ALL BEANS, ALL REMAPPINGS + * ) + * + * + * Using this mechanism you can build your own 'preloader' with tiny function + * snippets (and those can be re-used and shared online of course). + * + * Example: + * + * + * array( + * 'a' => 'movie' //define A as movie + * 'b' => 'review' //define B as review + * 'matcher' => function( $a, $b ) { + * return ( $b->movie_id == $a->id ); //Perform action if review.movie_id equals movie.id + * } + * 'do' => function( $a, $b ) { + * $a->noLoad()->ownReviewList[] = $b; //Add the review to the movie + * $a->clearHistory(); //optional, act 'as if these beans have been loaded through ownReviewList'. + * } + * ) + * + * + * The Query Template parameter is optional as well but can be used to + * set a different SQL template (sprintf-style) for processing the original query. + * + * @note the SQL query provided IS NOT THE ONE used internally by this function, + * this function will pre-process the query to get all the data required to find the beans. + * + * @note if you use the 'book.*' notation make SURE you're + * selector starts with a SPACE. ' book.*' NOT ',book.*'. This is because + * it's actually an SQL-like template SLOT, not real SQL. + * + * @note instead of an SQL query you can pass a result array as well. + * + * @param string|array $types a list of types (either array or comma separated string) + * @param string|array $sqlOrArr an SQL query or an array of prefetched records + * @param array $bindings optional, bindings for SQL query + * @param array $remappings optional, an array of remapping arrays + * @param string $queryTemplate optional, query template + * + * @return array + */ + public function findMulti( $types, $sql, $bindings = array(), $remappings = array(), $queryTemplate = ' %s.%s AS %s__%s' ) + { + if ( !is_array( $types ) ) $types = explode( ',', $types ); + if ( !is_array( $sql ) ) { + $writer = $this->toolbox->getWriter(); + $adapter = $this->toolbox->getDatabaseAdapter(); + + //Repair the query, replace book.* with book.id AS book_id etc.. + foreach( $types as $type ) { + $pattern = " {$type}.*"; + if ( strpos( $sql, $pattern ) !== FALSE ) { + $newSelectorArray = array(); + $columns = $writer->getColumns( $type ); + foreach( $columns as $column => $definition ) { + $newSelectorArray[] = sprintf( $queryTemplate, $type, $column, $type, $column ); + } + $newSelector = implode( ',', $newSelectorArray ); + $sql = str_replace( $pattern, $newSelector, $sql ); + } + } + + $rows = $adapter->get( $sql, $bindings ); + } else { + $rows = $sql; + } + + //Gather the bean data from the query results using the prefix + $wannaBeans = array(); + foreach( $types as $type ) { + $wannaBeans[$type] = array(); + $prefix = "{$type}__"; + foreach( $rows as $rowkey=>$row ) { + $wannaBean = array(); + foreach( $row as $cell => $value ) { + if ( strpos( $cell, $prefix ) === 0 ) { + $property = substr( $cell, strlen( $prefix ) ); + unset( $rows[$rowkey][$cell] ); + $wannaBean[$property] = $value; + } + } + if ( !isset( $wannaBean['id'] ) ) continue; + if ( is_null( $wannaBean['id'] ) ) continue; + $wannaBeans[$type][$wannaBean['id']] = $wannaBean; + } + } + + //Turn the rows into beans + $beans = array(); + foreach( $wannaBeans as $type => $wannabees ) { + $beans[$type] = $this->redbean->convertToBeans( $type, $wannabees ); + } + + //Apply additional re-mappings + foreach($remappings as $remapping) { + $a = $remapping['a']; + $b = $remapping['b']; + $matcher = $remapping['matcher']; + $do = $remapping['do']; + foreach( $beans[$a] as $bean ) { + foreach( $beans[$b] as $putBean ) { + if ( $matcher( $bean, $putBean, $beans ) ) $do( $bean, $putBean, $beans, $remapping ); + } + } + } + return $beans; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\SQL as SQLException; + +/** + * Association Manager. + * Manages simple bean associations. + * + * @file RedBeanPHP/AssociationManager.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class AssociationManager extends Observable +{ + /** + * @var OODB + */ + protected $oodb; + + /** + * @var DBAdapter + */ + protected $adapter; + + /** + * @var QueryWriter + */ + protected $writer; + + /** + * Handles exceptions. Suppresses exceptions caused by missing structures. + * + * @param \Exception $exception exception to handle + * + * @return void + */ + private function handleException( \Exception $exception ) + { + if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) + ) + ) { + throw $exception; + } + } + + /** + * Internal method. + * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and + * $bindings bindings. If $getLinks is TRUE, link rows are returned instead. + * + * @param OODBBean $bean reference bean instance + * @param string $type target bean type + * @param string $sql additional SQL snippet + * @param array $bindings bindings for query + * + * @return array + */ + private function relatedRows( $bean, $type, $sql = '', $bindings = array() ) + { + $ids = array( $bean->id ); + $sourceType = $bean->getMeta( 'type' ); + try { + return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + return array(); + } + } + + /** + * Associates a pair of beans. This method associates two beans, no matter + * what types. Accepts a base bean that contains data for the linking record. + * This method is used by associate. This method also accepts a base bean to be used + * as the template for the link record in the database. + * + * @param OODBBean $bean1 first bean + * @param OODBBean $bean2 second bean + * @param OODBBean $bean base bean (association record) + * + * @return mixed + */ + protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + $property1 = $bean1->getMeta( 'type' ) . '_id'; + $property2 = $bean2->getMeta( 'type' ) . '_id'; + + if ( $property1 == $property2 ) { + $property2 = $bean2->getMeta( 'type' ) . '2_id'; + } + + $this->oodb->store( $bean1 ); + $this->oodb->store( $bean2 ); + + $bean->setMeta( "cast.$property1", "id" ); + $bean->setMeta( "cast.$property2", "id" ); + $bean->setMeta( 'sys.buildcommand.unique', array( $property1, $property2 ) ); + + $bean->$property1 = $bean1->id; + $bean->$property2 = $bean2->id; + + $results = array(); + + try { + $id = $this->oodb->store( $bean ); + $results[] = $id; + } catch ( SQLException $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) ) + ) { + throw $exception; + } + } + + return $results; + } + + /** + * Constructor + * + * @param ToolBox $tools toolbox + */ + public function __construct( ToolBox $tools ) + { + $this->oodb = $tools->getRedBean(); + $this->adapter = $tools->getDatabaseAdapter(); + $this->writer = $tools->getWriter(); + $this->toolbox = $tools; + } + + /** + * Creates a table name based on a types array. + * Manages the get the correct name for the linking table for the + * types provided. + * + * @param array $types 2 types as strings + * + * @return string + */ + public function getTable( $types ) + { + return $this->writer->getAssocTable( $types ); + } + + /** + * Associates two beans in a many-to-many relation. + * This method will associate two beans and store the connection between the + * two in a link table. Instead of two single beans this method also accepts + * two sets of beans. Returns the ID or the IDs of the linking beans. + * + * @param OODBBean|array $beans1 one or more beans to form the association + * @param OODBBean|array $beans2 one or more beans to form the association + * + * @return array + */ + public function associate( $beans1, $beans2 ) + { + if ( !is_array( $beans1 ) ) { + $beans1 = array( $beans1 ); + } + + if ( !is_array( $beans2 ) ) { + $beans2 = array( $beans2 ); + } + + $results = array(); + foreach ( $beans1 as $bean1 ) { + foreach ( $beans2 as $bean2 ) { + $table = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) ); + $bean = $this->oodb->dispense( $table ); + $results[] = $this->associateBeans( $bean1, $bean2, $bean ); + } + } + + return ( count( $results ) > 1 ) ? $results : reset( $results ); + } + + /** + * Counts the number of related beans in an N-M relation. + * This method returns the number of beans of type $type associated + * with reference bean(s) $bean. The query can be tuned using an + * SQL snippet for additional filtering. + * + * @param OODBBean|array $bean a bean object or an array of beans + * @param string $type type of bean you're interested in + * @param string $sql SQL snippet (optional) + * @param array $bindings bindings for your SQL string + * + * @return integer + */ + public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() ) + { + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( + 'Expected array or OODBBean but got:' . gettype( $bean ) + ); + } + + if ( !$bean->id ) { + return 0; + } + + $beanType = $bean->getMeta( 'type' ); + + try { + return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + + return 0; + } + } + + /** + * Breaks the association between two beans. This method unassociates two beans. If the + * method succeeds the beans will no longer form an association. In the database + * this means that the association record will be removed. This method uses the + * OODB trash() method to remove the association links, thus giving FUSE models the + * opportunity to hook-in additional business logic. If the $fast parameter is + * set to boolean TRUE this method will remove the beans without their consent, + * bypassing FUSE. This can be used to improve performance. + * + * @param OODBBean $bean1 first bean in target association + * @param OODBBean $bean2 second bean in target association + * @param boolean $fast if TRUE, removes the entries by query without FUSE + * + * @return void + */ + public function unassociate( $beans1, $beans2, $fast = NULL ) + { + $beans1 = ( !is_array( $beans1 ) ) ? array( $beans1 ) : $beans1; + $beans2 = ( !is_array( $beans2 ) ) ? array( $beans2 ) : $beans2; + + foreach ( $beans1 as $bean1 ) { + foreach ( $beans2 as $bean2 ) { + try { + $this->oodb->store( $bean1 ); + $this->oodb->store( $bean2 ); + + $type1 = $bean1->getMeta( 'type' ); + $type2 = $bean2->getMeta( 'type' ); + + $row = $this->writer->queryRecordLink( $type1, $type2, $bean1->id, $bean2->id ); + $linkType = $this->getTable( array( $type1, $type2 ) ); + + if ( $fast ) { + $this->writer->deleteRecord( $linkType, array( 'id' => $row['id'] ) ); + + return; + } + + $beans = $this->oodb->convertToBeans( $linkType, array( $row ) ); + + if ( count( $beans ) > 0 ) { + $bean = reset( $beans ); + $this->oodb->trash( $bean ); + } + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + } + } + } + + /** + * Removes all relations for a bean. This method breaks every connection between + * a certain bean $bean and every other bean of type $type. Warning: this method + * is really fast because it uses a direct SQL query however it does not inform the + * models about this. If you want to notify FUSE models about deletion use a foreach-loop + * with unassociate() instead. (that might be slower though) + * + * @param OODBBean $bean reference bean + * @param string $type type of beans that need to be unassociated + * + * @return void + */ + public function clearRelations( OODBBean $bean, $type ) + { + $this->oodb->store( $bean ); + try { + $this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id ); + } catch ( SQLException $exception ) { + $this->handleException( $exception ); + } + } + + /** + * Returns all the beans associated with $bean. + * This method will return an array containing all the beans that have + * been associated once with the associate() function and are still + * associated with the bean specified. The type parameter indicates the + * type of beans you are looking for. You can also pass some extra SQL and + * values for that SQL to filter your results after fetching the + * related beans. + * + * Don't try to make use of subqueries, a subquery using IN() seems to + * be slower than two queries! + * + * Since 3.2, you can now also pass an array of beans instead just one + * bean as the first parameter. + * + * @param OODBBean|array $bean the bean you have + * @param string $type the type of beans you want + * @param string $sql SQL snippet for extra filtering + * @param array $bindings values to be inserted in SQL slots + * + * @return array + */ + public function related( $bean, $type, $sql = '', $bindings = array() ) + { + $sql = $this->writer->glueSQLCondition( $sql ); + $rows = $this->relatedRows( $bean, $type, $sql, $bindings ); + $links = array(); + + foreach ( $rows as $key => $row ) { + if ( !isset( $links[$row['id']] ) ) $links[$row['id']] = array(); + $links[$row['id']][] = $row['linked_by']; + unset( $rows[$key]['linked_by'] ); + } + + $beans = $this->oodb->convertToBeans( $type, $rows ); + foreach ( $beans as $bean ) $bean->setMeta( 'sys.belongs-to', $links[$bean->id] ); + + return $beans; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Bean Helper Interface. + * + * Interface for Bean Helper. + * A little bolt that glues the whole machinery together. + * The Bean Helper is passed to the OODB RedBeanPHP Object to + * faciliatte the creation of beans and providing them with + * a toolbox. The Helper also facilitates the FUSE feature, + * determining how beans relate to their models. By overriding + * the getModelForBean method you can tune the FUSEing to + * fit your business application needs. + * + * @file RedBeanPHP/IBeanHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface BeanHelper +{ + /** + * Returns a toolbox to empower the bean. + * This allows beans to perform OODB operations by themselves, + * as such the bean is a proxy for OODB. This allows beans to implement + * their magic getters and setters and return lists. + * + * @return ToolBox + */ + public function getToolbox(); + + /** + * Does approximately the same as getToolbox but also extracts the + * toolbox for you. + * This method returns a list with all toolbox items in Toolbox Constructor order: + * OODB, adapter, writer and finally the toolbox itself!. + * + * @return array + */ + public function getExtractedToolbox(); + + /** + * Given a certain bean this method will + * return the corresponding model. + * If no model is returned (NULL), RedBeanPHP might ask again. + * + * @note You can make RedBeanPHP faster by doing the setup wiring yourself. + * The event listeners take time, so to speed-up RedBeanPHP you can + * drop 'FUSE', if you're not interested in the Models. + * + * @note You can do funny stuff with this method but please be careful. + * You *could* create a model depending on properties of the bean, but + * it's a bit well... adventurous, here is an example: + * + * + * class Book extends RedBeanPHP\SimpleModel {}; + * class Booklet extends RedBeanPHP\SimpleModel {}; + * + * class FlexBeanHelper extends RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper { + * public function getModelForBean( RedBeanPHP\OODBBean $bean ) { + * if (!isset($bean->pages)) return NULL; //will ask again + * if ($bean->pages <= 10) return new Booklet; + * return new Book; + * } + * } + * + * $h = new FlexBeanHelper; + * R::getRedBean()->setBeanHelper($h); + * $book = R::dispense('book'); + * var_dump($book->box()); //NULL cant reach model + * $book->pages = 5; + * var_dump($book->box()); //Booklet + * $book->pages = 15; + * var_dump($book->box()); //still.. Booklet, model has been set + * $book2 = R::dispense('book'); + * $book2->pages = 15; + * var_dump($book2->box()); //Book, more than 10 pages + * + * + * @param OODBBean $bean bean to obtain the corresponding model of + * + * @return SimpleModel|CustomModel|NULL + */ + public function getModelForBean( OODBBean $bean ); +} +} + +namespace RedBeanPHP\BeanHelper { + +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\Facade as Facade; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; + +/** + * Bean Helper. + * + * The Bean helper helps beans to access access the toolbox and + * FUSE models. This Bean Helper makes use of the facade to obtain a + * reference to the toolbox. + * + * @file RedBeanPHP/BeanHelperFacade.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleFacadeBeanHelper implements BeanHelper +{ + /** + * Factory function to create instance of Simple Model, if any. + * + * @var \Closure + */ + private static $factory = null; + + /** + * Factory method using a customizable factory function to create + * the instance of the Simple Model. + * + * @param string $modelClassName name of the class + * + * @return SimpleModel + */ + public static function factory( $modelClassName ) + { + $factory = self::$factory; + return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName(); + } + + /** + * Sets the factory function to create the model when using FUSE + * to connect a bean to a model. + * + * @param \Closure $factory factory function + * + * @return void + */ + public static function setFactoryFunction( $factory ) + { + self::$factory = $factory; + } + + /** + * @see BeanHelper::getToolbox + */ + public function getToolbox() + { + return Facade::getToolBox(); + } + + /** + * @see BeanHelper::getModelForBean + */ + public function getModelForBean( OODBBean $bean ) + { + $model = $bean->getMeta( 'type' ); + $prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_'; + + if ( strpos( $model, '_' ) !== FALSE ) { + $modelParts = explode( '_', $model ); + $modelName = ''; + foreach( $modelParts as $part ) { + $modelName .= ucfirst( $part ); + } + $modelName = $prefix . $modelName; + + if ( !class_exists( $modelName ) ) { + //second try + $modelName = $prefix . ucfirst( $model ); + + if ( !class_exists( $modelName ) ) { + return NULL; + } + } + + } else { + + $modelName = $prefix . ucfirst( $model ); + if ( !class_exists( $modelName ) ) { + return NULL; + } + } + $obj = self::factory( $modelName ); + $obj->loadBean( $bean ); + + return $obj; + } + + /** + * @see BeanHelper::getExtractedToolbox + */ + public function getExtractedToolbox() + { + return Facade::getExtractedToolbox(); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\OODBBean as OODBBean; + +/** + * SimpleModel + * Base Model For All RedBeanPHP Models using FUSE. + * + * RedBeanPHP FUSE is a mechanism to connect beans to posthoc + * models. Models are connected to beans by naming conventions. + * Actions on beans will result in actions on models. + * + * @file RedBeanPHP/SimpleModel.php + * @author Gabor de Mooij and the RedBeanPHP Team + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModel +{ + /** + * @var OODBBean + */ + protected $bean; + + /** + * Used by FUSE: the ModelHelper class to connect a bean to a model. + * This method loads a bean in the model. + * + * @param OODBBean $bean bean to load + * + * @return void + */ + public function loadBean( OODBBean $bean ) + { + $this->bean = $bean; + } + + /** + * Magic Getter to make the bean properties available from + * the $this-scope. + * + * @note this method returns a value, not a reference! + * To obtain a reference unbox the bean first! + * + * @param string $prop property to get + * + * @return mixed + */ + public function __get( $prop ) + { + return $this->bean->$prop; + } + + /** + * Magic Setter. + * Sets the value directly as a bean property. + * + * @param string $prop property to set value of + * @param mixed $value value to set + * + * @return void + */ + public function __set( $prop, $value ) + { + $this->bean->$prop = $value; + } + + /** + * Isset implementation. + * Implements the isset function for array-like access. + * + * @param string $key key to check + * + * @return boolean + */ + public function __isset( $key ) + { + return isset( $this->bean->$key ); + } + + /** + * Box the bean using the current model. + * This method wraps the current bean in this model. + * This method can be reached using FUSE through a simple + * OODBBean. The method returns a RedBeanPHP Simple Model. + * This is useful if you would like to rely on PHP type hinting. + * You can box your beans before passing them to functions or methods + * with typed parameters. + * + * @return SimpleModel + */ + public function box() + { + return $this; + } + + /** + * Unbox the bean from the model. + * This method returns the bean inside the model. + * + * @return OODBBean + */ + public function unbox() + { + return $this->bean; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Observer as Observer; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; + +/** + * RedBean Model Helper. + * + * Connects beans to models. + * This is the core of so-called FUSE. + * + * @file RedBeanPHP/ModelHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModelHelper implements Observer +{ + /** + * Gets notified by an observable. + * This method decouples the FUSE system from the actual beans. + * If a FUSE event happens 'update', this method will attempt to + * invoke the corresponding method on the bean. + * + * @param string $eventName i.e. 'delete', 'after_delete' + * @param OODBean $bean affected bean + * + * @return void + */ + public function onEvent( $eventName, $bean ) + { + $bean->$eventName(); + } + + /** + * Attaches the FUSE event listeners. Now the Model Helper will listen for + * CRUD events. If a CRUD event occurs it will send a signal to the model + * that belongs to the CRUD bean and this model will take over control from + * there. This method will attach the following event listeners to the observable: + * + * - 'update' (gets called by R::store, before the records gets inserted / updated) + * - 'after_update' (gets called by R::store, after the records have been inserted / updated) + * - 'open' (gets called by R::load, after the record has been retrieved) + * - 'delete' (gets called by R::trash, before deletion of record) + * - 'after_delete' (gets called by R::trash, after deletion) + * - 'dispense' (gets called by R::dispense) + * + * For every event type, this method will register this helper as a listener. + * The observable will notify the listener (this object) with the event ID and the + * affected bean. This helper will then process the event (onEvent) by invoking + * the event on the bean. If a bean offers a method with the same name as the + * event ID, this method will be invoked. + * + * @param Observable $observable object to observe + * + * @return void + */ + public function attachEventListeners( Observable $observable ) + { + foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $eventID ) { + $observable->addEventListener( $eventID, $this ); + } + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * RedBeanPHP Tag Manager. + * + * The tag manager offers an easy way to quickly implement basic tagging + * functionality. + * + * Provides methods to tag beans and perform tag-based searches in the + * bean database. + * + * @file RedBeanPHP/TagManager.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class TagManager +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var AssociationManager + */ + protected $associationManager; + + /** + * @var OODBBean + */ + protected $redbean; + + /** + * Checks if the argument is a comma separated string, in this case + * it will split the string into words and return an array instead. + * In case of an array the argument will be returned 'as is'. + * + * @param array|string $tagList list of tags + * + * @return array + */ + private function extractTagsIfNeeded( $tagList ) + { + if ( $tagList !== FALSE && !is_array( $tagList ) ) { + $tags = explode( ',', (string) $tagList ); + } else { + $tags = $tagList; + } + + return $tags; + } + + /** + * Finds a tag bean by it's title. + * Internal method. + * + * @param string $title title to search for + * + * @return OODBBean + */ + protected function findTagByTitle( $title ) + { + $beans = $this->redbean->find( 'tag', array( 'title' => array( $title ) ) ); + + if ( $beans ) { + $bean = reset( $beans ); + + return $bean; + } + + return NULL; + } + + /** + * Constructor. + * The tag manager offers an easy way to quickly implement basic tagging + * functionality. + * + * @param ToolBox $toolbox toolbox object + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + + $this->associationManager = $this->redbean->getAssociationManager(); + } + + /** + * Tests whether a bean has been associated with one ore more + * of the listed tags. If the third parameter is TRUE this method + * will return TRUE only if all tags that have been specified are indeed + * associated with the given bean, otherwise FALSE. + * If the third parameter is FALSE this + * method will return TRUE if one of the tags matches, FALSE if none + * match. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to check for tags + * @param array|string $tags list of tags + * @param boolean $all whether they must all match or just some + * + * @return boolean + */ + public function hasTag( $bean, $tags, $all = FALSE ) + { + $foundtags = $this->tag( $bean ); + + $tags = $this->extractTagsIfNeeded( $tags ); + $same = array_intersect( $tags, $foundtags ); + + if ( $all ) { + return ( implode( ',', $same ) === implode( ',', $tags ) ); + } + + return (bool) ( count( $same ) > 0 ); + } + + /** + * Removes all sepcified tags from the bean. The tags specified in + * the second parameter will no longer be associated with the bean. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean tagged bean + * @param array|string $tagList list of tags (names) + * + * @return void + */ + public function untag( $bean, $tagList ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + + foreach ( $tags as $tag ) { + if ( $t = $this->findTagByTitle( $tag ) ) { + $this->associationManager->unassociate( $bean, $t ); + } + } + } + + /** + * Tags a bean or returns tags associated with a bean. + * If $tagList is NULL or omitted this method will return a + * comma separated list of tags associated with the bean provided. + * If $tagList is a comma separated list (string) of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to be tagged + * @param array|string $tagList a list of tags + * + * @return array + */ + public function tag( OODBBean $bean, $tagList = NULL ) + { + if ( is_null( $tagList ) ) { + + $tags = $bean->sharedTag; + $foundTags = array(); + + foreach ( $tags as $tag ) { + $foundTags[] = $tag->title; + } + + return $foundTags; + } + + $this->associationManager->clearRelations( $bean, 'tag' ); + $this->addTags( $bean, $tagList ); + + return $tagList; + } + + /** + * Adds tags to a bean. + * If $tagList is a comma separated list of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param OODBBean $bean bean to add tags to + * @param array|string $tagList list of tags to add to bean + * + * @return void + */ + public function addTags( OODBBean $bean, $tagList ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + + if ( $tagList === FALSE ) { + return; + } + + foreach ( $tags as $tag ) { + if ( !$t = $this->findTagByTitle( $tag ) ) { + $t = $this->redbean->dispense( 'tag' ); + $t->title = $tag; + + $this->redbean->store( $t ); + } + + $this->associationManager->associate( $bean, $t ); + } + } + + /** + * Returns all beans that have been tagged with one or more + * of the specified tags. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param string $beanType type of bean you are looking for + * @param array|string $tagList list of tags to match + * @param string $sql additional SQL (use only for pagination) + * @param array $bindings bindings + * + * @return array + */ + public function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings ); + + return $this->redbean->convertToBeans( $beanType, $records ); + } + + /** + * Returns all beans that have been tagged with ALL of the tags given. + * + * Tag list can be either an array with tag names or a comma separated list + * of tag names. + * + * @param string $beanType type of bean you are looking for + * @param array|string $tagList list of tags to match + * + * @return array + */ + public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) + { + $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings ); + + return $this->redbean->convertToBeans( $beanType, $records ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Label Maker. + * Makes so-called label beans. + * A label is a bean with only an id, type and name property. + * Labels can be used to create simple entities like categories, tags or enums. + * This service class provides convenience methods to deal with this kind of + * beans. + * + * @file RedBeanPHP/LabelMaker.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class LabelMaker +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * Constructor. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + } + + /** + * A label is a bean with only an id, type and name property. + * This function will dispense beans for all entries in the array. The + * values of the array will be assigned to the name property of each + * individual bean. + * + * + * $people = R::dispenseLabels( 'person', [ 'Santa', 'Claus' ] ); + * + * + * @param string $type type of beans you would like to have + * @param array $labels list of labels, names for each bean + * + * @return array + */ + public function dispenseLabels( $type, $labels ) + { + $labelBeans = array(); + foreach ( $labels as $label ) { + $labelBean = $this->toolbox->getRedBean()->dispense( $type ); + $labelBean->name = $label; + $labelBeans[] = $labelBean; + } + + return $labelBeans; + } + + /** + * Gathers labels from beans. This function loops through the beans, + * collects the value of the name property for each individual bean + * and stores the names in a new array. The array then gets sorted using the + * default sort function of PHP (sort). + * + * Usage: + * + * + * $o1->name = 'hamburger'; + * $o2->name = 'pizza'; + * implode( ',', R::gatherLabels( [ $o1, $o2 ] ) ); //hamburger,pizza + * + * + * Note that the return value is an array of strings, not beans. + * + * @param array $beans list of beans to loop through + * + * @return array + */ + public function gatherLabels( $beans ) + { + $labels = array(); + + foreach ( $beans as $bean ) { + $labels[] = $bean->name; + } + + sort( $labels ); + + return $labels; + } + + /** + * Fetches an ENUM from the database and creates it if necessary. + * An ENUM has the following format: + * + * + * ENUM:VALUE + * + * + * If you pass 'ENUM' only, this method will return an array of its + * values: + * + * + * implode( ',', R::gatherLabels( R::enum( 'flavour' ) ) ) //'BANANA,MOCCA' + * + * + * If you pass 'ENUM:VALUE' this method will return the specified enum bean + * and create it in the database if it does not exist yet: + * + * + * $bananaFlavour = R::enum( 'flavour:banana' ); + * $bananaFlavour->name; + * + * + * So you can use this method to set an ENUM value in a bean: + * + * + * $shake->flavour = R::enum( 'flavour:banana' ); + * + * + * the property flavour now contains the enum bean, a parent bean. + * In the database, flavour_id will point to the flavour record with name 'banana'. + * + * @param string $enum ENUM specification for label + * + * @return array|OODBBean + */ + public function enum( $enum ) + { + $oodb = $this->toolbox->getRedBean(); + + if ( strpos( $enum, ':' ) === FALSE ) { + $type = $enum; + $value = FALSE; + } else { + list( $type, $value ) = explode( ':', $enum ); + $value = preg_replace( '/\W+/', '_', strtoupper( trim( $value ) ) ); + } + + /** + * We use simply find here, we could use inspect() in fluid mode etc, + * but this would be useless. At first sight it looks clean, you could even + * bake this into find(), however, find not only has to deal with the primary + * search type, people can also include references in the SQL part, so avoiding + * find failures does not matter, this is still the quickest way making use + * of existing functionality. + * + * @note There seems to be a bug in XDebug v2.3.2 causing suppressed + * exceptions like these to surface anyway, to prevent this use: + * + * "xdebug.default_enable = 0" + * + * Also see Github Issue #464 + */ + $values = $oodb->find( $type ); + + if ( $value === FALSE ) { + return $values; + } + + foreach( $values as $enumItem ) { + if ( $enumItem->name === $value ) return $enumItem; + } + + $newEnumItems = $this->dispenseLabels( $type, array( $value ) ); + $newEnumItem = reset( $newEnumItems ); + + $oodb->store( $newEnumItem ); + + return $newEnumItem; + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\RedException\SQL as SQLException; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\Logger\RDefault\Debug as Debug; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper; +use RedBeanPHP\Driver\RPDO as RPDO; +use RedBeanPHP\Util\MultiLoader as MultiLoader; +use RedBeanPHP\Util\Transaction as Transaction; +use RedBeanPHP\Util\Dump as Dump; +use RedBeanPHP\Util\DispenseHelper as DispenseHelper; +use RedBeanPHP\Util\ArrayTool as ArrayTool; + +/** + * RedBean Facade + * + * Version Information + * RedBean Version @version 4.3 + * + * This class hides the object landscape of + * RedBeanPHP behind a single letter class providing + * almost all functionality with simple static calls. + * + * @file RedBeanPHP/Facade.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Facade +{ + /** + * RedBeanPHP version constant. + */ + const C_REDBEANPHP_VERSION = '4.3'; + + /** + * @var ToolBox + */ + public static $toolbox; + + /** + * @var OODB + */ + private static $redbean; + + /** + * @var QueryWriter + */ + private static $writer; + + /** + * @var DBAdapter + */ + private static $adapter; + + /** + * @var AssociationManager + */ + private static $associationManager; + + /** + * @var TagManager + */ + private static $tagManager; + + /** + * @var DuplicationManager + */ + private static $duplicationManager; + + /** + * @var LabelMaker + */ + private static $labelMaker; + + /** + * @var Finder + */ + private static $finder; + + /** + * @var Logger + */ + private static $logger; + + /** + * @var array + */ + private static $plugins = array(); + + /** + * @var string + */ + private static $exportCaseStyle = 'default'; + + /** + * Not in use (backward compatibility SQLHelper) + */ + public static $f; + + /** + * @var string + */ + public static $currentDB = ''; + + /** + * @var array + */ + public static $toolboxes = array(); + + /** + * Internal Query function, executes the desired query. Used by + * all facade query functions. This keeps things DRY. + * + * @param string $method desired query method (i.e. 'cell', 'col', 'exec' etc..) + * @param string $sql the sql you want to execute + * @param array $bindings array of values to be bound to query statement + * + * @return array + */ + private static function query( $method, $sql, $bindings ) + { + if ( !self::$redbean->isFrozen() ) { + try { + $rs = Facade::$adapter->$method( $sql, $bindings ); + } catch ( SQLException $exception ) { + if ( self::$writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + return ( $method === 'getCell' ) ? NULL : array(); + } else { + throw $exception; + } + } + + return $rs; + } else { + return Facade::$adapter->$method( $sql, $bindings ); + } + } + + /** + * Returns the RedBeanPHP version string. + * The RedBeanPHP version string always has the same format "X.Y" + * where X is the major version number and Y is the minor version number. + * Point releases are not mentioned in the version string. + * + * @return string + */ + public static function getVersion() + { + return self::C_REDBEANPHP_VERSION; + } + + /** + * Tests the connection. + * Returns TRUE if connection has been established and + * FALSE otherwise. + * + * @return boolean + */ + public static function testConnection() + { + if ( !isset( self::$adapter ) ) return FALSE; + + $database = self::$adapter->getDatabase(); + try { + @$database->connect(); + } catch ( \Exception $e ) {} + return $database->isConnected(); + } + + /** + * Kickstarts redbean for you. This method should be called before you start using + * RedBean. The Setup() method can be called without any arguments, in this case it will + * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems). + * + * @param string $dsn Database connection string + * @param string $username Username for database + * @param string $password Password for database + * @param boolean $frozen TRUE if you want to setup in frozen mode + * + * @return ToolBox + */ + public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE ) + { + if ( is_null( $dsn ) ) { + $dsn = 'sqlite:/' . sys_get_temp_dir() . '/red.db'; + } + + self::addDatabase( 'default', $dsn, $username, $password, $frozen ); + self::selectDatabase( 'default' ); + + return self::$toolbox; + } + + /** + * Toggles Narrow Field Mode. + * See documentation in QueryWriter. + * + * @param boolean $mode TRUE = Narrow Field Mode + * + * @return void + */ + public static function setNarrowFieldMode( $mode ) + { + AQueryWriter::setNarrowFieldMode( $mode ); + } + + /** + * Wraps a transaction around a closure or string callback. + * If an Exception is thrown inside, the operation is automatically rolled back. + * If no Exception happens, it commits automatically. + * It also supports (simulated) nested transactions (that is useful when + * you have many methods that needs transactions but are unaware of + * each other). + * + * Example: + * + * + * $from = 1; + * $to = 2; + * $amount = 300; + * + * R::transaction(function() use($from, $to, $amount) + * { + * $accountFrom = R::load('account', $from); + * $accountTo = R::load('account', $to); + * $accountFrom->money -= $amount; + * $accountTo->money += $amount; + * R::store($accountFrom); + * R::store($accountTo); + * }); + * + * + * @param callable $callback Closure (or other callable) with the transaction logic + * + * @return mixed + */ + public static function transaction( $callback ) + { + return Transaction::transaction( self::$adapter, $callback ); + } + + /** + * Adds a database to the facade, afterwards you can select the database using + * selectDatabase($key), where $key is the name you assigned to this database. + * + * Usage: + * + * + * R::addDatabase( 'database-1', 'sqlite:/tmp/db1.txt' ); + * R::selectDatabase( 'database-1' ); //to select database again + * + * + * This method allows you to dynamically add (and select) new databases + * to the facade. Adding a database with the same key will cause an exception. + * + * @param string $key ID for the database + * @param string $dsn DSN for the database + * @param string $user user for connection + * @param NULL|string $pass password for connection + * @param bool $frozen whether this database is frozen or not + * + * @return void + */ + public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE ) + { + if ( isset( self::$toolboxes[$key] ) ) { + throw new RedException( 'A database has already been specified for this key.' ); + } + + if ( is_object($dsn) ) { + $db = new RPDO( $dsn ); + $dbType = $db->getDatabaseType(); + } else { + $db = new RPDO( $dsn, $user, $pass, TRUE ); + $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) ); + } + + $adapter = new DBAdapter( $db ); + + $writers = array( + 'pgsql' => 'PostgreSQL', + 'sqlite' => 'SQLiteT', + 'cubrid' => 'CUBRID', + 'mysql' => 'MySQL', + 'sqlsrv' => 'SQLServer', + ); + + $wkey = trim( strtolower( $dbType ) ); + if ( !isset( $writers[$wkey] ) ) { + $wkey = preg_replace( '/\W/', '' , $wkey ); + throw new RedException( 'Unsupported database ('.$wkey.').' ); + } + $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey]; + $writer = new $writerClass( $adapter ); + $redbean = new OODB( $writer, $frozen ); + + self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer ); + } + + /** + * Determines whether a database identified with the specified key has + * already been added to the facade. This function will return TRUE + * if the database indicated by the key is available and FALSE otherwise. + * + * @param string $key the key/name of the database to check for + * + * @return boolean + */ + public static function hasDatabase( $key ) + { + return ( isset( self::$toolboxes[$key] ) ); + } + + /** + * Selects a different database for the Facade to work with. + * If you use the R::setup() you don't need this method. This method is meant + * for multiple database setups. This method selects the database identified by the + * database ID ($key). Use addDatabase() to add a new database, which in turn + * can be selected using selectDatabase(). If you use R::setup(), the resulting + * database will be stored under key 'default', to switch (back) to this database + * use R::selectDatabase( 'default' ). This method returns TRUE if the database has been + * switched and FALSE otherwise (for instance if you already using the specified database). + * + * @param string $key Key of the database to select + * + * @return boolean + */ + public static function selectDatabase( $key ) + { + if ( self::$currentDB === $key ) { + return FALSE; + } + + if ( !isset( self::$toolboxes[$key] ) ) { + throw new RedException( 'Database not found in registry. Add database using R::addDatabase().' ); + } + + self::configureFacadeWithToolbox( self::$toolboxes[$key] ); + self::$currentDB = $key; + + return TRUE; + } + + /** + * Toggles DEBUG mode. + * In Debug mode all SQL that happens under the hood will + * be printed to the screen and/or logged. + * If no database connection has been configured using R::setup() or + * R::selectDatabase() this method will throw an exception. + * + * There are 2 debug styles: + * + * Classic: separate parameter bindings, explicit and complete but less readable + * Fancy: interpersed bindings, truncates large strings, highlighted schema changes + * + * Fancy style is more readable but sometimes incomplete. + * + * The first parameter turns debugging ON or OFF. + * The second parameter indicates the mode of operation: + * + * 0 Log and write to STDOUT classic style (default) + * 1 Log only, class style + * 2 Log and write to STDOUT fancy style + * 3 Log only, fancy style + * + * This function always returns the logger instance created to generate the + * debug messages. + * + * @param boolean $tf debug mode (TRUE or FALSE) + * @param integer $mode mode of operation + * + * @return RDefault + * @throws RedException + */ + public static function debug( $tf = TRUE, $mode = 0 ) + { + if ($mode > 1) { + $mode -= 2; + $logger = new Debug; + } else { + $logger = new RDefault; + } + + if ( !isset( self::$adapter ) ) { + throw new RedException( 'Use R::setup() first.' ); + } + $logger->setMode($mode); + self::$adapter->getDatabase()->setDebugMode( $tf, $logger ); + + return $logger; + } + + /** + * Turns on the fancy debugger. + * In 'fancy' mode the debugger will output queries with bound + * parameters inside the SQL itself. This method has been added to + * offer a convenient way to activate the fancy debugger system + * in one call. + * + * @param boolean $toggle TRUE to activate debugger and select 'fancy' mode + * + * @return void + */ + public static function fancyDebug( $toggle = TRUE ) + { + self::debug( $toggle, 2 ); + } + + /** + * Inspects the database schema. If you pass the type of a bean this + * method will return the fields of its table in the database. + * The keys of this array will be the field names and the values will be + * the column types used to store their values. + * If no type is passed, this method returns a list of all tables in the database. + * + * @param string $type Type of bean (i.e. table) you want to inspect + * + * @return array + */ + public static function inspect( $type = NULL ) + { + return ($type === NULL) ? self::$writer->getTables() : self::$writer->getColumns( $type ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + */ + public static function store( $bean ) + { + return self::$redbean->store( $bean ); + } + + /** + * Toggles fluid or frozen mode. In fluid mode the database + * structure is adjusted to accomodate your objects. In frozen mode + * this is not the case. + * + * You can also pass an array containing a selection of frozen types. + * Let's call this chilly mode, it's just like fluid mode except that + * certain types (i.e. tables) aren't touched. + * + * @param boolean|array $trueFalse + */ + public static function freeze( $tf = TRUE ) + { + self::$redbean->freeze( $tf ); + } + + /** + * Loads multiple types of beans with the same ID. + * This might look like a strange method, however it can be useful + * for loading a one-to-one relation. + * + * Usage: + * list( $author, $bio ) = R::loadMulti( 'author, bio', $id ); + * + * @param string|array $types the set of types to load at once + * @param mixed $id the common ID + * + * @return OODBBean + */ + public static function loadMulti( $types, $id ) + { + return MultiLoader::load( self::$redbean, $types, $id ); + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @return OODBBean + */ + public static function load( $type, $id ) + { + return self::$redbean->load( $type, $id ); + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * This facade method also accepts a type-id combination, + * in the latter case this method will attempt to load the specified bean + * and THEN trash it. + * + * @param string|OODBBean|SimpleModel $bean bean you want to remove from database + * @param integer $id ID if the bean to trash (optional, type-id variant only) + * + * @return void + */ + public static function trash( $beanOrType, $id = NULL ) + { + if ( is_string( $beanOrType ) ) return self::trash( self::load( $beanOrType, $id ) ); + return self::$redbean->trash( $beanOrType ); + } + + /** + * Dispenses a new RedBean OODB Bean for use with + * the rest of the methods. + * + * @param string|array $typeOrBeanArray type or bean array to import + * @param integer $number number of beans to dispense + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return array|OODBBean + */ + public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) + { + return DispenseHelper::dispense( self::$redbean, $typeOrBeanArray, $num, $alwaysReturnArray ); + } + + /** + * Takes a comma separated list of bean types + * and dispenses these beans. For each type in the list + * you can specify the number of beans to be dispensed. + * + * Usage: + * + * + * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' ); + * + * + * This will dispense a book, a page and a text. This way you can + * quickly dispense beans of various types in just one line of code. + * + * Usage: + * + * + * list($book, $pages) = R::dispenseAll('book,page*100'); + * + * + * This returns an array with a book bean and then another array + * containing 100 page beans. + * + * @param string $order a description of the desired dispense order using the syntax above + * @param boolean $onlyArrays return only arrays even if amount < 2 + * + * @return array + */ + public static function dispenseAll( $order, $onlyArrays = FALSE ) + { + return DispenseHelper::dispenseAll( self::$redbean, $order, $onlyArrays ); + } + + /** + * Convience method. Tries to find beans of a certain type, + * if no beans are found, it dispenses a bean of that type. + * Note that this function always returns an array. + * + * @param string $type type of bean you are looking for + * @param string $sql SQL code for finding the bean + * @param array $bindings parameters to bind to SQL + * + * @return array + */ + public static function findOrDispense( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findOrDispense( $type, $sql, $bindings ); + } + + /** + * Same as findOrDispense but returns just one element. + * + * @param string $type type of bean you are looking for + * @param string $sql SQL code for finding the bean + * @param array $bindings parameters to bind to SQL + * + * @return OODBBean + */ + public static function findOneOrDispense( $type, $sql = NULL, $bindings = array() ) + { + return reset( self::findOrDispense( $type, $sql, $bindings ) ); + } + + /** + * Finds a bean using a type and a where clause (SQL). + * As with most Query tools in RedBean you can provide values to + * be inserted in the SQL statement by populating the value + * array parameter; you can either use the question mark notation + * or the slot-notation (:keyname). + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function find( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->find( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * The findAll() method differs from the find() method in that it does + * not assume a WHERE-clause, so this is valid: + * + * R::findAll('person',' ORDER BY name DESC '); + * + * Your SQL does not have to start with a valid WHERE-clause condition. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function findAll( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->find( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * The variation also exports the beans (i.e. it returns arrays). + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return array + */ + public static function findAndExport( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findAndExport( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * This variation returns the first bean only. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return OODBBean + */ + public static function findOne( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findOne( $type, $sql, $bindings ); + } + + /** + * @see Facade::find + * This variation returns the last bean only. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return OODBBean + */ + public static function findLast( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findLast( $type, $sql, $bindings ); + } + + /** + * Finds a bean collection. + * Use this for large datasets. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings array of values to be bound to parameters in query + * + * @return BeanCollection + */ + public static function findCollection( $type, $sql = NULL, $bindings = array() ) + { + return self::$finder->findCollection( $type, $sql, $bindings ); + } + + /** + * Finds multiple types of beans at once and offers additional + * remapping functionality. This is a very powerful yet complex function. + * For details see Finder::findMulti(). + * + * @see Finder::findMulti() + * + * @param array|string $types a list of bean types to find + * @param string|array $sqlOrArr SQL query string or result set array + * @param array $bindings SQL bindings + * @param array $remappings an array of remapping arrays containing closures + * + * @return array + */ + public static function findMulti( $types, $sql, $bindings = array(), $remappings = array() ) + { + return self::$finder->findMulti( $types, $sql, $bindings, $remappings ); + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public static function batch( $type, $ids ) + { + return self::$redbean->batch( $type, $ids ); + } + + /** + * @see Facade::batch + * + * Alias for batch(). Batch method is older but since we added so-called *All + * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to + * improve the consistency of the Facade API and also add an alias for batch() called + * loadAll. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public static function loadAll( $type, $ids ) + { + return self::$redbean->batch( $type, $ids ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return integer + */ + public static function exec( $sql, $bindings = array() ) + { + return self::query( 'exec', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAll( $sql, $bindings = array() ) + { + return self::query( 'get', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return string + */ + public static function getCell( $sql, $bindings = array() ) + { + return self::query( 'getCell', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getRow( $sql, $bindings = array() ) + { + return self::query( 'getRow', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getCol( $sql, $bindings = array() ) + { + return self::query( 'getCol', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * Results will be returned as an associative array. The first + * column in the select clause will be used for the keys in this array and + * the second column will be used for the values. If only one column is + * selected in the query, both key and value of the array will have the + * value of this field for each row. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAssoc( $sql, $bindings = array() ) + { + return self::query( 'getAssoc', $sql, $bindings ); + } + + /** + * Convenience function to execute Queries directly. + * Executes SQL. + * Results will be returned as an associative array indexed by the first + * column in the select. + * + * @param string $sql SQL query to execute + * @param array $bindings a list of values to be bound to query parameters + * + * @return array + */ + public static function getAssocRow( $sql, $bindings = array() ) + { + return self::query( 'getAssocRow', $sql, $bindings ); + } + + /** + * Returns the insert ID for databases that support/require this + * functionality. Alias for R::getAdapter()->getInsertID(). + * + * @return mixed + */ + public static function getInsertID() + { + return self::$adapter->getInsertID(); + } + + /** + * Makes a copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following features. + * - All beans in own-lists will be duplicated as well + * - All references to shared beans will be copied but not the shared beans themselves + * - All references to parent objects (_id fields) will be copied but not the parents themselves + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * @deprecated + * This function is deprecated in favour of R::duplicate(). + * This function has a confusing method signature, the R::duplicate() function + * only accepts two arguments: bean and filters. + * + * @param OODBBean $bean bean to be copied + * @param array $trail for internal usage, pass array() + * @param boolean $pid for internal usage + * @param array $white white list filter with bean types to duplicate + * + * @return array + */ + public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() ) + { + self::$duplicationManager->setFilters( $filters ); + return self::$duplicationManager->dup( $bean, $trail, $pid ); + } + + /** + * Makes a deep copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following: + * + * * All beans in own-lists will be duplicated as well + * * All references to shared beans will be copied but not the shared beans themselves + * * All references to parent objects (_id fields) will be copied but not the parents themselves + * + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * Note: + * This is a simplified version of the deprecated R::dup() function. + * + * @param OODBBean $bean bean to be copied + * @param array $white white list filter with bean types to duplicate + * + * @return array + */ + public static function duplicate( $bean, $filters = array() ) + { + return self::dup( $bean, array(), FALSE, $filters ); + } + + /** + * Exports a collection of beans. Handy for XML/JSON exports with a + * Javascript framework like Dojo or ExtJS. + * What will be exported: + * + * * contents of the bean + * * all own bean lists (recursively) + * * all shared beans (not THEIR own lists) + * + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents whether you want parent beans to be exported + * @param array $filters whitelist of types + * + * @return array + */ + public static function exportAll( $beans, $parents = FALSE, $filters = array()) + { + return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle ); + } + + /** + * Selects case style for export. + * This will determine the case style for the keys of exported beans (see exportAll). + * The following options are accepted: + * + * * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid ) + * * 'camel' Camel Case (i.e. bookId isValid ) + * * 'dolphin' Dolphin Case (i.e. bookID isValid ) Like CamelCase but ID is written all uppercase + * + * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different + * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id. + * Due to information loss this cannot be corrected. However if you might try + * DolphinCase for IDs it takes into account the exception concerning IDs. + * + * @param string $caseStyle case style identifier + * + * @return void + */ + public static function useExportCase( $caseStyle = 'default' ) + { + if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' ); + self::$exportCaseStyle = $caseStyle; + } + + /** + * Converts a series of rows to beans. + * This method converts a series of rows to beans. + * The type of the desired output beans can be specified in the + * first parameter. The second parameter is meant for the database + * result rows. + * + * Usage: + * + * + * $rows = R::getAll( 'SELECT * FROM ...' ) + * $beans = R::convertToBeans( $rows ); + * + * + * As of version 4.3.2 you can specify a meta-mask. + * Data from columns with names starting with the value specified in the mask + * will be transferred to the meta section of a bean (under data.bundle). + * + * + * $rows = R::getAll( 'SELECT FROM... COUNT(*) AS extra_count ...' ); + * $beans = R::convertToBeans( $rows ); + * $bean = reset( $beans ); + * $data = $bean->getMeta( 'data.bundle' ); + * $extra_count = $data['extra_count']; + * + * + * @param string $type type of beans to produce + * @param array $rows must contain an array of array + * + * @return array + */ + public static function convertToBeans( $type, $rows, $metamask = NULL ) + { + return self::$redbean->convertToBeans( $type, $rows, $metamask ); + } + + /** + * Just like converToBeans, but for one bean. + * @see convertToBeans for more details. + * + * @param string $type type of beans to produce + * @param array $row one row from the database + * + * @return array + */ + public static function convertToBean( $type, $row, $metamask = NULL ) + { + $beans = self::$redbean->convertToBeans( $type, array( $row ), $metamask ); + $bean = reset( $beans ); + return $bean; + } + + /** + * Part of RedBeanPHP Tagging API. + * Tests whether a bean has been associated with one ore more + * of the listed tags. If the third parameter is TRUE this method + * will return TRUE only if all tags that have been specified are indeed + * associated with the given bean, otherwise FALSE. + * If the third parameter is FALSE this + * method will return TRUE if one of the tags matches, FALSE if none + * match. + * + * @param OODBBean $bean bean to check for tags + * @param array $tags list of tags + * @param boolean $all whether they must all match or just some + * + * @return boolean + */ + public static function hasTag( $bean, $tags, $all = FALSE ) + { + return self::$tagManager->hasTag( $bean, $tags, $all ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Removes all specified tags from the bean. The tags specified in + * the second parameter will no longer be associated with the bean. + * + * @param OODBBean $bean tagged bean + * @param array $tagList list of tags (names) + * + * @return void + */ + public static function untag( $bean, $tagList ) + { + self::$tagManager->untag( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Tags a bean or returns tags associated with a bean. + * If $tagList is NULL or omitted this method will return a + * comma separated list of tags associated with the bean provided. + * If $tagList is a comma separated list (string) of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * @param OODBBean $bean bean to tag + * @param mixed $tagList tags to attach to the specified bean + * + * @return string + */ + public static function tag( OODBBean $bean, $tagList = NULL ) + { + return self::$tagManager->tag( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Adds tags to a bean. + * If $tagList is a comma separated list of tags all tags will + * be associated with the bean. + * You may also pass an array instead of a string. + * + * @param OODBBean $bean bean to tag + * @param array $tagList list of tags to add to bean + * + * @return void + */ + public static function addTags( OODBBean $bean, $tagList ) + { + self::$tagManager->addTags( $bean, $tagList ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Returns all beans that have been tagged with one of the tags given. + * + * @param string $beanType type of bean you are looking for + * @param array $tagList list of tags to match + * @param string $sql additional SQL query snippet + * @param array $bindings a list of values to bind to the query parameters + * + * @return array + */ + public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) + { + return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings ); + } + + /** + * Part of RedBeanPHP Tagging API. + * Returns all beans that have been tagged with ALL of the tags given. + * + * @param string $beanType type of bean you are looking for + * @param array $tagList list of tags to match + * @param string $sql additional SQL query snippet + * @param array $bindings a list of values to bind to the query parameters + * + * @return array + */ + public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) + { + return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings ); + } + + /** + * Wipes all beans of type $beanType. + * + * @param string $beanType type of bean you want to destroy entirely + * + * @return boolean + */ + public static function wipe( $beanType ) + { + return Facade::$redbean->wipe( $beanType ); + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + */ + public static function count( $type, $addSQL = '', $bindings = array() ) + { + return Facade::$redbean->count( $type, $addSQL, $bindings ); + } + + /** + * Configures the facade, want to have a new Writer? A new Object Database or a new + * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new + * toolbox. + * + * @param ToolBox $tb toolbox to configure facade with + * + * @return ToolBox + */ + public static function configureFacadeWithToolbox( ToolBox $tb ) + { + $oldTools = self::$toolbox; + self::$toolbox = $tb; + self::$writer = self::$toolbox->getWriter(); + self::$adapter = self::$toolbox->getDatabaseAdapter(); + self::$redbean = self::$toolbox->getRedBean(); + self::$finder = new Finder( self::$toolbox ); + self::$associationManager = new AssociationManager( self::$toolbox ); + self::$redbean->setAssociationManager( self::$associationManager ); + self::$labelMaker = new LabelMaker( self::$toolbox ); + $helper = new SimpleModelHelper(); + $helper->attachEventListeners( self::$redbean ); + self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper ); + self::$duplicationManager = new DuplicationManager( self::$toolbox ); + self::$tagManager = new TagManager( self::$toolbox ); + return $oldTools; + } + + /** + * Facade Convience method for adapter transaction system. + * Begins a transaction. + * + * @return bool + */ + public static function begin() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->startTransaction(); + return TRUE; + } + + /** + * Facade Convience method for adapter transaction system. + * Commits a transaction. + * + * @return bool + */ + public static function commit() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->commit(); + return TRUE; + } + + /** + * Facade Convience method for adapter transaction system. + * Rolls back a transaction. + * + * @return bool + */ + public static function rollback() + { + if ( !self::$redbean->isFrozen() ) return FALSE; + self::$adapter->rollback(); + return TRUE; + } + + /** + * Returns a list of columns. Format of this array: + * array( fieldname => type ) + * Note that this method only works in fluid mode because it might be + * quite heavy on production servers! + * + * @param string $table name of the table (not type) you want to get columns of + * + * @return array + */ + public static function getColumns( $table ) + { + return self::$writer->getColumns( $table ); + } + + /** + * Generates question mark slots for an array of values. + * + * @param array $array array to generate question mark slots for + * + * @return string + */ + public static function genSlots( $array, $template = NULL ) + { + return ArrayTool::genSlots( $array, $template ); + } + + /** + * Flattens a multi dimensional bindings array for use with genSlots(). + * + * @param array $array array to flatten + * + * @return array + */ + public static function flat( $array, $result = array() ) + { + return ArrayTool::flat( $array, $result ); + } + + /** + * Nukes the entire database. + * This will remove all schema structures from the database. + * Only works in fluid mode. Be careful with this method. + * + * @warning dangerous method, will remove all tables, columns etc. + * + * @return void + */ + public static function nuke() + { + if ( !self::$redbean->isFrozen() ) { + self::$writer->wipeAll(); + } + } + + /** + * Short hand function to store a set of beans at once, IDs will be + * returned as an array. For information please consult the R::store() + * function. + * A loop saver. + * + * @param array $beans list of beans to be stored + * + * @return array + */ + public static function storeAll( $beans ) + { + $ids = array(); + foreach ( $beans as $bean ) { + $ids[] = self::store( $bean ); + } + return $ids; + } + + /** + * Short hand function to trash a set of beans at once. + * For information please consult the R::trash() function. + * A loop saver. + * + * @param array $beans list of beans to be trashed + * + * @return void + */ + public static function trashAll( $beans ) + { + foreach ( $beans as $bean ) { + self::trash( $bean ); + } + } + + /** + * Toggles Writer Cache. + * Turns the Writer Cache on or off. The Writer Cache is a simple + * query based caching system that may improve performance without the need + * for cache management. This caching system will cache non-modifying queries + * that are marked with special SQL comments. As soon as a non-marked query + * gets executed the cache will be flushed. Only non-modifying select queries + * have been marked therefore this mechanism is a rather safe way of caching, requiring + * no explicit flushes or reloads. Of course this does not apply if you intend to test + * or simulate concurrent querying. + * + * @param boolean $yesNo TRUE to enable cache, FALSE to disable cache + * + * @return void + */ + public static function useWriterCache( $yesNo ) + { + self::getWriter()->setUseCache( $yesNo ); + } + + /** + * A label is a bean with only an id, type and name property. + * This function will dispense beans for all entries in the array. The + * values of the array will be assigned to the name property of each + * individual bean. + * + * @param string $type type of beans you would like to have + * @param array $labels list of labels, names for each bean + * + * @return array + */ + public static function dispenseLabels( $type, $labels ) + { + return self::$labelMaker->dispenseLabels( $type, $labels ); + } + + /** + * Generates and returns an ENUM value. This is how RedBeanPHP handles ENUMs. + * Either returns a (newly created) bean respresenting the desired ENUM + * value or returns a list of all enums for the type. + * + * To obtain (and add if necessary) an ENUM value: + * + * + * $tea->flavour = R::enum( 'flavour:apple' ); + * + * + * Returns a bean of type 'flavour' with name = apple. + * This will add a bean with property name (set to APPLE) to the database + * if it does not exist yet. + * + * To obtain all flavours: + * + * + * R::enum('flavour'); + * + * + * To get a list of all flavour names: + * + * + * R::gatherLabels( R::enum( 'flavour' ) ); + * + * + * @param string $enum either type or type-value + * + * @return array|OODBBean + */ + public static function enum( $enum ) + { + return self::$labelMaker->enum( $enum ); + } + + /** + * Gathers labels from beans. This function loops through the beans, + * collects the values of the name properties of each individual bean + * and stores the names in a new array. The array then gets sorted using the + * default sort function of PHP (sort). + * + * @param array $beans list of beans to loop + * + * @return array + */ + public static function gatherLabels( $beans ) + { + return self::$labelMaker->gatherLabels( $beans ); + } + + /** + * Closes the database connection. + * + * @return void + */ + public static function close() + { + if ( isset( self::$adapter ) ) { + self::$adapter->close(); + } + } + + /** + * Simple convenience function, returns ISO date formatted representation + * of $time. + * + * @param mixed $time UNIX timestamp + * + * @return string + */ + public static function isoDate( $time = NULL ) + { + if ( !$time ) { + $time = time(); + } + + return @date( 'Y-m-d', $time ); + } + + /** + * Simple convenience function, returns ISO date time + * formatted representation + * of $time. + * + * @param mixed $time UNIX timestamp + * + * @return string + */ + public static function isoDateTime( $time = NULL ) + { + if ( !$time ) $time = time(); + return @date( 'Y-m-d H:i:s', $time ); + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param Adapter $adapter Database Adapter for facade to use + * + * @return void + */ + public static function setDatabaseAdapter( Adapter $adapter ) + { + self::$adapter = $adapter; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param QueryWriter $writer Query Writer instance for facade to use + * + * @return void + */ + public static function setWriter( QueryWriter $writer ) + { + self::$writer = $writer; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @param OODB $redbean Object Database for facade to use + */ + public static function setRedBean( OODB $redbean ) + { + self::$redbean = $redbean; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return DBAdapter + */ + public static function getDatabaseAdapter() + { + return self::$adapter; + } + + /** + * In case you use PDO (which is recommended and the default but not mandatory, hence + * the database adapter), you can use this method to obtain the PDO object directly. + * This is a convenience method, it will do the same as: + * + * + * R::getDatabaseAdapter()->getDatabase()->getPDO(); + * + * + * If the PDO object could not be found, for whatever reason, this method + * will return NULL instead. + * + * @return NULL|PDO + */ + public static function getPDO() + { + $databaseAdapter = self::getDatabaseAdapter(); + if ( is_null( $databaseAdapter ) ) return NULL; + $database = $databaseAdapter->getDatabase(); + if ( is_null( $database ) ) return NULL; + if ( !method_exists( $database, 'getPDO' ) ) return NULL; + return $database->getPDO(); + } + + /** + * Returns the current duplication manager instance. + * + * @return DuplicationManager + */ + public static function getDuplicationManager() + { + return self::$duplicationManager; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return QueryWriter + */ + public static function getWriter() + { + return self::$writer; + } + + /** + * Optional accessor for neat code. + * Sets the database adapter you want to use. + * + * @return OODB + */ + public static function getRedBean() + { + return self::$redbean; + } + + /** + * Returns the toolbox currently used by the facade. + * To set the toolbox use R::setup() or R::configureFacadeWithToolbox(). + * To create a toolbox use Setup::kickstart(). Or create a manual + * toolbox using the ToolBox class. + * + * @return ToolBox + */ + public static function getToolBox() + { + return self::$toolbox; + } + + /** + * Mostly for internal use, but might be handy + * for some users. + * This returns all the components of the currently + * selected toolbox. + * + * Returns the components in the following order: + * + * # OODB instance (getRedBean()) + * # Database Adapter + * # Query Writer + * # Toolbox itself + * + * @return array + */ + public static function getExtractedToolbox() + { + return array( self::$redbean, self::$adapter, self::$writer, self::$toolbox ); + } + + /** + * Facade method for AQueryWriter::renameAssociation() + * + * @param string|array $from + * @param string $to + * + * @return void + */ + public static function renameAssociation( $from, $to = NULL ) + { + AQueryWriter::renameAssociation( $from, $to ); + } + + /** + * Little helper method for Resty Bean Can server and others. + * Takes an array of beans and exports each bean. + * Unlike exportAll this method does not recurse into own lists + * and shared lists, the beans are exported as-is, only loaded lists + * are exported. + * + * @param array $beans beans + * + * @return array + */ + public static function beansToArray( $beans ) + { + $list = array(); + foreach( $beans as $bean ) $list[] = $bean->export(); + return $list; + } + + /** + * Sets the error mode for FUSE. + * What to do if a FUSE model method does not exist? + * You can set the following options: + * + * * OODBBean::C_ERR_IGNORE (default), ignores the call, returns NULL + * * OODBBean::C_ERR_LOG, logs the incident using error_log + * * OODBBean::C_ERR_NOTICE, triggers a E_USER_NOTICE + * * OODBBean::C_ERR_WARN, triggers a E_USER_WARNING + * * OODBBean::C_ERR_EXCEPTION, throws an exception + * * OODBBean::C_ERR_FUNC, allows you to specify a custom handler (function) + * * OODBBean::C_ERR_FATAL, triggers a E_USER_ERROR + * + * + * Custom handler method signature: handler( array ( + * 'message' => string + * 'bean' => OODBBean + * 'method' => string + * ) ) + * + * + * This method returns the old mode and handler as an array. + * + * @param integer $mode mode, determines how to handle errors + * @param callable|NULL $func custom handler (if applicable) + * + * @return array + */ + public static function setErrorHandlingFUSE( $mode, $func = NULL ) + { + return OODBBean::setErrorHandlingFUSE( $mode, $func ); + } + + /** + * Simple but effective debug function. + * Given a one or more beans this method will + * return an array containing first part of the string + * representation of each item in the array. + * + * @param OODBBean|array $data either a bean or an array of beans + * + * @return array + */ + public static function dump( $data ) + { + return Dump::dump( $data ); + } + + /** + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. + * + * Example: + * + * + * R::bindFunc( 'read', 'location.point', 'asText' ); + * R::bindFunc( 'write', 'location.point', 'GeomFromText' ); + * + * + * Passing NULL as the function will reset (clear) the function + * for this column/mode. + * + * @param string $mode mode for function: i.e. read or write + * @param string $field field (table.column) to bind function to + * @param string $function SQL function to bind to specified column + * + * @return void + */ + public static function bindFunc( $mode, $field, $function ) + { + self::$redbean->bindFunc( $mode, $field, $function ); + } + + /** + * Sets global aliases. + * Registers a batch of aliases in one go. This works the same as + * fetchAs and setAutoResolve but explicitly. For instance if you register + * the alias 'cover' for 'page' a property containing a reference to a + * page bean called 'cover' will correctly return the page bean and not + * a (non-existant) cover bean. + * + * + * R::aliases( array( 'cover' => 'page' ) ); + * $book = R::dispense( 'book' ); + * $page = R::dispense( 'page' ); + * $book->cover = $page; + * R::store( $book ); + * $book = $book->fresh(); + * $cover = $book->cover; + * echo $cover->getMeta( 'type' ); //page + * + * + * The format of the aliases registration array is: + * + * {alias} => {actual type} + * + * In the example above we use: + * + * cover => page + * + * From that point on, every bean reference to a cover + * will return a 'page' bean. Note that with autoResolve this + * feature along with fetchAs() is no longer very important, although + * relying on explicit aliases can be a bit faster. + * + * @param array $list list of global aliases to use + * + * @return void + */ + public static function aliases( $list ) + { + OODBBean::aliases( $list ); + } + + /** + * Tries to find a bean matching a certain type and + * criteria set. If no beans are found a new bean + * will be created, the criteria will be imported into this + * bean and the bean will be stored and returned. + * If multiple beans match the criteria only the first one + * will be returned. + * + * @param string $type type of bean to search for + * @param array $like criteria set describing the bean to search for + * + * @return OODBBean + */ + public static function findOrCreate( $type, $like = array() ) + { + return self::$finder->findOrCreate( $type, $like ); + } + + /** + * Tries to find beans matching the specified type and + * criteria set. + * + * If the optional additional SQL snippet is a condition, it will + * be glued to the rest of the query using the AND operator. + * + * @param string $type type of bean to search for + * @param array $like optional criteria set describing the bean to search for + * @param string $sql optional additional SQL for sorting + * + * @return array + */ + public static function findLike( $type, $like = array(), $sql = '' ) + { + return self::$finder->findLike( $type, $like, $sql ); + } + + /** + * Starts logging queries. + * Use this method to start logging SQL queries being + * executed by the adapter. + * + * @note you cannot use R::debug and R::startLogging + * at the same time because R::debug is essentially a + * special kind of logging. + * + * @return void + */ + public static function startLogging() + { + self::debug( TRUE, RDefault::C_LOGGER_ARRAY ); + } + + /** + * Stops logging, comfortable method to stop logging of queries. + * + * @return void + */ + public static function stopLogging() + { + self::debug( FALSE ); + } + + /** + * Returns the log entries written after the startLogging. + * + * @return array + */ + public static function getLogs() + { + return self::getLogger()->getLogs(); + } + + /** + * Resets the Query counter. + * + * @return integer + */ + public static function resetQueryCount() + { + self::$adapter->getDatabase()->resetCounter(); + } + + /** + * Returns the number of SQL queries processed. + * + * @return integer + */ + public static function getQueryCount() + { + return self::$adapter->getDatabase()->getQueryCount(); + } + + /** + * Returns the current logger instance being used by the + * database object. + * + * @return Logger + */ + public static function getLogger() + { + return self::$adapter->getDatabase()->getLogger(); + } + + /** + * Alias for setAutoResolve() method on OODBBean. + * Enables or disables auto-resolving fetch types. + * Auto-resolving aliased parent beans is convenient but can + * be slower and can create infinite recursion if you + * used aliases to break cyclic relations in your domain. + * + * @param boolean $automatic TRUE to enable automatic resolving aliased parents + * + * @return void + */ + public static function setAutoResolve( $automatic = TRUE ) + { + OODBBean::setAutoResolve( (boolean) $automatic ); + } + + /** + * Dynamically extends the facade with a plugin. + * Using this method you can register your plugin with the facade and then + * use the plugin by invoking the name specified plugin name as a method on + * the facade. + * + * Usage: + * + * + * R::ext( 'makeTea', function() { ... } ); + * + * + * Now you can use your makeTea plugin like this: + * + * + * R::makeTea(); + * + * + * @param string $pluginName name of the method to call the plugin + * @param callable $callable a PHP callable + * + * @return void + */ + public static function ext( $pluginName, $callable ) + { + if ( !ctype_alnum( $pluginName ) ) { + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); + } + self::$plugins[$pluginName] = $callable; + } + + /** + * Call static for use with dynamic plugins. This magic method will + * intercept static calls and route them to the specified plugin. + * + * @param string $pluginName name of the plugin + * @param array $params list of arguments to pass to plugin method + * + * @return mixed + */ + public static function __callStatic( $pluginName, $params ) + { + if ( !ctype_alnum( $pluginName) ) { + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); + } + if ( !isset( self::$plugins[$pluginName] ) ) { + throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' ); + } + return call_user_func_array( self::$plugins[$pluginName], $params ); + } +} + +} + +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; + +/** + * Duplication Manager + * The Duplication Manager creates deep copies from beans, this means + * it can duplicate an entire bean hierarchy. You can use this feature to + * implement versioning for instance. Because duplication and exporting are + * closely related this class is also used to export beans recursively + * (i.e. we make a duplicate and then convert to array). This class allows + * you to tune the duplication process by specifying filters determining + * which relations to take into account and by specifying tables + * (in which case no reflective queries have to be issued thus improving + * performance). This class also hosts the Camelfy function used to + * reformat the keys of an array, this method is publicly available and + * used internally by exportAll(). + * + * @file RedBeanPHP/DuplicationManager.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DuplicationManager +{ + /** + * @var ToolBox + */ + protected $toolbox; + + /** + * @var AssociationManager + */ + protected $associationManager; + + /** + * @var OODB + */ + protected $redbean; + + /** + * @var array + */ + protected $tables = array(); + + /** + * @var array + */ + protected $columns = array(); + + /** + * @var array + */ + protected $filters = array(); + + /** + * @var array + */ + protected $cacheTables = FALSE; + + /** + * Copies the shared beans in a bean, i.e. all the sharedBean-lists. + * + * @param OODBBean $copy target bean to copy lists to + * @param string $shared name of the shared list + * @param array $beans array with shared beans to copy + * + * @return void + */ + private function copySharedBeans( OODBBean $copy, $shared, $beans ) + { + $copy->$shared = array(); + + foreach ( $beans as $subBean ) { + array_push( $copy->$shared, $subBean ); + } + } + + /** + * Copies the own beans in a bean, i.e. all the ownBean-lists. + * Each bean in the own-list belongs exclusively to its owner so + * we need to invoke the duplicate method again to duplicate each bean here. + * + * @param OODBBean $copy target bean to copy lists to + * @param string $owned name of the own list + * @param array $beans array with shared beans to copy + * @param array $trail array with former beans to detect recursion + * @param boolean $preserveIDs TRUE means preserve IDs, for export only + * + * @return void + */ + private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs ) + { + $copy->$owned = array(); + foreach ( $beans as $subBean ) { + array_push( $copy->$owned, $this->duplicate( $subBean, $trail, $preserveIDs ) ); + } + } + + /** + * Creates a copy of bean $bean and copies all primitive properties (not lists) + * and the parents beans to the newly created bean. Also sets the ID of the bean + * to 0. + * + * @param OODBBean $bean bean to copy + * + * @return OODBBean + */ + private function createCopy( OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + + $copy = $this->redbean->dispense( $type ); + $copy->setMeta( 'sys.dup-from-id', $bean->id ); + $copy->setMeta( 'sys.old-id', $bean->id ); + $copy->importFrom( $bean ); + $copy->id = 0; + + return $copy; + } + + /** + * Generates a key from the bean type and its ID and determines if the bean + * occurs in the trail, if not the bean will be added to the trail. + * Returns TRUE if the bean occurs in the trail and FALSE otherwise. + * + * @param array $trail list of former beans + * @param OODBBean $bean currently selected bean + * + * @return boolean + */ + private function inTrailOrAdd( &$trail, OODBBean $bean ) + { + $type = $bean->getMeta( 'type' ); + $key = $type . $bean->getID(); + + if ( isset( $trail[$key] ) ) { + return TRUE; + } + + $trail[$key] = $bean; + + return FALSE; + } + + /** + * Given the type name of a bean this method returns the canonical names + * of the own-list and the shared-list properties respectively. + * Returns a list with two elements: name of the own-list, and name + * of the shared list. + * + * @param string $typeName bean type name + * + * @return array + */ + private function getListNames( $typeName ) + { + $owned = 'own' . ucfirst( $typeName ); + $shared = 'shared' . ucfirst( $typeName ); + + return array( $owned, $shared ); + } + + /** + * Determines whether the bean has an own list based on + * schema inspection from realtime schema or cache. + * + * @param string $type bean type to get list for + * @param string $target type of list you want to detect + * + * @return boolean + */ + protected function hasOwnList( $type, $target ) + { + return isset( $this->columns[$target][$type . '_id'] ); + } + + /** + * Determines whether the bea has a shared list based on + * schema inspection from realtime schema or cache. + * + * @param string $type bean type to get list for + * @param string $target type of list you are looking for + * + * @return boolean + */ + protected function hasSharedList( $type, $target ) + { + return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables ); + } + + /** + * @see DuplicationManager::dup + * + * @param OODBBean $bean bean to be copied + * @param array $trail trail to prevent infinite loops + * @param boolean $preserveIDs preserve IDs + * + * @return OODBBean + */ + protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + { + if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean; + + $type = $bean->getMeta( 'type' ); + + $copy = $this->createCopy( $bean ); + foreach ( $this->tables as $table ) { + + if ( !empty( $this->filters ) ) { + if ( !in_array( $table, $this->filters ) ) continue; + } + + list( $owned, $shared ) = $this->getListNames( $table ); + + if ( $this->hasSharedList( $type, $table ) ) { + if ( $beans = $bean->$shared ) { + $this->copySharedBeans( $copy, $shared, $beans ); + } + } elseif ( $this->hasOwnList( $type, $table ) ) { + if ( $beans = $bean->$owned ) { + $this->copyOwnBeans( $copy, $owned, $beans, $trail, $preserveIDs ); + } + + $copy->setMeta( 'sys.shadow.' . $owned, NULL ); + } + + $copy->setMeta( 'sys.shadow.' . $shared, NULL ); + } + + $copy->id = ( $preserveIDs ) ? $bean->id : $copy->id; + + return $copy; + } + + /** + * Constructor, + * creates a new instance of DupManager. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + $this->associationManager = $this->redbean->getAssociationManager(); + } + + /** + * Recursively turns the keys of an array into + * camelCase. + * + * @param array $array array to camelize + * @param boolean $dolphinMode whether you want the exception for IDs. + * + * @return array + */ + public function camelfy( $array, $dolphinMode = false ) { + $newArray = array(); + foreach( $array as $key => $element ) { + $newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){ + return strtoupper( $matches[1] ); + }, $key); + + if ( $dolphinMode ) { + $newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey ); + } + + $newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element; + } + return $newArray; + } + + /** + * For better performance you can pass the tables in an array to this method. + * If the tables are available the duplication manager will not query them so + * this might be beneficial for performance. + * + * This method allows two array formats: + * + * + * array( TABLE1, TABLE2 ... ) + * + * + * or + * + * + * array( TABLE1 => array( COLUMN1, COLUMN2 ... ) ... ) + * + * + * @param array $tables a table cache array + * + * @return void + */ + public function setTables( $tables ) + { + foreach ( $tables as $key => $value ) { + if ( is_numeric( $key ) ) { + $this->tables[] = $value; + } else { + $this->tables[] = $key; + $this->columns[$key] = $value; + } + } + + $this->cacheTables = TRUE; + } + + /** + * Returns a schema array for cache. + * You can use the return value of this method as a cache, + * store it in RAM or on disk and pass it to setTables later. + * + * @return array + */ + public function getSchema() + { + return $this->columns; + } + + /** + * Indicates whether you want the duplication manager to cache the database schema. + * If this flag is set to TRUE the duplication manager will query the database schema + * only once. Otherwise the duplicationmanager will, by default, query the schema + * every time a duplication action is performed (dup()). + * + * @param boolean $yesNo TRUE to use caching, FALSE otherwise + */ + public function setCacheTables( $yesNo ) + { + $this->cacheTables = $yesNo; + } + + /** + * A filter array is an array with table names. + * By setting a table filter you can make the duplication manager only take into account + * certain bean types. Other bean types will be ignored when exporting or making a + * deep copy. If no filters are set all types will be taking into account, this is + * the default behavior. + * + * @param array $filters list of tables to be filtered + * + * @return void + */ + public function setFilters( $filters ) + { + if ( !is_array( $filters ) ) { + $filters = array( $filters ); + } + + $this->filters = $filters; + } + + /** + * Makes a copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following features. + * - All beans in own-lists will be duplicated as well + * - All references to shared beans will be copied but not the shared beans themselves + * - All references to parent objects (_id fields) will be copied but not the parents themselves + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * Note: + * this function actually passes the arguments to a protected function called + * duplicate() that does all the work. This method takes care of creating a clone + * of the bean to avoid the bean getting tainted (triggering saving when storing it). + * + * @param OODBBean $bean bean to be copied + * @param array $trail for internal usage, pass array() + * @param boolean $preserveIDs for internal usage + * + * @return OODBBean + */ + public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + { + if ( !count( $this->tables ) ) { + $this->tables = $this->toolbox->getWriter()->getTables(); + } + + if ( !count( $this->columns ) ) { + foreach ( $this->tables as $table ) { + $this->columns[$table] = $this->toolbox->getWriter()->getColumns( $table ); + } + } + + $rs = $this->duplicate( ( clone $bean ), $trail, $preserveIDs ); + + if ( !$this->cacheTables ) { + $this->tables = array(); + $this->columns = array(); + } + + return $rs; + } + + /** + * Exports a collection of beans recursively. + * This method will export an array of beans in the first argument to a + * set of arrays. This can be used to send JSON or XML representations + * of bean hierarchies to the client. + * + * For every bean in the array this method will export: + * + * - contents of the bean + * - all own bean lists (recursively) + * - all shared beans (but not THEIR own lists) + * + * If the second parameter is set to TRUE the parents of the beans in the + * array will be exported as well (but not THEIR parents). + * + * The third parameter can be used to provide a white-list array + * for filtering. This is an array of strings representing type names, + * only the type names in the filter list will be exported. + * + * The fourth parameter can be used to change the keys of the resulting + * export arrays. The default mode is 'snake case' but this leaves the + * keys as-is, because 'snake' is the default case style used by + * RedBeanPHP in the database. You can set this to 'camel' for + * camel cased keys or 'dolphin' (same as camelcase but id will be + * converted to ID instead of Id). + * + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents also export parents + * @param array $filters only these types (whitelist) + * @param string $caseStyle case style identifier + * + * @return array + */ + public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake') + { + $array = array(); + + if ( !is_array( $beans ) ) { + $beans = array( $beans ); + } + + foreach ( $beans as $bean ) { + $this->setFilters( $filters ); + + $duplicate = $this->dup( $bean, array(), TRUE ); + + $array[] = $duplicate->export( FALSE, $parents, FALSE, $filters ); + } + + if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array ); + if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true ); + + return $array; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException as RedException; + +/** + * Array Tool Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * This is a helper or service class containing frequently used + * array functions for dealing with SQL queries. + * + * @file RedBeanPHP/Util/ArrayTool.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class ArrayTool +{ + /** + * Generates question mark slots for an array of values. + * + * @param array $array array to generate question mark slots for + * + * @return string + */ + public static function genSlots( $array, $template = NULL ) + { + $str = count( $array ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : ''; + return ( is_null( $template ) || $str === '' ) ? $str : sprintf( $template, $str ); + } + + /** + * Flattens a multi dimensional bindings array for use with genSlots(). + * + * @param array $array array to flatten + * @param array $result result array parameter (for recursion) + * + * @return array + */ + public static function flat( $array, $result = array() ) + { + foreach( $array as $value ) { + if ( is_array( $value ) ) $result = self::flat( $value, $result ); + else $result[] = $value; + } + return $result; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\RedException as RedException; + +/** + * Dispense Helper + * + * A helper class containing a dispense utility. + * + * @file RedBeanPHP/Util/DispenseHelper.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DispenseHelper +{ + /** + * Dispenses a new RedBean OODB Bean for use with + * the rest of the methods. + * + * @param OODB $oodb OODB + * @param string|array $typeOrBeanArray type or bean array to import + * @param integer $number number of beans to dispense + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return array|OODBBean + */ + public static function dispense( OODB $oodb, $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) { + + if ( is_array($typeOrBeanArray) ) { + + if ( !isset( $typeOrBeanArray['_type'] ) ) { + $list = array(); + foreach( $typeOrBeanArray as $beanArray ) { + if ( + !( is_array( $beanArray ) + && isset( $beanArray['_type'] ) ) ) { + throw new RedException( 'Invalid Array Bean' ); + } + } + foreach( $typeOrBeanArray as $beanArray ) $list[] = self::dispense( $oodb, $beanArray ); + return $list; + } + + $import = $typeOrBeanArray; + $type = $import['_type']; + unset( $import['_type'] ); + } else { + $type = $typeOrBeanArray; + } + + if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) { + throw new RedException( 'Invalid type: ' . $type ); + } + + $beanOrBeans = $oodb->dispense( $type, $num, $alwaysReturnArray ); + + if ( isset( $import ) ) { + $beanOrBeans->import( $import ); + } + + return $beanOrBeans; + } + + + /** + * Takes a comma separated list of bean types + * and dispenses these beans. For each type in the list + * you can specify the number of beans to be dispensed. + * + * Usage: + * + * + * list( $book, $page, $text ) = R::dispenseAll( 'book,page,text' ); + * + * + * This will dispense a book, a page and a text. This way you can + * quickly dispense beans of various types in just one line of code. + * + * Usage: + * + * + * list($book, $pages) = R::dispenseAll('book,page*100'); + * + * + * This returns an array with a book bean and then another array + * containing 100 page beans. + * + * @param OODB $oodb OODB + * @param string $order a description of the desired dispense order using the syntax above + * @param boolean $onlyArrays return only arrays even if amount < 2 + * + * @return array + */ + public static function dispenseAll( OODB $oodb, $order, $onlyArrays = FALSE ) + { + $list = array(); + + foreach( explode( ',', $order ) as $order ) { + if ( strpos( $order, '*' ) !== FALSE ) { + list( $type, $amount ) = explode( '*', $order ); + } else { + $type = $order; + $amount = 1; + } + + $list[] = self::dispense( $oodb, $type, $amount, $onlyArrays ); + } + + return $list; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Dump helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * Dumps the contents of a bean in an array for + * debugging purposes. + * + * @file RedBeanPHP/Util/Dump.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Dump +{ + /** + * Simple but effective debug function. + * Given a one or more beans this method will + * return an array containing first part of the string + * representation of each item in the array. + * + * @param OODBBean|array $data either a bean or an array of beans + * + * @return array + */ + public static function dump( $data ) + { + $array = array(); + + if ( $data instanceof OODBBean ) { + $str = strval( $data ); + if (strlen($str) > 35) { + $beanStr = substr( $str, 0, 35 ).'... '; + } else { + $beanStr = $str; + } + return $beanStr; + } + + if ( is_array( $data ) ) { + foreach( $data as $key => $item ) { + $array[$key] = self::dump( $item ); + } + } + + return $array; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; + +/** + * Multi Bean Loader Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * This helper class offers limited support for one-to-one + * relations by providing a service to load a set of beans + * with differnt types and a common ID. + * + * @file RedBeanPHP/Util/MultiLoader.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class MultiLoader +{ + /** + * Loads multiple types of beans with the same ID. + * This might look like a strange method, however it can be useful + * for loading a one-to-one relation. + * + * @param OODB $oodb OODB object + * @param string|array $types the set of types to load at once + * @param mixed $id the common ID + * + * @return OODBBean + */ + public static function load( OODB $oodb, $types, $id ) + { + if ( is_string( $types ) ) { + $types = explode( ',', $types ); + } + + if ( !is_array( $types ) ) { + return array(); + } + + foreach ( $types as $k => $typeItem ) { + $types[$k] = $oodb->load( $typeItem, $id ); + } + + return $types; + } +} +} + +namespace RedBeanPHP\Util { + +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\Adapter as Adapter; + +/** + * Transaction Helper + * + * This code was originally part of the facade, however it has + * been decided to remove unique features to service classes like + * this to make them available to developers not using the facade class. + * + * Database transaction helper. This is a convenience class + * to perform a callback in a database transaction. This class + * contains a method to wrap your callback in a transaction. + * + * @file RedBeanPHP/Util/Transaction.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Transaction +{ + /** + * Wraps a transaction around a closure or string callback. + * If an Exception is thrown inside, the operation is automatically rolled back. + * If no Exception happens, it commits automatically. + * It also supports (simulated) nested transactions (that is useful when + * you have many methods that needs transactions but are unaware of + * each other). + * + * Example: + * + * + * $from = 1; + * $to = 2; + * $amount = 300; + * + * R::transaction(function() use($from, $to, $amount) + * { + * $accountFrom = R::load('account', $from); + * $accountTo = R::load('account', $to); + * $accountFrom->money -= $amount; + * $accountTo->money += $amount; + * R::store($accountFrom); + * R::store($accountTo); + * }); + * + * + * @param Adapter $adapter Database Adapter providing transaction mechanisms. + * @param callable $callback Closure (or other callable) with the transaction logic + * + * @return mixed + */ + public static function transaction( Adapter $adapter, $callback ) + { + if ( !is_callable( $callback ) ) { + throw new RedException( 'R::transaction needs a valid callback.' ); + } + + static $depth = 0; + $result = null; + try { + if ( $depth == 0 ) { + $adapter->startTransaction(); + } + $depth++; + $result = call_user_func( $callback ); //maintain 5.2 compatibility + $depth--; + if ( $depth == 0 ) { + $adapter->commit(); + } + } catch ( \Exception $exception ) { + $depth--; + if ( $depth == 0 ) { + $adapter->rollback(); + } + throw $exception; + } + return $result; + } +} +} + +namespace RedBeanPHP { + +/** + * RedBean Plugin. + * Marker interface for plugins. + * Use this interface when defining new plugins, it's an + * easy way for the rest of the application to recognize your + * plugin. This plugin interface does not require you to + * implement a specific API. + * + * @file RedBean/Plugin.php + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Plugin +{ +} + +; +} +namespace { + +//make some classes available for backward compatibility +class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {}; + +if (!class_exists('R')) { + class R extends \RedBeanPHP\Facade{}; +} + + + +/** + * Support functions for RedBeanPHP. + * Additional convenience shortcut functions for RedBeanPHP. + * + * @file RedBeanPHP/Functions.php + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * @copyright + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ + +/** + * Convenience function for ENUM short syntax in queries. + * + * Usage: + * + * + * R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] ); + * + * + * If a function called EID() already exists you'll have to write this + * wrapper yourself ;) + * + * @param string $enumName enum code as you would pass to R::enum() + * + * @return mixed + */ +if (!function_exists('EID')) { + + function EID($enumName) + { + return \RedBeanPHP\Facade::enum( $enumName )->id; + } + +} + +/** + * Prints the result of R::dump() to the screen using + * print_r. + * + * @param mixed $data data to dump + * + * @return void + */ +if ( !function_exists( 'dmp' ) ) { + + function dmp( $list ) + { + print_r( \RedBeanPHP\Facade::dump( $list ) ); + } +} + +/** + * Function alias for R::genSlots(). + */ +if ( !function_exists( 'genslots' ) ) { + + function genslots( $slots, $tpl = NULL ) + { + return \RedBeanPHP\Facade::genSlots( $slots, $tpl ); + } +} + +/** + * Function alias for R::flat(). + */ +if ( !function_exists( 'array_flatten' ) ) { + + function array_flatten( $array ) + { + return \RedBeanPHP\Facade::flat( $array ); + } +} + + +} From d13d3e108f9b64acdce99e5235a99cb059dfbf36 Mon Sep 17 00:00:00 2001 From: Smony Date: Wed, 21 Jun 2017 15:06:08 +0300 Subject: [PATCH 3/6] testing RedBeanPHP --- .htaccess | 4 ++-- tests/index.php | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.htaccess b/.htaccess index ced2c2c..9064ba4 100644 --- a/.htaccess +++ b/.htaccess @@ -1,6 +1,6 @@ AddDefaultCharset utf-8 RewriteEngine On -#RewriteRule ^(.*)$ public/$1 +RewriteRule ^(.*)$ public/$1 -RewriteRule ^(.*)$ tests/$1 +#RewriteRule ^(.*)$ tests/$1 diff --git a/tests/index.php b/tests/index.php index ffca59e..83da5bd 100644 --- a/tests/index.php +++ b/tests/index.php @@ -6,6 +6,8 @@ //R::setup( 'mysql:host=localhost;dbname=mydatabase', 'myusername', 'mypassword' ); R::setup($db['dsn'], $db['user'], $db['pass']); + R::freeze( TRUE ); //не изменять размер колонок + R::fancyDebug( TRUE ); //включить отладчик on-true off-false /* if(R::testConnection() == true) { @@ -16,14 +18,36 @@ echo "Проверьте соединение с базой!"; } */ - //Create tets table Category - $category = R::dispense('category'); + //CREATE + // $category = R::dispense('category'); - $category->title = 'Name'; - $category->text = 'Text'; + // $category->title = 'new title5'; + // $category->text = 'new text5'; - $res = R::store($category); - // $category = R::load('category', 9); + // $res = R::store($category); + + + //READ +/* $res = R::load('category', 27); + echo $res->title; + echo $res['title']; */ + + // UPDATE + // $cat_name = R::load('category', 6); + // $cat_name->title = 'new title6'; + // $cat_name->text = 'new text6'; + // R::store($cat_name); + // echo $cat_name->title .'
'; + + // DELETE + // $delete = R::load('category', 26); + // R::trash($delete); + // R::wipe( 'category' ); + + // $res = R::findAll('category', 'id > ?', [5]); + // $res = R::findAll('category', 'title LIKE ?', ['%cat%']); + // $res = R::findOne('category', 'id = 2'); + // $res = R::findOne('category', 'id = 2'); + // dd($res); - \ No newline at end of file From f86136e51ac63ea82798d81fb359bac0dcd05b69 Mon Sep 17 00:00:00 2001 From: Smony Date: Thu, 22 Jun 2017 11:12:52 +0300 Subject: [PATCH 4/6] testing Registry config components --- .htaccess | 4 +-- tests/classes/Cache.php | 7 ++++ tests/classes/Test.php | 11 ++++++ tests/classes/Test2.php | 11 ++++++ tests/reg.php | 77 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 tests/classes/Cache.php create mode 100644 tests/classes/Test.php create mode 100644 tests/classes/Test2.php create mode 100644 tests/reg.php diff --git a/.htaccess b/.htaccess index 9064ba4..ced2c2c 100644 --- a/.htaccess +++ b/.htaccess @@ -1,6 +1,6 @@ AddDefaultCharset utf-8 RewriteEngine On -RewriteRule ^(.*)$ public/$1 +#RewriteRule ^(.*)$ public/$1 -#RewriteRule ^(.*)$ tests/$1 +RewriteRule ^(.*)$ tests/$1 diff --git a/tests/classes/Cache.php b/tests/classes/Cache.php new file mode 100644 index 0000000..28db470 --- /dev/null +++ b/tests/classes/Cache.php @@ -0,0 +1,7 @@ + [ + 'cache' => 'classes\Cache', + 'test' => 'classes\Test', + ], + ]; + + +class Reg { + + protected static $objects = []; + protected static $instance; + + public function __construct() + { + global $config; + + foreach($config['components'] as $name => $component){ + self::$objects[$name] = new $component; + } + } + + public static function instance() + { + if(self::$instance === null) + { + self::$instance = new self; + } + return self::$instance; + } + + public function __get($name) + { + if(is_object(self::$objects[$name])) + { + return self::$objects[$name]; + } + } + + public function __set($name, $value) + { + if(!isset(self::$objects[$name])) + { + self::$objects[$name] = new $value; + } + } + + public function getComponents() + { + echo '
';
+		var_dump(self::$objects);
+		echo '
'; + } + +} + + $app = new Reg(); + $app->getComponents(); + $app->test->go(); + $app->test2 = 'classes\Test2'; + $app->getComponents(); + $app->test2->hi(); + + //когда обращаемся к неизвестному свойству срабатывает Магической метод __get() возвращает NULL; + // $test = $app->test; + // dd($test); \ No newline at end of file From c7dab0a8a343598fccd2d2cb72e78e3a5d108a85 Mon Sep 17 00:00:00 2001 From: Smony Date: Fri, 23 Jun 2017 16:19:19 +0300 Subject: [PATCH 5/6] Added testing error Handler and logs --- .idea/dataSources.ids | 115 + .idea/dataSources.local.xml | 11 + .idea/dataSources.xml | 18 + .idea/workspace.xml | 448 ++ tests/bootstrap/css/bootstrap-theme.css | 587 ++ tests/bootstrap/css/bootstrap-theme.css.map | 1 + tests/bootstrap/css/bootstrap-theme.min.css | 6 + .../bootstrap/css/bootstrap-theme.min.css.map | 1 + tests/bootstrap/css/bootstrap.css | 6757 +++++++++++++++++ tests/bootstrap/css/bootstrap.css.map | 1 + tests/bootstrap/css/bootstrap.min.css | 6 + tests/bootstrap/css/bootstrap.min.css.map | 1 + .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../fonts/glyphicons-halflings-regular.svg | 288 + .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes tests/bootstrap/js/bootstrap.js | 2377 ++++++ tests/bootstrap/js/bootstrap.min.js | 7 + tests/bootstrap/js/npm.js | 13 + tests/error/errors.log | 10 + tests/error/index.php | 101 + tests/error/views/developer.php | 32 + tests/error/views/prodaction.php | 25 + 24 files changed, 10805 insertions(+) create mode 100644 .idea/dataSources.ids create mode 100644 .idea/dataSources.local.xml create mode 100644 .idea/dataSources.xml create mode 100644 .idea/workspace.xml create mode 100644 tests/bootstrap/css/bootstrap-theme.css create mode 100644 tests/bootstrap/css/bootstrap-theme.css.map create mode 100644 tests/bootstrap/css/bootstrap-theme.min.css create mode 100644 tests/bootstrap/css/bootstrap-theme.min.css.map create mode 100644 tests/bootstrap/css/bootstrap.css create mode 100644 tests/bootstrap/css/bootstrap.css.map create mode 100644 tests/bootstrap/css/bootstrap.min.css create mode 100644 tests/bootstrap/css/bootstrap.min.css.map create mode 100644 tests/bootstrap/fonts/glyphicons-halflings-regular.eot create mode 100644 tests/bootstrap/fonts/glyphicons-halflings-regular.svg create mode 100644 tests/bootstrap/fonts/glyphicons-halflings-regular.ttf create mode 100644 tests/bootstrap/fonts/glyphicons-halflings-regular.woff create mode 100644 tests/bootstrap/fonts/glyphicons-halflings-regular.woff2 create mode 100644 tests/bootstrap/js/bootstrap.js create mode 100644 tests/bootstrap/js/bootstrap.min.js create mode 100644 tests/bootstrap/js/npm.js create mode 100644 tests/error/errors.log create mode 100644 tests/error/index.php create mode 100644 tests/error/views/developer.php create mode 100644 tests/error/views/prodaction.php diff --git a/.idea/dataSources.ids b/.idea/dataSources.ids new file mode 100644 index 0000000..1f6f1f0 --- /dev/null +++ b/.idea/dataSources.ids @@ -0,0 +1,115 @@ + + + + + #@ + ` + + + + +
+
+
+
+ + 1 + int(11) unsigned + + + varchar(191) + + + varchar(191) + + + id + 1 + + + 1 + int(11) + + + 1 + char(35) + '' + + + 1 + char(3) + '' + + + 1 + char(20) + '' + + + 1 + int(11) + '0' + + + CountryCode + + + + ID + 1 + + + 1 + int(11) + + + 1 + varchar(255) + + + 1 + text + + + 1 + date + + + 1 + varchar(255) + + + 1 + varchar(255) + + + id + 1 + + + 1 + int(11) + + + 1 + varchar(255) + + + 1 + varchar(255) + + + 1 + varchar(255) + + + 1 + varchar(255) + + + id + 1 + + + + \ No newline at end of file diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..f957cb1 --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,11 @@ + + + + + master_key + root + fw.* + fw.* + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..fa13bee --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,18 @@ + + + + + mysql + true + com.mysql.jdbc.Driver + jdbc:mysql://localhost:3306/fw + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..2a383ea --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1498205910643 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/bootstrap/css/bootstrap-theme.css b/tests/bootstrap/css/bootstrap-theme.css new file mode 100644 index 0000000..31d8882 --- /dev/null +++ b/tests/bootstrap/css/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/tests/bootstrap/css/bootstrap-theme.css.map b/tests/bootstrap/css/bootstrap-theme.css.map new file mode 100644 index 0000000..d876f60 --- /dev/null +++ b/tests/bootstrap/css/bootstrap-theme.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/tests/bootstrap/css/bootstrap-theme.min.css b/tests/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 0000000..5e39401 --- /dev/null +++ b/tests/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/tests/bootstrap/css/bootstrap-theme.min.css.map b/tests/bootstrap/css/bootstrap-theme.min.css.map new file mode 100644 index 0000000..94813e9 --- /dev/null +++ b/tests/bootstrap/css/bootstrap-theme.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/tests/bootstrap/css/bootstrap.css b/tests/bootstrap/css/bootstrap.css new file mode 100644 index 0000000..6167622 --- /dev/null +++ b/tests/bootstrap/css/bootstrap.css @@ -0,0 +1,6757 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/tests/bootstrap/css/bootstrap.css.map b/tests/bootstrap/css/bootstrap.css.map new file mode 100644 index 0000000..f010c82 --- /dev/null +++ b/tests/bootstrap/css/bootstrap.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,iBAAA;CH8O9C;AG7OmC;EAAW,iBAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EEnDA,2CAAA;EACA,qBAAA;CNokCD;AIvgCD;EACE,UAAA;CJygCD;AIngCD;EACE,uBAAA;CJqgCD;AIjgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CP+kCD;AIrgCD;EACE,mBAAA;CJugCD;AIjgCD;EACE,aAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CP+lCD;AIjgCD;EACE,mBAAA;CJmgCD;AI7/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJ+/BD;AIv/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJy/BD;AIj/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJm/BH;AIx+BD;EACE,gBAAA;CJ0+BD;AQjoCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR6oCD;AQlpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRmqCH;AQ/pCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRoqCD;AQxqCD;;;;;;;;;;;;EAQI,eAAA;CR8qCH;AQ3qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRgrCD;AQprCD;;;;;;;;;;;;EAQI,eAAA;CR0rCH;AQtrCD;;EAAU,gBAAA;CR0rCT;AQzrCD;;EAAU,gBAAA;CR6rCT;AQ5rCD;;EAAU,gBAAA;CRgsCT;AQ/rCD;;EAAU,gBAAA;CRmsCT;AQlsCD;;EAAU,gBAAA;CRssCT;AQrsCD;;EAAU,gBAAA;CRysCT;AQnsCD;EACE,iBAAA;CRqsCD;AQlsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRosCD;AQ/rCD;EAwOA;IA1OI,gBAAA;GRqsCD;CACF;AQ7rCD;;EAEE,eAAA;CR+rCD;AQ5rCD;;EAEE,0BAAA;EACA,cAAA;CR8rCD;AQ1rCD;EAAuB,iBAAA;CR6rCtB;AQ5rCD;EAAuB,kBAAA;CR+rCtB;AQ9rCD;EAAuB,mBAAA;CRisCtB;AQhsCD;EAAuB,oBAAA;CRmsCtB;AQlsCD;EAAuB,oBAAA;CRqsCtB;AQlsCD;EAAuB,0BAAA;CRqsCtB;AQpsCD;EAAuB,0BAAA;CRusCtB;AQtsCD;EAAuB,2BAAA;CRysCtB;AQtsCD;EACE,eAAA;CRwsCD;AQtsCD;ECrGE,eAAA;CT8yCD;AS7yCC;;EAEE,eAAA;CT+yCH;AQ1sCD;ECxGE,eAAA;CTqzCD;ASpzCC;;EAEE,eAAA;CTszCH;AQ9sCD;EC3GE,eAAA;CT4zCD;AS3zCC;;EAEE,eAAA;CT6zCH;AQltCD;EC9GE,eAAA;CTm0CD;ASl0CC;;EAEE,eAAA;CTo0CH;AQttCD;ECjHE,eAAA;CT00CD;ASz0CC;;EAEE,eAAA;CT20CH;AQttCD;EAGE,YAAA;EE3HA,0BAAA;CVk1CD;AUj1CC;;EAEE,0BAAA;CVm1CH;AQxtCD;EE9HE,0BAAA;CVy1CD;AUx1CC;;EAEE,0BAAA;CV01CH;AQ5tCD;EEjIE,0BAAA;CVg2CD;AU/1CC;;EAEE,0BAAA;CVi2CH;AQhuCD;EEpIE,0BAAA;CVu2CD;AUt2CC;;EAEE,0BAAA;CVw2CH;AQpuCD;EEvIE,0BAAA;CV82CD;AU72CC;;EAEE,0BAAA;CV+2CH;AQnuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRquCD;AQ7tCD;;EAEE,cAAA;EACA,oBAAA;CR+tCD;AQluCD;;;;EAMI,iBAAA;CRkuCH;AQ3tCD;EACE,gBAAA;EACA,iBAAA;CR6tCD;AQztCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR4tCD;AQ9tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR4tCH;AQvtCD;EACE,cAAA;EACA,oBAAA;CRytCD;AQvtCD;;EAEE,wBAAA;CRytCD;AQvtCD;EACE,kBAAA;CRytCD;AQvtCD;EACE,eAAA;CRytCD;AQhsCD;EA6EA;IAvFM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXq6CC;EQ7nCH;IAhFM,mBAAA;GRgtCH;CACF;AQvsCD;;EAGE,aAAA;EACA,kCAAA;CRwsCD;AQtsCD;EACE,eAAA;EA9IqB,0BAAA;CRu1CtB;AQpsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRssCD;AQjsCG;;;EACE,iBAAA;CRqsCL;AQ/sCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRisCH;AQ/rCG;;;EACE,uBAAA;CRmsCL;AQ3rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR6rCD;AQvrCG;;;;;;EAAW,YAAA;CR+rCd;AQ9rCG;;;;;;EACE,uBAAA;CRqsCL;AQ/rCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRisCD;AYv+CD;;;;EAIE,+DAAA;CZy+CD;AYr+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZu+CD;AYn+CD;EACE,iBAAA;EACA,eAAA;EACA,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZq+CD;AY3+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZq+CH;AYh+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;CZk+CD;AY7+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZi+CH;AY59CD;EACE,kBAAA;EACA,mBAAA;CZ89CD;AaxhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd8hDD;AaxhDC;EAqEF;IAvEI,aAAA;Gb8hDD;CACF;Aa1hDC;EAkEF;IApEI,aAAA;GbgiDD;CACF;Aa5hDD;EA+DA;IAjEI,cAAA;GbkiDD;CACF;AazhDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdmjDD;AathDD;ECvBE,mBAAA;EACA,oBAAA;CdgjDD;AehjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfgjDL;AehiDG;EACE,YAAA;CfkiDL;Ae3hDC;EACE,YAAA;Cf6hDH;Ae9hDC;EACE,oBAAA;CfgiDH;AejiDC;EACE,oBAAA;CfmiDH;AepiDC;EACE,WAAA;CfsiDH;AeviDC;EACE,oBAAA;CfyiDH;Ae1iDC;EACE,oBAAA;Cf4iDH;Ae7iDC;EACE,WAAA;Cf+iDH;AehjDC;EACE,oBAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,WAAA;CfwjDH;AezjDC;EACE,oBAAA;Cf2jDH;Ae5jDC;EACE,mBAAA;Cf8jDH;AehjDC;EACE,YAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,oBAAA;CfwjDH;AezjDC;EACE,WAAA;Cf2jDH;Ae5jDC;EACE,oBAAA;Cf8jDH;Ae/jDC;EACE,oBAAA;CfikDH;AelkDC;EACE,WAAA;CfokDH;AerkDC;EACE,oBAAA;CfukDH;AexkDC;EACE,oBAAA;Cf0kDH;Ae3kDC;EACE,WAAA;Cf6kDH;Ae9kDC;EACE,oBAAA;CfglDH;AejlDC;EACE,mBAAA;CfmlDH;Ae/kDC;EACE,YAAA;CfilDH;AejmDC;EACE,WAAA;CfmmDH;AepmDC;EACE,mBAAA;CfsmDH;AevmDC;EACE,mBAAA;CfymDH;Ae1mDC;EACE,UAAA;Cf4mDH;Ae7mDC;EACE,mBAAA;Cf+mDH;AehnDC;EACE,mBAAA;CfknDH;AennDC;EACE,UAAA;CfqnDH;AetnDC;EACE,mBAAA;CfwnDH;AeznDC;EACE,mBAAA;Cf2nDH;Ae5nDC;EACE,UAAA;Cf8nDH;Ae/nDC;EACE,mBAAA;CfioDH;AeloDC;EACE,kBAAA;CfooDH;AehoDC;EACE,WAAA;CfkoDH;AepnDC;EACE,kBAAA;CfsnDH;AevnDC;EACE,0BAAA;CfynDH;Ae1nDC;EACE,0BAAA;Cf4nDH;Ae7nDC;EACE,iBAAA;Cf+nDH;AehoDC;EACE,0BAAA;CfkoDH;AenoDC;EACE,0BAAA;CfqoDH;AetoDC;EACE,iBAAA;CfwoDH;AezoDC;EACE,0BAAA;Cf2oDH;Ae5oDC;EACE,0BAAA;Cf8oDH;Ae/oDC;EACE,iBAAA;CfipDH;AelpDC;EACE,0BAAA;CfopDH;AerpDC;EACE,yBAAA;CfupDH;AexpDC;EACE,gBAAA;Cf0pDH;Aa1pDD;EElCI;IACE,YAAA;Gf+rDH;EexrDD;IACE,YAAA;Gf0rDD;Ee3rDD;IACE,oBAAA;Gf6rDD;Ee9rDD;IACE,oBAAA;GfgsDD;EejsDD;IACE,WAAA;GfmsDD;EepsDD;IACE,oBAAA;GfssDD;EevsDD;IACE,oBAAA;GfysDD;Ee1sDD;IACE,WAAA;Gf4sDD;Ee7sDD;IACE,oBAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,WAAA;GfqtDD;EettDD;IACE,oBAAA;GfwtDD;EeztDD;IACE,mBAAA;Gf2tDD;Ee7sDD;IACE,YAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,oBAAA;GfqtDD;EettDD;IACE,WAAA;GfwtDD;EeztDD;IACE,oBAAA;Gf2tDD;Ee5tDD;IACE,oBAAA;Gf8tDD;Ee/tDD;IACE,WAAA;GfiuDD;EeluDD;IACE,oBAAA;GfouDD;EeruDD;IACE,oBAAA;GfuuDD;EexuDD;IACE,WAAA;Gf0uDD;Ee3uDD;IACE,oBAAA;Gf6uDD;Ee9uDD;IACE,mBAAA;GfgvDD;Ee5uDD;IACE,YAAA;Gf8uDD;Ee9vDD;IACE,WAAA;GfgwDD;EejwDD;IACE,mBAAA;GfmwDD;EepwDD;IACE,mBAAA;GfswDD;EevwDD;IACE,UAAA;GfywDD;Ee1wDD;IACE,mBAAA;Gf4wDD;Ee7wDD;IACE,mBAAA;Gf+wDD;EehxDD;IACE,UAAA;GfkxDD;EenxDD;IACE,mBAAA;GfqxDD;EetxDD;IACE,mBAAA;GfwxDD;EezxDD;IACE,UAAA;Gf2xDD;Ee5xDD;IACE,mBAAA;Gf8xDD;Ee/xDD;IACE,kBAAA;GfiyDD;Ee7xDD;IACE,WAAA;Gf+xDD;EejxDD;IACE,kBAAA;GfmxDD;EepxDD;IACE,0BAAA;GfsxDD;EevxDD;IACE,0BAAA;GfyxDD;Ee1xDD;IACE,iBAAA;Gf4xDD;Ee7xDD;IACE,0BAAA;Gf+xDD;EehyDD;IACE,0BAAA;GfkyDD;EenyDD;IACE,iBAAA;GfqyDD;EetyDD;IACE,0BAAA;GfwyDD;EezyDD;IACE,0BAAA;Gf2yDD;Ee5yDD;IACE,iBAAA;Gf8yDD;Ee/yDD;IACE,0BAAA;GfizDD;EelzDD;IACE,yBAAA;GfozDD;EerzDD;IACE,gBAAA;GfuzDD;CACF;Aa/yDD;EE3CI;IACE,YAAA;Gf61DH;Eet1DD;IACE,YAAA;Gfw1DD;Eez1DD;IACE,oBAAA;Gf21DD;Ee51DD;IACE,oBAAA;Gf81DD;Ee/1DD;IACE,WAAA;Gfi2DD;Eel2DD;IACE,oBAAA;Gfo2DD;Eer2DD;IACE,oBAAA;Gfu2DD;Eex2DD;IACE,WAAA;Gf02DD;Ee32DD;IACE,oBAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,WAAA;Gfm3DD;Eep3DD;IACE,oBAAA;Gfs3DD;Eev3DD;IACE,mBAAA;Gfy3DD;Ee32DD;IACE,YAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,oBAAA;Gfm3DD;Eep3DD;IACE,WAAA;Gfs3DD;Eev3DD;IACE,oBAAA;Gfy3DD;Ee13DD;IACE,oBAAA;Gf43DD;Ee73DD;IACE,WAAA;Gf+3DD;Eeh4DD;IACE,oBAAA;Gfk4DD;Een4DD;IACE,oBAAA;Gfq4DD;Eet4DD;IACE,WAAA;Gfw4DD;Eez4DD;IACE,oBAAA;Gf24DD;Ee54DD;IACE,mBAAA;Gf84DD;Ee14DD;IACE,YAAA;Gf44DD;Ee55DD;IACE,WAAA;Gf85DD;Ee/5DD;IACE,mBAAA;Gfi6DD;Eel6DD;IACE,mBAAA;Gfo6DD;Eer6DD;IACE,UAAA;Gfu6DD;Eex6DD;IACE,mBAAA;Gf06DD;Ee36DD;IACE,mBAAA;Gf66DD;Ee96DD;IACE,UAAA;Gfg7DD;Eej7DD;IACE,mBAAA;Gfm7DD;Eep7DD;IACE,mBAAA;Gfs7DD;Eev7DD;IACE,UAAA;Gfy7DD;Ee17DD;IACE,mBAAA;Gf47DD;Ee77DD;IACE,kBAAA;Gf+7DD;Ee37DD;IACE,WAAA;Gf67DD;Ee/6DD;IACE,kBAAA;Gfi7DD;Eel7DD;IACE,0BAAA;Gfo7DD;Eer7DD;IACE,0BAAA;Gfu7DD;Eex7DD;IACE,iBAAA;Gf07DD;Ee37DD;IACE,0BAAA;Gf67DD;Ee97DD;IACE,0BAAA;Gfg8DD;Eej8DD;IACE,iBAAA;Gfm8DD;Eep8DD;IACE,0BAAA;Gfs8DD;Eev8DD;IACE,0BAAA;Gfy8DD;Ee18DD;IACE,iBAAA;Gf48DD;Ee78DD;IACE,0BAAA;Gf+8DD;Eeh9DD;IACE,yBAAA;Gfk9DD;Een9DD;IACE,gBAAA;Gfq9DD;CACF;Aa18DD;EE9CI;IACE,YAAA;Gf2/DH;Eep/DD;IACE,YAAA;Gfs/DD;Eev/DD;IACE,oBAAA;Gfy/DD;Ee1/DD;IACE,oBAAA;Gf4/DD;Ee7/DD;IACE,WAAA;Gf+/DD;EehgED;IACE,oBAAA;GfkgED;EengED;IACE,oBAAA;GfqgED;EetgED;IACE,WAAA;GfwgED;EezgED;IACE,oBAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,WAAA;GfihED;EelhED;IACE,oBAAA;GfohED;EerhED;IACE,mBAAA;GfuhED;EezgED;IACE,YAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,oBAAA;GfihED;EelhED;IACE,WAAA;GfohED;EerhED;IACE,oBAAA;GfuhED;EexhED;IACE,oBAAA;Gf0hED;Ee3hED;IACE,WAAA;Gf6hED;Ee9hED;IACE,oBAAA;GfgiED;EejiED;IACE,oBAAA;GfmiED;EepiED;IACE,WAAA;GfsiED;EeviED;IACE,oBAAA;GfyiED;Ee1iED;IACE,mBAAA;Gf4iED;EexiED;IACE,YAAA;Gf0iED;Ee1jED;IACE,WAAA;Gf4jED;Ee7jED;IACE,mBAAA;Gf+jED;EehkED;IACE,mBAAA;GfkkED;EenkED;IACE,UAAA;GfqkED;EetkED;IACE,mBAAA;GfwkED;EezkED;IACE,mBAAA;Gf2kED;Ee5kED;IACE,UAAA;Gf8kED;Ee/kED;IACE,mBAAA;GfilED;EellED;IACE,mBAAA;GfolED;EerlED;IACE,UAAA;GfulED;EexlED;IACE,mBAAA;Gf0lED;Ee3lED;IACE,kBAAA;Gf6lED;EezlED;IACE,WAAA;Gf2lED;Ee7kED;IACE,kBAAA;Gf+kED;EehlED;IACE,0BAAA;GfklED;EenlED;IACE,0BAAA;GfqlED;EetlED;IACE,iBAAA;GfwlED;EezlED;IACE,0BAAA;Gf2lED;Ee5lED;IACE,0BAAA;Gf8lED;Ee/lED;IACE,iBAAA;GfimED;EelmED;IACE,0BAAA;GfomED;EermED;IACE,0BAAA;GfumED;EexmED;IACE,iBAAA;Gf0mED;Ee3mED;IACE,0BAAA;Gf6mED;Ee9mED;IACE,yBAAA;GfgnED;EejnED;IACE,gBAAA;GfmnED;CACF;AgBvrED;EACE,8BAAA;ChByrED;AgBvrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChByrED;AgBvrED;EACE,iBAAA;ChByrED;AgBnrED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBqrED;AgBxrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,2BAAA;ChBqrEP;AgBnsED;EAoBI,uBAAA;EACA,8BAAA;ChBkrEH;AgBvsED;;;;;;EA8BQ,cAAA;ChBirEP;AgB/sED;EAoCI,2BAAA;ChB8qEH;AgBltED;EAyCI,uBAAA;ChB4qEH;AgBrqED;;;;;;EAOQ,aAAA;ChBsqEP;AgB3pED;EACE,uBAAA;ChB6pED;AgB9pED;;;;;;EAQQ,uBAAA;ChB8pEP;AgBtqED;;EAeM,yBAAA;ChB2pEL;AgBjpED;EAEI,0BAAA;ChBkpEH;AgBzoED;EAEI,0BAAA;ChB0oEH;AgBjoED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBmoED;AgB9nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBioEL;AiB7wEC;;;;;;;;;;;;EAOI,0BAAA;CjBoxEL;AiB9wEC;;;;;EAMI,0BAAA;CjB+wEL;AiBlyEC;;;;;;;;;;;;EAOI,0BAAA;CjByyEL;AiBnyEC;;;;;EAMI,0BAAA;CjBoyEL;AiBvzEC;;;;;;;;;;;;EAOI,0BAAA;CjB8zEL;AiBxzEC;;;;;EAMI,0BAAA;CjByzEL;AiB50EC;;;;;;;;;;;;EAOI,0BAAA;CjBm1EL;AiB70EC;;;;;EAMI,0BAAA;CjB80EL;AiBj2EC;;;;;;;;;;;;EAOI,0BAAA;CjBw2EL;AiBl2EC;;;;;EAMI,0BAAA;CjBm2EL;AgBjtED;EACE,iBAAA;EACA,kBAAA;ChBmtED;AgBtpED;EACA;IA3DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,uBAAA;GhBotED;EgB7pEH;IAnDM,iBAAA;GhBmtEH;EgBhqEH;;;;;;IA1CY,oBAAA;GhBktET;EgBxqEH;IAlCM,UAAA;GhB6sEH;EgB3qEH;;;;;;IAzBY,eAAA;GhB4sET;EgBnrEH;;;;;;IArBY,gBAAA;GhBgtET;EgB3rEH;;;;IARY,iBAAA;GhBysET;CACF;AkBn6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBk6ED;AkB/5ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBi6ED;AkB95ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBg6ED;AkBr5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL43ET;AkBr5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBu5ED;AkBp5ED;EACE,eAAA;ClBs5ED;AkBl5ED;EACE,eAAA;EACA,YAAA;ClBo5ED;AkBh5ED;;EAEE,aAAA;ClBk5ED;AkB94ED;;;EZrEE,2CAAA;EACA,qBAAA;CNw9ED;AkB74ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClB+4ED;AkBr3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CLwzET;AmBh8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CLy7ET;AKx5EC;EACE,YAAA;EACA,WAAA;CL05EH;AKx5EC;EAA0B,YAAA;CL25E3B;AK15EC;EAAgC,YAAA;CL65EjC;AkBj4EC;EACE,UAAA;EACA,8BAAA;ClBm4EH;AkB33EC;;;EAGE,0BAAA;EACA,WAAA;ClB63EH;AkB13EC;;EAEE,oBAAA;ClB43EH;AkBx3EC;EACE,aAAA;ClB03EH;AkB92ED;EACE,yBAAA;ClBg3ED;AkBx0ED;EAtBI;;;;IACE,kBAAA;GlBo2EH;EkBj2EC;;;;;;;;IAEE,kBAAA;GlBy2EH;EkBt2EC;;;;;;;;IAEE,kBAAA;GlB82EH;CACF;AkBp2ED;EACE,oBAAA;ClBs2ED;AkB91ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBg2ED;AkBr2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2EH;AkB91ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBg2ED;AkB71ED;;EAEE,iBAAA;ClB+1ED;AkB31ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClB61ED;AkB31ED;;EAEE,cAAA;EACA,kBAAA;ClB61ED;AkBp1EC;;;;;;EAGE,oBAAA;ClBy1EH;AkBn1EC;;;;EAEE,oBAAA;ClBu1EH;AkBj1EC;;;;EAGI,oBAAA;ClBo1EL;AkBz0ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClBy0ED;AkBv0EC;;EAEE,gBAAA;EACA,iBAAA;ClBy0EH;AkB5zED;ECnQE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBkkFD;AmBhkFC;EACE,aAAA;EACA,kBAAA;CnBkkFH;AmB/jFC;;EAEE,aAAA;CnBikFH;AkBx0ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClBy0EH;AkB/0ED;EASI,aAAA;EACA,kBAAA;ClBy0EH;AkBn1ED;;EAcI,aAAA;ClBy0EH;AkBv1ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClBy0EH;AkBr0ED;EC/RE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBumFD;AmBrmFC;EACE,aAAA;EACA,kBAAA;CnBumFH;AmBpmFC;;EAEE,aAAA;CnBsmFH;AkBj1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBk1EH;AkBx1ED;EASI,aAAA;EACA,kBAAA;ClBk1EH;AkB51ED;;EAcI,aAAA;ClBk1EH;AkBh2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBk1EH;AkBz0ED;EAEE,mBAAA;ClB00ED;AkB50ED;EAMI,sBAAA;ClBy0EH;AkBr0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBn0ED;;;;;;;;;;EC1ZI,eAAA;CnByuFH;AkB/0ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL0rFT;AmBxuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL+rFT;AkBz1ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBwuFH;AkB91ED;ECtYI,eAAA;CnBuuFH;AkB91ED;;;;;;;;;;EC7ZI,eAAA;CnBuwFH;AkB12ED;ECzZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwtFT;AmBtwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6tFT;AkBp3ED;EC/YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBswFH;AkBz3ED;ECzYI,eAAA;CnBqwFH;AkBz3ED;;;;;;;;;;EChaI,eAAA;CnBqyFH;AkBr4ED;EC5ZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLsvFT;AmBpyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2vFT;AkB/4ED;EClZI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBoyFH;AkBp5ED;EC5YI,eAAA;CnBmyFH;AkBh5EC;EACE,UAAA;ClBk5EH;AkBh5EC;EACE,OAAA;ClBk5EH;AkBx4ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB04ED;AkBvzED;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBy3EH;EkBrvEH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBu3EH;EkB1vEH;IAxHM,sBAAA;GlBq3EH;EkB7vEH;IApHM,sBAAA;IACA,uBAAA;GlBo3EH;EkBjwEH;;;IA9GQ,YAAA;GlBo3EL;EkBtwEH;IAxGM,YAAA;GlBi3EH;EkBzwEH;IApGM,iBAAA;IACA,uBAAA;GlBg3EH;EkB7wEH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB62EH;EkBpxEH;;IAtFQ,gBAAA;GlB82EL;EkBxxEH;;IAjFM,mBAAA;IACA,eAAA;GlB62EH;EkB7xEH;IA3EM,OAAA;GlB22EH;CACF;AkBj2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClB81EH;AkBz2ED;;EAiBI,iBAAA;ClB41EH;AkB72ED;EJthBE,mBAAA;EACA,oBAAA;Cds4FD;AkB10EC;EAyBF;IAnCM,kBAAA;IACA,iBAAA;IACA,iBAAA;GlBw1EH;CACF;AkBx3ED;EAwCI,YAAA;ClBm1EH;AkBr0EC;EAUF;IAdQ,kBAAA;IACA,gBAAA;GlB60EL;CACF;AkBn0EC;EAEF;IANQ,iBAAA;IACA,gBAAA;GlB20EL;CACF;AoBp6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC0CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB+JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL+tFT;AoBv6FG;;;;;;EdnBF,2CAAA;EACA,qBAAA;CNk8FD;AoB16FC;;;EAGE,YAAA;EACA,sBAAA;CpB46FH;AoBz6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLi5FT;AoBz6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CL05FT;AoBz6FG;;EAEE,qBAAA;CpB26FL;AoBl6FD;EC3DE,YAAA;EACA,uBAAA;EACA,mBAAA;CrBg+FD;AqB99FC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBs+FT;AqBn+FC;;;EAGE,uBAAA;CrBq+FH;AqBh+FG;;;;;;;;;EAGE,uBAAA;EACI,mBAAA;CrBw+FT;AoBv9FD;ECZI,YAAA;EACA,uBAAA;CrBs+FH;AoBx9FD;EC9DE,YAAA;EACA,0BAAA;EACA,sBAAA;CrByhGD;AqBvhGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB+hGT;AqB5hGC;;;EAGE,uBAAA;CrB8hGH;AqBzhGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBiiGT;AoB7gGD;ECfI,eAAA;EACA,uBAAA;CrB+hGH;AoB7gGD;EClEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBklGD;AqBhlGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBwlGT;AqBrlGC;;;EAGE,uBAAA;CrBulGH;AqBllGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB0lGT;AoBlkGD;ECnBI,eAAA;EACA,uBAAA;CrBwlGH;AoBlkGD;ECtEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB2oGD;AqBzoGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBipGT;AqB9oGC;;;EAGE,uBAAA;CrBgpGH;AqB3oGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBmpGT;AoBvnGD;ECvBI,eAAA;EACA,uBAAA;CrBipGH;AoBvnGD;EC1EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBosGD;AqBlsGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB0sGT;AqBvsGC;;;EAGE,uBAAA;CrBysGH;AqBpsGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB4sGT;AoB5qGD;EC3BI,eAAA;EACA,uBAAA;CrB0sGH;AoB5qGD;EC9EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB6vGD;AqB3vGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBmwGT;AqBhwGC;;;EAGE,uBAAA;CrBkwGH;AqB7vGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBqwGT;AoBjuGD;EC/BI,eAAA;EACA,uBAAA;CrBmwGH;AoB5tGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpB8tGD;AoB5tGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLkwGT;AoB7tGC;;;;EAIE,0BAAA;CpB+tGH;AoB7tGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpB+tGH;AoB3tGG;;;;EAEE,eAAA;EACA,sBAAA;CpB+tGL;AoBttGD;;ECxEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBkyGD;AoBztGD;;EC5EE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrByyGD;AoB5tGD;;EChFE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBgzGD;AoB3tGD;EACE,eAAA;EACA,YAAA;CpB6tGD;AoBztGD;EACE,gBAAA;CpB2tGD;AoBptGC;;;EACE,YAAA;CpBwtGH;AuBl3GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLisGT;AuBr3GC;EACE,WAAA;CvBu3GH;AuBn3GD;EACE,cAAA;CvBq3GD;AuBn3GC;EAAY,eAAA;CvBs3Gb;AuBr3GC;EAAY,mBAAA;CvBw3Gb;AuBv3GC;EAAY,yBAAA;CvB03Gb;AuBv3GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CL2sGT;AwBr5GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxBu5GD;AwBn5GD;;EAEE,mBAAA;CxBq5GD;AwBj5GD;EACE,WAAA;CxBm5GD;AwB/4GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBk5GD;AwB74GC;EACE,SAAA;EACA,WAAA;CxB+4GH;AwBx6GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBo8GD;AwB96GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB84GH;AwBx4GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB04GH;AwBp4GC;;;EAGE,YAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxBs4GH;AwB73GC;;;EAGE,eAAA;CxB+3GH;AwB33GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxB63GH;AwBx3GD;EAGI,eAAA;CxBw3GH;AwB33GD;EAQI,WAAA;CxBs3GH;AwB92GD;EACE,WAAA;EACA,SAAA;CxBg3GD;AwBx2GD;EACE,QAAA;EACA,YAAA;CxB02GD;AwBt2GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBw2GD;AwBp2GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxBs2GD;AwBl2GD;EACE,SAAA;EACA,WAAA;CxBo2GD;AwB51GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxB41GH;AwBn2GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxB41GH;AwBv0GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB05GC;EwBv1GD;IA1DA,QAAA;IACA,YAAA;GxBo5GC;CACF;A2BpiHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3BsiHD;A2B1iHD;;EAMI,mBAAA;EACA,YAAA;C3BwiHH;A2BtiHG;;;;;;;;EAIE,WAAA;C3B4iHL;A2BtiHD;;;;EAKI,kBAAA;C3BuiHH;A2BliHD;EACE,kBAAA;C3BoiHD;A2BriHD;;;EAOI,YAAA;C3BmiHH;A2B1iHD;;;EAYI,iBAAA;C3BmiHH;A2B/hHD;EACE,iBAAA;C3BiiHD;A2B7hHD;EACE,eAAA;C3B+hHD;A2B9hHC;EClDA,8BAAA;EACG,2BAAA;C5BmlHJ;A2B7hHD;;EC/CE,6BAAA;EACG,0BAAA;C5BglHJ;A2B5hHD;EACE,YAAA;C3B8hHD;A2B5hHD;EACE,iBAAA;C3B8hHD;A2B5hHD;;ECnEE,8BAAA;EACG,2BAAA;C5BmmHJ;A2B3hHD;ECjEE,6BAAA;EACG,0BAAA;C5B+lHJ;A2B1hHD;;EAEE,WAAA;C3B4hHD;A2B3gHD;EACE,kBAAA;EACA,mBAAA;C3B6gHD;A2B3gHD;EACE,mBAAA;EACA,oBAAA;C3B6gHD;A2BxgHD;EtB/CE,yDAAA;EACQ,iDAAA;CL0jHT;A2BxgHC;EtBnDA,yBAAA;EACQ,iBAAA;CL8jHT;A2BrgHD;EACE,eAAA;C3BugHD;A2BpgHD;EACE,wBAAA;EACA,uBAAA;C3BsgHD;A2BngHD;EACE,wBAAA;C3BqgHD;A2B9/GD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3B+/GH;A2BtgHD;EAcM,YAAA;C3B2/GL;A2BzgHD;;;;EAsBI,iBAAA;EACA,eAAA;C3By/GH;A2Bp/GC;EACE,iBAAA;C3Bs/GH;A2Bp/GC;EC3KA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B4pHF;A2Bt/GC;EC/KA,2BAAA;EACC,0BAAA;EAOD,gCAAA;EACC,+BAAA;C5BkqHF;A2Bv/GD;EACE,iBAAA;C3By/GD;A2Bv/GD;;EC/KE,8BAAA;EACC,6BAAA;C5B0qHF;A2Bt/GD;EC7LE,2BAAA;EACC,0BAAA;C5BsrHF;A2Bl/GD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3Bo/GD;A2Bx/GD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3Bq/GH;A2B9/GD;EAYI,YAAA;C3Bq/GH;A2BjgHD;EAgBI,WAAA;C3Bo/GH;A2Bn+GD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3Bo+GL;A6B9sHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BgtHD;A6B7sHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7B+sHH;A6BxtHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7BusHH;A6BrsHG;EACE,WAAA;C7BusHL;A6B7rHD;;;EV0BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwqHD;AmBtqHC;;;EACE,aAAA;EACA,kBAAA;CnB0qHH;AmBvqHC;;;;;;EAEE,aAAA;CnB6qHH;A6B/sHD;;;EVqBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+rHD;AmB7rHC;;;EACE,aAAA;EACA,kBAAA;CnBisHH;AmB9rHC;;;;;;EAEE,aAAA;CnBosHH;A6B7tHD;;;EAGE,oBAAA;C7B+tHD;A6B7tHC;;;EACE,iBAAA;C7BiuHH;A6B7tHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7B+tHD;A6B1tHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;C7B4tHD;A6BztHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6BztHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6B/uHD;;EA0BI,cAAA;C7BytHH;A6BptHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;C5Bi0HJ;A6BrtHD;EACE,gBAAA;C7ButHD;A6BrtHD;;;;;;;EDxGE,6BAAA;EACG,0BAAA;C5Bs0HJ;A6BttHD;EACE,eAAA;C7BwtHD;A6BntHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BmtHD;A6BxtHD;EAUI,mBAAA;C7BitHH;A6B3tHD;EAYM,kBAAA;C7BktHL;A6B/sHG;;;EAGE,WAAA;C7BitHL;A6B5sHC;;EAGI,mBAAA;C7B6sHL;A6B1sHC;;EAGI,WAAA;EACA,kBAAA;C7B2sHL;A8B12HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B42HD;A8B/2HD;EAOI,mBAAA;EACA,eAAA;C9B22HH;A8Bn3HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B22HL;A8B12HK;;EAEE,sBAAA;EACA,0BAAA;C9B42HP;A8Bv2HG;EACE,eAAA;C9By2HL;A8Bv2HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By2HP;A8Bl2HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo2HL;A8B74HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm5HD;A8Bn5HD;EA0DI,gBAAA;C9B41HH;A8Bn1HD;EACE,8BAAA;C9Bq1HD;A8Bt1HD;EAGI,YAAA;EAEA,oBAAA;C9Bq1HH;A8B11HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo1HL;A8Bn1HK;EACE,mCAAA;C9Bq1HP;A8B/0HK;;;EAGE,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,iCAAA;EACA,gBAAA;C9Bi1HP;A8B50HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6vHD;A8Bh1HC;EAwDE,YAAA;C9B2xHH;A8Bn1HC;EA0DI,mBAAA;EACA,mBAAA;C9B4xHL;A8Bv1HC;EAgEE,UAAA;EACA,WAAA;C9B0xHH;A8B9wHD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9ByxHH;E8BztHH;IA9DQ,iBAAA;G9B0xHL;CACF;A8Bp2HC;EAuFE,gBAAA;EACA,mBAAA;C9BgxHH;A8Bx2HC;;;EA8FE,uBAAA;C9B+wHH;A8BjwHD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9B8wHH;E8B3uHH;;;IA9BM,0BAAA;G9B8wHH;CACF;A8B/2HD;EAEI,YAAA;C9Bg3HH;A8Bl3HD;EAMM,mBAAA;C9B+2HL;A8Br3HD;EASM,iBAAA;C9B+2HL;A8B12HK;;;EAGE,YAAA;EACA,0BAAA;C9B42HP;A8Bp2HD;EAEI,YAAA;C9Bq2HH;A8Bv2HD;EAIM,gBAAA;EACA,eAAA;C9Bs2HL;A8B11HD;EACE,YAAA;C9B41HD;A8B71HD;EAII,YAAA;C9B41HH;A8Bh2HD;EAMM,mBAAA;EACA,mBAAA;C9B61HL;A8Bp2HD;EAYI,UAAA;EACA,WAAA;C9B21HH;A8B/0HD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B01HH;E8B1xHH;IA9DQ,iBAAA;G9B21HL;CACF;A8Bn1HD;EACE,iBAAA;C9Bq1HD;A8Bt1HD;EAKI,gBAAA;EACA,mBAAA;C9Bo1HH;A8B11HD;;;EAYI,uBAAA;C9Bm1HH;A8Br0HD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9Bk1HH;E8B/yHH;;;IA9BM,0BAAA;G9Bk1HH;CACF;A8Bz0HD;EAEI,cAAA;C9B00HH;A8B50HD;EAKI,eAAA;C9B00HH;A8Bj0HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8iIF;A+BxiID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0iID;A+BliID;EA8nBA;IAhoBI,mBAAA;G/BwiID;CACF;A+BzhID;EAgnBA;IAlnBI,YAAA;G/B+hID;CACF;A+BjhID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkhID;A+BhhIC;EACE,iBAAA;C/BkhIH;A+Bt/HD;EA6jBA;IArlBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkhID;E+BhhIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkhIH;E+B/gIC;IACE,oBAAA;G/BihIH;E+B5gIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8gIH;CACF;A+B1gID;;EAGI,kBAAA;C/B2gIH;A+BtgIC;EAmjBF;;IArjBM,kBAAA;G/B6gIH;CACF;A+BpgID;;;;EAII,oBAAA;EACA,mBAAA;C/BsgIH;A+BhgIC;EAgiBF;;;;IAniBM,gBAAA;IACA,eAAA;G/B0gIH;CACF;A+B9/HD;EACE,cAAA;EACA,sBAAA;C/BggID;A+B3/HD;EA8gBA;IAhhBI,iBAAA;G/BigID;CACF;A+B7/HD;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+/HD;A+Bz/HD;EAggBA;;IAlgBI,iBAAA;G/BggID;CACF;A+B9/HD;EACE,OAAA;EACA,sBAAA;C/BggID;A+B9/HD;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BggID;A+B1/HD;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4/HD;A+B1/HC;;EAEE,sBAAA;C/B4/HH;A+BrgID;EAaI,eAAA;C/B2/HH;A+Bl/HD;EALI;;IAEE,mBAAA;G/B0/HH;CACF;A+Bh/HD;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/Bm/HD;A+B/+HC;EACE,WAAA;C/Bi/HH;A+B//HD;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B++HH;A+BrgID;EAyBI,gBAAA;C/B++HH;A+Bz+HD;EAqbA;IAvbI,cAAA;G/B++HD;CACF;A+Bt+HD;EACE,oBAAA;C/Bw+HD;A+Bz+HD;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/Bw+HH;A+B58HC;EA2YF;IAjaM,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/Bs+HH;E+B3kHH;;IAxZQ,2BAAA;G/Bu+HL;E+B/kHH;IArZQ,kBAAA;G/Bu+HL;E+Bt+HK;;IAEE,uBAAA;G/Bw+HP;CACF;A+Bt9HD;EA+XA;IA1YI,YAAA;IACA,UAAA;G/Bq+HD;E+B5lHH;IAtYM,YAAA;G/Bq+HH;E+B/lHH;IApYQ,kBAAA;IACA,qBAAA;G/Bs+HL;CACF;A+B39HD;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4vID;AkBtuHD;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBwyHH;EkBpqHH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBsyHH;EkBzqHH;IAxHM,sBAAA;GlBoyHH;EkB5qHH;IApHM,sBAAA;IACA,uBAAA;GlBmyHH;EkBhrHH;;;IA9GQ,YAAA;GlBmyHL;EkBrrHH;IAxGM,YAAA;GlBgyHH;EkBxrHH;IApGM,iBAAA;IACA,uBAAA;GlB+xHH;EkB5rHH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB4xHH;EkBnsHH;;IAtFQ,gBAAA;GlB6xHL;EkBvsHH;;IAjFM,mBAAA;IACA,eAAA;GlB4xHH;EkB5sHH;IA3EM,OAAA;GlB0xHH;CACF;A+BpgIC;EAmWF;IAzWM,mBAAA;G/B8gIH;E+B5gIG;IACE,iBAAA;G/B8gIL;CACF;A+B7/HD;EAoVA;IA5VI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmwIP;CACF;A+BngID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B00IF;A+BngID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By0IF;A+B//HD;EChVE,gBAAA;EACA,mBAAA;ChCk1ID;A+BhgIC;ECnVA,iBAAA;EACA,oBAAA;ChCs1ID;A+BjgIC;ECtVA,iBAAA;EACA,oBAAA;ChC01ID;A+B3/HD;EChWE,iBAAA;EACA,oBAAA;ChC81ID;A+Bv/HD;EAsSA;IA1SI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+/HD;CACF;A+Bl+HD;EAhBE;IExWA,uBAAA;GjC81IC;E+Br/HD;IE5WA,wBAAA;IF8WE,oBAAA;G/Bu/HD;E+Bz/HD;IAKI,gBAAA;G/Bu/HH;CACF;A+B9+HD;EACE,0BAAA;EACA,sBAAA;C/Bg/HD;A+Bl/HD;EAKI,YAAA;C/Bg/HH;A+B/+HG;;EAEE,eAAA;EACA,8BAAA;C/Bi/HL;A+B1/HD;EAcI,YAAA;C/B++HH;A+B7/HD;EAmBM,YAAA;C/B6+HL;A+B3+HK;;EAEE,YAAA;EACA,8BAAA;C/B6+HP;A+Bz+HK;;;EAGE,YAAA;EACA,0BAAA;C/B2+HP;A+Bv+HK;;;EAGE,YAAA;EACA,8BAAA;C/By+HP;A+BjhID;EA8CI,mBAAA;C/Bs+HH;A+Br+HG;;EAEE,uBAAA;C/Bu+HL;A+BxhID;EAoDM,uBAAA;C/Bu+HL;A+B3hID;;EA0DI,sBAAA;C/Bq+HH;A+B99HK;;;EAGE,0BAAA;EACA,YAAA;C/Bg+HP;A+B/7HC;EAoKF;IA7LU,YAAA;G/B49HP;E+B39HO;;IAEE,YAAA;IACA,8BAAA;G/B69HT;E+Bz9HO;;;IAGE,YAAA;IACA,0BAAA;G/B29HT;E+Bv9HO;;;IAGE,YAAA;IACA,8BAAA;G/By9HT;CACF;A+B3jID;EA8GI,YAAA;C/Bg9HH;A+B/8HG;EACE,YAAA;C/Bi9HL;A+BjkID;EAqHI,YAAA;C/B+8HH;A+B98HG;;EAEE,YAAA;C/Bg9HL;A+B58HK;;;;EAEE,YAAA;C/Bg9HP;A+Bx8HD;EACE,uBAAA;EACA,sBAAA;C/B08HD;A+B58HD;EAKI,eAAA;C/B08HH;A+Bz8HG;;EAEE,YAAA;EACA,8BAAA;C/B28HL;A+Bp9HD;EAcI,eAAA;C/By8HH;A+Bv9HD;EAmBM,eAAA;C/Bu8HL;A+Br8HK;;EAEE,YAAA;EACA,8BAAA;C/Bu8HP;A+Bn8HK;;;EAGE,YAAA;EACA,0BAAA;C/Bq8HP;A+Bj8HK;;;EAGE,YAAA;EACA,8BAAA;C/Bm8HP;A+B3+HD;EA+CI,mBAAA;C/B+7HH;A+B97HG;;EAEE,uBAAA;C/Bg8HL;A+Bl/HD;EAqDM,uBAAA;C/Bg8HL;A+Br/HD;;EA2DI,sBAAA;C/B87HH;A+Bx7HK;;;EAGE,0BAAA;EACA,YAAA;C/B07HP;A+Bn5HC;EAwBF;IAvDU,sBAAA;G/Bs7HP;E+B/3HH;IApDU,0BAAA;G/Bs7HP;E+Bl4HH;IAjDU,eAAA;G/Bs7HP;E+Br7HO;;IAEE,YAAA;IACA,8BAAA;G/Bu7HT;E+Bn7HO;;;IAGE,YAAA;IACA,0BAAA;G/Bq7HT;E+Bj7HO;;;IAGE,YAAA;IACA,8BAAA;G/Bm7HT;CACF;A+B3hID;EA+GI,eAAA;C/B+6HH;A+B96HG;EACE,YAAA;C/Bg7HL;A+BjiID;EAsHI,eAAA;C/B86HH;A+B76HG;;EAEE,YAAA;C/B+6HL;A+B36HK;;;;EAEE,YAAA;C/B+6HP;AkCzjJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2jJD;AkChkJD;EAQI,sBAAA;ClC2jJH;AkCnkJD;EAWM,kBAAA;EACA,eAAA;EACA,YAAA;ClC2jJL;AkCxkJD;EAkBI,eAAA;ClCyjJH;AmC7kJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+kJD;AmCnlJD;EAOI,gBAAA;CnC+kJH;AmCtlJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,kBAAA;CnCglJL;AmC9kJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2lJJ;AmC7kJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwmJJ;AmCxkJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CnC4kJL;AmCtkJG;;;;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2kJL;AmCloJD;;;;;;EAkEM,eAAA;EACA,uBAAA;EACA,mBAAA;EACA,oBAAA;CnCwkJL;AmC/jJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8oJL;AoC5oJG;;ERKF,+BAAA;EACG,4BAAA;C5B2oJJ;AoC3oJG;;ERTF,gCAAA;EACG,6BAAA;C5BwpJJ;AmC1kJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8pJL;AoC5pJG;;ERKF,+BAAA;EACG,4BAAA;C5B2pJJ;AoC3pJG;;ERTF,gCAAA;EACG,6BAAA;C5BwqJJ;AqC3qJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6qJD;AqCjrJD;EAOI,gBAAA;CrC6qJH;AqCprJD;;EAUM,sBAAA;EACA,kBAAA;EACA,uBAAA;EACA,uBAAA;EACA,oBAAA;CrC8qJL;AqC5rJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6qJL;AqCjsJD;;EA2BM,aAAA;CrC0qJL;AqCrsJD;;EAkCM,YAAA;CrCuqJL;AqCzsJD;;;;EA2CM,eAAA;EACA,uBAAA;EACA,oBAAA;CrCoqJL;AsCltJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCotJD;AsChtJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CtCktJL;AsC7sJC;EACE,cAAA;CtC+sJH;AsC3sJC;EACE,mBAAA;EACA,UAAA;CtC6sJH;AsCtsJD;ECtCE,0BAAA;CvC+uJD;AuC5uJG;;EAEE,0BAAA;CvC8uJL;AsCzsJD;EC1CE,0BAAA;CvCsvJD;AuCnvJG;;EAEE,0BAAA;CvCqvJL;AsC5sJD;EC9CE,0BAAA;CvC6vJD;AuC1vJG;;EAEE,0BAAA;CvC4vJL;AsC/sJD;EClDE,0BAAA;CvCowJD;AuCjwJG;;EAEE,0BAAA;CvCmwJL;AsCltJD;ECtDE,0BAAA;CvC2wJD;AuCxwJG;;EAEE,0BAAA;CvC0wJL;AsCrtJD;EC1DE,0BAAA;CvCkxJD;AuC/wJG;;EAEE,0BAAA;CvCixJL;AwCnxJD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCqxJD;AwClxJC;EACE,cAAA;CxCoxJH;AwChxJC;EACE,mBAAA;EACA,UAAA;CxCkxJH;AwC/wJC;;EAEE,OAAA;EACA,iBAAA;CxCixJH;AwC5wJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CxC8wJL;AwCzwJC;;EAEE,eAAA;EACA,uBAAA;CxC2wJH;AwCxwJC;EACE,aAAA;CxC0wJH;AwCvwJC;EACE,kBAAA;CxCywJH;AwCtwJC;EACE,iBAAA;CxCwwJH;AyCl0JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo0JD;AyCz0JD;;EASI,eAAA;CzCo0JH;AyC70JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm0JH;AyCl1JD;EAmBI,0BAAA;CzCk0JH;AyC/zJC;;EAEE,mBAAA;EACA,mBAAA;EACA,oBAAA;CzCi0JH;AyC31JD;EA8BI,gBAAA;CzCg0JH;AyC9yJD;EACA;IAfI,kBAAA;IACA,qBAAA;GzCg0JD;EyC9zJC;;IAEE,mBAAA;IACA,oBAAA;GzCg0JH;EyCvzJH;;IAJM,gBAAA;GzC+zJH;CACF;A0C52JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL8rJT;A0Cx3JD;;EAaI,kBAAA;EACA,mBAAA;C1C+2JH;A0C32JC;;;EAGE,sBAAA;C1C62JH;A0Cl4JD;EA0BI,aAAA;EACA,eAAA;C1C22JH;A2Cp4JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Cs4JD;A2C14JD;EAQI,cAAA;EAEA,eAAA;C3Co4JH;A2C94JD;EAeI,kBAAA;C3Ck4JH;A2Cj5JD;;EAqBI,iBAAA;C3Cg4JH;A2Cr5JD;EAyBI,gBAAA;C3C+3JH;A2Cv3JD;;EAEE,oBAAA;C3Cy3JD;A2C33JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cy3JH;A2Cj3JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C26JD;A2Ct3JD;EClDI,0BAAA;C5C26JH;A2Cz3JD;EC/CI,eAAA;C5C26JH;A2Cx3JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cs7JD;A2C73JD;ECtDI,0BAAA;C5Cs7JH;A2Ch4JD;ECnDI,eAAA;C5Cs7JH;A2C/3JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Ci8JD;A2Cp4JD;EC1DI,0BAAA;C5Ci8JH;A2Cv4JD;ECvDI,eAAA;C5Ci8JH;A2Ct4JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C48JD;A2C34JD;EC9DI,0BAAA;C5C48JH;A2C94JD;EC3DI,eAAA;C5C48JH;A6C98JD;EACE;IAAQ,4BAAA;G7Ci9JP;E6Ch9JD;IAAQ,yBAAA;G7Cm9JP;CACF;A6Ch9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6Cx9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6C98JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CL26JT;A6C78JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL+zJT;A6C18JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C88JD;A6Cv8JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLu/JT;A6Cp8JD;EErEE,0BAAA;C/C4gKD;A+CzgKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C49JH;A6Cx8JD;EEzEE,0BAAA;C/CohKD;A+CjhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co+JH;A6C58JD;EE7EE,0BAAA;C/C4hKD;A+CzhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C4+JH;A6Ch9JD;EEjFE,0BAAA;C/CoiKD;A+CjiKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co/JH;AgD5iKD;EAEE,iBAAA;ChD6iKD;AgD3iKC;EACE,cAAA;ChD6iKH;AgDziKD;;EAEE,QAAA;EACA,iBAAA;ChD2iKD;AgDxiKD;EACE,eAAA;ChD0iKD;AgDviKD;EACE,eAAA;ChDyiKD;AgDtiKC;EACE,gBAAA;ChDwiKH;AgDpiKD;;EAEE,mBAAA;ChDsiKD;AgDniKD;;EAEE,oBAAA;ChDqiKD;AgDliKD;;;EAGE,oBAAA;EACA,oBAAA;ChDoiKD;AgDjiKD;EACE,uBAAA;ChDmiKD;AgDhiKD;EACE,uBAAA;ChDkiKD;AgD9hKD;EACE,cAAA;EACA,mBAAA;ChDgiKD;AgD1hKD;EACE,gBAAA;EACA,iBAAA;ChD4hKD;AiDnlKD;EAEE,oBAAA;EACA,gBAAA;CjDolKD;AiD5kKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,uBAAA;EACA,uBAAA;CjD6kKD;AiD1kKC;ErB3BA,6BAAA;EACC,4BAAA;C5BwmKF;AiD3kKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BqmKF;AiDpkKD;;EAEE,YAAA;CjDskKD;AiDxkKD;;EAKI,YAAA;CjDukKH;AiDnkKC;;;;EAEE,sBAAA;EACA,YAAA;EACA,0BAAA;CjDukKH;AiDnkKD;EACE,YAAA;EACA,iBAAA;CjDqkKD;AiDhkKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDkkKH;AiDvkKC;;;EASI,eAAA;CjDmkKL;AiD5kKC;;;EAYI,eAAA;CjDqkKL;AiDhkKC;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;CjDkkKH;AiDxkKC;;;;;;;;;EAYI,eAAA;CjDukKL;AiDnlKC;;;EAeI,eAAA;CjDykKL;AkD3qKC;EACE,eAAA;EACA,0BAAA;ClD6qKH;AkD3qKG;;EAEE,eAAA;ClD6qKL;AkD/qKG;;EAKI,eAAA;ClD8qKP;AkD3qKK;;;;EAEE,eAAA;EACA,0BAAA;ClD+qKP;AkD7qKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDkrKP;AkDxsKC;EACE,eAAA;EACA,0BAAA;ClD0sKH;AkDxsKG;;EAEE,eAAA;ClD0sKL;AkD5sKG;;EAKI,eAAA;ClD2sKP;AkDxsKK;;;;EAEE,eAAA;EACA,0BAAA;ClD4sKP;AkD1sKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD+sKP;AkDruKC;EACE,eAAA;EACA,0BAAA;ClDuuKH;AkDruKG;;EAEE,eAAA;ClDuuKL;AkDzuKG;;EAKI,eAAA;ClDwuKP;AkDruKK;;;;EAEE,eAAA;EACA,0BAAA;ClDyuKP;AkDvuKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD4uKP;AkDlwKC;EACE,eAAA;EACA,0BAAA;ClDowKH;AkDlwKG;;EAEE,eAAA;ClDowKL;AkDtwKG;;EAKI,eAAA;ClDqwKP;AkDlwKK;;;;EAEE,eAAA;EACA,0BAAA;ClDswKP;AkDpwKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDywKP;AiDxqKD;EACE,cAAA;EACA,mBAAA;CjD0qKD;AiDxqKD;EACE,iBAAA;EACA,iBAAA;CjD0qKD;AmDpyKD;EACE,oBAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL6uKT;AmDnyKD;EACE,cAAA;CnDqyKD;AmDhyKD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5BuzKF;AmDtyKD;EAMI,eAAA;CnDmyKH;AmD9xKD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnDgyKD;AmDpyKD;;;;;EAWI,eAAA;CnDgyKH;AmD3xKD;EACE,mBAAA;EACA,0BAAA;EACA,2BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bs0KF;AmDrxKD;;EAGI,iBAAA;CnDsxKH;AmDzxKD;;EAMM,oBAAA;EACA,iBAAA;CnDuxKL;AmDnxKG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B61KF;AmDjxKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5B21KF;AmD1yKD;EvB1DE,2BAAA;EACC,0BAAA;C5Bu2KF;AmD7wKD;EAEI,oBAAA;CnD8wKH;AmD3wKD;EACE,oBAAA;CnD6wKD;AmDrwKD;;;EAII,iBAAA;CnDswKH;AmD1wKD;;;EAOM,mBAAA;EACA,oBAAA;CnDwwKL;AmDhxKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B63KF;AmDrxKD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDwwKP;AmD5xKD;;;;;;;;EAwBU,4BAAA;CnD8wKT;AmDtyKD;;;;;;;;EA4BU,6BAAA;CnDoxKT;AmDhzKD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bq5KF;AmDrzKD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDkxKP;AmD5zKD;;;;;;;;EA8CU,+BAAA;CnDwxKT;AmDt0KD;;;;;;;;EAkDU,gCAAA;CnD8xKT;AmDh1KD;;;;EA2DI,2BAAA;CnD2xKH;AmDt1KD;;EA+DI,cAAA;CnD2xKH;AmD11KD;;EAmEI,UAAA;CnD2xKH;AmD91KD;;;;;;;;;;;;EA0EU,eAAA;CnDkyKT;AmD52KD;;;;;;;;;;;;EA8EU,gBAAA;CnD4yKT;AmD13KD;;;;;;;;EAuFU,iBAAA;CnD6yKT;AmDp4KD;;;;;;;;EAgGU,iBAAA;CnD8yKT;AmD94KD;EAsGI,UAAA;EACA,iBAAA;CnD2yKH;AmDjyKD;EACE,oBAAA;CnDmyKD;AmDpyKD;EAKI,iBAAA;EACA,mBAAA;CnDkyKH;AmDxyKD;EASM,gBAAA;CnDkyKL;AmD3yKD;EAcI,iBAAA;CnDgyKH;AmD9yKD;;EAkBM,2BAAA;CnDgyKL;AmDlzKD;EAuBI,cAAA;CnD8xKH;AmDrzKD;EAyBM,8BAAA;CnD+xKL;AmDxxKD;EC1PE,mBAAA;CpDqhLD;AoDnhLC;EACE,eAAA;EACA,0BAAA;EACA,mBAAA;CpDqhLH;AoDxhLC;EAMI,uBAAA;CpDqhLL;AoD3hLC;EASI,eAAA;EACA,0BAAA;CpDqhLL;AoDlhLC;EAEI,0BAAA;CpDmhLL;AmDvyKD;EC7PE,sBAAA;CpDuiLD;AoDriLC;EACE,YAAA;EACA,0BAAA;EACA,sBAAA;CpDuiLH;AoD1iLC;EAMI,0BAAA;CpDuiLL;AoD7iLC;EASI,eAAA;EACA,uBAAA;CpDuiLL;AoDpiLC;EAEI,6BAAA;CpDqiLL;AmDtzKD;EChQE,sBAAA;CpDyjLD;AoDvjLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDyjLH;AoD5jLC;EAMI,0BAAA;CpDyjLL;AoD/jLC;EASI,eAAA;EACA,0BAAA;CpDyjLL;AoDtjLC;EAEI,6BAAA;CpDujLL;AmDr0KD;ECnQE,sBAAA;CpD2kLD;AoDzkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2kLH;AoD9kLC;EAMI,0BAAA;CpD2kLL;AoDjlLC;EASI,eAAA;EACA,0BAAA;CpD2kLL;AoDxkLC;EAEI,6BAAA;CpDykLL;AmDp1KD;ECtQE,sBAAA;CpD6lLD;AoD3lLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6lLH;AoDhmLC;EAMI,0BAAA;CpD6lLL;AoDnmLC;EASI,eAAA;EACA,0BAAA;CpD6lLL;AoD1lLC;EAEI,6BAAA;CpD2lLL;AmDn2KD;ECzQE,sBAAA;CpD+mLD;AoD7mLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD+mLH;AoDlnLC;EAMI,0BAAA;CpD+mLL;AoDrnLC;EASI,eAAA;EACA,0BAAA;CpD+mLL;AoD5mLC;EAEI,6BAAA;CpD6mLL;AqD7nLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD+nLD;AqDpoLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD+nLH;AqD1nLD;EACE,uBAAA;CrD4nLD;AqDxnLD;EACE,oBAAA;CrD0nLD;AsDrpLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CLgmLT;AsD/pLD;EASI,mBAAA;EACA,kCAAA;CtDypLH;AsDppLD;EACE,cAAA;EACA,mBAAA;CtDspLD;AsDppLD;EACE,aAAA;EACA,mBAAA;CtDspLD;AuD5qLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,0BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBqrLD;AuD7qLC;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB6rLD;AuDzqLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvD2qLH;AwDhsLD;EACE,iBAAA;CxDksLD;AwD9rLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD6rLD;AwD1rLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL6gLT;AwDhsLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLwlLT;AwDpsLD;EACE,mBAAA;EACA,iBAAA;CxDssLD;AwDlsLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDosLD;AwDhsLD;EACE,mBAAA;EACA,uBAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDksLD;AwD9rLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,uBAAA;CxDgsLD;AwD9rLC;ElCrEA,WAAA;EAGA,yBAAA;CtBowLD;AwDjsLC;ElCtEA,aAAA;EAGA,0BAAA;CtBwwLD;AwDhsLD;EACE,cAAA;EACA,iCAAA;CxDksLD;AwD9rLD;EACE,iBAAA;CxDgsLD;AwD5rLD;EACE,UAAA;EACA,wBAAA;CxD8rLD;AwDzrLD;EACE,mBAAA;EACA,cAAA;CxD2rLD;AwDvrLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDyrLD;AwD5rLD;EAQI,iBAAA;EACA,iBAAA;CxDurLH;AwDhsLD;EAaI,kBAAA;CxDsrLH;AwDnsLD;EAiBI,eAAA;CxDqrLH;AwDhrLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDkrLD;AwDhqLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD+qLD;EwD7qLD;InDvEA,kDAAA;IACQ,0CAAA;GLuvLP;EwD5qLD;IAAY,aAAA;GxD+qLX;CACF;AwD1qLD;EAFE;IAAY,aAAA;GxDgrLX;CACF;AyD/zLD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBs1LD;AyD30LC;EnCdA,aAAA;EAGA,0BAAA;CtB01LD;AyD90LC;EAAW,iBAAA;EAAmB,eAAA;CzDk1L/B;AyDj1LC;EAAW,iBAAA;EAAmB,eAAA;CzDq1L/B;AyDp1LC;EAAW,gBAAA;EAAmB,eAAA;CzDw1L/B;AyDv1LC;EAAW,kBAAA;EAAmB,eAAA;CzD21L/B;AyDv1LD;EACE,iBAAA;EACA,iBAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;CzDy1LD;AyDr1LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDu1LD;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,yBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,wBAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;A2Dl7LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,uBAAA;EACA,qCAAA;UAAA,6BAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLk5LT;A2D77LC;EAAY,kBAAA;C3Dg8Lb;A2D/7LC;EAAY,kBAAA;C3Dk8Lb;A2Dj8LC;EAAY,iBAAA;C3Do8Lb;A2Dn8LC;EAAY,mBAAA;C3Ds8Lb;A2Dn8LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Dq8LD;A2Dl8LD;EACE,kBAAA;C3Do8LD;A2D57LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D87LH;A2D37LD;EACE,mBAAA;C3D67LD;A2D37LD;EACE,mBAAA;EACA,YAAA;C3D67LD;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,uBAAA;C3D47LL;A2Dz7LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;C3D47LL;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,0BAAA;C3D47LL;A2Dx7LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3D07LH;A2Dz7LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,wBAAA;EACA,cAAA;C3D27LL;A4DpjMD;EACE,mBAAA;C5DsjMD;A4DnjMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DqjMD;A4DxjMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLy4LT;A4D/jMD;;EAcM,eAAA;C5DqjML;A4D3hMC;EA4NF;IvD3DE,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL86LP;E4DzjMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D4jML;E4D1jMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D6jML;E4D3jMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D8jML;CACF;A4DpmMD;;;EA6CI,eAAA;C5D4jMH;A4DzmMD;EAiDI,QAAA;C5D2jMH;A4D5mMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5D0jMH;A4DlnMD;EA4DI,WAAA;C5DyjMH;A4DrnMD;EA+DI,YAAA;C5DyjMH;A4DxnMD;;EAmEI,QAAA;C5DyjMH;A4D5nMD;EAuEI,YAAA;C5DwjMH;A4D/nMD;EA0EI,WAAA;C5DwjMH;A4DhjMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,mCAAA;C5DmjMD;A4D9iMC;EdnGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CopMH;A4DljMC;EACE,WAAA;EACA,SAAA;EdxGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C6pMH;A4DpjMC;;EAEE,WAAA;EACA,YAAA;EACA,sBAAA;EtCvHF,aAAA;EAGA,0BAAA;CtB4qMD;A4DtlMD;;;;EAuCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DqjMH;A4DhmMD;;EA+CI,UAAA;EACA,mBAAA;C5DqjMH;A4DrmMD;;EAoDI,WAAA;EACA,oBAAA;C5DqjMH;A4D1mMD;;EAyDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DqjMH;A4DhjMG;EACE,iBAAA;C5DkjML;A4D9iMG;EACE,iBAAA;C5DgjML;A4DtiMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DwiMD;A4DjjMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D8hMH;A4D7jMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;C5D8hMH;A4DvhMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;C5DyhMD;A4DxhMC;EACE,kBAAA;C5D0hMH;A4Dj/LD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DmhMH;E4D3hMD;;IAYI,mBAAA;G5DmhMH;E4D/hMD;;IAgBI,oBAAA;G5DmhMH;E4D9gMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5DghMD;E4D5gMD;IACE,aAAA;G5D8gMD;CACF;A6D7wMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7D6yMH;A6D3yMC;;;;;;;;;;;;;;;;EACE,YAAA;C7D4zMH;AiCp0MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D+0MD;AiCt0MD;EACE,wBAAA;CjCw0MD;AiCt0MD;EACE,uBAAA;CjCw0MD;AiCh0MD;EACE,yBAAA;CjCk0MD;AiCh0MD;EACE,0BAAA;CjCk0MD;AiCh0MD;EACE,mBAAA;CjCk0MD;AiCh0MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/D41MD;AiC9zMD;EACE,yBAAA;CjCg0MD;AiCzzMD;EACE,gBAAA;CjC2zMD;AgE51MD;EACE,oBAAA;ChE81MD;AgEx1MD;;;;ECdE,yBAAA;CjE42MD;AgEv1MD;;;;;;;;;;;;EAYE,yBAAA;ChEy1MD;AgEl1MD;EA6IA;IC7LE,0BAAA;GjEs4MC;EiEr4MD;IAAU,0BAAA;GjEw4MT;EiEv4MD;IAAU,8BAAA;GjE04MT;EiEz4MD;;IACU,+BAAA;GjE44MT;CACF;AgE51MD;EAwIA;IA1II,0BAAA;GhEk2MD;CACF;AgE51MD;EAmIA;IArII,2BAAA;GhEk2MD;CACF;AgE51MD;EA8HA;IAhII,iCAAA;GhEk2MD;CACF;AgE31MD;EAwHA;IC7LE,0BAAA;GjEo6MC;EiEn6MD;IAAU,0BAAA;GjEs6MT;EiEr6MD;IAAU,8BAAA;GjEw6MT;EiEv6MD;;IACU,+BAAA;GjE06MT;CACF;AgEr2MD;EAmHA;IArHI,0BAAA;GhE22MD;CACF;AgEr2MD;EA8GA;IAhHI,2BAAA;GhE22MD;CACF;AgEr2MD;EAyGA;IA3GI,iCAAA;GhE22MD;CACF;AgEp2MD;EAmGA;IC7LE,0BAAA;GjEk8MC;EiEj8MD;IAAU,0BAAA;GjEo8MT;EiEn8MD;IAAU,8BAAA;GjEs8MT;EiEr8MD;;IACU,+BAAA;GjEw8MT;CACF;AgE92MD;EA8FA;IAhGI,0BAAA;GhEo3MD;CACF;AgE92MD;EAyFA;IA3FI,2BAAA;GhEo3MD;CACF;AgE92MD;EAoFA;IAtFI,iCAAA;GhEo3MD;CACF;AgE72MD;EA8EA;IC7LE,0BAAA;GjEg+MC;EiE/9MD;IAAU,0BAAA;GjEk+MT;EiEj+MD;IAAU,8BAAA;GjEo+MT;EiEn+MD;;IACU,+BAAA;GjEs+MT;CACF;AgEv3MD;EAyEA;IA3EI,0BAAA;GhE63MD;CACF;AgEv3MD;EAoEA;IAtEI,2BAAA;GhE63MD;CACF;AgEv3MD;EA+DA;IAjEI,iCAAA;GhE63MD;CACF;AgEt3MD;EAyDA;ICrLE,yBAAA;GjEs/MC;CACF;AgEt3MD;EAoDA;ICrLE,yBAAA;GjE2/MC;CACF;AgEt3MD;EA+CA;ICrLE,yBAAA;GjEggNC;CACF;AgEt3MD;EA0CA;ICrLE,yBAAA;GjEqgNC;CACF;AgEn3MD;ECnJE,yBAAA;CjEygND;AgEh3MD;EA4BA;IC7LE,0BAAA;GjEqhNC;EiEphND;IAAU,0BAAA;GjEuhNT;EiEthND;IAAU,8BAAA;GjEyhNT;EiExhND;;IACU,+BAAA;GjE2hNT;CACF;AgE93MD;EACE,yBAAA;ChEg4MD;AgE33MD;EAqBA;IAvBI,0BAAA;GhEi4MD;CACF;AgE/3MD;EACE,yBAAA;ChEi4MD;AgE53MD;EAcA;IAhBI,2BAAA;GhEk4MD;CACF;AgEh4MD;EACE,yBAAA;ChEk4MD;AgE73MD;EAOA;IATI,iCAAA;GhEm4MD;CACF;AgE53MD;EACA;ICrLE,yBAAA;GjEojNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n border: 0;\n background-color: transparent;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #fff;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #fff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #ccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #fff;\n border: 1px solid #ddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #fff;\n border-color: #ddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #fff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #fff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #fff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #fff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #fff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #fff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #fff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: floor((@gutter / 2));\n padding-right: ceil((@gutter / 2));\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n\n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on