commit f55c91276e68c5a2e065ffd269ef791adc5db464 Author: Malin Date: Mon May 11 09:49:52 2026 +0200 feat: add videodb media index with Docker stack - Add videodb PHP/MySQL media collection manager (Blu-ray, DVD, CD) - Dockerfile: PHP 8.1 + Apache with GD/mysqli/exif extensions - docker-compose.yml: app on port 6761 + MySQL 8.0 with health checks - docker-entrypoint.sh: auto-generates config.inc.php from env vars, waits for MySQL, initializes DB schema idempotently - init-db.php: CLI schema installer using app's own prefix_query() logic - Persistent volumes for DB, cache, and cover images Co-Authored-By: Claude Sonnet 4.6 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ebeea54 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +services: + + app: + build: + context: ./videodb + container_name: videodb_app + ports: + - "6761:80" + environment: + DB_HOST: db + DB_USER: videodb + DB_PASSWORD: videodb_secret + DB_NAME: videodb + DB_PREFIX: videodb_ + depends_on: + db: + condition: service_healthy + volumes: + # Persist cover images and cached thumbnails across rebuilds + - videodb_cache:/var/www/html/cache + - videodb_images:/var/www/html/images + restart: unless-stopped + + db: + image: mysql:8.0 + container_name: videodb_db + environment: + MYSQL_ROOT_PASSWORD: root_secret + MYSQL_DATABASE: videodb + MYSQL_USER: videodb + MYSQL_PASSWORD: videodb_secret + volumes: + - videodb_db:/var/lib/mysql + # MySQL 8 defaults to caching_sha2_password; keep native auth for broad client compat + command: --default-authentication-plugin=mysql_native_password + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "videodb", "-pvideodb_secret"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 30s + restart: unless-stopped + +volumes: + videodb_db: + videodb_cache: + videodb_images: diff --git a/videodb/.gitignore b/videodb/.gitignore new file mode 100644 index 0000000..8ea4c48 --- /dev/null +++ b/videodb/.gitignore @@ -0,0 +1,7 @@ +# Files to be ignored by git +config.inc.php +cache +# ignore logfiles +*.log +**/Thumbs.db +.idea diff --git a/videodb/.htaccess b/videodb/.htaccess new file mode 100644 index 0000000..0b1a866 --- /dev/null +++ b/videodb/.htaccess @@ -0,0 +1,39 @@ +# +# Apache access control +# +# @author Andreas Goetz +# $Id: .htaccess,v 1.2 2008/04/29 13:32:48 andig2 Exp $ +# + +# Don't show directory listings for URLs which map to a directory. +Options -Indexes + +# go directly to index.php +DirectoryIndex index.php + +# make sure Apache doesn't set default charset on it's own (bug 1943523) +AddDefaultCharset utf-8 + +# PHP 4, Apache 1. + + php_value default_charset utf-8 + + +# PHP 4, Apache 2. + + php_value default_charset utf-8 + + +# PHP 5, Apache 1 and 2. + + php_value default_charset utf-8 + + +# enable compression + + # Deflate zum zippen + AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html + diff --git a/videodb/Dockerfile b/videodb/Dockerfile new file mode 100644 index 0000000..6af5bc9 --- /dev/null +++ b/videodb/Dockerfile @@ -0,0 +1,51 @@ +FROM php:8.1-apache + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + libpng-dev \ + libjpeg-dev \ + libfreetype6-dev \ + default-mysql-client \ + && rm -rf /var/lib/apt/lists/* + +# Install PHP extensions needed by videoDB +RUN docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) \ + gd \ + mysqli \ + exif \ + mbstring + +# Enable Apache mod_rewrite for .htaccess +RUN a2enmod rewrite + +# Allow .htaccess overrides in document root +RUN sed -i '//,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf + +# Set working directory to document root +WORKDIR /var/www/html + +# Copy application (including vendor which is committed to the repo) +COPY . . + +# Create cache directories and set permissions +RUN mkdir -p \ + cache/smarty \ + cache/imdb \ + cache/img \ + cache/thumbs \ + cache/javascript \ + cache/local \ + && chown -R www-data:www-data \ + cache \ + images + +# Copy entrypoint and db-init scripts +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +COPY init-db.php /usr/local/bin/init-db.php +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +EXPOSE 80 + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/videodb/README.md b/videodb/README.md new file mode 100644 index 0000000..2c5966d --- /dev/null +++ b/videodb/README.md @@ -0,0 +1,34 @@ +videoDB +======= + +VideoDB is a PHP-based web application to manage a personal video collection. Multiple video types are supported, ranging from VHS tapes and DVDs to Blu-ray discs and DivX files on hard-disc. Even video games are supported. + +Introduction +------------ + +### Browse + +You can use videoDB to manage your video and CD collection, be it DVD, BluRay or plain Files: + +![Browse Movies](https://raw.github.com/andig/videodb/master/doc/screenshots/0.png) + +### View + +![View Details](https://raw.github.com/andig/videodb/master/doc/screenshots/1.png) + +### Edit +All data is editable in nice layed out forms: + +![Edit](https://raw.github.com/andig/videodb/master/doc/screenshots/2.png) + +### IMDB + +New movies are easily added directly from IMDB or other sources: + +![IMDB](https://raw.github.com/andig/videodb/master/doc/screenshots/3.png) + +### Config + +videoDB is also highly customizable- almost every aspect can be changed from template selection to detailed customization: + +![Config](https://raw.github.com/andig/videodb/master/doc/screenshots/4.png) diff --git a/videodb/borrow.php b/videodb/borrow.php new file mode 100644 index 0000000..b79e78d --- /dev/null +++ b/videodb/borrow.php @@ -0,0 +1,111 @@ + + * @version $Id: borrow.php,v 2.21 2013/03/10 16:20:10 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/output.php'; + +// check for localnet +localnet_or_die(); + +// permission check +permission_or_die(PERM_WRITE, PERM_ANY); + +/** + * input + */ +$diskid = req_string('diskid'); +$return = req_string('return'); +$who = req_string('who'); + +// borrowmanagement for single disk +$editable = false; +$dt = null; +if (!empty($diskid)) +{ + if (check_permission(PERM_WRITE, get_owner_id($diskid,true))) + { + $editable = true; + if ($return) { + $SQL = "DELETE FROM ".TBL_LENT." WHERE diskid = '".escapeSQL($diskid)."'"; + runSQL($SQL); + } + if (!empty($who)) { + $SQL = "INSERT INTO ".TBL_LENT." SET who = '".escapeSQL($who)."', diskid = '".escapeSQL($diskid)."'"; + runSQL($SQL); + } + + $SQL = "SELECT who, DATE_FORMAT(dt,'%d.%m.%Y') AS dt + FROM ".TBL_LENT." + WHERE diskid = '".escapeSQL($diskid)."'"; + $result = runSQL($SQL); + + if (isset($result[0]['who'])) + { $who = $result[0]['who']; } + if (isset($result[0]['dt'])) + { $dt = $result[0]['dt']; } + } +} + +$WHERES = ''; +$JOINS = ''; + +if ($config['multiuser']) +{ + // get owner from session- or use current user + session_default_owner(); + // build html select box + $all = $lang['filter_any']; + $smarty->assign('owners', out_owners(array($all => $all), PERM_READ)); + $smarty->assign('owner', $owner); + + // if we don't have read all permissions, limit visibility using cross-user permissions + if (!check_permission(PERM_READ)) + { + $JOINS = ' LEFT JOIN '.TBL_PERMISSIONS.' ON '.TBL_DATA.'.owner_id = '.TBL_PERMISSIONS.'.to_uid'; + $WHERES .= ' AND '.TBL_PERMISSIONS.'.from_uid = '.get_current_user_id().' AND '.TBL_PERMISSIONS.'.permissions & '.PERM_READ.' != 0'; + } + + // further limit to single owner + if ($owner != $all) $WHERES .= " AND ".TBL_USERS.".name = '".escapeSQL($owner)."'"; +} + +// overview on lent disks +$SQL = "SELECT who, DATE_FORMAT(dt,'%d.%m.%Y') as dt, ".TBL_LENT.".diskid, + CASE WHEN subtitle = '' THEN title ELSE CONCAT(title,' - ',subtitle) END AS title, + ".TBL_DATA.".id, COUNT(".TBL_LENT.".diskid) AS count, ".TBL_USERS.".name AS owner + FROM ".TBL_LENT.", ".TBL_DATA." + LEFT JOIN ".TBL_USERS." ON owner_id = ".TBL_USERS.".id + $JOINS + WHERE ".TBL_LENT.".diskid = ".TBL_DATA.".diskid + $WHERES + GROUP BY ".TBL_LENT.".diskid, ".TBL_DATA.".id + ORDER BY who, ".TBL_LENT.".diskid"; +$result = runSQL($SQL); + +// check permissions +for($i=0; $i < count($result); $i++) +{ + $result[$i]['editable'] = check_permission(PERM_WRITE, get_userid($result[$i]['owner'])); +} + +// prepare templates +tpl_page(); + +$smarty->assign('diskid', $diskid); +$smarty->assign('who', $who); +$smarty->assign('dt', $dt); +$smarty->assign('editable', $editable); +$smarty->assign('borrowlist', $result); + +// display templates +tpl_display('borrow.tpl'); + + diff --git a/videodb/borrowask.php b/videodb/borrowask.php new file mode 100644 index 0000000..39eaea6 --- /dev/null +++ b/videodb/borrowask.php @@ -0,0 +1,80 @@ + + * @version $Id: borrowask.php,v 2.13 2008/06/15 13:58:13 andig2 Exp $ + */ + +require_once './core/functions.php'; + +// Auth-Checks +$user_id = get_current_user_id(); +$user = get_username($user_id); + +if (empty($user)) +{ + errorpage('Access denied','You don\'t have enough permissions to access this + page try to login first. (This feature is not + available in Single User Mode)'); +} + +/** + * input + */ +$id = req_int('id'); +$diskid = req_string('diskid'); + +if (empty($id) || empty($diskid)) +{ + errorpage('Error', 'No Ids given'); +} + +$owner = get_owner($diskid, true); +$result = runSQL('SELECT email FROM '.TBL_USERS." WHERE name = '".escapeSQL($owner)."'"); +$owner_email = $result[0]['email']; +$result = runSQL('SELECT email FROM '.TBL_USERS." WHERE id = '".escapeSQL($user_id)."'"); +$user_email = $result[0]['email']; +$result = runSQL('SELECT title FROM '.TBL_DATA." WHERE id = '".escapeSQL($id)."'"); +$title = $result[0]['title']; + +$mail = $lang['msg_borrowaskmail']; +$subject = $lang['msg_borrowasksubject']; +$url = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME']).'/show.php?id='.$id; + +// replace place holders +$mail = str_replace('%id%', $id, $mail); +$mail = str_replace('%diskid%', $diskid, $mail); +$mail = str_replace('%owner%', $owner, $mail); +$mail = str_replace('%ownermail%', $owner_email, $mail); +$mail = str_replace('%user%', $user, $mail); +$mail = str_replace('%usermail%', $user_email, $mail); +$mail = str_replace('%title%', $title, $mail); +$mail = str_replace('%url%', $url, $mail); + +$subject = str_replace('%id%', $id, $subject); +$subject = str_replace('%diskid%', $diskid, $subject); +$subject = str_replace('%owner%', $owner, $subject); +$subject = str_replace('%ownermail%', $owner_email, $subject); +$subject = str_replace('%user%', $user, $subject); +$subject = str_replace('%usermail%', $user_email, $subject); +$subject = str_replace('%title%', $title, $subject); +$subject = str_replace('%url%', $url, $subject); + + +// prepare templates +tpl_page(); + +/* +$smarty->assign('success', @mail($owner_email, $subject, $mail)); +Fix for https://sourceforge.net/tracker/?func=detail&atid=586362&aid=1570618&group_id=88349 +*/ +$smarty->assign('success', @mail($owner_email, $subject, $mail, "From: $user <$user_email>\r\nReply-To: $user_email\r\n")); + +// display templates +smarty_display('borrowask.tpl'); + + diff --git a/videodb/composer.json b/videodb/composer.json new file mode 100644 index 0000000..7840378 --- /dev/null +++ b/videodb/composer.json @@ -0,0 +1,18 @@ +{ + "name": "andig/videodb", + "type": "project", + "require": { + "guzzlehttp/guzzle": "^6.3", + "smarty/smarty": "^3.1", + "pear/ole": "^v1.0.0RC8", + "pear/spreadsheet_excel_writer": "^v0.9.7", + "setasign/fpdf": "^1.8", + "james-heinrich/phpthumb": "v1.7.19" + }, + "authors": [ + { + "name": "andig", + "email": "cpuidle@gmx.de" + } + ] +} diff --git a/videodb/composer.lock b/videodb/composer.lock new file mode 100644 index 0000000..afa6279 --- /dev/null +++ b/videodb/composer.lock @@ -0,0 +1,1256 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6fa0a66bc53ce70c0b87e10cc5d5b630", + "packages": [ + { + "name": "guzzlehttp/guzzle", + "version": "6.5.8", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981", + "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.9", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.17" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/6.5.8" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-06-20T22:16:07+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "b94b2807d85443f9719887892882d0329d1e2598" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", + "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2022-08-28T14:55:35+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.9.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2023-04-17T16:00:37+00:00" + }, + { + "name": "james-heinrich/phpthumb", + "version": "v1.7.19", + "source": { + "type": "git", + "url": "https://github.com/JamesHeinrich/phpThumb.git", + "reference": "f580578bd119b50719772705f14158b9b72aaf2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JamesHeinrich/phpThumb/zipball/f580578bd119b50719772705f14158b9b72aaf2c", + "reference": "f580578bd119b50719772705f14158b9b72aaf2c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "suggest": { + "ext-gd": "PHP GD library", + "ext-imagick": "PHP ImageMagick" + }, + "type": "library", + "autoload": { + "files": [ + "phpthumb.class.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-1.0-or-later", + "LGPL-3.0-only", + "MPL-2.0" + ], + "authors": [ + { + "name": "James Heinrich", + "email": "info@silisoftware.com", + "homepage": "http://www.silisoftware.com/", + "role": "Developer" + } + ], + "description": "The PHP thumbnail generator", + "homepage": "http://phpthumb.sourceforge.net/", + "keywords": [ + "ImageMagick", + "gd", + "image", + "magic", + "thumb", + "thumbnail" + ], + "support": { + "issues": "https://github.com/JamesHeinrich/phpThumb/issues", + "source": "https://github.com/JamesHeinrich/phpThumb/tree/v1.7.19" + }, + "time": "2022-11-24T15:39:54+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.21", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae", + "reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2022-02-16T17:07:03+00:00" + }, + { + "name": "pear/console_getopt", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/pear/Console_Getopt.git", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Console": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Andrei Zmievski", + "email": "andrei@php.net", + "role": "Lead" + }, + { + "name": "Stig Bakken", + "email": "stig@php.net", + "role": "Developer" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net", + "role": "Helper" + } + ], + "description": "More info available on: http://pear.php.net/package/Console_Getopt", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt", + "source": "https://github.com/pear/Console_Getopt" + }, + "time": "2019-11-20T18:27:48+00:00" + }, + { + "name": "pear/ole", + "version": "v1.0.0RC8", + "source": { + "type": "git", + "url": "https://github.com/pear/OLE.git", + "reference": "1ee17e4ac71d6e39ff98f78c4674e8a748d16db3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/OLE/zipball/1ee17e4ac71d6e39ff98f78c4674e8a748d16db3", + "reference": "1ee17e4ac71d6e39ff98f78c4674e8a748d16db3", + "shasum": "" + }, + "require": { + "pear/pear_exception": "^1.0", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2", + "pear/pear-core-minimal": "^1.10", + "phpunit/phpunit": ">=5 <10", + "sanmai/phpunit-legacy-adapter": "^6 || ^8" + }, + "type": "library", + "autoload": { + "psr-0": { + "OLE": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "PHP-3.01" + ], + "authors": [ + { + "name": "Christian Schmidt", + "email": "schmidt@php.net", + "role": "Lead" + }, + { + "name": "Xavier Noguer", + "email": "xnoguer@php.net", + "role": "Lead" + } + ], + "description": "This package allows reading and writing of OLE (Object Linking and Embedding) compound documents. This format is used as container for Excel (.xls), Word (.doc) and other Microsoft file formats.", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=OLE", + "source": "https://github.com/pear/OLE" + }, + "time": "2021-02-22T12:41:39+00:00" + }, + { + "name": "pear/pear-core-minimal", + "version": "v1.10.11", + "source": { + "type": "git", + "url": "https://github.com/pear/pear-core-minimal.git", + "reference": "68d0d32ada737153b7e93b8d3c710ebe70ac867d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/68d0d32ada737153b7e93b8d3c710ebe70ac867d", + "reference": "68d0d32ada737153b7e93b8d3c710ebe70ac867d", + "shasum": "" + }, + "require": { + "pear/console_getopt": "~1.4", + "pear/pear_exception": "~1.0" + }, + "replace": { + "rsky/pear-core-min": "self.version" + }, + "type": "library", + "autoload": { + "psr-0": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "src/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@php.net", + "role": "Lead" + } + ], + "description": "Minimal set of PEAR core files to be used as composer dependency", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR", + "source": "https://github.com/pear/pear-core-minimal" + }, + "time": "2021-08-10T22:31:03+00:00" + }, + { + "name": "pear/pear_exception", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/pear/PEAR_Exception.git", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "<9" + }, + "type": "class", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "PEAR/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "." + ], + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Helgi Thormar", + "email": "dufuz@php.net" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net" + } + ], + "description": "The PEAR Exception base class.", + "homepage": "https://github.com/pear/PEAR_Exception", + "keywords": [ + "exception" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception", + "source": "https://github.com/pear/PEAR_Exception" + }, + "time": "2021-03-21T15:43:46+00:00" + }, + { + "name": "pear/spreadsheet_excel_writer", + "version": "v0.9.7", + "source": { + "type": "git", + "url": "https://github.com/pear/Spreadsheet_Excel_Writer.git", + "reference": "f6e1821bf20cbcd8202b21dc5104b0e5688386c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pear/Spreadsheet_Excel_Writer/zipball/f6e1821bf20cbcd8202b21dc5104b0e5688386c5", + "reference": "f6e1821bf20cbcd8202b21dc5104b0e5688386c5", + "shasum": "" + }, + "require": { + "pear/ole": ">=1.0.0RC4", + "pear/pear-core-minimal": "^1.10", + "php": ">=5.6" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2", + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": ">=5 <10", + "sanmai/phpunit-legacy-adapter": "^6 || ^8" + }, + "type": "library", + "autoload": { + "psr-0": { + "Spreadsheet": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "./" + ], + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Carsten Schmitz", + "email": "cschmitz@limesurvey.org", + "role": "Lead" + }, + { + "name": "Xavier Noguer", + "email": "xnoguer@php.net", + "role": "Lead" + }, + { + "name": "Franck Lefevre", + "email": "progi1984@gmail.com", + "role": "Developer" + }, + { + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "role": "Developer" + }, + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "role": "Lead" + } + ], + "description": "Allows writing of Excel spreadsheets without the need for COM objects. Supports formulas, images (BMP) and all kinds of formatting for text and cells.", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Spreadsheet_Excel_Writer", + "source": "https://github.com/pear/Spreadsheet_Excel_Writer" + }, + "time": "2021-01-01T11:29:43+00:00" + }, + { + "name": "psr/http-message", + "version": "1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/1.1" + }, + "time": "2023-04-04T09:50:52+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "setasign/fpdf", + "version": "1.8.5", + "source": { + "type": "git", + "url": "https://github.com/Setasign/FPDF.git", + "reference": "f4104a04c9a3f95c4c26a0a0531abebcc980987a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Setasign/FPDF/zipball/f4104a04c9a3f95c4c26a0a0531abebcc980987a", + "reference": "f4104a04c9a3f95c4c26a0a0531abebcc980987a", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-zlib": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "fpdf.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Olivier Plathey", + "email": "oliver@fpdf.org", + "homepage": "http://fpdf.org/" + } + ], + "description": "FPDF is a PHP class which allows to generate PDF files with pure PHP. F from FPDF stands for Free: you may use it for any kind of usage and modify it to suit your needs.", + "homepage": "http://www.fpdf.org", + "keywords": [ + "fpdf", + "pdf" + ], + "support": { + "source": "https://github.com/Setasign/FPDF/tree/1.8.5" + }, + "time": "2022-11-18T07:02:00+00:00" + }, + { + "name": "smarty/smarty", + "version": "v3.1.48", + "source": { + "type": "git", + "url": "https://github.com/smarty-php/smarty.git", + "reference": "2fc443806cdcaee4441be4d0bb09f8fa56a17f2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/smarty-php/smarty/zipball/2fc443806cdcaee4441be4d0bb09f8fa56a17f2c", + "reference": "2fc443806cdcaee4441be4d0bb09f8fa56a17f2c", + "shasum": "" + }, + "require": { + "php": "^5.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^6.5 || ^5.7 || ^4.8", + "smarty/smarty-lexer": "^3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "libs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Monte Ohrt", + "email": "monte@ohrt.com" + }, + { + "name": "Uwe Tews", + "email": "uwe.tews@googlemail.com" + }, + { + "name": "Rodney Rehm", + "email": "rodney.rehm@medialize.de" + } + ], + "description": "Smarty - the compiling PHP template engine", + "homepage": "http://www.smarty.net", + "keywords": [ + "templating" + ], + "support": { + "forum": "http://www.smarty.net/forums/", + "irc": "irc://irc.freenode.org/smarty", + "issues": "https://github.com/smarty-php/smarty/issues", + "source": "https://github.com/smarty-php/smarty/tree/v3.1.48" + }, + "time": "2023-03-28T19:45:54+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/4ad5115c0f5d5172a9fe8147675ec6de266d8826", + "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php70": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-21T09:57:48+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8db0ae7936b42feb370840cf24de1a144fb0ef27", + "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", + "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "beecef6b463b06954638f02378f52496cb84bacc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", + "reference": "beecef6b463b06954638f02378f52496cb84bacc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "pear/ole": 5 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/videodb/config.sample.php b/videodb/config.sample.php new file mode 100644 index 0000000..1e5db76 --- /dev/null +++ b/videodb/config.sample.php @@ -0,0 +1,357 @@ + + * @author Andreas Goetz + * @version $Id: config.sample.php,v 1.10 2012/06/14 17:17:36 andig2 Exp $ + */ + +/** + * Database configuration + */ + +/** + * the hostname of your database server + * @default 'localhost' + */ +$config['db_server'] = 'localhost'; + +/** + * the username of your database server + * @default 'videodb' + */ +$config['db_user'] = 'videodb'; + +/** + * the password for above user + * @default '' + */ +$config['db_password'] = ''; + +/** + * the name of the database + * @default 'videodb' + */ +$config['db_database'] = 'videodb'; + +/** + * an optional prefix for the tables (for use in hosting environments) + * can be empty + * @default: 'videodb_' + */ +$config['db_prefix'] = 'videodb_'; + +/** + * Offline flag, set to 1 to take videoDB offline + * @default 0 + */ +$config['offline'] = 0; + +/** + * Debug options, set to 1 to enable debug logs + * Usually leave this at 0 (to keep videoDB fast) + * @default 0 + */ +$config['debug'] = 0; + +/** + * HttpClient logging, is for debugging only + * Usually leave this at 0 (to keep videoDB fast) + * @default 0 + */ +$config['httpclientlog'] = 0; + +/** + * Boxee box configuration + * If you still have and use one of these you can enter + * the hostname and port used. If not, leave as-is + * @default '' + */ +$config['boxeeHost'] = ''; +/** + * @default 9090 + */ +$config['boxeePort'] = 9090; + +/** + * Cache configuration + * This setting determines how long to keep remote assets locally + * After this many seconds they are considered stale and will be + * fetched again for a fresh copy. You can set this to 0, but you + * probably want to keep this at the default or higher. + * @default 604800 (one week, 7*24*60*60) + */ +$config['IMDBage'] = 604800; + +/** + * Hierarchical cache folders, used to distribute the cached files + * over multiple folders instead of one, prevents hitting filesystem + * limits in due time. Set to 1 to enable, 0 to disable + * @default 1 + */ +$config['hierarchical'] = 1; + +/** + * Pruning means automatically cleaning the cache folders, removing + * old/stale files. Set to 1 to enable, 0 to disable + * @default 1 + */ +$config['cache_pruning'] = 1; + +/** + * Enable use of HTTP 304 headers for unmodified content to save bandwidth + * Set to 1 to enable, 0 to disable + * @default 0 + */ +$config['http_caching'] = 0; + +/** + * Defaults for external data lookup when editing entries + * Set to 0 to ignore external data + * Set to 1 to lookup missing data + * Set to 2 to overwrite all entered data with the external version + * @default 0 + */ +$config['lookupdefault_edit'] = 0; + +/** + * Defaults for external data lookup when adding entries + * Set to 0 to ignore external data + * Set to 1 to lookup missing data + * Set to 2 to overwrite all entered data with the external version + * @default 2 + */ +$config['lookupdefault_new'] = 2; + +/** + * Amount of digits which are automatically generated as DiskID + * if "Automatic DiskID" is enabled in the configuration tab + * @default 4 + */ +$config['diskid_digits'] = 4; + +/** + * Thumbnail configuration + * + * If you're running videodb over a low bandwidth connection with many users or want to enhance + * image quality by applying smooth scaling, use the following settings to control the behavior. + * + * Define when thumbnails are created and which jpeg quality to use: + * -1 : no scaling - use of thumbnails is disabled + * 0 : reduce only - create thumbnails when requested image dimensions are smaller than original image + * 1 : always scale - create thumbnails for all images (applies aliasing when scaling) + * + * or define a positive integer to check filesize - thumbnail is created when existing file is bigger + * than the specified value in bytes + * @default 1 + */ +$config['thumbnail_level'] = 1; + +/** + * Control the quality setting when generating jpeg images + * Is a range from 0 to 100, where 0 is the lowest quality with smallest filesize + * and 100 is the best quality with largest filesize. Industry standard setting is 70 + * @default 80 + */ +$config['thumbnail_quality'] = 80; + +/** + * Export settings + */ + +/** + * XML Import/Export + * Set to 1 to enable XML data im/export, set to 0 to disable + * @note import is currently broken + * @default 0 + */ +$config['xml'] = 0; + +/** + *XML export thumbnail URLs + * Set to 1 to enable, set to 0 to disable + * @default 0 + */ +$config['xml_thumbnails'] = 0; + +/** + * RSS Feed + * Set to 1 to enable RSS Feed, set to 0 to disable + * @default 1 + */ +$config['rss'] = 1; + +/** + * PDF Export + * set to 1 to enable PDF data export, set to 0 to disable + * @default 1 + */ +$config['pdf'] = 1; + +/** + * Here you can set the fonts used for title. Available fonts: + * - Arial + * - Courier + * - Helvetica + * - Symbol + * - Times + * - ZapfDingBats + * @default 'Arial' + */ +$config['pdf_font_title'] = 'Arial'; + +/** + * Here you can set the fonts used for plot. Available fonts: + * - Arial + * - Courier + * - Helvetica + * - Symbol + * - Times + * - ZapfDingBats + * @default 'Times' + */ +$config['pdf_font_plot'] = 'Times'; + +/** + * Overall font size. Title will be this size and the plot will be one point smaller. + * @default 10 + */ +$config['pdf_font_size'] = 10; + +/** + * Maximum "rescale" width for images + * @default 95 + */ +$config['pdf_image_max_width'] = 95; + +/** + * Maximum "rescale" height for images + * @default 135 + */ +$config['pdf_image_max_height'] = 135; + +/** + * Set the width of the mediatype icon + * @default 8 + */ +$config['pdf_image_media_width'] = 8; + +/** + * Total Page width + * @default 210 + */ +$config['pdf_page_width'] = 210; + +/** + * Maximum plot text length + * @default 500 + */ +$config['pdf_text_length'] = 500; + +/** + * Margins between fields + * @default 5 + */ +$config['pdf_margin'] = 5; + +/** + * Left margin + * @default 5 + */ +$config['pdf_left_margin'] = 5; + +/** + * Right margin + * @default 5 + */ +$config['pdf_right_margin'] = 5; + +/** + * Image height and width on generated PDF + * @default 24 + */ +$config['pdf_image_height'] = 24; + +/** + * Image width on generated PDF, do not change + * @default + */ +$config['pdf_image_width'] = intval(($config['pdf_image_max_width'] / $config['pdf_image_max_height']) * $config['pdf_image_height']); + +/** + * XLS Export, set to 1 to enable Excel data export, 0 to disable + * @default 1 + */ +$config['xls'] = 1; + +/** + * Name of the Excel sheet and headline for printing + * @default 'VideoDB' + */ +$config['xls_sheet_title'] = 'VideoDB'; + +/** + * Filename for the output file without xls extension! + * @default 'VideoDB' + */ +$config['xls_output_filename'] = 'VideoDB'; + +/** + * Show column headlines in the first row (1=Yes;0=No) + * @default 1 + */ +$config['xls_show_headline'] = 1; + +/** + * Set background color of unseen movie titles to yellow? (1=Yes;0=No) + * @default 1 + */ +$config['xls_mark_unseen'] = 1; + +/** + * Set background color of borrowed movies to red? (1=Yes;0=No) + * @default 1 + */ +$config['xls_mark_lent'] = 1; + +/** + * Build your own list, where you define which fields you want and in + * which order they should appear, separated by comma. + * + * Supported fields are: + * title diskid language mediatype runtime year + * custom1 custom2 custom3 custom4 owner lent + * insertdate genres plot + * + * The length of the plot is limited to 253 characters!!! + * + * It's possible to use those fields also as an Excel note. For example if you want + * to see the title of the movie, followed by the diskid and the running time. As a + * note you want to see the plot next to the title and the owner next to the diskid. + * For this example the xls_extra_fields list would look like this: + * + * $config['xls_extra_fields'] = 'title (plot), diskid (owner), runtime'; + * @default 'title (plot), diskid, genres, language, mediatype, runtime, year, custom1, custom2, custom3, custom4, insertdate, owner, lent' + */ +$config['xls_extra_fields'] = 'title (plot), diskid, genres, language, mediatype, runtime, year, custom1, custom2, custom3, custom4, insertdate, owner, lent'; + +/** + * To get access to FSK18 rated movies in the german dvdb engine you + * have to enter your dvdb user id and password. If you don't have a + * user you can go to http://www.dvdb.de and click on 'Neu registrieren'. + * Don't forget to enter the identification card id to get FSK18 access! + * @default '' + * @deprecated The dvdb.de website does not appear to exist anymore + */ +$config['dvdb_user'] = ''; +$config['dvdb_password'] = ''; + diff --git a/videodb/contrib.php b/videodb/contrib.php new file mode 100644 index 0000000..fc5c29c --- /dev/null +++ b/videodb/contrib.php @@ -0,0 +1,91 @@ + + * @version $Id: contrib.php,v 1.3 2005/05/26 12:24:15 andig2 Exp $ + */ + +require_once './core/functions.php'; + +// check for localnet +localnet_or_die(); + +// multiuser permission check +permission_or_die(PERM_WRITE); + +/** + * Sort multi-dimensional arrays + * (thanks to phpdotnet) + */ +function multidimsort($array_in, $column) +{ + $multiarray = array_column($array_in, $column); + $array_out = array(); + + asort($multiarray); + + // traverse new array of index values and add the corresponding element of the input array to the correct position in the output array + foreach ($multiarray as $key => $val) + { + $array_out[] = $array_in[$key]; + } + + // return the output array which is all nicely sorted by the index you wanted! + return $array_out; +} + + +// dynamic contrib loader +$files = array(); +$dirpath = './contrib'; + +if ($dh = opendir($dirpath)) +{ + while (($filename = readdir($dh)) !== false) + { + $access = ''; + if (!preg_match('/.*\.php$/', $filename)) continue; + if ($filename == 'index.php') continue; + + $info = array('contrib/'.$filename); + $file = $dirpath.'/'.$filename; + $content = file_get_contents($file); + + // title + if (preg_match('/(.+?)assign('files', $files); + +// display templates +tpl_display('contrib.tpl'); + +?> diff --git a/videodb/contrib/add_recommended_movies.php b/videodb/contrib/add_recommended_movies.php new file mode 100644 index 0000000..0999f8e --- /dev/null +++ b/videodb/contrib/add_recommended_movies.php @@ -0,0 +1,141 @@ + +* @version $Id: add_recommended_movies.php,v 1.8 2014/02/25 21:22:00 kec2 Exp $ + */ + +// move out of contrib for includes +chdir('..'); + +require_once './core/functions.php'; +require_once './engines/engines.php'; + +// since we don't need session functionality, use this as workaround +// for php bug #22526 session_start/popen hang +session_write_close(); + +?> + + + + + Find Movie Recommendations + + + + + + + +{$video['title']} ($engine Id {$video['imdbID']})
"; + + $data = engineGetRecommendations($video['imdbID'], $required_rating, $required_year, 'imdb'); + if (!empty($CLIENTERROR)) + { + echo $CLIENTERROR."
"; + continue; + } + + if (empty($data)) + { + // sometimes there are no recommendations for a movie. This is true for Underworld: imdbId 0320691 + echo "No recommendations for {$video['title']}.

"; + continue; + } + + echo ''; + echo " "; + echo " "; + echo " "; + + foreach ($data as $recommended) + { + $available = (count(runSQL("SELECT * FROM ".TBL_DATA." WHERE imdbID like '%".$recommended['id']."'")) > 0); + + if (!$available) + { + $recommended['title'] = ''.$recommended['title'].' '; + } + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + if ($download && !$available) engineGetData($recommended['id']); + } + echo "
Title Year Rating Id
{$recommended['title']}{$recommended['year']}{$recommended['rating']}{$recommended['id']}
"; + echo "
"; + } +} +else +{ +?> +
+ + + + + + + + + + +
+ Limit to movies to no earlier then + + +
+ At least require this rating + + +
+ + +
+ + +
+ + +
+ + + + diff --git a/videodb/contrib/borrowByBarcode.php b/videodb/contrib/borrowByBarcode.php new file mode 100644 index 0000000..b76da6d --- /dev/null +++ b/videodb/contrib/borrowByBarcode.php @@ -0,0 +1,202 @@ + + * + */ +chdir('..'); +require_once './core/session.php'; +require_once './core/functions.php'; +require_once './core/genres.php'; +require_once './core/custom.php'; +require_once './core/security.php'; + + +// check for localnet +localnet_or_die(); + +// multiuser permission check +permission_or_die(PERM_WRITE, $_COOKIE['VDBuserid']); + +$SELECT = 'SELECT opt FROM '.TBL_CONFIG." WHERE LOWER(opt) LIKE 'custom_type' AND value = 'barcode'"; +$result = runSQL($SELECT); +if (count($result)>0) $customFieldName = preg_replace('/type/','',$result[0]['opt']); + +if (count($result) == 0) { +?> + + + Borrow / return movie via barcode + + + Please select &qt;barcode&qt; as a custom field in the &qt;configuration&qt; tab. + +0) { + // If there is a rotten apple - just skip + if (preg_match('/[^0]+/',substr($row['barcode'],0,$lenDiff))) { + continue; + } + } + + $DELETE = 'DELETE FROM '.TBL_LENT.' WHERE diskid = '.escapeSQL($row['diskid']); + $INSERT = 'INSERT '.TBL_LENT." SET who = '".escapeSQL($who)."', diskid = '".escapeSQL($row['diskid'])."'"; + runSQL($DELETE,false); + runSQL($INSERT); + $specialJsCode = "parent.mainFrame.location.href='../borrow.php';"; + $notFound=0; + } + } + } else if ($_GET['process'] == "RETURN") { + + $barcode = trim($_GET['barcode']); + + if ($barcode == '' || preg_match('/[^0-9]+/',$barcode)) { + $notFound=1; + } else { + + $result = runSQL('SELECT diskid, '.$customFieldName.' AS barcode + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' + ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + WHERE '.TBL_USERS.".name = '".escapeSQL($_COOKIE['VDBusername'])."'".' + AND '.TBL_DATA.'.'.$customFieldName." LIKE '%".$barcode."'"); + + foreach($result as $row) + { + // missing zeros at the beginning? + if (($lenDiff = strlen($row['barcode'])-strlen($barcode))>0) { + // If there is a rotten apple - just skip + if (preg_match('/[^0]+/',substr($row['barcode'],0,$lenDiff))) { + continue; + } + } + + $DELETE = 'DELETE FROM '.TBL_LENT.' WHERE diskid = '.escapeSQL($row['diskid']); + runSQL($DELETE); + $specialJsCode = "parent.mainFrame.location.href='../borrow.php';"; + $notFound=0; + } + } + } +?> + + + + Borrow / return movie via barcode + + + + + + + + + +
+ + + + + + Borrow / return movie via barcode + + + + + + + + + + <body>Please use a browser which supports frames!</body> + + + \ No newline at end of file diff --git a/videodb/contrib/clean_unused_images.php b/videodb/contrib/clean_unused_images.php new file mode 100644 index 0000000..cf4d932 --- /dev/null +++ b/videodb/contrib/clean_unused_images.php @@ -0,0 +1,194 @@ + + * @modified Constantinos Neophytou + */ + +// move out of contrib for includes +chdir('..'); + +require_once './core/functions.php'; +require_once './core/setup.core.php'; + +?> + + + + + Cleanup Image Cache + + + + + + + + + $actorNum out of %d files with a size of %.2fMB are used for headshots
+ $unused out of %d files with a size of %.2fMB are currently unused
", + count($files), $coverSize/(1024*1024), + count($files), $actorSize/(1024*1024), + count($files), $size/(1024*1024)); + +if ($unused) +{ + if ($submit) + { + echo "$unused files with a size of ".round($size/(1024*1024),2)."Mb have been deleted
"; + } +?> +
> + +
+ + + + diff --git a/videodb/contrib/convertOwnerlessMovies.php b/videodb/contrib/convertOwnerlessMovies.php new file mode 100644 index 0000000..2a3299d --- /dev/null +++ b/videodb/contrib/convertOwnerlessMovies.php @@ -0,0 +1,67 @@ + + * + * @meta ACCESS:PERM_ADMIN + */ + +// move out of contrib for includes +chdir('..'); + +require_once './core/functions.php'; +require_once './core/output.php'; + +// check for localnet +localnet_or_die(); + +// multiuser permission check +permission_or_die(PERM_ADMIN); + +$owners = out_owners(null, 0, true); + +if (empty($owner_id)) $owner_id = $_COOKIE['VDBuserid']; + +if ($convert) +{ + runSQL("UPDATE ".TBL_DATA." + SET owner_id = ".$owner_id." + WHERE owner_id = 0"); + // show the saved movie + header('Location: ../index.php'); + exit(); +} +else +{ +?> + + + + + Transfer ownerless movies to a user + + + + +
+

Do you realy want to convert all ownerless movies to be owned by ?

+ + +
+ + + + diff --git a/videodb/contrib/count_actors.php b/videodb/contrib/count_actors.php new file mode 100644 index 0000000..181ad0a --- /dev/null +++ b/videodb/contrib/count_actors.php @@ -0,0 +1,135 @@ + + * @version $Id: count_actors.php,v 1.4 2007/09/08 09:17:16 andig2 Exp $ + */ + +// move out of contrib for includes +chdir('..'); + +require_once './core/functions.php'; +?> + + + + + List actor counts + + + + + + +$val) + { + if ($val > $maxcount) + { + // Build name search url + $url = "../search.php?q=%22" . htmlentities(urlencode($key)) . "%22&isname=Y"; + + // Text for director counts + $dirText = ''; + if ($displayDirectorCount && $directors[$key]) { + $dirText = ", " . $directors[$key] . " director entries"; + } + + echo "$i - $key: $val actor entries" . $dirText . "
"; + $i++; + } + } +} else { +?> +
+ + +
+ +
+ +
+ +
+
+ +
+
+ Note: Duplicate movie entries (determined by imdbID) will not be counted. + + + + diff --git a/videodb/contrib/decode_entities.php b/videodb/contrib/decode_entities.php new file mode 100644 index 0000000..e68efac --- /dev/null +++ b/videodb/contrib/decode_entities.php @@ -0,0 +1,102 @@ + + * @version $Id: decode_entities.php,v 1.6 2008/01/23 09:06:25 andig2 Exp $ + * @meta ACCESS:PERM_ADMIN + */ + +// move out of contrib for includes +chdir('..'); + +require_once './core/functions.php'; +require_once './engines/engines.php'; + +?> + + + + + Decode HTML Entities + + + + + + + + + +Warning- be sure to backup your data before submitting the cleanup request!"; + +$SQL = 'SELECT * FROM '.TBL_DATA; +$result = runSQL($SQL); + +$count = 0; +foreach ($result as $video) +{ + $SQL = ''; + + $keys = array(); + + foreach ($video as $key => $value) + { + if ($key == 'id') continue; + + $new = html_clean_utf8($value); + if ($new != $value) + { + $keys[] = $key; + + if ($SQL) $SQL .= ', '; + $SQL .= "$key = '".escapeSQL($new)."'"; + } + } + + if ($SQL) + { + $count++; + echo (($submit) ? 'Converting: ' : 'Conversion needed: ').$video['title']."
\n"; + + // actually perform the conversion? + if ($submit) + { + $SQL = "UPDATE ".TBL_DATA." SET $SQL WHERE id = ".$video['id']; + runSQL($SQL); + } + else + { + foreach($keys as $key) + { + echo $key.': '.htmlentities($video[$key])."
\n"; + } + echo "
\n"; + } + } +} + +$action = ($submit) ? 'Converted' : 'Analyzed'; +echo "$action $count of ".count($result)." movies.
\n"; + +if (empty($submit)) +{ +?> +
+ +
+ + + + diff --git a/videodb/contrib/direxport.pl b/videodb/contrib/direxport.pl new file mode 100644 index 0000000..9936ccc --- /dev/null +++ b/videodb/contrib/direxport.pl @@ -0,0 +1,142 @@ +#!/usr/bin/perl +use DBI; + +#DB-Connection +$db_server = "localhost"; +$db_user = "www"; +$db_password = "leech"; +$db_database = "VideoDB"; + +#output directory +$outdir = '/ftp/moviez/VideoDB/'; + +#imgcache +$imgcache = 'http://xerxes/videodb/imgcache'; + +#showurl +$showurl = 'http://xerxes/videodb/show.php'; + +#lynx +$lynx = '/usr/bin/lynx'; + +############################################################################### +#okay lets go + +#delete old stuff +system("rm -rf $outdir/*"); + +#connect +$dbh = DBI->connect("dbi:mysql:$db_database:$db_server",$db_user,$db_password) || die("Can't connect"); + +#unseen nontv +$out = "$outdir/unseen"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE seen = 0 + AND istv = 0"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +#unseen nontv +$out = "$outdir/unseen/tv"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE seen = 0 + AND istv = 1"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +#seen nontv +$out = "$outdir/seen"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE seen = 1 + AND istv = 0"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +#unseen tv +$out = "$outdir/seen/tv"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE seen = 1 + AND istv = 1"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +#all nontv +$out = "$outdir"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE istv = 0"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +#all tv +$out = "$outdir/tv"; +mkdir($out) unless(-e $out); +$SELECT = "SELECT id, title , subtitle + FROM videodata + WHERE istv = 1"; +$result = $dbh->selectall_arrayref($SELECT); +$row=0; +while (defined($result->[$row][0])){ + &show($result->[$row][0],$result->[$row][1],$result->[$row][2],$out); + $row++; +} + +############################################################################### +sub show($$$$){ + my $id = $_[0]; + my $title = $_[1]; + my $subtitle = $_[2]; + my $out = $_[3]; + print '.'; + + my $name; + if ($subtitle ne ''){ + $name = "$title - $subtitle.html"; + }else{ + $name = "$title.html"; + } + + my $output = `$lynx --dump --source '$showurl?id=$id'`; +# print "$lynx --dump '$showurl?id=$id'\n"; + + + $output =~ m/(.*)/is; + $output = $1; + $output =~ s/SRC="imgcache/SRC="$imgcache/is; + + $output =~ s/(.*?)<\/span>/

$1/is; + $output =~ s/(.*?)<\/span>/$1<\/H1>/is; + + open (FILE,">$out/$name") || die("file open '$out/$name' failed"); + print FILE $output; + close FILE; +} diff --git a/videodb/contrib/dvdadd.pl b/videodb/contrib/dvdadd.pl new file mode 100644 index 0000000..502d22a --- /dev/null +++ b/videodb/contrib/dvdadd.pl @@ -0,0 +1,314 @@ +#!/usr/bin/perl + +#program version +my $VERSION="0.1.0"; + +# Path to your lsdvd binary (http://acidrip.thirtythreeandathird.net/lsdvd.html) +$lsdvd = '/usr/bin/lsdvd'; + +# DVD drive to use (reads it from commandline) +$device = $ARGV[ 0 ]; +$device = "/dev/cdrom" unless (defined($device)); + +# if you use the multiuser feature you may want to give an +# owner of the movies to add here - if you don't need it just +# set it to a blank string. It has to be a number for videoDB 2.x +$owner = 3; + +# if you want to define what mediatype you are going to add (7=dvd-r 1=dvd) +$mediatype = 7; + +# Database stuff +$driver = "mysql"; +$database = "VideoDB"; +$hostname = "localhost"; +$user = "www"; +$password = "leech"; + +################################################################################ +use DBI; +use Data::Dumper; + +#Connect to database +$dsn = "DBI:$driver:database=$database;host=$hostname"; +$dbh = DBI->connect($dsn, $user, $password); + +#quote this only once: +$owner = $dbh->quote($owner); + +#prepare language codes +%lc = preparelc(); + +#work +&readlsdvd(); + +#disconnect +$dbh->disconnect(); + +# +############################################################################### +sub readlsdvd($) +{ + my $output = `$lsdvd -t 1 -a -s -v -p $device`; + if ($output eq '') + { + print STDERR "running lsdvd failed - check pathnames\n"; + exit 1; + } + eval($output); + + # prepare for inserts + my $title = $dbh->quote($lsdvd{ title }); + my $video_width = $dbh->quote($lsdvd{ track }[ 0 ]{ width }); + my $video_height = $dbh->quote($lsdvd{ track }[ 0 ]{ height }); + my $runtime = $dbh->quote(sprintf("%d", $lsdvd{ track }[ 0 ]{ 'length' } / 60)); + + # get languages... first is default, the next are written to custom1 and custom2 respectively (add custom3 and four if you want 4 languages + my $language1 = $dbh->quote($lc{ $lsdvd{ track }[ 0 ]{ audio }[ 0 ]{ langcode } }); + my $language2 = $dbh->quote($lc{ $lsdvd{ track }[ 0 ]{ audio }[ 1 ]{ langcode } }); + my $language3 = $dbh->quote($lc{ $lsdvd{ track }[ 0 ]{ audio }[ 2 ]{ langcode } }); + + # reads if PAL or NTSC and how many channels the audio has + my $audio_codec = $dbh->quote($lsdvd{ track }[ 0 ]{ audio }[ 0 ]{ format } . ' ' . $lsdvd{ track }[ 0 ]{ audio }[ 0 ]{ channels } . ' channels'); + + # video codec is either PAL or NTSC ... Aspectratio is 16x9 or 4x3 + my $video_codec = $dbh->quote($lsdvd{ track }[ 0 ]{ format }); + my $aspectratio = $dbh->quote($lsdvd{ track }[ 0 ]{ aspect }); + + # just for those who want to use pixels instead of Aspectratio + my $videowidth = $dbh->quote($lsdvd{ track }[ 0 ]{ width }); + my $videoheight = $dbh->quote($lsdvd{ track }[ 0 ]{ height }); + + # Get subtitles prepared... based on GPL code from acidrip-0.12 + my $this_track = 0; + my $subtitle = "Subtitles: "; + + foreach my $this_subp (@{ $lsdvd{ track }[ 0 ]->{ 'subp' } }) + { + my $subp_ix = $this_subp->{ 'ix' }; + my $label = $subp_ix . " " . $this_subp->{ 'language' }; + $label .= "\: " . $this_subp->{ 'content' } if $this_subp->{ 'content' } ne "Undefined"; + $subtitle = $subtitle . $label . "\, "; + } + + # Detect aspect ratio and put into video width and height... comment if you prefer pixel count + + if ($aspectratio = "16/9") + { + $video_width = "16"; + $video_height = "9"; + } + elsif ($aspectratio = "4/3") + { + $video_width = "4"; + $video_height = "3"; + } + + # insert... remove both custom1 and custom2 if you use them for something else or change them to + # something different + # + # video_width can be replaced with $videowidth and video_height with $videoheight to achieve pixel + # count like width = 720, height = 576 + + my $INSERT = "INSERT INTO videodata + SET title = $title, + video_width = $video_width, + video_height = $video_height, + video_codec = $video_codec, + mediatype = $mediatype, + created = NOW(), + runtime = $runtime, + language = $language1, + custom1 = $language2, + custom2 = $language3, + audio_codec = $audio_codec, + comment = '$subtitle', + owner_id = $owner"; + + # comment if you are testing the script... this writes to the database + $dbh->do($INSERT); + + #print $INSERT; +} + +sub preparelc() +{ + my %lc; + $lc{ 'aa' } = 'afar'; + $lc{ 'ab' } = 'abkhazian'; + $lc{ 'af' } = 'afrikaans'; + $lc{ 'am' } = 'amharic'; + $lc{ 'ar' } = 'arabic'; + $lc{ 'as' } = 'assamese'; + $lc{ 'ay' } = 'aymara'; + $lc{ 'az' } = 'azerbaijani'; + $lc{ 'ba' } = 'bashkir'; + $lc{ 'be' } = 'byelorussian'; + $lc{ 'bg' } = 'bulgarian'; + $lc{ 'bh' } = 'bihari'; + $lc{ 'bi' } = 'bislama'; + $lc{ 'bn' } = 'bengali'; + $lc{ 'bo' } = 'tibetan'; + $lc{ 'br' } = 'breton'; + $lc{ 'ca' } = 'catalan'; + $lc{ 'co' } = 'corsican'; + $lc{ 'cs' } = 'czech'; + $lc{ 'cy' } = 'welsh'; + $lc{ 'da' } = 'danish'; + $lc{ 'de' } = 'german'; + $lc{ 'dz' } = 'bhutani'; + $lc{ 'el' } = 'greek'; + $lc{ 'en' } = 'english'; + $lc{ 'eo' } = 'esperanto'; + $lc{ 'es' } = 'spanish'; + $lc{ 'et' } = 'estonian'; + $lc{ 'eu' } = 'basque'; + $lc{ 'fa' } = 'persian'; + $lc{ 'fi' } = 'finnish'; + $lc{ 'fj' } = 'fiji'; + $lc{ 'fo' } = 'faroese'; + $lc{ 'fr' } = 'french'; + $lc{ 'fy' } = 'frisian'; + $lc{ 'ga' } = 'irish'; + $lc{ 'gd' } = 'gaelic'; + $lc{ 'gl' } = 'galician'; + $lc{ 'gn' } = 'guarani'; + $lc{ 'gu' } = 'gujarati'; + $lc{ 'ha' } = 'hausa'; + $lc{ 'he' } = 'hebrew'; + $lc{ 'hi' } = 'hindi'; + $lc{ 'hr' } = 'croatian'; + $lc{ 'hu' } = 'hungarian'; + $lc{ 'hy' } = 'armenian'; + $lc{ 'ia' } = 'interlingua'; + $lc{ 'id' } = 'indonesian'; + $lc{ 'ie' } = 'interlingue'; + $lc{ 'ik' } = 'inupiak'; + $lc{ 'is' } = 'icelandic'; + $lc{ 'it' } = 'italian'; + $lc{ 'iu' } = 'inuktitut'; + $lc{ 'ja' } = 'japanese'; + $lc{ 'jw' } = 'javanese'; + $lc{ 'ka' } = 'georgian'; + $lc{ 'kk' } = 'kazakh'; + $lc{ 'kl' } = 'greenlandic'; + $lc{ 'km' } = 'cambodian'; + $lc{ 'kn' } = 'kannada'; + $lc{ 'ko' } = 'korean'; + $lc{ 'ks' } = 'kashmiri'; + $lc{ 'ku' } = 'kurdish'; + $lc{ 'ky' } = 'kirghiz'; + $lc{ 'la' } = 'latin'; + $lc{ 'ln' } = 'lingala'; + $lc{ 'lo' } = 'laothian'; + $lc{ 'lt' } = 'lithuanian'; + $lc{ 'lv' } = 'latvian'; + $lc{ 'mg' } = 'malagasy'; + $lc{ 'mi' } = 'maori'; + $lc{ 'mk' } = 'macedonian'; + $lc{ 'ml' } = 'malayalam'; + $lc{ 'mn' } = 'mongolian'; + $lc{ 'mo' } = 'moldavian'; + $lc{ 'mr' } = 'marathi'; + $lc{ 'ms' } = 'malay'; + $lc{ 'mt' } = 'maltese'; + $lc{ 'my' } = 'burmese'; + $lc{ 'na' } = 'nauru'; + $lc{ 'ne' } = 'nepali'; + $lc{ 'nl' } = 'dutch'; + $lc{ 'no' } = 'norwegian'; + $lc{ 'oc' } = 'occitan'; + $lc{ 'om' } = 'oromo'; + $lc{ 'or' } = 'oriya'; + $lc{ 'pa' } = 'punjabi'; + $lc{ 'pl' } = 'polish'; + $lc{ 'ps' } = 'pashto'; + $lc{ 'pt' } = 'portuguese'; + $lc{ 'qu' } = 'quechua'; + $lc{ 'rm' } = 'rhaeto-romance'; + $lc{ 'rn' } = 'kirundi'; + $lc{ 'ro' } = 'romanian'; + $lc{ 'ru' } = 'russian'; + $lc{ 'rw' } = 'kinyarwanda'; + $lc{ 'sa' } = 'sanskrit'; + $lc{ 'sd' } = 'sindhi'; + $lc{ 'sg' } = 'sangho'; + $lc{ 'sh' } = 'serbo-croatian'; + $lc{ 'si' } = 'sinhalese'; + $lc{ 'sk' } = 'slovak'; + $lc{ 'sl' } = 'slovenian'; + $lc{ 'sm' } = 'samoan'; + $lc{ 'sn' } = 'shona'; + $lc{ 'so' } = 'somali'; + $lc{ 'sq' } = 'albanian'; + $lc{ 'sr' } = 'serbian'; + $lc{ 'ss' } = 'siswati'; + $lc{ 'st' } = 'sesotho'; + $lc{ 'su' } = 'sundanese'; + $lc{ 'sv' } = 'swedish'; + $lc{ 'sw' } = 'swahili'; + $lc{ 'ta' } = 'tamil'; + $lc{ 'te' } = 'telugu'; + $lc{ 'tg' } = 'tajik'; + $lc{ 'th' } = 'thai'; + $lc{ 'ti' } = 'tigrinya'; + $lc{ 'tk' } = 'turkmen'; + $lc{ 'tl' } = 'tagalog'; + $lc{ 'tn' } = 'setswana'; + $lc{ 'to' } = 'tonga'; + $lc{ 'tr' } = 'turkish'; + $lc{ 'ts' } = 'tsonga'; + $lc{ 'tt' } = 'tatar'; + $lc{ 'tw' } = 'twi'; + $lc{ 'ug' } = 'uighur'; + $lc{ 'uk' } = 'ukrainian'; + $lc{ 'ur' } = 'urdu'; + $lc{ 'uz' } = 'uzbek'; + $lc{ 'vi' } = 'vietnamese'; + $lc{ 'vo' } = 'volapuk'; + $lc{ 'wo' } = 'wolof'; + $lc{ 'xh' } = 'xhosa'; + $lc{ 'yi' } = 'yiddish'; + $lc{ 'yo' } = 'yoruba'; + $lc{ 'za' } = 'zhuang'; + $lc{ 'zh' } = 'chinese'; + $lc{ 'zu' } = 'zulu'; + + return %lc; +} + +__END__ + +=head1 NAME + +dvdadd - reads DVD Video Data and writes it to videoDB + +=head1 SYNOPSIS + + reads DVD Video Data and writes it to videoDB + +=head1 DESCRIPTION + + reads DVD Video Data and writes it to videoDB using Perl and lsdvd. Linux or Unix is required, not tested on MS Windows. + +=head1 SEE ALSO + + videoDB http://videodb.sf.net + lsdvd 0.10 http://acidrip.thirtythreeandathird.net/lsdvd.html + perl http://perl.org + +=head1 AUTHOR + + Elkin Fricke, videoDB DevTeam. + +=head1 LICENSE + + VideoDB is released under the GNU General Public License (GPL) + See COPYING for more Info + + VideoDB comes with the Smarty Template Engine + Smarty is released under the GNU Lesser General Public License (LGPL) + See COPYING.lib in the smarty directory for more Info + +=cut + diff --git a/videodb/contrib/fetch_imdb_all.php b/videodb/contrib/fetch_imdb_all.php new file mode 100644 index 0000000..1eb2760 --- /dev/null +++ b/videodb/contrib/fetch_imdb_all.php @@ -0,0 +1,152 @@ + + */ + +chdir('..'); +require_once './engines/engines.php'; +require_once './core/functions.php'; +require_once './core/genres.php'; +require_once './core/custom.php'; +require_once './core/edit.core.php'; + +//Id is imdb id +//lookup is either 1 (add missing) or 2 (overwrite) +function FetchSaveMovie($id,$lookup) +{ + $debug = 0; + + $video = runSQL('SELECT * FROM '.TBL_DATA.' WHERE id = '.$id); + // get fields (according to list) from db to be saved later + + if ($debug){ + echo "\n=================== Video DB Data ============================\n"; + print_r( $video[0]); + echo "\n=================== Video DB Data ============================\n"; + } + + $imdbID = $video[0]['imdbID']; + echo "Movie/imdb -- ".$video[0]['title']."/".$video[0]['imdbID']."\n"; + + + if (empty($imdbID)) { + echo "No imdbID\n"; + return; + } + + if (empty($engine)) $engine = engineGetEngine($imdbID); + + if ($debug) { + echo "IMDBID = $imdbID, engine = $engine\n"; + } + + $imdbdata = engineGetData($imdbID, $engine); + # removed due to performance issues of is_utf8 + // fix erroneous IMDB encoding issues + if (!is_utf8($imdbdata)) { + echo "Applying encoding fix\n"; + $imdbdata = fix_utf8($imdbdata); + } + + if (empty($imdbdata[title])) { + echo "Fetch failed , try again...\n"; + $imdbdata = engineGetData($imdbID, $engine); + } + + if (empty($imdbdata[title])) { + echo "Fetch failed again , next movie"; + return; + } + + if ($debug) { + echo "\n=================== IMDB Data ============================\n"; + print_r($imdbdata); + echo "\n=================== IMDB Data ============================\n"; + } + + if (!empty($imdbdata[title])) { + // + // NOTE: comment out any of the following lines if you do not want them updated + // + $video[0][title]=$imdbdata[title]; + $video[0][subtitle]=$imdbdata[subtitle]; + $video[0][year]=$imdbdata[year]; + $video[0][imgurl]=$imdbdata[coverurl]; + $video[0][runtime]=$imdbdata[runtime]; + $video[0][director]=$imdbdata[director]; + $video[0][rating]=$imdbdata[rating]; + $video[0][country]=$imdbdata[country]; + $video[0][language]=$imdbdata[language]; + $video[0][actors]=$imdbdata[cast]; + $video[0][plot]=$imdbdata[plot]; + } + + if (count($genres) == 0 || ($lookup > 1)) + { + $genres = array(); + $gnames = $imdbdata['genres']; + if (isset($gnames)) + { + foreach ($gnames as $gname) + { + // check if genre is found- otherwise fail silently + if (is_numeric($genre = getGenreId($gname))) { + $genres[] = $genre; + } else { + echo "MISSING GENRE $gname\n"; + } + } + } + } + + // custom filds , not working for now + for ($i=1; $i<=4; $i++) + { + $custom = 'custom'.$i; + $type = $config[$custom.'type']; + if (!empty($type)) + { + // copy imdb data into corresponding custom field + $video[0][$custom]=$imdbdata[$type]; + echo "CUSTOM $custom $type = $imdbdata[$type]\n"; + } + } + + // -------- SAVE + + $SETS = prepareSQL($video[0]); + + if ($debug) { + echo "\n=================== Final Data ============================\n"; + echo "SETS = $SETS \n"; + echo "\n=================== Final Data ============================\n"; + } + + $id = updateDB($SETS, $id); + + // save genres + setItemGenres($id, $genres); + + // set seen for currently logged in user + set_userseen($id, $seen); +} + +// NOTE: Edit this line if you want to update specific set of files by adding WHERE statment +$allids = runSQL('SELECT id FROM '.TBL_DATA); + +foreach ($allids as $id) { + #if ($id['id'] <= 2113) continue; + echo "Updating ID:".$id['id']."\n"; + FetchSaveMovie($id['id'],3); +} +// for testing +// FetchSaveMovie(1,3); + +?> diff --git a/videodb/contrib/index.html b/videodb/contrib/index.html new file mode 100644 index 0000000..510ab52 --- /dev/null +++ b/videodb/contrib/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/videodb/contrib/langcheck.php b/videodb/contrib/langcheck.php new file mode 100644 index 0000000..4e6145f --- /dev/null +++ b/videodb/contrib/langcheck.php @@ -0,0 +1,116 @@ + + + + Translation statistics + + +'.$code.' '; + + if ($c) + { + print $c.' translations '.$type; + print '

'; + + foreach($missing as $key) + { + print $key.'
'; + } + } + else + { + print 'complete'; + } + print '

'; +} + +function getlangs(&$tooltipLangs) +{ + global $LANGDIR; + + if ($dh = opendir($LANGDIR)) + { + while (($file = readdir($dh)) !== false) + { + if(preg_match("/(.*)\.php$/",$file,$matches)) + { + $langs[]=$matches[1]; + if(substr($file, -16) == "withtooltips.php") + { + $tooltipLangs[] = $matches[1]; + } + } + } + closedir($dh); + } + else + { + print "could not open language directory $LANGDIR"; + exit; + } + + return $langs; +} + +function loadlang($code) +{ + global $LANGDIR; + + include($LANGDIR.'/'.$code.'.php'); + return $lang; +} + + +foreach(getlangs($tooltipLangs) as $code) if ($base_lang !== $code) +{ + $foreign = loadlang($code); + + $missing = array(); + $identical = array(); + + $useLang = $lang; + + foreach (array_keys($useLang) as $key) + { + if(empty($foreign[$key])) + { + $missing[]=$key; + } + + if ($useLang[$key] == $foreign[$key]) + { + $identical[] = $key; + } + } + + printlang($missing, $code, 'missing'); + + if ($_GET['copycheck']) printlang($identical, $code, 'identical'); +} + +?> + + diff --git a/videodb/contrib/lookup_barcode.php b/videodb/contrib/lookup_barcode.php new file mode 100644 index 0000000..8bd9415 --- /dev/null +++ b/videodb/contrib/lookup_barcode.php @@ -0,0 +1,97 @@ + + * @version $Id: lookup_barcode.php,v 1.4 2007/09/08 09:17:16 andig2 Exp $ + */ + + chdir('..'); + require_once('./core/functions.php'); + + $notFound = 0; + if (isset($_GET['barcode'])) + { + // Base URL for the search + $url = 'http://s1.amazon.co.uk/exec/varzea/sdp/sai-condition/'; + + // Add our post options and get the data + $post = 'sdp-sai-asin='.$_GET['barcode']; + $amazon_data = httpClient ($url, 0, $post); + + // If it succeeds.... + if ($amazon_data['success'] == 1) + { + if (preg_match("/(.*)<\/b>/", $amazon_data['data'], $matches)) + { + if ($matches[1] == 'Identify the exact item you&//039;re selling') + { + $notFound = 1; + } + else + { + $media_type = 1; + $title = urlencode($matches[1]); + if (preg_match("/http:\/\/www.amazon.co.uk\/exec\/obidos\/ASIN\/(.*)\//", $amazon_data, $matches)) + { + $asin_number = "ASIN: $matches[1]"; + } + else + { + $asin_number = 'ASIN not found'; + } + if (preg_match("/alt=\"VHS\"/", $amazon_data['data'], $matches)) + { + $media_type = 6; + } + header("Location: ../edit.php?save=1&lookup=1&title=$title&diskid={$_GET['barcode']}&mediatype=$media_type&subtitle=$asin_number"); + } + } + else + { + $notFound = 2; + } + } + else + { + // Print the error message + print "Failed to download:
\n"; + print $amazon_data['error']; + } + } +?> + + + + Add movie by Amazon-UK barcode + + +

Add movie by Amazon-UK barcode

+ +
+ + + +
+ + +\n"; + //print_r($amazon_data); + } + elseif ($notFound == 2) + { + print "No data returned!
\n"; + print_r($amazon_data); + } +?> + + \ No newline at end of file diff --git a/videodb/contrib/mass_add.php b/videodb/contrib/mass_add.php new file mode 100644 index 0000000..c454723 --- /dev/null +++ b/videodb/contrib/mass_add.php @@ -0,0 +1,265 @@ + + * @version $Id: mass_add.php,v 1.2 2007/07/27 10:09:07 andig2 Exp $ + */ + +chdir('..'); + +require_once './core/functions.php'; +require_once './core/genres.php'; +require_once './core/custom.php'; +require_once './core/security.php'; +?> + + + + + Mass IMDB movie add + + + + +
"; + $id = runSQL($INSERT); + // save genres + setItemGenres($id, $genres); + //------------------------------------------- + + // insert userseen data + $INSERTSEEN = 'INSERT INTO `userseen` (`video_id`, `user_id`) VALUES ('.$id.','.$owner_id.')'; + runSQL($INSERTSEEN); + $ret_title=$title; + return 1; +} + +if ((isset($_POST['Submit'])) && (is_uploaded_file($_FILES['id_list']['tmp_name']))){ + //lets set time limit of we can + set_time_limit(30000); + $filename = $_FILES['id_list']['tmp_name']; + echo "File uploaded. Starting fetching and inserting into database...
"; + ob_flush(); + flush(); + //get seen field from form + $seen=isset($_POST['seen'])?1:0; + //get mediatype id from form + $mediatype=$_POST['mediatype']; + $lines=file($filename); + //iterate for all lines in uploaded file + foreach( $lines as $line){ + //trim \n from end of line + $line=rtrim($line); + //if line is empty, go to the next line + if ($line=="") continue; + //if we import by title, first get id from best matcing title + if ($_POST['import_type']=='title'){ + $all=engineSearch($line); + $id=$all[0][id]; + } + else{ + $id=$line; + } + if (strpos($id,"imdb:")===false){ + $id="imdb:".$id; + } + //try to insert movie + if (InsertMovie($id,&$title,$seen,$mediatype)==1){ + echo "$title inserted "; + if ($_POST['import_type']=='title'){ + echo "(additional info - title form file was $line)"; + } + echo "
"; + } + else{ //ops, error + echo "Error while inserting ID - ".$id." (additional info - "; + if ($_POST['import_type']=='title') + echo "title from file was $line)
"; + else{ + echo "id from file was $line)
"; + } + } + ob_flush(); + flush(); + } +} + +?> + +

Mass IMDB movie add v0.2


+Ok, here is the thing:
+ +1. Browse for file with movie data (imdb ids or titles), hit Submit and pray:) +
+2. Script will try to set time limit, but this also depends from PHP configuration, so if script stops before all movies has been entered, check for PHP config. +
+3. File you need to browse is in form - one line per movie. So, if you want to import imdb ids, it should be something like: +
+
+0088247 +
+0088248 +
+or +
+imdb:0088247 +
+imdb:0088248 +
+
+and for titles: +
+
+terminator +
+terrible joe moran +
+
+In second case (import by titles), first matching title will be imported. +
+4. You must be logged to VideoDB as user who has write access (i.e. you can add movies on your own). All imported movies will be assigned to you. If you don't have write access or you are not logged, owner id will be 0. +
+5. Backup your data, make sure nothing can be lost, no responsibility, blah, blah...you already know it all. +
+
+
+ Import by ids +
+ Import by titles +
+ Set all movies as seen +
+Media type: + +
+Import list: +
+ +
+
+ + + + + + diff --git a/videodb/contrib/mklist.pl b/videodb/contrib/mklist.pl new file mode 100644 index 0000000..b616810 --- /dev/null +++ b/videodb/contrib/mklist.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +use DBI; + +$db_server = "localhost"; +$db_user = "www"; +$db_password = "leech"; +$db_database = "VideoDB"; + +$dbh = DBI->connect("dbi:mysql:$db_database:$db_server",$db_user,$db_password) || die("Can't connect"); + + +$SELECT = "SELECT filename, filesize, diskid + FROM videodata + ORDER BY filename"; + +$result = $dbh->selectall_arrayref($SELECT); + +print "DiskID\tSize\t\tFilename\n"; +print "-"x74; +print "\n"; + +$row=0; +while (defined($result->[$row][0])){ + printf("%s\t",$result->[$row][2]); + printf("%3.2f MB\t",($result->[$row][1]/(1024*1024))); + printf("%s\n",$result->[$row][0]); + + $row++; +} diff --git a/videodb/contrib/refetchAllInfos.php b/videodb/contrib/refetchAllInfos.php new file mode 100644 index 0000000..3de8e98 --- /dev/null +++ b/videodb/contrib/refetchAllInfos.php @@ -0,0 +1,276 @@ + + * @meta ACCESS:PERM_ADMIN + */ + +chdir('..'); +require_once './core/functions.php'; +require_once './core/custom.php'; +require_once './core/security.php'; +require_once './engines/engines.php'; +require_once './core/compatibility.php'; + +// check for localnet +localnet_or_die(); + +// multiuser permission check +permission_or_die(PERM_WRITE); + + +/** + * Fetch a list of all editable video fields (keys) + * and assign 1 (value) if they should be preselected else 0 + */ +function getFields() +{ + $edit_file = file_get_contents('./core/edit.core.php'); + $edit_file = preg_replace("/\n/",'',$edit_file); + + + if (preg_match('/\$imdb_set_fields\s*=\s*array\s*\((.*?)\)/', $edit_file, $fieldslist) && + preg_match('/\$imdb_overwrite_fields.*?array\s*\((.*?)\)/', $edit_file, $overwritelist)) + { + $fields = array_map('trim', split(',', preg_replace("/'/", '', $fieldslist[1]))); + $overwrites = array_map('trim', split(',', preg_replace("/'/", '', $overwritelist[1]))); + + $ret = array(); + foreach ($fields as $field) + { + $value = (in_array($field, $overwrites)) ? 1 : 0; + if (preg_match('/custom/', $field)) $value = 0; + $ret = array_merge ($ret, array($field => $value)); + } + + return $ret; + } +} + +if (!check_permission(PERM_ADMIN)) { +?> + + + Refetch all external engine information + + + + + + + $_COOKIE, 'no_proxy' => true, 'no_redirect' => true)); + if (!$resp['success']) + { + $CLIENTERRORS[] = $video['title']." (".$video['diskid']."/".engineGetEngine($video['imdbID'])."): ".$resp['error']; + } + else $CLIENTOKS[] = $video['title']." (".$video['diskid']."/".engineGetEngine($video['imdbID']).")"; + } + + if (isset($resetDI) && $resetDI == "true") + { + // fix lent table after upper temp. changes + $SELECT = "SELECT diskid FROM ".TBL_LENT." WHERE diskid like 'TMP%'"; + $lentResult = runSQL($SELECT); + foreach ($lentResult as $lentRow) + { + $diskid = preg_replace('/^TMP/','',$lentRow['diskid']); + $UPDATE = "UPDATE ".TBL_LENT." SET diskid = '".$diskid."' WHERE diskid = 'TMP".$diskid."'"; + runSQL($UPDATE); + } + } + ?> + + + Refetch all external engine information + + +

Report

+

ERROR:

+ + +
+ + +

SUCCESS:

+ + +
+ + + + + + + Refetch all external engine information + + + + + +

+ +
+
+ +
+ + Refetch and overwrite selected fields of movies for this User: + +
+ +
+ Update fields only for movies fetched by this engine: + +
+ +
+ Data Lookup: + + +
+ +
+ + "; + print ''; + } + for ($i = 0; $i < ($fields_in_a_row - ($field_amount % $fields_in_a_row)); $i++) { + print ''; + } + ?> +
'.$keys[$i].' 
+
+ +
+ reset DiskIDs FOR ALL MOVIES AND ALL USERS? +
+ +
+ Are you shure to know what you are doing? + + +
+
+ + + + + + Refetch all external engine information + + + + + + + + + + <body>Please use a browser which supports frames!</body> + + + diff --git a/videodb/contrib/setGenre.pl b/videodb/contrib/setGenre.pl new file mode 100644 index 0000000..a6f58bf --- /dev/null +++ b/videodb/contrib/setGenre.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +# Set title +$TITLE="South Park"; + +# Set genres (see list below for ids) +@GENRES=(4,3); + +# 1 Action +# 2 Adventure +# 3 Animation +# 4 Comedy +# 5 Crime +# 6 Documentary +# 7 Drama +# 8 Family +# 9 Fantasy +# 10 Film-Noir +# 11 Horror +# 12 Musical +# 13 Mystery +# 14 Romance +# 15 Sci-Fi +# 16 Short +# 17 Thriller +# 18 War +# 19 Western + +$db_server = "localhost"; +$db_user = "www"; +$db_password = "leech"; +$db_database = "VideoDB"; + +###################################################################### +use DBI; +$dbh = DBI->connect("dbi:mysql:$db_database:$db_server",$db_user,$db_password) || die("Can't connect"); + +$SELECT = "SELECT id from videodata + WHERE title LIKE '$TITLE'"; +$idr = $dbh->selectall_arrayref($SELECT); + +$row=0; +while (defined($idr->[$row][0])){ + my $id = $idr->[$row][0]; + + #clear existing genres: + $dbh->do("DELETE FROM videogenre WHERE id = $id"); + + #insert new genres + foreach my $gid (@GENRES){ + $dbh->do("INSERT INTO videogenre SET id = $id, gid = $gid"); + } + $row++; +} diff --git a/videodb/contrib/setOwner.sql b/videodb/contrib/setOwner.sql new file mode 100644 index 0000000..3eaef71 --- /dev/null +++ b/videodb/contrib/setOwner.sql @@ -0,0 +1,7 @@ +# This SQL statement changes the owner of all unowned movies be sure to change +# NEWOWNER to the name of the user who should own the movies. + +UPDATE videodata + SET owner = 'NEWOWNER' + WHERE owner IS NULL + OR owner = ''; diff --git a/videodb/contrib/utf_migration.php b/videodb/contrib/utf_migration.php new file mode 100644 index 0000000..3c4d9d8 --- /dev/null +++ b/videodb/contrib/utf_migration.php @@ -0,0 +1,177 @@ + + * $Id: utf_migration.php,v 1.2 2008/01/06 12:30:00 andig2 Exp $ + */ + +chdir('..'); +require_once './core/functions.php'; +require_once './core/encoding.php'; + +?> + + + + Migrate database contents to UTF-8 + + + + + + +Attention: Be sure to perform a backup before running the encoding migration! + + +

1. Choose source and target encoding

+ +
+ +
+ Source encoding: + +
+ +
+ Target encoding: + +
+ +
+ Simulate only: +
+ + + +
+ +Edit the database settings in ".CONFIG_FILE.".

+

Alternatively, consider running the installation script.

"); + } + + $res = mysqli_query($db_native, $sql_string); + + // mysqli_db_query returns either positive result ressource or true/false for an insert/update statement + if ($res === false) + { + // report DB Problem + errorpage('Database Problem', mysqli_error($db_native)."\n
\n".$sql_string); + } + elseif ($res === true) + { + // on insert, return id of created record + $result = mysqli_insert_id($db_native); + } + else + { + // return associative result array + $result = array(); + + for ($i=0; $i'latin1', 'iso-8859-7'=>'latin7', 'iso-8859-9'=>'latin9', 'windows-1251'=>'cp1251', 'koi8-r'=>'koi8r', 'utf-8'=>'utf8'); +$tables = array(TBL_DATA, TBL_ACTORS); + +extract($_REQUEST); + +$db_sourceencoding = $db_encodings[$sourceencoding]; +$db_targetencoding = $db_encodings[$targetencoding]; + +if ($sourceencoding && $targetencoding && ($sourceencoding != $targetencoding)) +{ +# if (!preg_match('/^(\w\d)+$/', $sourceencoding)) die ('Security violation'); +# if (!preg_match('/^(\w\d)+$/', $targetencoding)) die ('Security violation'); + +?> + +

2. Validate data correctness and execute

+ +"); + + $enc = iconv_array($sourceencoding, $targetencoding, $res); + + for ($i=0; $i$val) + { + if ($SQL) $SQL .= ', '; + $SQL .= $key.'='.db_encode($val); + } + $SQL = "UPDATE $table SET ".$SQL." WHERE id=".$id; + dump($SQL); + + if (!$simulate) sql_native($SQL); + } + } +} + +?> + + diff --git a/videodb/contrib/vdb_wiki.sh b/videodb/contrib/vdb_wiki.sh new file mode 100644 index 0000000..02ab681 --- /dev/null +++ b/videodb/contrib/vdb_wiki.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# +# Extract documentation from wiki to include in release package +# +# @package Release +# @author Andreas Gohr +# @author Andreas Goetz +# @link http://www.splitbrain.org/dokuwiki/vdb:videodb +# @version $Id: vdb_wiki.sh,v 1.1 2004/08/11 10:09:13 andig2 Exp $ +# + +# remove existing doku +rm -f *.html + +# get doku from dokuwiki +wget --level 2 -r -np -nc -nd -E -k -A 'vdb*' -R '*\?*' http://www.splitbrain.org/dokuwiki/vdb:videodb + +# delete files +for x in `ls *@*` +do + rm $x +done + +# fix filenames +for x in `ls *%3A*` +do + mv $x `echo $x|tr -s %3A _` +done + +#cp ../Copy\ of\ vdb/* . + +# fix html +for x in `ls *.html` +do + # move to tempfile + cp $x $x.tmp + + # add new header + cat >$x < + + VideoDB - Documentation + + + + Table of Contents + +EOF + + # add fixed content + cat $x.tmp | \ + perl -e '$foo=join("",<>); + $foo=~s/vdb(:|%3A)/vdb_/gs; + $foo=~m/(.*) + + +EOF + + # remove tempfile + rm -f $x.tmp +done + diff --git a/videodb/contrib/videoadd.php b/videodb/contrib/videoadd.php new file mode 100644 index 0000000..0fcf5c4 --- /dev/null +++ b/videodb/contrib/videoadd.php @@ -0,0 +1,422 @@ + + * @author Alexander Mondshain + * @version $Id: videoadd.php,v 1.3 2018/09/23 13:02:11 kec2 Exp $ + */ +chdir('..'); + +require './core/functions.php'; + +// Movies on the file system. +// File name is key and full path is value. +$moviesFS = array(); +// duplicate file on file system. +// File name is key and full path is value. +$doubles = array(); + +// NOTE : as a minimum change the following movie_dirs variable to point to a movie directory $movieDirs = array("/moviedir1","/moviedir2); +$movieDirs = array(); +$movieExts = '(avi|mpg|bin|mpeg|ogm|bin|mkv|m2ts|iso)'; +$cleanFileName = '(.avi|.mkv|.iso|.m2ts|XVID|XviD|XViD|Xvid|CD1)'; +$skipFolders = '/^\.|^CVS|^lost|^photos|^music|^staff/'; + +// NOTE:update_missing, update missing files and associeted movies as wanted +// NOTE:update_moved. update moved files with a new file location +$update_missing = false; +$update_moved = false; + +/** + * Check if MPlayer is installed. + * + * @return boolean True if it is installed. + */ +function isMPlayerInstalled() +{ + $out = null; + $ret = - 1; + @exec("mplayer -v", $out, $ret); + return $ret == '0'; +} + +/** + * If a movies consits of multiple file then all but one will be removed. + * fx. Thor_CD1.avi, Thor_CD2.avi and Thor_CD3.avi => Thor_CD1.avi. + */ +function removeCDNumbers($moviesFS) +{ + foreach ($moviesFS as $movie) { + $filePathParts = preg_split("/\//", $movie); + $fileName = end($filePathParts); + + if (preg_match("/CD2/i", $movie)) { + unset($moviesFS[$fileName]); + } else if (preg_match("/CD3/i", $movie)) { + unset($moviesFS[$fileName]); + } else if (preg_match("/CD4/i", $movie)) { + unset($moviesFS[$fileName]); + } + } + return $moviesFS; +} + +function getFileSizeLinux($file) +{ + $out = null; + $ret = - 1; + @exec("stat -c %s \"$file\"", $out, $ret); + if ($ret != '0') { + return FALSE; + } else { + return ($out[0]); + } +} + +function getFileTimeLinux($file) +{ + $out = null; + $ret = - 1; + @exec("stat -c %Y \"$file\"", $out, $ret); + if ($ret != '0') { + return FALSE; + } else { + return ($out[0]); + } +} + +/** + * + * Get all movie files requrcily starting from \$dir. + * + * @param String $dir + * The starting point. + * @param String $ext + * Files with these file extensions are or included in the result. + * @param String $skip + * Directories to skip. + */ +function recurseDir($dir, $ext, $skip) +{ + // echo "In DIR $dir
"; + global $moviesFS, $doubles; + if ($dh = opendir($dir)) { + while ($file = readdir($dh)) { + // Exclude all dot file, CVS and lost+found directories + if (preg_match($skip, $file)) { + continue; + } + + // If the file is a movie, we add it to the list + if (preg_match("/\.$ext$/", $file)) { + if (isset($moviesFS[$file])) { + array_push($doubles, $moviesFS[$file]); + array_push($doubles, "$dir/$file"); + } else { + $moviesFS[$file] = "$dir/$file"; + } + } + + // If this is a directory we search it + if (is_dir("$dir/$file")) { + recurseDir("$dir/$file", $ext, $skip); + } + } + closedir($dh); + } +} + +/** + * Get combined file size of a movies with multiple files. + * fx. Thor_CD1.avi (100k), Thor_CD2.avi (200k) and Thor_CD3.avi (150k) => 450k. + * + * @param string $filePath + * Path to the first file. + * @return integer The size of the file(s). + */ +function getFileSize($filePath) +{ + $fileSize = getFileSizeLinux($filePath); + + if (preg_match("/CD1/i", $filePath)) { + $newfile = preg_replace("/(CD)(\d)/i", '${1}2', $filePath); + $fileSize += getFileSizeLinux($newfile); + + $newfile = preg_replace("/(CD)(\d)/i", '${1}3', $filePath); + $fileSize += getFileSizeLinux($newfile); + + $newfile = preg_replace("/(CD)(\d)/i", '${1}4', $filePath); + $fileSize += getFileSizeLinux($newfile); + } + + return $fileSize; +} + +/** + * Get metadata of a movie. + * Data include video codec, audio codec, video width and video height. + * MPlayer is used to retrive these data. + * + * @param String $filePath + * File path to the movie. + * @return array An assosiative array with keys: videoCodec, audioCodec, videoHeight and videoWidth. + */ +function getMetadata($filePath) +{ + $metadata = array(); + $command = "mplayer -vo null -ao null -frames 3 -identify \"$filePath\""; + // echo "command: ".$command."
"; + $output = array(); + $return_var = 0; + + $match = array(); + exec($command, $output, $return_var); + // parse mplayer output + foreach ($output as $line) { + trim($line); + + if (preg_match("/ID_VIDEO_CODEC=(.*)/", $line, $match)) { + $videoCodec = strtolower($match[1]); + switch ($videoCodec) { + case 'ffh264': + $metadata['videoCodec'] = 'H.264'; + break; + case 'ffvc1': + $metadata['videoCodec'] = 'VC1'; + break; + case '0x10000001': + $metadata['videoCodec'] = 'MPEG1'; + break; + case 'ffmpeg2': + $metadata['videoCodec'] = 'MPEG2'; + break; + case '0x10000002': + $metadata['videoCodec'] = 'MPEG2'; + break; + case 'mpg4': + $metadata['videoCodec'] = 'MPEG4'; + break; + case 'div3': + $metadata['videoCodec'] = 'DivX3'; + break; + case 'div4': + $metadata['videoCodec'] = 'DivX4'; + break; + case 'divx': + $metadata['videoCodec'] = 'DivX4'; + break; + case 'dx50': + $metadata['videoCodec'] = 'DivX5'; + break; + case 'xvid': + $metadata['videoCodec'] = 'XviD'; + break; + default: + $metadata['videoCodec'] = 'Unknown'; + echo 'Unknown Video Codec: ' . $match[1] . '
'; + } + } else if (preg_match("/ID_AUDIO_CODEC=(.*)/", $line, $match)) { + $audioCodec = strtolower($match[1]); + switch ($audioCodec) { + case 'fftruehd': + $metadata['audioCodec'] = 'Dolby TrueHD'; + break; + case 'ffdca': + $metadata['audioCodec'] = 'DTS-HD Master Audio'; + break; + case 'ffac3': + $metadata['audioCodec'] = 'AC-3'; + break; + case 'a52': + $metadata['audioCodec'] = 'AC3'; + break; + case 'pcm': + $metadata['audioCodec'] = 'Uncompressed PCM'; + break; + case 'dvdpcm': + $metadata['audioCodec'] = 'Uncompressed DVD/VOB LPCM'; + break; + case 'mad': + $metadata['audioCodec'] = 'MP3'; + break; + case 'mp3': + $metadata['audioCodec'] = 'MP3'; + break; + case 'ffvorbis': + $metadata['audioCodec'] = 'Vorbis'; + break; + case 'ffwmav1': + $metadata['audioCodec'] = 'WMA1'; + break; + case 'ffwmav2': + $metadata['audioCodec'] = 'WMA2'; + break; + default: + $metadata['audioCodec'] = 'Unknown'; + echo 'Unknown Audio Codec: ' . $match[1] . '
'; + } + } else if (preg_match("/ID_VIDEO_WIDTH=(.*)/", $line, $match)) { + $metadata['videoWidth'] = $match[1]; + } else if (preg_match("/ID_VIDEO_HEIGHT=(.*)/", $line, $match)) { + $metadata['videoHeight'] = $match[1]; + } + } + + return $metadata; +} +?> + + + +Add and move files + + + + + +mplayer is not installed! Please install it.


'; + exit(); +} + +if (count($movieDirs) == 0) { + echo '

PLEASE edit the script and set at least one folder in \$movie_dirs variable


'; + exit(); +} + +// get all files. Result is in $moviesFS and $doubles; +foreach ($movieDirs as $dir) { + recurseDir($dir, $movieExts, $skipFolders); +} + +// Get all movies that are not on the whishlist (50) or inserted by this tool (51) +$SELECT = 'SELECT id, filename FROM ' . TBL_DATA . ' WHERE mediatype < 50 ORDER BY filename'; +$rows = runSQL($SELECT); + +echo '

Scanning...

'; +echo 'Total movie files found on disk: ' . sizeof($moviesFS) . '
'; +echo 'Total movie files found in DB: ' . count($rows) . '

'; +echo '

Comparing results.

'; + +foreach ($rows as $row) { + $filePathParts = preg_split("/\//", $row['filename']); + $fileName = end($filePathParts); + $id = $row['id']; + + // The path does not match the path found in the database + if ($moviesFS[$fileName] != $row['filename']) { + if (! isset($moviesFS[$fileName])) { + echo "MISSING - {$fileName}
"; + if ($update_missing) { + $UPDATE = "UPDATE " . TBL_DATA . " SET mediatype='" . MEDIA_WISHLIST . "' WHERE id='{$id}'"; + runSQL($UPDATE); + } + } else { + echo "MOVED - {$fileName}
"; + if ($update_moved) { + $UPDATE = "UPDATE " . TBL_DATA . " SET filename='" . escapeSQL($moviesFS[$fileName]) . "' WHERE id='{$id}'"; + runSQL($UPDATE); + } + } + } else { + unset($moviesFS[$fileName]); + } +} + +echo '
New files found: ' . sizeof($moviesFS) . '
'; +asort($moviesFS); +$moviesFS = removeCDNumbers($moviesFS); +echo 'New files found with no CD[2-3-4]: ' . sizeof($moviesFS) . '
'; + +// remove all movies added last time and not updated with real information +// mediatype 51 indicates that the movie was inserted/modified by this tool. +$UPDATE = "DELETE FROM " . TBL_DATA . " WHERE mediatype='51'"; +runSQL($UPDATE); + +echo '

Data on movies to add.

'; +echo " + + + + + + + + + + "; + +$currentUserId = get_current_user_id(); + +foreach ($moviesFS as $movie) { + $filePathParts = preg_split("/\//", $movie); + $fileName = end($filePathParts); + $title = preg_replace($cleanFileName, '', $fileName); + $title = str_replace('_', ' ', $title); + + $filePath = $moviesFS[end($filePathParts)]; + $fileSize = (int) getFileSize($filePath); + $fileDate = getFileTimeLinux($filePath); + + $metadata = getMetadata($filePath); + $audioCodec = $metadata['audioCodec']; + $videoCodec = $metadata['videoCodec']; + $videoWidth = (int) $metadata['videoWidth']; + $videoHeight = (int) $metadata['videoHeight']; + + echo " + + + + + + + + + "; + + $UPDATE = "INSERT " . TBL_DATA . " SET filename='" . escapeSQL($filePath) . "', + mediatype = 51, + title = '$title', + filesize = $fileSize, + audio_codec = '$audioCodec', + video_codec = '$videoCodec', + video_width = $videoWidth, + video_height = $videoHeight, + owner_id = $currentUserId, + filedate = FROM_UNIXTIME($fileDate)"; + + runSQL($UPDATE); +} +echo '
File NameTitleFile SizeAudio CodecVideo CodecVideo WidthVideo HeightFile Date
$filePath$title$fileSize$audioCodec$videoCodec$videoWidth$videoHeight$fileDate
'; + +$SELECT = "SELECT id, title FROM " . TBL_DATA . " WHERE mediatype='51' ORDER BY title"; +$rows = runSQL($SELECT); + +echo '

'; +foreach ($rows as $row) { + echo "{$row['title']}
"; +} + +?> +

All Done

+ + diff --git a/videodb/contrib/videoadd.pl b/videodb/contrib/videoadd.pl new file mode 100644 index 0000000..3203385 --- /dev/null +++ b/videodb/contrib/videoadd.pl @@ -0,0 +1,324 @@ +#!/usr/bin/perl + +## +# Add video files from local disc +# +# @package videoDB +# @author Andreas Gohr +# +# TODO check paths for correctness +## + +# path to md4sum +# Get it from http://www-tet.ee.tu-berlin.de/solyga/linux/ +# +# If you don't want md4 sums (time consuming) set it to a +# blank string +$md4sum = ''; #/usr/local/bin/md4sum'; + +# If you want md4 sums: in which custom field should it be +# stored? In VideoDB it should have 'ed2k' as type +#$md4field = 'custom2'; + +# If you want a download link to the file: in which custom +# field should it be stored? In VideoDB it should have +# 'url' as type +#$urlfield = 'custom1'; + +# path to the eject tool, +#found this not real useful when you can rub the script to process a directory, +#but if you going to us a cdrom you un comment it it. +#$eject = '/usr/bin/eject'; + +# path to mplayer (only version 0.90 was tested, 1.0rc1 works too) +$mplayer = '/usr/bin/mplayer'; + +# cdrom to use (reads it from commandline) +$device = $ARGV[ 0 ]; +# alter "/writer" to the directory you have videos in to process. +$device = "/writer" unless (defined($device)); + +# do the mount ? +$do_mount = 0; + +# remove articles +$remove_article = 1; + +# if you use the multiuser feature you may want to give an +# owner of the movies to add here - if you don't need it just +# set it to a blank string +$owner_id = 1; + +# allowed suffixes +$suffix_re = 'avi|ogm|ogg|bin|mpe?g|ra?m|mov|asf|wmv|mp4|mkv'; + +# Database stuff +$driver = "mysql"; +$database = "VideoDB"; +$prefix = ""; // enter your DB prefix like videodb. in here +$hostname = "server"; +$user = "www"; +$password = "leech"; + +################################################################################ +use DBI; + +#Connect to database +$dsn = "DBI:$driver:database=$database;host=$hostname"; +$dbh = DBI->connect($dsn, $user, $password); + +#quote this only once: +$owner = $dbh->quote($owner); + +if (-f $device) +{ + $plain = $device; + $plain =~ s#.*/([^/]+)$#\1#i; + add($device,$plain); +} +else +{ + + # mount + ($do_mount) && system("$eject -t $device"); + ($do_mount) && system("mount $device"); + + #work + &readfiles($device); + + #umount + ($do_mount) && system("umount $device"); + ($do_mount) && system("$eject $device"); +} + +#disconnect +$dbh->disconnect(); + +# +############################################################################### + +sub readfiles($) +{ + my $path = $_[ 0 ]; + + opendir(ROOT, $path); + my @files = readdir(ROOT); + closedir(ROOT); + + my ($file, $ffile); + foreach $file (@files) + { + $ffile = "$path/$file"; + + next if ($file =~ /^\.|\.\.$/); #skip upper dirs + if (-d $ffile) + { + readfiles($ffile); + } + + if ($ffile =~ /\.($suffix_re)$/i) + { + &add($ffile, $file); #add it + print STDERR "$ffile\n"; + } + } +} + +sub add($$) +{ + my $ffile = $_[ 0 ]; #full path + my $file = $_[ 1 ]; #file only + + # get filestatistics + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($ffile); + + # get md4 + my $md4; + if ($md4sum ne '') + { + $md4 = `$md4sum "$ffile"`; + $md4 =~ s/\s.*$//; + } + + # get videoinfos + my ($audio_codec, $video_codec, $video_width, $video_height, $runtime); + print qq($mplayer -identify -ao null -vo null -frames 0 "$ffile" 2>/dev/null); + my @out = `$mplayer -identify -ao null -vo null -frames 0 "$ffile" 2>/dev/null` if ($mplayer); + foreach my $line (@out) + { + next unless ($line =~ m/^ID_/); + chomp($line); + if ($line =~ m/^ID_VIDEO_FORMAT=(.*)/) + { + $video_codec = $1; + $video_codec = 'MPEG1' if ($video_codec eq '0x10000001'); + $video_codec = 'MPEG2' if ($video_codec eq '0x10000002'); + $video_codec = 'MPEG4' if ($video_codec eq 'MPG4'); + #FIXME id's of other mpegs?? + $video_codec = 'DivX3' if ($video_codec eq 'DIV3'); + $video_codec = 'DivX3' if ($video_codec eq 'div3'); + $video_codec = 'DivX4' if ($video_codec eq 'DIV4'); + $video_codec = 'DivX4' if ($video_codec eq 'DIVX'); + $video_codec = 'DivX4' if ($video_codec eq 'divx'); + $video_codec = 'DivX5' if ($video_codec eq 'DX50'); + $video_codec = 'XviD' if ($video_codec eq 'XVID'); + #FIXME aliases of other codecs? + #Add more 20/1/2015 by LinuxHam couple extra codecs to help get data needed + $video_codec = 'H264' if ($video_codec eq 'ffh264'); + $video_codec = 'avc1' if ($video_codec eq 'ffh264'); + $video_codec = 'MP4V' if ($video_codec eq 'ffodivx'); + } + elsif ($line =~ m/^ID_VIDEO_WIDTH=(.*)/) + { + $video_width = $1; + } + elsif ($line =~ m/^ID_VIDEO_HEIGHT=(.*)/) + { + $video_height = $1; + } + elsif ($line =~ m/^ID_AUDIO_CODEC=(.*)/) + { + $audio_codec = $1; + $audio_codec = 'MP3' if ($audio_codec eq 'mad'); + $audio_codec = 'MP3' if ($audio_codec eq 'mp3'); + $audio_codec = 'AC3' if ($audio_codec eq 'a52'); + $audio_codec = 'Vorbis' if ($audio_codec eq 'ffvorbis'); + $audio_codec = 'PCM' if ($audio_codec eq 'pcm'); + $audio_codec = 'WMA2' if ($audio_codec eq 'ffwmav2'); + $audio_codec = 'WMA1' if ($audio_codec eq 'ffwmav1'); # just a guess, needs confirmation + #Add more 20/1/2015 by LinuxHam, couple extra codecs to help get data needed + $audio_codec = '8192' if ($audio_codec eq 'ffac3'); + $audio_codec = 'MP4A' if ($audio_codec eq 'ffaac'); + $audio_codec = '85' if ($audio_codec eq 'ffmp3float'); + } + elsif ($line =~ m/^ID_LENGTH=(.*)/) + { + $runtime = $1; + $runtime = sprintf("%d", $runtime / 60); + } + } + + # get titles + my ($lang, $title, $subtitle, $istv) = &guessnames($file); + + # prepare for inserts + $file = $dbh->quote($file); + $size = $dbh->quote($size); + $audio_codec = $dbh->quote($audio_codec); + $video_codec = $dbh->quote($video_codec); + $video_width = $dbh->quote($video_width); + $video_height = $dbh->quote($video_height); + $runtime = $dbh->quote($runtime); + $lang = $dbh->quote($lang); + $title = $dbh->quote($title); + $subtitle = $dbh->quote($subtitle); + $md4 = $dbh->quote($md4); + $ffile = $dbh->quote($ffile); + + # insert + $INSERT = "INSERT INTO ".$prefix."videodata + SET filename = $file, + filesize = $size, + audio_codec = $audio_codec, + video_codec = $video_codec, + video_width = $video_width, + video_height = $video_height, + language = $lang, + title = $title, + subtitle = $subtitle, + runtime = $runtime, + mediatype = 4, + istv = $istv, + filedate = FROM_UNIXTIME($mtime), + created = NOW(), + owner_id = $owner_id"; + if ($md4sum ne '') + { + $INSERT .= ", $md4field = $md4"; + } + + if ($urlfield ne '') + { + $INSERT .= ", $urlfield = $ffile"; + } + $dbh->do($INSERT); + + #print "$file \n$title - $subtitle\n\n"; +} + +sub guessnames($) +{ + my $episode = ""; + my $istv = 0; + + my $file = $_[ 0 ]; #file only + + # try to get language + # backdrafts: es matches german word, de probably some french stuff + my $lang = ""; + $lang = "german" if ($file =~ m/\b(german|deutsch|ger|de)\b/i); + $lang = "english" if ($file =~ m/\b(english|eng|en)\b/i); + $lang = "french" if ($file =~ m/\b(french|français|fra|fr)\b/i); + $lang = "spanish" if ($file =~ m/\b(spanish|español|es)\b/i); + + # remove add. info + $file =~ s/\(.*\)//; + + #remove common trash and suffixes + $file =~ s/(\[[^\]]\]|bin|cd\d|dvd\d|divx|xvid|[ms]?vcd|dvdscr|dvdrip|shareconnector|eselfilme)//gi; + $file =~ s/\.($suffix_re)$//gi; + + # get episode Number + if ($file =~ s/(s\d+e\d+)/-/i) + { + $episode = $1; + } + elsif ($file =~ s/(\d+x\d+)/-/i) + { + $episode = $1; + } + + # change dots to underscores + $file =~ s/\./_/g; + + # change underscores to spaces + $file =~ s/_/ /g; + + # split title and subtitle and cleanup + my @parts = split ("-", $file, 2); + my $title = $parts[ 0 ]; + my $subtitle = $parts[ 1 ]; + $title =~ s/^[\s-]*//g; + $title =~ s/[\s-]*$//g; + $subtitle =~ s/^[\s-]*//g; + $subtitle =~ s/[\s-]*$//g; + + if ($episode) + { + $subtitle = "[$episode] $subtitle"; + $istv = 1; + } + + if ($remove_article) + { + unless ($title =~ s/^(for the)\b(.*)$/$2, $1/i) + { + unless ($title =~ s/^(for a)\b(.*)$/$2, $1/i) + { + unless ($title =~ s/^(for)\b(.*)$/$2, $1/i) + { + unless ($title =~ s/^(the)\b(.*)$/$2, $1/i) + { + unless ($title =~ s/^(a)\b(.*)$/$2, $1/i) + { + ($title =~ s/^(der|die|das)\b(.*)$/$2, $1/i); + } + } + } + } + } + } + chomp ($title); + + return ($lang, $title, $subtitle, $istv); +} diff --git a/videodb/core/VariableStream.class.php b/videodb/core/VariableStream.class.php new file mode 100644 index 0000000..304a438 --- /dev/null +++ b/videodb/core/VariableStream.class.php @@ -0,0 +1,56 @@ + + * @version $Id: VariableStream.class.php,v 1.4 2004/10/30 11:48:36 andig2 Exp $ + */ + +// stream wrappers require php > 4.3 +if (version_compare(phpversion(), '4.3') < 0) +{ + errorpage('PHP version mismatch', + 'At least PHP version 4.3.0 is required to run the VariableStream, please check the documentation!'); +} + +/** + * VariableStream allows XML reading from variables + * @package Core + */ +class VariableStream +{ + var $position; + var $varname; + var $context; + + function stream_open($path, $mode, $options, &$opened_path) { + $url = parse_url($path); + $this->varname = $url['host']; + $this->position = 0; + return true; + } + + function stream_read($count) { + $ret = substr($GLOBALS[$this->varname], $this->position, $count); + $this->position += strlen($ret); + return $ret; + } + + function stream_eof() { + return $this->position >= strlen($GLOBALS[$this->varname]); + } + + function stream_stat() { + return array('size' => strlen($GLOBALS[$this->varname])); + } + + function url_stat() { + return array(); + } +} + +// register stream type to allow use in xml->load +stream_wrapper_register('var', 'VariableStream'); + +?> diff --git a/videodb/core/cache.php b/videodb/core/cache.php new file mode 100644 index 0000000..df04e65 --- /dev/null +++ b/videodb/core/cache.php @@ -0,0 +1,196 @@ + + * @version $Id: cache.php,v 1.11 2013/04/26 15:09:35 andig2 Exp $ + */ + +// define cache folder +if (!defined('CACHE')) define('CACHE', 'cache'); + +/** + * Get the hashed filename + * + * @param string url of the item + * @param string $cache_folder name ob the sub-cache to adress + * @param string ext file extension of the cache file + */ +function cache_get_filename($url, $cache_folder, $ext = '') +{ + $hash = md5($url) . (($ext) ? '.'.$ext : ''); + $cache_file = cache_get_folder($cache_folder, $hash) . $hash; + + return $cache_file; +} + +/** + * Get name of the cache folder + * + * @TODO decouple from global config options + * + * @param string $cache_folder name ob the sub-cache to adress + * @param string $cache_filename name of the item to be cached for use with hierarchical caches + * @return string cache folder path including trailing / + */ +function cache_get_folder($cache_folder, $cache_filename = '') +{ + global $config; + + $cache_folder = CACHE.'/' . + (($cache_folder) ? $cache_folder.'/' : ''); + + if ($cache_filename) + $cache_folder .= substr($cache_filename, 0, @(int)$config['hierarchical']).'/'; + + return $cache_folder; +} + +/** + * Cleanup a single cache folder + * + * @param string $cache_folder path to cache folder + * @param int $cache_max_age maximum age of cached items in seconds + * @param bool $force_prune force cache pruning even if not due according to schedule + */ +function cache_prune_folder($cache_folder, $cache_max_age, $force_prune = false, $simulate = false, $pattern = '*') +{ + if (!preg_match('#/$#', $cache_folder)) $cache_folder .= '/'; + $stamp = $cache_folder.'cache_last_purge'; + $cache_mtime = @filemtime($stamp); // get time the cache was last purged (once a day) + + // if cache was last purged a day or more ago + if ($force_prune || ((time() - $cache_mtime) > ($cache_max_age / 24))) # 86400) + { + foreach (glob($cache_folder.$pattern, GLOB_NOSORT) as $file) + { + // avoid hidden files and directories + if (is_file($file) &! preg_match("/^\./", $file) && time() - filemtime($file) > $cache_max_age) + { + if ($simulate) + $files[] = $file; // add to list of potentially purged files + else + @unlink($file); // purge cache + } + } + + if ($simulate) return $files; + + @touch($stamp); // mark purge as having occurred + return true; + } + + return false; +} + +/** + * Cleanup a cache folder hierarchy + * + * @TODO decouple from global config options + * + * @param string $cache_folder path to cache folder + * @param int $cache_max_age maximum age of cached items in seconds + * @param bool $force_prune force cache pruning even if not due according to schedule + */ +function cache_prune_folders($cache_folder, $cache_max_age, $force_prune = false, $simulate = false, $pattern = '*', $levels = 0) +{ + global $config; + + // root folder + cache_prune_folder($cache_folder, $cache_max_age, $force_prune, $simulate, $pattern, $levels); + + // descent hierarchy + if ($levels > 0) + { + if (!isset($error)) + { + $error = ''; + } + for ($i=0; $i<16; $i++) + $error .= cache_prune_folders($cache_folder.dechex($i).'/', $cache_max_age, $force_prune, $simulate, $pattern, $levels-1); + } +} + + /** + * Create cache folders + * + * Check individual cache folder for existance, check if folder is writable and create folder if it doesn't exist + */ +function cache_create_folders($dir, $levels = 0) +{ + $error = ''; + if (!is_dir($dir)) + { + if (!@mkdir($dir, 0700)) $error = 'Directory '.$dir.' does not exist.
'; + } + elseif (!is_writable($dir)) + { + $error = 'Directory '.$dir.' is not writable.
'; + } + + // check hierarchical folders + if (empty($error) && ($levels > 0)) + { + for ($i=0; $i<16; $i++) + $error .= cache_create_folders($dir.'/'.dechex($i), $levels-1); + } + + return $error; +} + +/** + * Verify existance of cached file for given url/ extension + * + * @author Andreas Goetz + * @param string url of the item + * @param string ext file extension of the cache file + * @param string file result: URL to the cached image if exists + * @return bool result of check + */ +function cache_file_exists($url, &$cache_file, $cache_folder, $ext = '') +{ + $cache_file = cache_get_filename($url, $cache_folder, $ext); +// Small performance fix + $result = file_exists($cache_file) && filesize($cache_file); +# $result = filesize($cache_file) > 0; + + return($result); +} + +function cache_get($url, $cache_folder, $cache_max_age, $serialize = false) +{ + $data = false; + + if ($cache_max_age > 0) + { + if (cache_file_exists($url, $cache_file, $cache_folder)) + { + if (time() - filemtime($cache_file) < $cache_max_age) + { + $data = file_get_contents($cache_file); + if (($data !== false) && $serialize) $data = unserialize($data); + } + // TODO Check if outdated cache files should really be auto-deleted + else @unlink($cache_file); + } + } + + return $data; +} + +function cache_put($url, $data, $cache_folder, $cache_max_age, $serialize = false) +{ + // only put file to cache if caching is enabled + if ($cache_max_age > 0) + { + // get the cache file name + $cache_file = cache_get_filename($url, $cache_folder); + + // commit to disk + if ($serialize) $data = serialize($data); + file_put_contents($cache_file, $data); + } +} + +?> \ No newline at end of file diff --git a/videodb/core/compatibility.php b/videodb/core/compatibility.php new file mode 100644 index 0000000..b611797 --- /dev/null +++ b/videodb/core/compatibility.php @@ -0,0 +1,312 @@ + + * @link http://pear.php.net PEAR + * @version $Id: compatibility.php,v 1.15 2013/03/13 16:38:19 andig2 Exp $ + */ + +/** + * Implements file_get_contents introduced in v4.3.0 + */ +if (!function_exists('file_get_contents')) +{ + function file_get_contents($filename) + { + $fh = @fopen($filename, 'rb'); + if (!$fh) return false; + $content = fread($fh, filesize($filename)); + fclose($fh); + return $content; + } +} + +/** + * Implements file_put_contents introduced in v5.0.0 + */ +if (!function_exists('file_put_contents')) +{ + function file_put_contents($filename, $content) + { + $fh = @fopen($filename, 'wb'); + if (!$fh) return false; + if (!fwrite($fh, $content, strlen($content))) return false; + fclose($fh); + return true; + } +} + +/** + * Implements html_entity_decode introduced in v4.3.0 + * @author + * @param string $string HTML encoded string + * @return string HTML decoded string + */ +if (!function_exists('html_entity_decode')) +{ + function html_entity_decode($string) + { + // replace numeric entities + $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string); + $string = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $string); + // replace literal entities + $trans_tbl = get_html_translation_table(HTML_ENTITIES); + $trans_tbl = array_flip($trans_tbl); + return strtr($string, $trans_tbl); + } +} + +/** + * Implements http_build_query introduced in v5.0.0 + */ +if (!function_exists('http_build_query')) +{ + function http_build_query ($formdata, $numeric_prefix = null) + { + // Check we have an array to work with + if (!is_array($formdata)) { + return $formdata; + } + + // Start building the query + $tmp = array (); + foreach ($formdata as $key => $val) + { + array_push($tmp, urlencode($key).'='.urlencode($val)); + } + + return implode('&', $tmp); + } +} + +/** + * Multibyte-aware character case conversion + * + * @author tedemo + */ +if (!function_exists('mb_convert_case')) +{ + function mb_convert_case($str) + { + return ucwords(strtolower($str)); + } +} + +/** + * iconv alternatives + * + * @author Andreas Goetz + */ +if (!function_exists('iconv')) +{ + function iconv($source_encoding, $target_encoding, $str) + { + // remove transliteration- only available in native iconv + $source_encoding = preg_replace('#^(.+?)(//.*)#', '\\1', $source_encoding); + $target_encoding = preg_replace('#^(.+?)(//.*)#', '\\1', $target_encoding); + + if (function_exists('mb_convert_encoding')) + return mb_convert_encoding($str, $target_encoding, $source_encoding); + elseif (function_exists('recode_string')) + return recode_string($source_encoding.'..'.$target_encoding, $str); + else + return $str; + } +} + +/** + * Implements json_encode introduced in v5.2.0 + * + * @author Andreas Goetz + */ +if (!function_exists('json_encode')) +{ + function json_encode($data) + { + require_once('./lib/json.php'); + $json = new Services_JSON(); + return($json->encode($data)); + } +} + +/** + * Implements json_decode introduced in v5.2.0 + * + * @author Andreas Goetz + */ +if (!function_exists('json_decode')) +{ + function json_decode($data) + { + require_once('./lib/json.php'); + $json = new Services_JSON(); + return($json->decode($data)); + } +} + +/** + * Implements json_decode introduced in v5.0.0 + * + * @author Andreas Goetz + */ +if (!function_exists('http_build_query')) +{ + function http_build_query($formdata, $numeric_prefix = null, $key = null) + { + $res = array(); + foreach ((array)$formdata as $k=>$v) + { + $tmp_key = urlencode(is_int($k) ? $numeric_prefix.$k : $k); + if ($key) $tmp_key = $key.'['.$tmp_key.']'; + + if ( is_array($v) || is_object($v) ) { + $res[] = http_build_query($v, null /* or $numeric_prefix if you want to add numeric_prefix to all indexes in array*/, $tmp_key); + } else { + $res[] = $tmp_key."=".urlencode($v); + } + } + $separator = ini_get('arg_separator.output'); + return implode($separator, $res); + } +} + +/** + * Quick image type detection + * + * @author Andreas Goetz + */ +if (!function_exists('exif_imagetype')) +{ + function exif_imagetype($filename) + { + if ((list($width, $height, $type, $attr) = getimagesize($filename )) !== false ) { + return $type; + } + return false; + } +} + +/** + * Ease PHP 5.3.0 requirement on Windows + * + * @author Andreas Goetz + */ +if (!function_exists('linkinfo')) +{ + function linkinfo($path) + { + return 0; + } +} + +/** + * Polyfill for PHP 4 - PHP 7 + * introduced in PHP 8 + */ +if (!function_exists('str_contains')) +{ + function str_contains(string $haystack, string $needle): bool + { + return strpos($haystack, $needle) !== false; + } +} + +/** + * This file is part of the array_column library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * @copyright Copyright (c) 2013 Ben Ramsey + * @license http://opensource.org/licenses/MIT MIT + */ + +/** + * Returns the values from a single column of the input array, identified by + * the $columnKey. + * + * Optionally, you may provide an $indexKey to index the values in the returned + * array by the values from the $indexKey column in the input array. + * + * @param array $input A multi-dimensional array (record set) from which to pull + * a column of values. + * @param mixed $columnKey The column of values to return. This value may be the + * integer key of the column you wish to retrieve, or it + * may be the string key name for an associative array. + * @param mixed $indexKey (Optional.) The column to use as the index/keys for + * the returned array. This value may be the integer key + * of the column, or it may be the string key name. + * @return array + */ +if (!function_exists('array_column')) +{ + function array_column($input = null, $columnKey = null, $indexKey = null) + { + // Using func_get_args() in order to check for proper number of + // parameters and trigger errors exactly as the built-in array_column() + // does in PHP 5.5. + $params = func_get_args(); + if (!isset($params[0])) { + trigger_error('array_column() expects at least 2 parameters, 0 given', E_USER_WARNING); + return null; + } elseif (!isset($params[1])) { + trigger_error('array_column() expects at least 2 parameters, 1 given', E_USER_WARNING); + return null; + } + if (!is_array($params[0])) { + trigger_error('array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given', E_USER_WARNING); + return null; + } + if (!is_int($params[1]) + && !is_string($params[1]) + && !(is_object($params[1]) && method_exists($params[1], '__toString')) + ) { + trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING); + return false; + } + if (isset($params[2]) + && !is_int($params[2]) + && !is_string($params[2]) + && !(is_object($params[2]) && method_exists($params[2], '__toString')) + ) { + trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING); + return false; + } + $paramsInput = $params[0]; + $paramsColumnKey = (string) $params[1]; + $paramsIndexKey = (isset($params[2]) ? (string) $params[2] : null); + $resultArray = array(); + foreach ($paramsInput as $row) { + $key = $value = null; + $keySet = $valueSet = false; + if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) { + $keySet = true; + $key = $row[$paramsIndexKey]; + } + if (is_array($row) && array_key_exists($paramsColumnKey, $row)) { + $valueSet = true; + $value = $row[$paramsColumnKey]; + } + if ($valueSet) { + if ($keySet) { + $resultArray[$key] = $value; + } else { + $resultArray[] = $value; + } + } + } + return $resultArray; + } +} + +?> \ No newline at end of file diff --git a/videodb/core/constants.php b/videodb/core/constants.php new file mode 100644 index 0000000..bb254d0 --- /dev/null +++ b/videodb/core/constants.php @@ -0,0 +1,76 @@ + + * @version $Id: constants.php,v 1.65 2013/04/25 15:00:32 andig2 Exp $ + */ + +// Config file +define('CONFIG_FILE', './config.inc.php'); +define('VERSION', '4.1.0'); + +define('LOG_FILE', 'debug.log'); + +// User Permission bit masks +define('PERM_ADMIN', 1); +define('PERM_READ', 2); +define('PERM_WRITE', 4); +define('PERM_ADULT', 8); +define('PERM_ALL', -1); // used to check for "all" permissions only +define('PERM_ANY', -2); // used to check for exististance of any cross-user permission + +// Cache folders +define('CACHE_IMG', 'img'); +define('CACHE_HTML', 'imdb'); +define('CACHE_THUMBS', 'thumbs'); +define('CACHE_LOCAL', 'local'); // local images for covers and actors + +// Table names +define('TBL_DATA', $config['db_prefix'].'videodata'); + +define('TBL_CONFIG', $config['db_prefix'].'config'); +define('TBL_USERCONFIG', $config['db_prefix'].'userconfig'); + +define('TBL_USERS', $config['db_prefix'].'users'); +define('TBL_USERSEEN', $config['db_prefix'].'userseen'); +define('TBL_PERMISSIONS', $config['db_prefix'].'permissions'); + +define('TBL_ACTORS', $config['db_prefix'].'actors'); +define('TBL_GENRES', $config['db_prefix'].'genres'); + +define('TBL_VIDEOGENRE', $config['db_prefix'].'videogenre'); +define('TBL_MEDIATYPES', $config['db_prefix'].'mediatypes'); + +define('TBL_LENT', $config['db_prefix'].'lent'); + +define('TBL_CACHE', $config['db_prefix'].'cache'); + +// Wishlist +define('MEDIA_WISHLIST', 50); + +// Amazon associates token +define('AMAZON_ASSOCIATE', 'cpuidle-20'); + +// Database character set - only valid values are UTF8, LATIN1 (legacy only) or empty +define('DB_CHARSET', 'UTF8'); +// Database sort order - if empty sorting is defined by language file or db standard. +// use UTF8_GENERAL_CI or other valid MySQL collation to override +define('DB_COLLATION', ''); + +// Required database version +define('DB_REQUIRED', 41); + +/** + * These used to be defined inside config.sample and therefore config.inc + * Removed the need for them inside config.* but since their definition can still + * be inside config.inc prefixed here with a check + */ +if (!defined('TUMB_NO_SCALE')) define('TUMB_NO_SCALE', -1); // no scaling - use of thumbnails is disabled +if (!defined('TUMB_REDUCE_ONLY')) define('TUMB_REDUCE_ONLY', 0); // reduce only - create thumbnails when requested image dimensions are smaller than original image +if (!defined('TUMB_SCALE')) define('TUMB_SCALE', 1); // always scale - create thumbnails for all images (applies aliasing when scaling) + diff --git a/videodb/core/custom.php b/videodb/core/custom.php new file mode 100644 index 0000000..4afbc39 --- /dev/null +++ b/videodb/core/custom.php @@ -0,0 +1,492 @@ + + * @version $Id: custom.php,v 1.16 2008/10/03 14:18:04 andig2 Exp $ + */ + + +/* + Hint: + + $cn + holds the name of the custom field (eg. 'custom2') use this as the name for + the input formfield + + $cv + holds the current value of that field if any. When you want to use it as + value in a formfield quote it with the formvar() function! + + When you add new types send them to me. I will include them here. +*/ + + +/////////////////////////////////////////////////////////////////////////////// + +/* This array contains all available types - be sure to add your type if + you ad a new one */ +$allcustomtypes=array('', + 'text', + 'url', + 'ed2k', + 'language', + 'orgtitle', + 'movix', + 'mpaa', + 'bbfc', + 'fsk', + 'barcode', + 'kijkwijzer' + ); + +/** + * Assigns custom field names and values to input object + * @author Andreas Goetz + * @param hashref $video Reference to video hash structure + * @param string $inout Either in or out, determines type of control + * returned (html input control or rendered output) + */ +function customfields(&$video, $inout) +{ + global $config; + + $inout_function = ($inout == 'in') ? '_input' : '_output'; + for ($i=1; $i < 5; $i++) + { + if (!empty($config['custom'.$i])) + { + $video['custom'.$i.'name'] = $config['custom'.$i]; + $run = 'custom_'.$config['custom'.$i.'type'].$inout_function; + $custom_value = null; + if (array_key_exists('custom'.$i, $video)) + { + $custom_value = $video['custom'.$i]; + } + $video['custom'.$i.$inout] = $run('custom'.$i,$custom_value); + } + } +} + +/** + * Custom Type: + * + * Standardinputhandler for custom fields -> just calls text type + */ +function custom__input($cn,$cv) +{ + return custom_text_input($cn,$cv); +} + +/** + * Custom Type: + * + * Standardoutputhandler for custom fields -> just calls text type + */ +function custom__output($cn,$cv) +{ + return custom_text_output($cn,$cv); +} + +/** + * Custom Type: text + * + * Standardinputhandler for custom fields + */ +function custom_text_input($cn,$cv) +{ + return ''; +} + +/** + * Custom Type: text + * + * Standardouputhandler for custom fields + */ +function custom_text_output($cn,$cv) +{ + return $cv; +} + +/** + * Custom Type: url + * + * Stores an URL in a custom file and shows a clickable link. + */ +function custom_url_input($cn,$cv) +{ + return ''; +} + +/** + * Custom Type: url + * + * Stores an URL in a custom file and shows a clickable link. + */ +function custom_url_output($cn,$cv) +{ + if (!empty($cv)) + { + return '[ Link ]'; + } else { + return ''; + } +} + +/** + * Custom Type: ed2k + * + * Stores the MD4 sum of the File in a custom file and shows a clickable ed2k + * Link for the eDonkey2000 client tools. + */ +function custom_ed2k_input($cn,$cv) +{ + return ' (MD4)'; +} + +/** + * Custom Type: ed2k + * + * Stores the MD4 sum of the File in a custom file and shows a clickable ed2k + * Link for the eDonkey2000 client tools. + */ +function custom_ed2k_output($cn,$cv) +{ + global $video; + + if (!empty($video[0]['filesize']) && !empty($video[0]['filename']) && !empty($cv)) { + return '[ Add to eDonkey ]'; + } else { + return ''; + } +} + +/** + * Custom Type: language + * + * Language Selection with Quickselectionbuttons configured in + * $config['languages'] + */ +function custom_language_input($cn,$cv) +{ + global $config; + + $output = ''; + $output .= ' '; + foreach ($config['languages'] as $flag) + { + $output .= ''; + $output .= ''.formvar($cv).' '; + } + return $output; +} + +/** + * Custom Type: language + * + * Language Selection with Quickselectionbuttons configured in + * $config[languages] + */ +function custom_language_output($cn,$cv) +{ + return custom_text_output($cn,$cv); +} + +/** + * Custom Type: FSK + * + * Allows you to set the FSK Rating of a movie. + * + * @author Chinamann + */ +function custom_fsk_input($cn,$cv) +{ + return custom_text_input($cn,$cv); +} + +/** + * Custom Type: FSK + * + * Allows you to display the FSK Rating. + * + * @author Chinamann + */ +function custom_fsk_output($cn,$cv) +{ + $allfsktypes = array('0','6','12','16','18'); + if (!in_array ($cv, $allfsktypes)) + { + return custom_text_output($cn,$cv); + } + + return ''; +} + + +/** + * Custom Type: Barcode + * + * Allows you to input the Barcode of a movie. + * + * @author Chinamann + */ +function custom_barcode_input($cn,$cv) +{ + return custom_text_input($cn,$cv); +} + +/** + * Custom Type: Barcode + * + * Allows you to display Barcode. + * + * @author Chinamann + */ +function custom_barcode_output($cn,$cv) +{ + return custom_text_output($cn,$cv); +} + + +/** + * Custom Type: Originaltitle + * + * Holds the Original title of an movie + * + * @author Stephan Zalewski + */ +function custom_orgtitle_input($cn,$cv) +{ + global $config; + global $imdbdata; + global $id; + + if (empty($cv)|| $config['lookupdefault_edit'] > 0 || $config['lookupdefault_new'] > 0) + { + if (!empty($imdbdata['title'])) {$cv .= ' - '.$imdbdata['title'];} + if (!empty($imdbdata['subtitle'])) {$cv .= ' - '.$imdbdata['subtitle'];} + + // we need to save our self here! + if (!empty($id) && $cv != '') + { + $qcv = escapeSQL($cv); + $UPDATE = "UPDATE ".TBL_DATA." SET $cn = '$qcv' WHERE id = $id"; + runSQL($UPDATE); + } + } + return custom_text_input($cn,$cv); +} + +/** + * Custom Type: Originaltitle + * + * Holds the Original title of an movie + * + * @author Stephan Zalewski + */ +function custom_orgtitle_output($cn,$cv) +{ + return custom_text_output($cn,$cv); +} + +/** + * Custom Type: Movix + * + * Allows you to indicate if a movie is stored in a cd/dvd + * with movix (http://movix.sourceforge.net/) + * + * @author Antonio Giungato + */ + +function custom_movix_input($cn,$cv) +{ + $output =''; + return $output; +} + +function custom_movix_output($cn,$cv) +{ + return custom_text_output($cn,$cv); +} + +/** + * Custom Type: MPAA + * + * Show the MPAA rating of a movie + * + * @author Tim M. Sanders + */ +function custom_mpaa_input($cn,$cv) +{ + global $config; + global $imdbdata; + global $id; + + if (empty($cv)|| $config['lookupdefault_edit'] > 0 || $config['lookupdefault_new'] > 0) + { + if (!empty($imdbdata['mpaa'])) {$cv .= $imdbdata['mpaa'];} + + //we need to save our self here! + if(!empty($id) && $cv != '') + { + $qcv = escapeSQL($cv); + $UPDATE = "UPDATE ".TBL_DATA." SET $cn = '$qcv' WHERE id = $id"; + runSQL($UPDATE); + } + } + $output = ' '; + return $output; +} + +/** + * Custom Type: MPAA + * + * Show the MPAA rating of a movie + * + * @author Tim M. Sanders + */ +function custom_mpaa_output($cn,$cv) +{ + global $imdbdata; + global $id; + + $ratings = array('R' => 'mpaa-R.gif', + 'NC-17' => 'mpaa-NC-17.gif', + 'PG-13' => 'mpaa-PG-13.gif', + 'PG' => 'mpaa-PG.gif', + 'G' => 'mpaa-G.gif'); + + $output = custom_text_output($cn,$cv); + foreach ($ratings as $rating => $image) + { + if (strstr($output, $rating)) + { + $output .= '
'.$rating.''; + break; + } + } + return $output; +} + +/** + * Custom Type: BBFC + * + * Show the BBFC rating of a movie + * Based on the MPAA ratings above + * + * @author Colin Ogilvie + */ +function custom_bbfc_input($cn,$cv) +{ + global $config; + global $imdbdata; + global $id; + + if (empty($cv)|| $config['lookupdefault_edit'] > 0 || $config['lookupdefault_new'] > 0) + { + $cv = $imdbdata['bbfc']; + if (!empty($imdbdata['bbfc'])) $cv .= $imdbdata['bbfc']; + + //we need to save our self here! + if(!empty($id) && $cv != '') + { + $qcv = escapeSQL($cv); + $UPDATE = "UPDATE ".TBL_DATA." SET $cn = '$qcv' WHERE id = $id"; + runSQL($UPDATE); + } + } + $output = ''; + $output .= " U"; + $output .= " 12"; + $output .= " 12A"; + $output .= " 15"; + $output .= " 18"; + $output .= " PG"; + return $output; +} + +/** + * Custom Type: BBFC + * + * Show the BBFC rating of a movie + * Based on the MPAA ratings above + * + * @author Colin Ogilvie + */ +function custom_bbfc_output($cn,$cv) +{ + global $imdbdata; + global $id; + + $ratings = array('PG' => 'bbfc-PG.gif', + '12A' => 'bbfc-12A.gif', + '12' => 'bbfc-12.gif', + '15' => 'bbfc-15.gif', + '18' => 'bbfc-18.gif', + 'U' => 'bbfc-U.gif'); + + $output = custom_text_output($cn,$cv); + foreach ($ratings as $rating => $image) + { + if (strstr($output, (string) $rating)) + { + $output = ''.$rating.''; + break; + } + } + return $output; +} + +function custom_kijkwijzer_kijkwijzerdata () { + return [ + 'AL' => [1, 'Alle leeftijden', 'kijkwijzer/al.png', ''], + '06' => [2, '6 jaar', 'kijkwijzer/6.png', ''], + '09' => [4, '9 jaar', 'kijkwijzer/9.png', ''], + '12' => [8, '12 jaar', 'kijkwijzer/12.png', ''], + '14' => [16, '14 jaar', 'kijkwijzer/14.png', ''], + '16' => [32, '16 jaar', 'kijkwijzer/16.png', ''], + '18' => [64, '18 jaar', 'kijkwijzer/18.png', ''], + 'AN' => [128, 'Angst', 'kijkwijzer/angst.png', ''], + 'DI' => [256, 'Discriminatie', 'kijkwijzer/discriminatie.png', ''], + 'DA' => [512, 'Roken, alcohol en drugs', 'kijkwijzer/drugs-en-alcohol.png', ''], + 'GE' => [1024, 'Geweld', 'kijkwijzer/geweld.png', ''], + 'SE' => [2048, 'Seks', 'kijkwijzer/seks.png', ''], + 'TA' => [4096, 'Grof taalgebruik', 'kijkwijzer/taal.png', ''] + ]; +} + +function custom_kijkwijzer_input ($cn, $cv) { + $kijkwijzer = custom_kijkwijzer_kijkwijzerdata(); + $output = ''; + foreach ($kijkwijzer as $val => $cfg) { + $output .= " \"Kijkwijzer:"; + } + return $output; +} + +function custom_kijkwijzer_output ($cn, $cv, $sm = false) { + $kijkwijzer = custom_kijkwijzer_kijkwijzerdata(); + $rv = ''; + $cv = explode(' ', trim($cv)); + foreach ($cv as $i) { + if (isset($kijkwijzer[$i])) { + $rv .= 'Kijkwijzer: ' . $kijkwijzer[$i][1] . ''; + } + } + return $rv; +} + diff --git a/videodb/core/edit.core.php b/videodb/core/edit.core.php new file mode 100644 index 0000000..7423fad --- /dev/null +++ b/videodb/core/edit.core.php @@ -0,0 +1,263 @@ + + * @author Andreas Gohr + * @author Chinamann + * @version $Id: edit.core.php,v 1.9 2009/12/05 13:56:04 andig2 Exp $ + */ + +require_once './core/security.php'; + +// list of fields to be read/written from/to html form +$imdb_set_fields = array('md5','title','subtitle','language','diskid','mediatype','comment','disklabel', + 'imdbID','year','imgurl','director','actors','runtime','country','plot','filename', + 'filesize','filedate','audio_codec','video_codec','video_width','video_height','istv', + 'rating', 'custom1','custom2','custom3','custom4'); + +// list of fields to be overwritten by refetchAllInfos-Script +$imdb_overwrite_fields = array('comment','disklabel','imdbID','year','director','actors','runtime','country','plot', + 'rating','custom1','custom2','custom3','custom4'); + +// special fields for SQL statements +$db_null_fields = array('runtime', 'filesize', 'filedate', 'video_width', 'video_height'); +$db_zero_fields = array('istv', 'year'); + +/** + * Obtain new disk id + * + * @return string Generated disk id + */ +function getDiskId() +{ + global $config; + + /* + * change this if you have some fancy naming style + */ + + // how many digits have to be used for DiskId? + $digits = ($config['diskid_digits']) ? $config['diskid_digits'] : 4; + + /* + * Old way automatic DiskID was generated: result was "highest ID + 1" + * + $NEXTUSERID = "SELECT LPAD(TRIM(LEADING '0' FROM MAX(LPAD(TRIM(LEADING '0' FROM diskid), 10, '0'))) + 1, ". + $digits.", '0') AS max FROM ".TBL_DATA.' WHERE diskid NOT REGEXP "[^0-9]"'; + $result = runSQL($NEXTUSERID); + return $result[0]['max']; + */ + + // get all DiskIds ordered from DB + $SQL = "SELECT LPAD(TRIM(LEADING '0' FROM diskid), 10, '0') AS id + FROM ".TBL_DATA.' + WHERE diskid NOT REGEXP "[^0-9]" AND + owner_id = '.get_current_user_id().' + ORDER BY id'; + // sql looks strange but fixes problems with users who change their + // diskid_digits while they have already movies in their DB. + // added owner_id as fix for https://github.com/andig/videodb/issues/6 + $results = runSQL($SQL); + + // find first 'free' diskId + $lastid = 0; + foreach ($results as $result) + { + $thisid = preg_replace('/^0+/','',$result['id']); + if ($lastid + 1 < $thisid) break; + $lastid = $thisid; + } + + // return the found id + return str_pad($lastid + 1, $digits, '0', STR_PAD_LEFT); +} + +/** + * Strip leading articles + * + * @param string $field Input field to be stripped + * @return string Input with articles rearranged to end of string + */ +function removeArticles($field) +{ + $articles = array('the ', 'la ', 'a ', 'der ', 'die ', 'das ', 'des ', 'dem ', 'den ', + 'ein ', 'eine ', 'eines ', 'le ', 'el ', "l'", 'il ', 'les ', 'i ', + 'o ', 'un ', 'los ', 'de ', 'an ', 'una ', 'las ', 'gli ', 'het ', + 'lo ', 'os ', 'az ', 'ha-', 'een ', 'det ', 'oi ', 'ang ', 'ta ', + 'al-', 'uno ', "un'", 'ett ', 'mga ', 'Ï ', 'Ç ', 'els ', 'Ôï ', 'Ïé '); + + foreach ($articles as $article) + { + if (preg_match("/^$article+/i", $field)) + { + $field = trim(preg_replace("/(^$article)(.+)/i", "$2, $1", $field)); + break; + } + } + + return $field; +} + +/** + * Prepare item for display based on input data + */ +function echoInput($data) +{ + global $imdb_set_fields; + + // error no owner specified + $video = array(); + + // select all fields according to list, plus id + foreach ($imdb_set_fields as $name) + { + $video[0][$name] = $data[$name]; + } + + return $video; +} + +/** + * Prepare update SQL + * + * @param array $data key/value pairs of data + * @returns string result SQL, suitable for INSERT/UPDATE + */ +function prepareSQL($data, $setonly = false) +{ + global $config, $imdb_set_fields, $db_null_fields, $db_zero_fields; + + // get global variables into local scope + extract($data); + + // Fix for Bugreport [1122052] Automatic DiskID generation problem + if ($config['autoid'] && !empty($diskid) && ($diskid == $autoid)) + { + // in case DiskID is already used in meanwhile + // -> update to new DiskId + $diskid = getDiskId(); + } + + // set default mediatype + if (empty($mediatype)) $mediatype = $config['mediadefault']; + + // set owner + if (is_numeric($owner_id)) $SQL = 'owner_id = '.$owner_id; + + // rating up to 10 + $rating = min($rating, 10); + + // update all fields according to list + foreach ($imdb_set_fields as $name) + { + if ($setonly && !isset($$name)) continue; + + // sanitize input + $$name = removeEvilTags($$name); + if (!is_null($$name)) + { + $$name = html_entity_decode($$name); + } + + // make sure no formatting contained in basic data + if (in_array($name, array('title', 'subtitle'))) + { + $$name = trim(strip_tags($$name)); + + // string leading articles? + if ($config['removearticles']) + { + $$name = removeArticles($$name); + } + } + + $SET = "$name = '".escapeSQL($$name)."'"; + + // special null/zero handling + if (empty($$name)) + { + if (in_array($name, $db_null_fields)) + $SET = "$name = NULL"; + elseif (in_array($name, $db_zero_fields)) + $SET = "$name = 0"; + } + + if ($SQL) $SQL .= ', '; + $SQL .= $SET; + } + + return $SQL; +} + +/** + * @param string $SQL set fields + * @param int $id id of item to update, insert if empty + * @param boolean $touch true specifies to update created data of item + */ +function updateDB($SQL, $id, $touch=false) +{ + if ($id) + { + // update existing record + if ($touch) + { + // if the disk was on the wishlist and is now available, make sure it appears under 'new' + $SQL .= ', created = NOW()'; + } + + $SQL = 'UPDATE '.TBL_DATA.' SET '.$SQL.' WHERE id = '.$id; + runSQL($SQL); + } + else + { + // insert new record + $SQL = 'INSERT INTO '.TBL_DATA.' SET '.$SQL.', created = NOW()'; + $id = runSQL($SQL); + } + + return $id; +} + +/** + * Process HTTP file upload + */ +function processUpload($id, $file, $mime, $name) +{ + if (!(isset($_FILES['coverupload']) && is_uploaded_file($_FILES['coverupload']['tmp_name']))) + return; + + // determine file extension + if (preg_match('=image/jpe?g=i', $mime)) + { + $ext = 'jpg'; + } + elseif (preg_match('=image/(gif|png)=i', $mime, $m)) + { + $ext = $m[1]; + } + elseif (preg_match('=application/octet-stream=i', $mime)) + { + if (preg_match("/\.(jpe?g|gif|png)$/i", $name, $m)) + { + $ext = $m[1]; + } + } + + // move to cache and update db + if (!empty($ext)) + { + $coverfile = 'cache/img/'.$id.'.'.$ext; + if (move_uploaded_file($file, $coverfile)) + { + // fix permission issues + chmod($coverfile, 0644); + + $sql = "UPDATE ".TBL_DATA." SET imgurl='$coverfile' WHERE id=$id"; + runSQL($sql); + } + } +} + +?> diff --git a/videodb/core/encoding.php b/videodb/core/encoding.php new file mode 100644 index 0000000..730e4dd --- /dev/null +++ b/videodb/core/encoding.php @@ -0,0 +1,282 @@ + + * @version $Id: encoding.php,v 1.6 2013/03/10 16:25:35 andig2 Exp $ + */ + +/** + * Check if string contains unicode characters + */ +function is_utf8($str) +{ + // array handling + if (is_array($str)) { + foreach($str as $k => $v) { + $res = is_utf8($v); + if (!$res) return(false); + } + return(true); + } + + // From http://w3.org/International/questions/qa-forms-utf-8.html + return preg_match('%^(?: + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 + )*$%xs', $str); +} + +/** + * @author "Sebasti�n Grignoli" + * @package Encoding + * @version 1.1 + * @link http://www.framework2.com.ar/dzone/forceUTF8-es/ + * @example http://www.framework2.com.ar/dzone/forceUTF8-es/ + */ +function fix_utf8($text) +{ + $utf8ToWin1252 = array( + "\xe2\x82\xac" => "\x80", + + "\xe2\x80\x9a" => "\x82", + "\xc6\x92" => "\x83", + "\xe2\x80\x9e" => "\x84", + "\xe2\x80\xa6" => "\x85", + "\xe2\x80\xa0" => "\x86", + "\xe2\x80\xa1" => "\x87", + "\xcb\x86" => "\x88", + "\xe2\x80\xb0" => "\x89", + "\xc5\xa0" => "\x8a", + "\xe2\x80\xb9" => "\x8b", + "\xc5\x92" => "\x8c", + + "\xc5\xbd" => "\x8e", + + + "\xe2\x80\x98" => "\x91", + "\xe2\x80\x99" => "\x92", + "\xe2\x80\x9c" => "\x93", + "\xe2\x80\x9d" => "\x94", + "\xe2\x80\xa2" => "\x95", + "\xe2\x80\x93" => "\x96", + "\xe2\x80\x94" => "\x97", + "\xcb\x9c" => "\x98", + "\xe2\x84\xa2" => "\x99", + "\xc5\xa1" => "\x9a", + "\xe2\x80\xba" => "\x9b", + "\xc5\x93" => "\x9c", + + "\xc5\xbe" => "\x9e", + "\xc5\xb8" => "\x9f" + ); + + if (is_array($text)) { + foreach($text as $k => $v) { + $text[$k] = fix_utf8($v); + } + return $text; + } + + $last = ""; + while ($last <> $text) { + $last = $text; + $text = utf8_encode(utf8_decode(str_replace(array_keys($utf8ToWin1252), array_values($utf8ToWin1252), $text))); + } + $text = utf8_encode(utf8_decode(str_replace(array_keys($utf8ToWin1252), array_values($utf8ToWin1252), $text))); + return $text; +} + +/** + * Decode string is utf-8. Typically used for later URL encoding of the string + */ +function utf8_smart_decode($str) +{ + return (is_utf8($str)) ? utf8_decode($str) : $str; +} + +/** + * Like html_entity_decode() but also supports numeric entities. + * Output encoding is ISO-8852-1. + * + * @author www.php.net + * @param string $string html entity loaded string + * @return string html entity free string + */ +function html_entity_decode_all($string) +{ + // replace numeric entities + $string = preg_replace_callback('~&#x([0-9a-f]+);~i', '_callback_chr_hexdec', $string); + $string = preg_replace_callback('~&#([0-9]+);~', '_callback_chr', $string); +# utf8 version commented out +# $string = preg_replace_callback('~&#x([0-9a-f]+);~i', '_callback_code2utf_hexdec', $string); +# $string = preg_replace_callback('~&#([0-9]+);~', '_callback_code2utf', $string); + + // replace literal entities + $trans_tbl = get_html_translation_table(HTML_ENTITIES); + $trans_tbl = array_flip($trans_tbl); +# utf8 version commented out +# foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) $trans_tbl[$key] = utf8_encode($val); + + return strtr($string, $trans_tbl); +} + +/** + * Like html_entity_decode() but also supports numeric entities. + * Output encoding is UTF-8. + * + * @author www.php.net + * @param string $string html entity loaded string + * @return string html entity free string + */ +function html_entity_decode_all_utf8($string) +{ + // replace numeric entities +# non-utf8 version commented out +# $string = preg_replace_callback('~&#x([0-9a-f]+);~i', '_callback_chr_hexdec', $string); +# $string = preg_replace_callback('~&#([0-9]+);~', '_callback_chr', $string); + $string = preg_replace_callback('~&#x([0-9a-f]+);~i', '_callback_code2utf_hexdec', $string); + $string = preg_replace_callback('~&#([0-9]+);~', '_callback_code2utf', $string); + + // replace literal entities +# non-utf8 version commented out +# $trans_tbl = get_html_translation_table(HTML_ENTITIES); +# $trans_tbl = array_flip($trans_tbl); + foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) $trans_tbl[$key] = utf8_encode($val); + + return strtr($string, $trans_tbl); +} + +/** + * Returns the utf-8 encoding corresponding to the unicode character value + * @author from php.net, courtesy - romans@void.lv + */ +function code2utf($num) +{ + if ($num < 128) return chr($num); + if ($num < 2048) return chr(($num >> 6) + 192) . chr(($num & 63) + 128); + if ($num < 65536) return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + if ($num < 2097152) return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); + return ''; +} + +/** + * Clean HTML entities and replace   special spaces + * + * @author Andreas Goetz + * @param string $string html entity loaded string + * @return string html entity free string + */ +function html_clean($str) +{ + return trim(str_replace(chr(160), ' ', html_entity_decode_all($str))); +} + +/** + * Clean HTML entities, tags and replace   special spaces + * Output encoding is UTF-8. + * + * @author Andreas Goetz + * @param string $str html entity loaded string + * @return string html entity free string + */ +function html_clean_utf8($str) +{ +# this replacement breaks unicode enitity encoding as A0 might occor as part of any character +# $str = str_replace(chr(160), ' ', $str); + $str = html_entity_decode_all_utf8(strip_tags($str)); + return trim($str); +} + +/** + * Chance character set encoding for hierarchical array + * + * @param mixed $data string or hierarchical array to convert + * @return mixed data in target encoding + */ +function iconv_array($source_encoding, $target_encoding, $data) +{ + if (is_array($data)) + { + // recursive call for array conversion + foreach ($data as $key => $val) + { + $data[$key] = iconv_array($source_encoding, $target_encoding, $val); + } + } + else + { + // finally convert string value + $data_saved = $data; // save data for output on error page if signalled + $data = iconv($source_encoding, $target_encoding."//TRANSLIT", (string)$data); + if ($data === FALSE) + { + errorpage('Character set conversion error', "Error converting from $source_encoding to $target_encoding.
String
$data_saved"); + } + } + + return $data; +} + +/** + * Convert HTML to plain text for some common entities + */ +function html_to_text($str) +{ + // create list items + $str = preg_replace("##i", "\n-", $str); + + // de-html line breaks + $str = preg_replace('#<(br|p).*?>#i', "\n", $str); + + // avoid double line breaks + $str = preg_replace("#\n+#", "\n", $str); + + return $str; +} + +/** + * Ensure that there is only one match from a preg_replace_callback and return it + */ +function _get_only_match_from_callback($matches) { + assert(sizeof($matches) === 2); + return $matches[1]; +} + +/** + * apply chr on the only match of a preg_replace_callback + */ +function _callback_chr($matches) { + return chr(_get_only_match_from_callback($matches)); +} + +/** + * apply hexdec and chr on the only match of a preg_replace_callback + */ +function _callback_chr_hexdec($matches) { + return chr(hexdec(_get_only_match_from_callback($matches))); +} + +/** + * apply code2utf on the only match of a preg_replace_callback + */ +function _callback_code2utf($matches) { + return code2utf(_get_only_match_from_callback($matches)); +} + +/** + * apply hexdec and code2utf on the only match of a preg_replace_callback + */ +function _callback_code2utf_hexdec($matches) { + return code2utf(hexdec(_get_only_match_from_callback($matches))); +} +?> \ No newline at end of file diff --git a/videodb/core/export.core.php b/videodb/core/export.core.php new file mode 100644 index 0000000..92a638b --- /dev/null +++ b/videodb/core/export.core.php @@ -0,0 +1,81 @@ + + * @author Chinamann + * @version $Id: export.core.php,v 1.8 2013/03/15 16:42:46 andig2 Exp $ + */ + +require_once './core/genres.php'; + +function listExports($link, $omit = array('rss')) +{ + global $config; + + $exports = array('xls' => 'Microsoft Excel', + 'pdf' => 'Adobe PDF', + 'xml' => 'XML', + 'rss' => 'RSS Feed'); + + $res = array(); + foreach ($exports as $export => $title) + { + if ($config[$export] &! in_array($export, $omit)) + $res[] = array('type' => $export, 'title' => $title, 'link' => $link); + } + return($res); +} + +function exportData($WHERE) +{ + $SQL = 'SELECT '.TBL_DATA.'.*, + '.TBL_USERS.'.name AS owner, + '.TBL_MEDIATYPES.'.name AS mediatype, + '.TBL_LENT.'.who AS lentto, + CASE WHEN '.TBL_USERSEEN.'.video_id IS NULL THEN 0 ELSE 1 END AS seen + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id().' + LEFT JOIN '.TBL_LENT.' ON '.TBL_DATA.'.diskid = '.TBL_LENT.'.diskid + LEFT JOIN '.TBL_MEDIATYPES.' ON mediatype = '.TBL_MEDIATYPES.'.id '. + $WHERE; + + $result = runSQL($SQL); + + // do adultcheck + if (is_array($result)) + { + $result = array_filter($result, function($video) {return adultcheck($video["id"]);}); + } + + // genres + for($i=0; $i $text_length+3) + { + $plot = substr($plot, 0, $text_length); + $space = strrpos($plot, ' '); + if ($space) $plot = substr($plot, 0, $space); + $plot .= '...'; + } + return $plot; +} + +?> diff --git a/videodb/core/functions.core.php b/videodb/core/functions.core.php new file mode 100644 index 0000000..e1e5f2b --- /dev/null +++ b/videodb/core/functions.core.php @@ -0,0 +1,279 @@ + + * @author Andreas Gohr + * @author Chinamann + * @version $Id: functions.core.php,v 1.1 2013/04/26 15:08:30 andig2 Exp $ + */ + +if (!function_exists('errorpage')) { + function errorpage($title = 'An error occured', $body = '', $stacktrace = false) { + } +} + +/** + * Output debug info + * + * @author Andreas Goetz + * @param mixed $var Variable to dump + * @param bool $ret Return result instead of outputting + * @param bool $plain Indicate that \n separator is used + */ +function dump($var, $ret = false, $plain = false) +{ + global $argv; + if (isset($argv) && is_array($argv) && count($argv) > 0) $plain = true; + + if (is_array($var) || is_object($var)) + $var = print_r($var, 1); + else if (is_bool($var)) + $var = ($var) ? 'TRUE' : 'FALSE'; + + $var .= (is_array($argv)&&count($argv) > 0 || $plain) ? "\n" : "
\n"; + + if ($ret) return $var; + echo $var; +} + +/** + * Write variable to file + * + * @author Chinamann + * @param string $filename Filename to dump to + * @param var $var Variable to dump + */ +function file_append($filename, $var, $append = true) +{ + $log = fopen($filename, $append ? 'a' : 'w'); + fwrite($log, dump($var, true, true)); + fclose($log); +} + +/** + * Write to debug log + * + * @author Andreas Goetz + * @param mixed $var Variable to dump + */ +function dlog($var) +{ + file_append(LOG_FILE, $var); +} + +/** + * safe formoutputter + * + * @param string $name The input string + * @return string The cleaned string + */ +function formvar($name) +{ + if (!is_null($name)) + { + return htmlspecialchars($name); + } + return ""; +} + +/** + * Get high resolution time + * + * @return integer current time in microseconds + */ +function getmicrotime() +{ + list($usec, $sec) = explode(' ', microtime()); + return ((float)$usec + (float)$sec); +} + +/** + * Return mysqli db connection object + * + * @return mysqli database handle + */ +function getConnection() +{ + global $config, $dbh; + + $dbh = mysqli_connect('p:'.$config['db_server'], $config['db_user'], $config['db_password'], $config['db_database']); + if (mysqli_connect_error()) + errorpage('DB Connection Error', + "

Edit the database settings in ".CONFIG_FILE.".

+

Alternatively, consider running the installation script.

"); + + if (DB_CHARSET) + { + if (mysqli_set_charset($dbh, DB_CHARSET) === false) + errorpage('DB Link Error', 'Couldn\'t set encoding to '.DB_CHARSET); + } + + return($dbh); +} + +/** + * Escape SQL string according to current DB charset settings + * + * @param string $sql_string SQL string to escape + * @return string escaped string + */ +function escapeSQL($sql_string) +{ + global $dbh; + + if (!is_null($sql_string)) + { + if (!is_resource($dbh)) $dbh = getConnection(); + + return(mysqli_real_escape_string($dbh, $sql_string)); + } + + return (""); +} + +/** + * SQL wrapper for all Database accesses + * + * @param string $sql_string The SQL-Statement to execute + * @return mixed either the resultset as an array with hashes or the insertid + */ +function runSQL($sql_string, $verify = true) +{ + global $config, $dbh, $SQLtrace; + + if ($config['debug']) + { + dlog("\n".$_SERVER['REQUEST_URI']); + if (function_exists('xdebug_get_function_stack')) dlog(join(' -> ', array_column(xdebug_get_function_stack(), 'function'))); + dlog($sql_string); + $timestamp = getmicrotime(); + } + + if (!is_resource($dbh)) $dbh = getConnection(); + $res = mysqli_query($dbh, $sql_string); + + // mysqli_db_query returns either positive result ressource or true/false for an insert/update statement + if ($res === false) + { + $result = false; + if ($verify) + { + // report DB Problem + errorpage('Database Problem', mysqli_error($dbh)."\n
\n".$sql_string, true); + } + } + elseif ($res === true) + { + // on insert, return id of created record + $result = mysqli_insert_id($dbh); + } + else + { + // return associative result array + $result = array(); + + for ($i=0; $i $sql_string, 'time' => $timestamp); + } + +# mysqli_close($dbh); + return $result; +} + +/** + * Checks if the page is accessed from within the local net. + * + * @return bool true if localnet + */ +function localnet() +{ + global $config; + return (preg_match('/'.$config['localnet'].'/', $_SERVER['REMOTE_ADDR'])); +} + +/** + * checks if the page is accessed from within the local net. + * If not, displays a simple error page and exits + */ +function localnet_or_die() +{ + if (!localnet()) errorpage('Forbidden', 'You are not allowed to access this page'); +} + +/** + * Set connection encoding according to config file or language specification + */ +function db_set_encoding() +{ + global $config, $lang; + + // set connection character set and collation + if (DB_CHARSET) + { + $sql = "SET NAMES '".DB_CHARSET."'"; + $collation = ($lang['collation']) ? DB_CHARSET.'_'.$lang['collation'] : DB_COLLATION; + if ($collation) $sql .= " COLLATE '".$collation."'"; + + runSQL($sql); + } +} + +/** + * Redirect to new location + * + * @author Andreas Goetz + * @param string $dest Redirect destination + * @todo Read somewhere that according to RFC redirects need to specify full URI + */ +function redirect($dest) +{ + header('Location: '.$dest); + exit(); +} + +/** + * Convert an array of associative arrays (e.g. a database query result) to an associative key=>value array + * + * Sample: array_associate( 0=>(a=>1a, b=1b) 1=>(a=>2a, b=>2b), "a", "b" ) gives 1a=>1b, 2a=>2b + * + * If $value is false, the whole array is associated instead of a specific value + * + * Sample: array_associate( 0=>(a=>1a, b=1b) 1=>(a=>2a, b=>2b), "a", false ) gives 1a=>(b=>1b), 2a=>(b=>2b + * + * TODO Check if this can be replaced by PHP5.5 array_column() function + * + * @author Andreas Goetz + * @param $ary SQL result array + * @param $key key index name + * @param $value value index name + * @return array resulting associative array + */ +function array_associate($ary, $columnKey, $value = false) +{ + $res = array(); + foreach ($ary as $row) + { + $res[$row[$columnKey]] = ($value) ? $row[$value] : $row; + } + return $res; +} + +?> diff --git a/videodb/core/functions.php b/videodb/core/functions.php new file mode 100644 index 0000000..f172832 --- /dev/null +++ b/videodb/core/functions.php @@ -0,0 +1,1095 @@ + + * @author Andreas Gohr + * @author Chinamann + * @version $Id: functions.php,v 1.131 2013/04/26 15:08:30 andig2 Exp $ + */ + +// add pwd to include_path +ini_set('include_path', '.' . PATH_SEPARATOR . ini_get('include_path')); + +/** + * Load the config.sample so we have all available configuration options loaded (with sane/safe defaults) + */ +$config = []; +require_once './config.sample.php'; +/** + * Now load this installation's config and overwrite the ones that are set. + * global const CONFIG_FILE is not yet defined at this point + */ +if (!@include_once './config.inc.php') +{ + errorpage('Could not find configuration file config.inc.php', + "

Please make sure you've run the installation script.

"); +} + +if ($config['offline']) +{ + errorpage('Maintenance', 'videoDB is currently offline for maintenance. Please check back later.'); +} + + +// Uncomment the following line to enable phpIDS +// requires phpIDS to be installed in lib/IDS +// require_once './core/ids.php'; + +require_once './core/functions.core.php'; +require_once './core/constants.php'; +require_once './core/session.php'; +require_once './core/encoding.php'; +require_once './core/template.php'; +require_once './core/cache.php'; +require_once './core/compatibility.php'; +require_once './vendor/smarty/smarty/libs/SmartyBC.class.php'; + +/* --------------------------------------------------------------------*/ +// exception handling beyond this point +set_exception_handler('exception_handler'); + +// Set up some defaults +error_reporting(isset($config['debug']) && $config['debug'] ? E_ALL ^ E_NOTICE : E_ERROR + E_PARSE); +// don't pollute output with errors +ini_set('display_errors', false); +// Log stuff to error.log when in debug mode +if (isset($config['debug']) && $config['debug']) ini_set('error_log', 'error.log'); + +// Remove environment variables from global scope- ensures clean namespace +foreach (array_keys($_ENV) as $key) unset($GLOBALS[$key]); + +// Smarty setup +$smarty = new SmartyBC(); +$smarty->compile_dir = './cache/smarty'; // path to compiled templates +$smarty->cache_dir = './cache/smarty'; // path to cached html +$smarty->plugins_dir = array('./lib/smarty/custom', './vendor/smarty/smarty/libs/plugins'); +$smarty->use_sub_dirs = 0; // restrict caching to one folder +$smarty->loadFilter('output', 'trimwhitespace'); // remove whitespace from output +#$smarty->setCaching(Smarty::CACHING_LIFETIME_SAVED); +#$smarty->force_compile = true; +#$smarty->debugging = true; +if ($config['debug']) +{ + $smarty->error_reporting = E_ALL & ~E_NOTICE; // added for Smarty 3 + $smarty->force_compile = true; +} +else +{ + $smarty->error_reporting = E_ERROR; // added for Smarty 3 +} +// load config +load_config(); + +// check authentification data for multiuser +if (basename($_SERVER['PHP_SELF']) != 'login.php') auth_check(); + + +/** + * General functions + */ + +/** + * Global exception handler + */ +function exception_handler($exception) +{ + errorpage('An exception occured: ', $exception->getMessage(), true); +} + +/** + * Checks if the cache directories exist and are writable by the webserver. + * If they don't exist it tries to create them. If this fails, too a simple + * error page is displayed. + * The function checks if the MySQL PHP extensions is loaded, too. + */ +function verify_installation($return = false) +{ + global $config; + + // check MySQL extension + if (!extension_loaded('mysqli')) + { + errorpage('MySQL extension for PHP not loaded', + '

The MySQL extension for PHP is not loaded.

+

Please make sure the MySQL module for PHP is installed and enabled + in your php.ini

'); + } + + // collect all directory-specific errors + $error = ''; + + // check cache + foreach (array(CACHE => 0,CACHE.'/smarty' => 0, CACHE.'/imdb' => 1, CACHE.'/img' => 1, CACHE.'/thumbs' => 1, CACHE.'/javascript' => 0) as $dir => $hierarchical) + { + // check top-level folders + $error .= cache_create_folders($dir, $hierarchical ? (int) $config['hierarchical'] : 0); + } + + if ($return) return $error; + + if ($error) + { + errorpage('Cache directories not writable', + '

The cache directories have to be writable by the webserver!

+

Please fix the following errors:

+

'.$error.'

'); + } +} + +/** + * Load config options from config.inc.php and database and + * setup sane defaults. + * Return configuration in global $config array variable + * + * @todo Add security check if install.php is still available + * @param boolean force reload of configuration data + */ +function load_config($force_reload = false) +{ + global $config, $lang, $smarty; + // configuration cached and not outdated? + if (array_key_exists('recompile',$config)){$recompile = $config['recompile'];} else {$recompile = 0;} + if (array_key_exists('VDBuserid',$_COOKIE)){$vdbuserid = $_COOKIE['VDBuserid'];} else {$vdbuserid = null;} + if (!$force_reload && !$recompile && session_get('config') && + (session_get('config_userid') === $vdbuserid) && + (session_get('config_timestamp') == filemtime(CONFIG_FILE))) + { + // load from cache + $config = session_get('config'); + } + else + { + // check MySQL extension and cache directories + verify_installation(); + + // remember modification time + session_set('config_timestamp', filemtime(CONFIG_FILE)); + + // get config options from the database + $SELECT = 'SELECT opt,value + FROM '.TBL_CONFIG; + $result = runSQL($SELECT); + $config = array_merge($config, array_associate($result, 'opt', 'value')); + + // check if database matches the current version + if ($config['dbversion'] < DB_REQUIRED) + { + // run installer + redirect('install.php?action=upgrade'); + } + + // get user config options from the database + // does not use get_current_user_id() to allow fallback to login page after loading config + if (array_key_exists('VDBuserid',$_COOKIE) && is_numeric($user_id = $_COOKIE['VDBuserid'])) + { + // store user id in session to identify reload point for config + session_set('config_userid', $user_id); + + $SQL = 'SELECT opt, value + FROM '.TBL_USERCONFIG.' + WHERE user_id = '.$user_id; + $result = runSQL($SQL); + $config = array_merge($config, array_associate($result, 'opt', 'value')); + } + + // set some defaults + if (empty($config['language'])) $config['language'] = 'en'; + if (empty($config['template'])) $config['template'] = 'modern::compact'; + if (empty($config['filterdefault'])) $config['filterdefault'] = 'unseen'; + +// if ($config['IMDBage'] < 1) $config['IMDBage'] = 60*60*24*5; + if ($config['castcolumns'] < 1) $config['castcolumns'] = 4; + if ($config['listcolumns'] < 1) $config['listcolumns'] = 1; + if ($config['thumbAge'] < 1) $config['thumbAge'] = 60*60*24*7*3; + if ($config['shownew'] < 1) $config['shownew'] = 12; + + // prepare som options for later use + $config['languages'] = explode('::', $config['languageflags']); + + // prepare template/style + $tpl = explode('::', $config['template']); + $config['style'] = 'templates/'.$tpl[0].'/'.$tpl[1].'.css'; + $config['templatedir'] = 'templates/'.$tpl[0].'/'; +/* + // multiple style files - use template name as base (e.g. elegant_grey.css) + if (!file_exists($config['style'])) + { + // this should be an array + $config['style'] = array('templates/'.$tpl[0].'/'.$tpl[0].'.css', + 'templates/'.$tpl[0].'/'.$tpl[0].'_'.$tpl[1].'.css'); + } +*/ + // check if selected template is valid + if (!file_exists($config['style'])) + { + $config['template'] = 'elegant::grey'; + $config['templatedir'] = 'templates/elegant/'; + $config['style'] = 'templates/elegant/grey.css'; + } + + // smarty cacheid for multiuser mode + $config['cacheid'] = $tpl[0]; + + // get installed engines meta information + if (empty($config['engines'])) + { + require_once './engines/engines.php'; + $config['engines'] = engineMeta(); + + // translate config options of type engine xyz into config[engine] + foreach ($config['engines'] as $engine => $meta) + { + // convert the db engine options into associative array of engine enabled status + if ($config['engine'.$engine]) + { + $config['engine'][$engine] = $config['engine'.$engine]; + + // add meta-engine if enabled + engine_setup_meta($engine, $meta); + } + } + } + +/* + // added proxy support for $_ENV + $proxy = $config['proxy_host']; + if (empty($proxy)) + { + $env = array_change_key_case($_ENV); + $proxy = $env['http_proxy']; + } + if (!empty($proxy)) + { + $uri = parse_url($proxy); + $config['proxy_host'] = ($uri['scheme']) ? $uri['host'] : $uri['path']; + $config['proxy_port'] = ($uri['port']) ? $uri['port'] : 8080; + } +*/ + // store loaded configuration + session_set('config', $config); + } + + // setup smarty + $smarty->template_dir = array($config['templatedir'], 'templates/modern'); + $smarty->assign('template', $config['templatedir']); + + // initialize languages + $lang = array(); + + // load english language as default + require './language/en.php'; + + // override it with local language if nessesary: + if ($config['language'] != 'en') + { + $languages = explode('_', $config['language']); + $file = ''; + foreach ($languages as $language) + { + if ($file) $file .= '_'; + $file .= $language; + include './language/'.$file.'.php'; + + // convert languages to utf-8 encoding + if ($lang['encoding'] != 'utf-8') + { + $lang = iconv_array($lang['encoding'], 'utf-8', $lang); + $lang['encoding'] = 'utf-8'; + } + } + } + + // set connection character set and collation +# db_set_encoding(); +} + +/** + * Displays an errorpage and exits + * + * @param string $title The pages headline + * @param string $body An additional message + */ +function errorpage($title = 'An error occurred', $body = '', $stacktrace = false) +{ + global $lang, $savedata_for_errorpage, $config; + + if ( $config['debug'] ) + { + // this contains the message from img.php and google.php + // when guzzle signals error exception initiated from browser which has already displayed data + // the message is lost. + // writing to debug log file + if ($savedata_for_errorpage) + { + $line = strtok($body, "\n"); //get first line of exception + $current_time = date("Y-m-d")." T".date("H-i-s"); + dlog(" "); + dlog("***"); + dlog($current_time." - ".$title); + dlog($current_time." - ".$savedata_for_errorpage." - ".$line); + dlog("***"); + unset($savedata_for_errorpage); + } + } + + $encoding = ($lang['encoding']) ? $lang['encoding'] : 'iso-8859-1'; + + // stacktrace desired and available? + if ($stacktrace) + { + if (function_exists('xdebug_get_function_stack')) + { + $body .= '
'.dump(xdebug_get_function_stack(), true); + } + elseif (function_exists('debug_backtrace')) // php funtion + { + $details = debug_backtrace(); + $body .= '

***Stack Traceback - Raw***
'; + $body .= var_export($details, True); + $body .= '

***End Stack Traceback - Raw***
'; + + $body .= '

***Stack Traceback - Formated***
'; + foreach($details AS $detail) + { + foreach($detail AS $key => $var) + { + if($key == 'args') + { + foreach($var AS $key_arg => $var_arg) + { + $body .= $key_arg.': '.$var_arg.'
'; + } + } + else + { + $body .= $key.': '.$var.'
'; + } + } + } + $body .= '
***End Stack Traceback - Formated***
'; + } + } + + echo ''; + echo " + + + VideoDB - ERROR + + + + +

$title

+ $body + + "; + + exit; +} + +/** + * Verify variable is valid according to validation function + * + * @author Andreas Goetz + * @param string $var variable to validate (e.g. $id) + * @param string $validation_func validation function name (e.g. is_numeric) + */ +function validate_input(&$var, $validation_func = 'is_numeric') +{ + if (function_exists($validation_func)) + { + if (!$validation_func($var)) + { + errorpage('Forbidden', 'You are not allowed to access this page.'); + } + } +} + +/** + * Display template with Smarty + * If Smarty caching is enabled and cache id present, then cache will be used + * + * @author Andreas Goetz + * @param string $template Template file name for display + * @parem string $id Cache id + */ +function smarty_display($template, $id = null) +{ + global $smarty, $config; + + // config[cacheid] is set to the template name + $smarty->display($template, $id, $config['cacheid']); +} + + +/** + * Image handling functions + */ + +/** + * Tries to find the given image in template directory then in the default + * image directory. + * + * @param string filename of image + * @return string path to the image + */ +function img($img = 'nocover.gif') +{ + global $config; + + $result = 'images/'.$img; + if (file_exists($config['templatedir'].$result)) $result = $config['templatedir'].$result; + return ($result); +} + +/** + * Internal function for supporting actor image multi-queries + */ +function get_actor_image_from_cache($result, $name, $actorid) +{ + global $config; + + $imgurl = 'img.php?name='.urlencode($name); + if ($actorid) $imgurl .= '&actorid='.urlencode($actorid); + + // really an image? + if (isset($result['imgurl']) && preg_match('/\.(jpe?g|gif|png)$/i', $result['imgurl'], $matches)) + { + if (cache_file_exists($result['imgurl'], $cache_file, CACHE_IMG, $matches[1])) + { + return($cache_file); + } + } + elseif (isset($result['cacheage']) && $result['cacheage'] <= $config['thumbAge']) + { + // checked only recently + return(img()); + } + + return($imgurl); +} + +/** + * get Thumbnail-URL for an actor + * + * @param string name of the Actor + * @param boolean idSearchAllowed can be used to search by name only if searching by id has already been performed before + * @return string the URL to the cached image if exists or a link to img.php + */ +function getActorThumbnail($name, $actorid = 0, $idSearchAllowed = true) +{ + global $config; + + $SQL = 'SELECT name, imgurl, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(checked) AS cacheage + FROM '.TBL_ACTORS; + + // identify actor by unique actor id, of by name + $result = null; + if ($actorid && $idSearchAllowed) { + $result = runSQL($SQL." WHERE actorid='".escapeSQL($actorid)."'"); + } + if (!$actorid || ((is_array($result) && count($result) == 0)) ) { + $result = runSQL($SQL." WHERE name='".escapeSQL(html_entity_decode($name))."'"); + } + + if (!is_null($result)) + { + $imgurl = get_actor_image_from_cache($result[0], $name, $actorid); + } + else + { + $imgurl = get_actor_image_from_cache(null, $name, $actorid); + } + + return($imgurl); +} + +function cleanFilename($filename) { + return preg_replace('/[^a-z0-9-_ ]/', '_', strtolower($filename)); +} + +/** + * get Thumbnail for a movie + * + * @param string URL + * @return string the URL to the cached image if exists or a link to img.php + */ +function getThumbnail($imgurl, $name = '') +{ + // cover url not set? try local path instead + if (!$imgurl && $name) + { + // be careful with the filename here- so clean it + $localname = CACHE.'/'.CACHE_LOCAL.'/'.cleanFilename($name).'.jpg'; +// Small performance fix +// if (file_exists($localname) && filesize($localname)) return($localname); + if (@filesize($localname) > 0) return($localname); + } + + // really an image? + if (preg_match('/\.(jpe?g|gif|png)$/i', $imgurl, $matches)) + { + // local file? - keep it! + if (!preg_match('/^http/i', $imgurl)) return($imgurl); + + // file in cache? + if (cache_file_exists($imgurl, $cache_file, CACHE_IMG, $matches[1])) + { + // double-check this is really an image + if (@exif_imagetype($cache_file)) { + return($cache_file); + } + } + else + { + // add cache_ignore=1& to suppress additional cache lookup in img.php + return('img.php?url='.urlencode($imgurl)); + } + } + + // no image url given -> nopic + return(img()); +} + + +/** + * Authorizatoin and access + */ + +/** + * Perform login as selected user. Sets session cookies accordingly. + * + * @author Andreas Goetz + */ +function login_as($userid, $permanent = false) +{ + global $config; + + if (!$userid || !is_numeric($userid)) errorpage('Error', 'Invalid login attempt'); + + $CookieCode = get_user_hash($userid); + if(!$CookieCode) $CookieCode = rand(100000000, 999999999); + // permanent cookie: 1 year, otherwise session only + $validtime = ($permanent) ? time() + 60*60*24*365 : 0; + $username = get_username($userid); + + // get script folder for cookie path + $subdir = substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'],'/')) . '/'; + + setcookie('VDBuserid', $userid, $validtime, $subdir); + setcookie('VDBusername', $username, $validtime, $subdir); + setcookie('VDBpassword', $CookieCode, $validtime, $subdir); + + // make cookies available right away + $_COOKIE['VDBuserid'] = $userid; + $_COOKIE['VDBusername'] = $username; + + if ($userid != $config['guestid']) + { + runSQL('UPDATE '.TBL_USERS." SET cookiecode='$CookieCode' WHERE id=$userid"); + } +} + +/** + * Create a user specific hash value to be used as the RememberMe cookie code + */ +function get_user_hash($userid) +{ + $res = runSQL("SELECT name,passwd,email FROM ".TBL_USERS." WHERE id=$userid"); + if(count($res)) { + return md5($res[0]['name']."|".$res[0]['email']."|".substr($res[0]['passwd'],0,10)."|".$userid); + } + return false; +} +/** + * Checks if the user was authenticated and if the received auth cookie is valid. + * Function is called for every page except login.php! + * + * TODO Check if guest login shouldn't also be effective if disable public access is enabled + * Currently userid returned is 0 in that case + * + * @param string $redirect Redirect to login page if authentication check unsuccessful + */ +function auth_check($redirect = true) +{ + global $config; + + $result = true; + + // single user mode- login as admin + if (!$config['multiuser']) + { + if (empty($_COOKIE['VDBuserid'])) login_as($config['adminid']); + } + + // auth check only in multiuser mode + if ($config['multiuser'] && ( array_key_exists('VDBuserid',$_COOKIE) && ($_COOKIE['VDBuserid'] !== $config['guestid']) )) + { + $result = false; + + $referer = substr($_SERVER['PHP_SELF'], strrpos($_SERVER['PHP_SELF'],'/')+1) .'?'. $_SERVER['QUERY_STRING']; + + // already logged in? + if (array_key_exists('VDBuserid',$_COOKIE)){$userid = $_COOKIE['VDBuserid'];} else {$userid = 0;} + if (array_key_exists('VDBusername',$_COOKIE)){$user = $_COOKIE['VDBusername'];} else {$user = '';} + if (array_key_exists('VDBpassword',$_COOKIE)){$pass = $_COOKIE['VDBpassword'];} + + // auth cookies present? + if (preg_match('/[a-z]+/i', $user) && preg_match('/[0-9]+/', $pass) && is_numeric($userid)) + { + // Dummy-Query to establish mysql connection. + // VERY UGLY hack - without an established connection escapeSQL returns false in some PHP/Mysql versions + // and this leads to getting logged out all the time + runSQL('SELECT 1'); + + // This is the crucial bit, lets just test the cookiecode with SQL again. + $res = runSQL("SELECT cookiecode FROM ".TBL_USERS." WHERE name='".escapeSQL($user)."' AND id=$userid"); + $result = $res[0]['cookiecode'] == $pass; + } + + // HTTP basic authentication (for RSS feed)? + + // Hack for mod_fastcgi [muddle @ 2010-01-17]: + if (!$result && !isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['Authorization']) && !empty($_SERVER['Authorization'])) + { + list ($auth_type, $auth_cred) = explode(' ', $_SERVER['Authorization']); + if ($auth_type == 'Basic') + { + list ($auth_user, $auth_pass) = explode(":", base64_decode($auth_cred)); + $_SERVER['PHP_AUTH_USER'] = $auth_user; + $_SERVER['PHP_AUTH_PW'] = $auth_pass; + } + } + + if (!$result && isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) + { + $user = $_SERVER['PHP_AUTH_USER']; + $pass = $_SERVER['PHP_AUTH_PW']; + + // check if basic auth headers are valid + if (preg_match('/[a-z]/i', $user)) + { + // auth successful if password matches + $res = runSQL("SELECT * FROM ".TBL_USERS." WHERE name='".escapeSQL($user)."'"); + + // if user is found, set cookie to make sure he's recognized + if (count($res)) + { + $result = md5($pass) == $res[0]['passwd']; + if ($result) login_as($res[0]['id']); + } + } + } + + // autologin as guest? + if (!$result && !$config['denyguest']) + { + login_as($config['guestid']); + $result = true; + } + + // goto login page if anything was fishy + if ($redirect && !$result && !defined('AUTH_NOREDIRECT')) + { + redirect('login.php?refer='.urlencode($referer)); + } + } + + return $result; +} + + +/** + * Permission handling + */ + +/** + * Setup clean permission cache. Triggers reading database on next permission access + * + * @author Andreas Goetz + */ +function clear_permission_cache() +{ + $_SESSION['vdb']['permissions'] = null; +} + +/** + * Checks if the logged in user has write permissions for the given video + * + * @author Chinamann + * @param integer $perm USER PERMISSIONS + * @param integer $id VideoID + * @return boolean + */ +function check_videopermission($perm, $id) +{ + return check_permission($perm, get_owner_id($id)); +} + +/** + * Used to check permissions on a user for a page + * + * @author Mike Clark + * @author Chinamann + * @author Andreas Goetz + * @param integer $permission Permission to check + * @param String $destUserId UserId to access + * @return boolean True if permission exists else false + */ +function check_permission($permission, $destUserId = null) +{ + global $config; + // initialize + $permissions = 0; + + // everything's allowed in single user mode + if (!$config['multiuser']) return true; + + // not logged in - this shouldn't happen in theory + // note: get_current_user_id() is valid at this point - authcheck has already run + // note: get_current_user_id() could return 0 if guest access is disabled and user has been redirected to login.php + if (!($userid = get_current_user_id())) return false; + + // check if permissions cache is initialized + if (!array_key_exists('permissions',$_SESSION['vdb']) || !is_array($_SESSION['vdb']['permissions'])) + { + $_SESSION['vdb']['permissions'] = array(); + $_SESSION['vdb']['permissions']['to_uid'] = array(); + + // ALL permissions + $result = runSQL('SELECT permissions FROM '.TBL_USERS.' WHERE id='.$userid); + $_SESSION['vdb']['permissions']['all'] = (count($result)) ? $result[0]['permissions'] : 0; + + // user-specific permissions + $result = runSQL('SELECT * FROM '.TBL_PERMISSIONS.' WHERE from_uid='.$userid); + + // add to cache + foreach ($result as $row) + { + $_SESSION['vdb']['permissions']['to_uid'][$row['to_uid']] = $row['permissions']; + } + } + + // User level permissions + $permissions |= $_SESSION['vdb']['permissions']['all']; + + // Cross-user permissions for target user + if ($destUserId && $destUserId !== PERM_ALL) + { + if (array_key_exists($destUserId, $_SESSION['vdb']['permissions']['to_uid'])) + { + $permissions |= $_SESSION['vdb']['permissions']['to_uid'][$destUserId]; + } + + // checking for _any_ cross-user permission? e.g. used for availability of "New", "Search" + if (($destUserId == PERM_ANY) && ($permissions & $permission) == 0) + { + foreach($_SESSION['vdb']['permissions']['to_uid'] as $user_perm) + { + $permissions |= $user_perm & $permission; + } + } + } + + // check permission bits + return (($permissions & $permission) == $permission); +} + +/** + * Check permissions on a user for a page and display error message on failure + * + * @author unknown + * @author Chinamann + * @param integer $permission Permission to check (admin,write,writeall) + * @param String $destUserId UserId to access + */ +function permission_or_die($permission, $destUserId = false) +{ + if (!check_permission($permission, $destUserId)) + { + errorpage("Access denied",'You don\'t have enough permissions to access this + page try to login first.'); + } +} + +/** + * Function to get the owner name from videodata table + * + * @author Mike Clark + * @param integer $id videodata id + * @param boolean $diskid is the given ID a disk ID instead of videoID? + * @return string Returns the owner of the given Video or Disk + */ +function get_owner($id, $diskid = false) +{ + $SELECT = "SELECT ".TBL_USERS.".name AS owner + FROM ".TBL_DATA.", ".TBL_USERS." + WHERE ".TBL_USERS.".id = ".TBL_DATA.".owner_id AND ".TBL_DATA."."; + + $SELECT .= ($diskid) ? "diskid = '$id'" : "id = $id"; + + $result = runSQL($SELECT); + return $result[0]['owner']; +} + +/** + * Function to get the owner id from videodata table + * + * @author Chinamann + * @param integer $id videodata id + * @param boolean $diskid is the given ID a disk ID instead of videoID? + * @return string Returns the owner of the given Video or Disk +*/ +function get_owner_id($id, $diskid = false) +{ + $SELECT = "SELECT owner_id + FROM ".TBL_DATA." + WHERE "; + + $SELECT .= ($diskid) ? "diskid = '$id'" : "id = $id"; + + $result = runSQL($SELECT); + if (isset($result[0]['owner_id'])) + { + return $result[0]['owner_id']; + } + return; +} + +/** + * Get list of adult genre ids + * + * @return array Array of adult genre ids + */ +function get_adult_genres() +{ + global $config; + + $adultgenres = array(); + foreach(explode('::', $config['adultgenres']) as $ag) + { + if (empty($ag)) continue; + $adultgenres[] = $ag; + } + + return $adultgenres; +} + +/** + * Checks if a movie is not prohibited because of adults content + * + * @param integer $id video id + * @return boolean Returns true if access is granted + */ +function adultcheck($id) +{ + global $config; + + if (check_permission(PERM_ADULT) || empty($config['adultgenres'])) + { + // no multiuser or adult genres set or we actually do have the + // permissions - whatever let's watch some pr0n ;-) + return true; + } + + $adultgenres = 'genre_id='.join(' OR genre_id=', get_adult_genres()); + $select = 'SELECT video_id + FROM '.TBL_VIDEOGENRE.' + WHERE video_id = '.$id.' + AND ('.$adultgenres.')'; + $result = runSQL($select); + + return(empty($result[0]['video_id'])); +} + +/** + * Checks if the given movie was already seen by the logged in user. If no + * user is logged in the $seen value is returned + * + * Gets username from cookie + * + * @author Andreas Goetz + * @param integer $id video id + * @param boolean $seen seen + * @return boolean True if seen + * + * @deprecated + */ +function get_userseen($id) +{ + $user_id= $_COOKIE['VDBuserid']; + + if (empty($user_id)) + errorpage('Security Error', "User id cookie was unexpectedly not set. Please report this problem to the developers."); + + $SELECT = 'SELECT video_id + FROM '.TBL_USERSEEN.', '.TBL_USERS.' + WHERE '.TBL_USERSEEN.'.video_id='.$id." AND + ".TBL_USERSEEN.".user_id = ".$user_id; + $result = runSQL($SELECT); + + $result = (count($result) > 0) ? 1 : 0; + return($result); +} + +/** + * Sets the status in userseen accordingly to the given seen value + * + * Gets username from cookie + * + * @author Andreas Goetz + * @param integer $id video id + * @param boolean $seen seen + */ +function set_userseen($id, $seen) +{ + $user_id = get_current_user_id(); + + if (empty($user_id)) errorpage('Security Error', + "User id cookie was unexpectedly not set. Please report this problem to the developers."); + + $SQL = ($seen) ? "REPLACE INTO ".TBL_USERSEEN." SET user_id=".$user_id.", video_id='".$id."'" + : "DELETE FROM ".TBL_USERSEEN." WHERE user_id=".$user_id." AND video_id='".$id."'"; + runSQL($SQL); + +/* + // future code when userseen contains more user-specific data + + // record already exists? + $SELECT = "SELECT seen, user_id FROM video_user, users ". + "WHERE video_user.video_id=".$id." AND video_user.user_id = user.id AND users.user='".$_COOKIE['VDBusername']."'"; + $result = runSQL($SELECT); + + if (empty($result[0]['id'])) { + $SELECT = "SELECT id FROM users ". + "WHERE user='".$_COOKIE['VDBusername']."'"; + $result = runSQL($SELECT); + + $SQL = "INSERT INTO video_user SET user_id='".$result[0]['id']."', id='".$id."', seen='".$seen."'"; + } + else { + $SQL = "UPDATE video_user SET seen='".$seen."' ". + "WHERE user_id='".$result[0]['id']."', id='".$id."', "; + } + runSQL($SQL); +*/ +} + +/** + * Return id of the currently logged in user. + * The value returned is safe to use in SQL statements. + * + * @author Andreas Goetz + * @result integer user id + */ +function get_current_user_id() +{ + // make sure userid is numeric- preventing SQL injection attacs + if (array_key_exists('VDBuserid',$_COOKIE) && !is_numeric($userid = $_COOKIE['VDBuserid'])) $userid = 0; +# errorpage('Security Error', 'Invalid user id in cookie: '.$userid, true); + return $userid; +} + +/** + * Return UserId to a given UserName + * + * @author Chinamann + * @param string $userName user name + * @result integer user id + */ +function get_userid($userName) +{ + $SELECT = "SELECT id + FROM ".TBL_USERS." + WHERE name='".escapeSQL($userName)."'"; + $result = runSQL($SELECT); + return $result[0]['id']; +} + +/** + * Return UserName to a given UserId + * + * @author Chinamann + * @param integer $userId user id + * @param string user name + */ +function get_username($userId) +{ + $SELECT = "SELECT name + FROM ".TBL_USERS." + WHERE id=".$userId; + $result = runSQL($SELECT); + return $result[0]['name']; +} + +/** + * A few functions for input filtering + */ + +/** + * @param string $name + * @return string[] array of strings + */ +function req_array ($name) { + return req_raw($name, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES | FILTER_REQUIRE_ARRAY); +} + +/** + * @param string $name + * @return string + */ +function req_email ($name) { + return req_raw($name, FILTER_SANITIZE_EMAIL); +} + +/** + * @param string $name + * @return string + */ +function req_string ($name) { + return req_raw($name, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES | FILTER_REQUIRE_SCALAR); +} + +/** + * @param string $name + * @return float + */ +function req_float ($name) { + return req_raw($name, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND | FILTER_REQUIRE_SCALAR); +} + +/** + * @param string $name + * @return int + */ +function req_int ($name) { + return req_raw($name, FILTER_SANITIZE_NUMBER_INT); +} + +/** + * @param string $name + * @return string + */ +function req_url ($name) { + return req_raw($name, FILTER_SANITIZE_URL); +} + +/** + * @param string $name + * @return mixed type depends on $filter, returns false on failure, null is not set. + */ +function req_raw ($name, $filter = FILTER_UNSAFE_RAW, $options = FILTER_REQUIRE_SCALAR) { + $value = filter_input(INPUT_POST, $name, $filter, $options); + if (is_null($value)) { + $value = filter_input(INPUT_GET, $name, $filter, $options); + } + return $value; +} + diff --git a/videodb/core/genres.php b/videodb/core/genres.php new file mode 100644 index 0000000..be6808c --- /dev/null +++ b/videodb/core/genres.php @@ -0,0 +1,137 @@ + + * @author Andreas Götz + * @version $Id: genres.php,v 1.14 2008/01/29 10:59:52 veal Exp $ + */ + +/** + * Map movie genres to versions existing in db + * + * @author Andreas Goetz + * @param array $genres A list of input genres + * @return array The mapped genres result array + */ +function mapGenres($genres) +{ + global $dbgenres; + + // load genres from DB once + if (empty($dbgenres)) + { + $dbgenres = array(); + foreach (runSQL('SELECT id, name FROM '.TBL_GENRES.' ORDER BY name') as $row) + { + $dbgenres[] = $row['name']; + } + } + + foreach ($genres as $in_genre) + { + $mapped_genre = ''; + $mapped_percent = 0; + + $in_genre = trim($in_genre); + + // direct match? + if (in_array($in_genre, $dbgenres)) + { + $gens[] = $in_genre; + } + else + { + // possible approximate match + foreach ($dbgenres as $genre_name) + { + // calculate similiarity and find best match + $chars = similar_text($in_genre, $genre_name, $percent); + if ($percent >= 50) + { + if (stristr($in_genre, $genre_name)) $percent += 10; + if ($percent > $mapped_percent) + { + $mapped_genre = $genre_name; + $mapped_percent = $percent; + } + } + } + if ($mapped_genre) $gens[] = $mapped_genre; + } + } + + return array_unique($gens); +} + +/** + * returns the genreID for a given name from the 'genres' table + * + * @todo check if this can be moved to edit.php + * @param string $name the name of the genre + * @return integer $genre the genre id + */ +function getGenreId($name) +{ + $name = escapeSQL($name); + $result = runSQL("SELECT id FROM ".TBL_GENRES." WHERE LCASE(name) = LCASE('".$name."')"); + return $result[0]['id']; +} + +/** + * retrieve genre ids/ genres of a video + * + * @param integer $id ID of the video + * @param boolean $names include genre names in output + * @return array genre id's OR + * @return array associative array of genre ids and names + */ +function getItemGenres($id, $names = false) +{ + $genres = array(); + if (empty($id)) return $genres; + + $SELECT = 'SELECT genres.id, genres.name + FROM '.TBL_GENRES.' AS genres, '.TBL_VIDEOGENRE.' AS videogenre + WHERE genres.id = videogenre.genre_id + AND videogenre.video_id = '.$id; + $result = runSQL($SELECT); + + if ($names) return $result; + + foreach ($result as $row) + { + $genres[] = $row['id']; + } + + return $genres; +} + +/** + * save genres for a movie + * + * @todo check if this can be moved to edit.php + * @param integer $id ID of the video + * @param array $genres genre IDs + */ +function setItemGenres($id, $genres) +{ + // Delete all genres for id + runSQL('DELETE FROM '.TBL_VIDEOGENRE.' WHERE video_id = '.$id); + + if (count($genres)) + { + $genres = array_unique($genres); + + foreach($genres as $genre) + { + runSQL('INSERT INTO '.TBL_VIDEOGENRE.' SET video_id = '.$id.', genre_id = '.$genre); + } + } +} + +?> diff --git a/videodb/core/httpcache.php b/videodb/core/httpcache.php new file mode 100644 index 0000000..3006fea --- /dev/null +++ b/videodb/core/httpcache.php @@ -0,0 +1,111 @@ + + * @version $Id: httpcache.php,v 1.5 2010/11/05 10:38:47 andig2 Exp $ + */ + +/** + * Start output buffering + */ +function httpCacheCaptureStart() +{ + ob_start(); +} + +/** + * Stop output buffering + * + * @param string MD5 hash of content + */ +function httpCacheCaptureEnd() +{ + $content = ob_get_contents(); + ob_end_clean(); + + return $content; +} + +/** + * Get last modified data for given etag + * Checks session for known etag and timestamp + */ +function httpCacheCheckTag($template, $etag) +{ + if ($etag != $_SESSION['vdb'][$template]['etag']) + { + $lastmod = time(); + $_SESSION['vdb'][$template]['etag'] = $etag; + $_SESSION['vdb'][$template]['time'] = $lastmod; + } + else + { + $lastmod = $_SESSION['vdb'][$template]['time']; + } + return($lastmod); +} + +/** + * Output 304 Not Modified header + * Require browser to re-check on next request + */ +function httpCacheHeaders($etag, $expires) +{ + header(php_sapi_name() == 'cgi' ? 'Status: 304 Not Modified' : 'HTTP/1.x 304 Not Modified'); + header("ETag: {$etag}"); + header("Cache-Control: private, max-age={$expires}, pre-check=0, post-check=0"); + header("Content-Length: 0"); + + header("Content-Type: !invalid"); + exit(); +} + +/** + * Check if output was modified since last request + * If unmodifed, output 304 Not Modified header + * Otherwise add additional ETag and LastModified headers + */ +function httpCacheOutput($template, $content) +{ + $etag = '"'.md5($content).'"'; + + // check if 'sending' is necessary (using cache functions) + $sendbody = true; + $expires = 0; + + $lastmod = httpCacheCheckTag($template, $etag); + + // check 'If-Modified-Since' header + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && gmdate('D, d M Y H:i:s', $lastmod)." GMT" == trim($_SERVER['HTTP_IF_MODIFIED_SINCE'])) + { + httpCacheHeaders($etag, $expires); + } + + // check 'If-None-Match' header (ETag) + if ($sendbody && isset($_SERVER['HTTP_IF_NONE_MATCH'])) + { + $inm = explode(',', $_SERVER['HTTP_IF_NONE_MATCH']); + foreach ($inm as $i) + { + if (trim($i) != $etag) continue; + httpCacheHeaders($etag, $expires); + } + } + + // send with caching headers (enable cache for one day) + $exp_gmt = gmdate('D, d M Y H:i:s', time() + $expires).' GMT'; + $mod_gmt = gmdate('D, d M Y H:i:s', $lastmod).' GMT'; + header("Expires: {$exp_gmt}"); + header("Last-Modified: {$mod_gmt}"); + header("Cache-Control: private, max-age={$expires}, pre-check=0, post-check=0"); + header("Pragma: !invalid"); + header("ETag: {$etag}"); + + echo $content; +} + +?> \ No newline at end of file diff --git a/videodb/core/httpclient.php b/videodb/core/httpclient.php new file mode 100644 index 0000000..5774ad7 --- /dev/null +++ b/videodb/core/httpclient.php @@ -0,0 +1,246 @@ + + * @author Andreas Gohr + * @author Chinamann + * @version $Id: httpclient.php,v 1.21 2013/04/26 15:09:35 andig2 Exp $ + */ + +require_once 'core/cache.php'; +require_once 'vendor/autoload.php'; + +use GuzzleHttp\Psr7 as Psr7; + +/** + * Reads a saved HTTP response from a cachefile. + * If caching is globally disabled ($config['IMDBage'] <= 0), file is not loaded. + * + * @param string $url URL of the cached response + * @return mixed HTTP Response, false on errors + */ +function getHTTPcache($url) +{ + global $config; + + if (@$config['cache_pruning']) + { + $cache_file = cache_get_filename($url, CACHE_HTML); + cache_prune_folder(dirname($cache_file).'/', $config['IMDBage']); + } + + return cache_get($url, CACHE_HTML, $config['IMDBage'], true); +} + +/** + * Saves a HTTP resonse to a cachefile + * If caching is globally disabled ($config['IMDBage'] <= 0), file is not saved. + * + * @param string $url URL of the response + * @param mixed $resp HTTP Response + */ +function putHTTPcache($url, $data) +{ + global $config; + + // for debugging purposes track there the request originated + $data['source'] = $url; + + cache_put($url, $data, CACHE_HTML, $config['IMDBage'], true); +} + +/** + * Extract source encoding from HTML code or HTTP header otherwise + */ +function get_response_encoding($response) +{ + $header = $encoding = null; + + // response array from cache + if (is_array($response)) { + if (isset($response['header']['Content-Type'])) { + $header = $response['header']['Content-Type']; + } + } + else { + // Psr response + $header = $response->getHeader('Content-Type'); + } + + if ($header) { + $parsed = Psr7\parse_header($header); + if (array_key_exists('charset', $parsed[0])) + { + $encoding = strtolower($parsed[0]['charset']); + } + } + + if (!$encoding) + { + $encoding = 'iso-8859-1'; + } + + return $encoding; +} + +/** + * HTTP Client + * + * Returns the raw data from the given URL, uses proxy when configured + * and follows redirects + * + * @author Andreas Goetz + * @param string $url URL to fetch + * @param bool $cache use caching? defaults to false + * @param string $post POST data, if nonempty POST is used instead of GET + * @param integer $timeout Timeout in seconds defaults to 15 + * @return mixed HTTP response + */ +function httpClient($url, $cache = false, $para = null, $reload = false) +{ + static $referer = 'https://www.imdb.com/search/'; + global $config; + $client = new GuzzleHttp\Client(); + + $requestConfig = []; + $headers = ''; // additional HTTP headers, used for post data + + if (!empty($para) && array_key_exists('cookies', $para) && $para['cookies']) + { + $jar = new GuzzleHttp\Cookie\CookieJar(); + $requestConfig += ['cookies' => $jar]; + } + + $method = 'GET'; + + $post = isset($para['post']) ? $para['post'] : ''; + if ($post) + { + $method = 'POST'; + $requestConfig += ['headers' => ['Content-Type' => 'application/x-www-form-urlencoded']]; + $requestConfig += ['body' => $post]; + } + + // get data from cache? + if ($cache &! $reload) + { + $resp = getHTTPcache($url.$post); + if ($resp !== false) + { + $resp['cached'] = true; + return $resp; + } + } + + // proxy setup + if (!empty($config['proxy_host']) && !$para['no_proxy']) + { + $server = $config['proxy_host']; + if (!($port = @$config['proxy_port'])) + { + $port = 8080; + } + $requestConfig += ['proxy' => sprintf('tcp://%s:%d', $server, $port)]; + } + + // additional request headers + if (!empty($para) && array_key_exists('header', $para) && $para['header']) + { + $requestConfig += ['headers' => $para['header']]; + } + + if (empty($requestConfig['headers']['Accept'])) $requestConfig['headers']['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; + if (empty($requestConfig['headers']['Accept-Language'])) $requestConfig['headers']['Accept-Language'] = ((isset($config['acclangbrowser']) && $config['acclangbrowser']) ? filter_input(INPUT_SERVER, 'HTTP_ACCEPT_LANGUAGE') : 'en-US;q=0.7,en;q=0.3'); + if (empty($requestConfig['headers']['DNT'])) $requestConfig['headers']['DNT'] = '1'; + if (empty($requestConfig['headers']['User-Agent'])) $requestConfig['headers']['User-Agent'] = filter_input(INPUT_SERVER, 'HTTP_USER_AGENT'); + if (empty($requestConfig['headers']['Referer'])) $requestConfig['headers']['Referer'] = $referer; + + $resp = $client->request($method, $url, $requestConfig); + + $response['error'] = ''; + $response['url'] = $url; + $response['success'] = false; + $response['encoding'] = get_response_encoding($resp); + $response['header'] = $resp->getHeaders(); + $response['data'] = (string) $resp->getBody(); + + if ($config['debug']) echoHeaders($response['header'])."

"; + if ($config['debug']) echo "data:
".htmlspecialchars($response['data'])."

"; + + + // log response + if ($config['httpclientlog']) + { + $log = fopen('httpClient.log', 'a'); + fwrite($log, headers_to_string($response['header'])); + fclose($log); + } + + // verify status code + if ($resp->getStatusCode() != 200) + { + $response['error'] = 'Server returned wrong status: ' . $resp->getStatusCode(); + $response['error'] .= " Reason: " . $resp->getReasonPhrase(); + return $response; + } + + $response['success'] = true; + // @todo i'm not sure on the side-effects of setting the previous requested URL as referer + // for the next, so disabled for now. might be something to investigate... + //$referer = $url; + + // commit successful request to cache + if ($cache) + { + putHTTPcache($url.$post, $response); + } + + return $response; +} + + +/** + * Print all header info using echo + * @param response Object homepage Psr7\Response + */ +function echoHeaders($headers) +{ + foreach ($headers as $name => $values) { + echo $name . ': ' . implode(', ', $values) . "
"; + } +} + +function headers_to_string($headers) +{ + $result = ''; + foreach ($headers as $name => $values) { + $result .= $name . ': ' . implode(', ', $values) . "\n"; + } + + return $result; +} + +/** + * Downloads an URL to the given local file + * + * @param string $url URL to download + * @param string $local Full path to save to + * @return bool true on succes else false + */ +function download($url, $local) +{ + $resp = httpClient($url); + + if (!$resp['success']) + { + return false; + } + + return(@file_put_contents($local, $resp['data']) !== false); +} + +?> \ No newline at end of file diff --git a/videodb/core/ids.php b/videodb/core/ids.php new file mode 100644 index 0000000..fb99c8a --- /dev/null +++ b/videodb/core/ids.php @@ -0,0 +1,68 @@ + + * @version $Id: ids.php,v 1.2 2010/02/01 22:38:42 andig2 Exp $ + */ + +// set the include path properly for PHPIDS +set_include_path( + get_include_path() + . PATH_SEPARATOR + . './lib/' +); + +if (!session_id()) { + session_start(); +} + +require_once 'IDS/Init.php'; + +try { + $init = IDS_Init::init(dirname(__FILE__) . '/../lib/IDS/Config/Config.ini'); + $init->config['General']['base_path'] = dirname(__FILE__) . '/../lib/IDS/'; + $init->config['General']['use_base_path'] = true; + $init->config['Caching']['caching'] = 'file'; + + $request = array( + 'GET' => $_GET, + 'POST' => $_POST, + 'COOKIE' => $_COOKIE + ); + + $ids = new IDS_Monitor($request, $init); + + $result = $ids->run(); + if (!$result->isEmpty() && $result->getImpact() > 50) + { + require_once 'IDS/Log/Database.php'; + require_once 'IDS/Log/Composite.php'; + $compositeLog = new IDS_Log_Composite(); + $compositeLog->addLogger( + IDS_Log_Database::getInstance($init) + ); + $compositeLog->execute($result); + + $hta = @file_get_contents('.htaccess'); + if (preg_match('/(.+?)^(allow from all.*)/ms', $hta, $m)) + { + $addr = $_SERVER['REMOTE_ADDR']; + + // block whole subnet + $addr = implode('.', array_slice(explode('.', $addr), 0, 3)); + + $hta = $m[1] . 'deny from '.$addr."\n" . $m[2]; + @file_put_contents('.htaccess', $hta); + } + + header("HTTP/1.0 403 Forbidden"); + die('Your IP has been blocked.
To find out why visit http://sourceforge.net/mailarchive/forum.php?forum_name=videodb-devel'); + } +} catch (Exception $e) { + //this shouldn't happen and if it does you don't want the notification public. +} + +?> diff --git a/videodb/core/install.core.php b/videodb/core/install.core.php new file mode 100644 index 0000000..3aa411c --- /dev/null +++ b/videodb/core/install.core.php @@ -0,0 +1,326 @@ + + * @version $Id: install.core.php,v 1.11 2010/02/18 15:15:37 andig2 Exp $ + */ + + +/** + * Build formatted message string for html output + * + * @param string $msg Message to print + * @param string $color Color code for

tag + * @param boolean $print If true, output directly, else append to message string + */ +function showmessage($msg, $color, $print = false) +{ + global $message; + + if ($msg) $msg = "$msg

\n"; + if ($print) + { + // print directly + echo $msg; + } + else + { + // return + $message .= $msg; + } +} + +/** + * Prepare formatted error message for output + * + * @param string $msg Message to print + * @param boolean $print If true, output directly, else append to message string + */ +function error($msg, $print = false) +{ + showmessage($msg, 'red', $print); +} + +/** + * Prepare formatted warning message for output + * + * @param string $msg Message to print + * @param boolean $print If true, output directly, else append to message string + */ +function warn($msg, $print = false) +{ + showmessage($msg, 'orange', $print); +} + +/** + * Prepare formatted info message for output + * + * @param string $msg Message to print + * @param boolean $print If true, output directly, else append to message string + */ +function info($msg, $print = false) +{ + showmessage($msg, 'green', $print); +} + + +/** + * Recursively delete files from directory + * used for Smarty cache cleanup during installation + * + * @param string $dir Directory name + * @param boolean $recursive Recurse into subfolders + */ +function delete_files($dir, $recursive = false) +{ + if ($dh = @opendir($dir)) + { + while (($file = readdir($dh)) !== false) + { + // next if . or .. + if (preg_match("/^\.\.?$/", $file)) continue; + + // recursion? + if (is_dir("$dir/$file")) + { + if ($recursive) delete_files("$dir/$file", $recursive); + } + else + { + // delete file + unlink("$dir/$file"); + } + } + closedir($dh); + + if ($recursive) rmdir($dir); + } +} + +/** + * Parse config file and replace settings according + * to associate array parameter + * + * @param array new parameter values + */ +function parse_config($vars) +{ + $raw = null; + + if ($file = @file_get_contents(CONFIG_FILE)) { + $raw = explode("\n", $file); + + for ($i = 0; $i < count($raw); $i++) + { + foreach ($vars as $name => $val) + { + if (preg_match("/^(.*?'$name'.*?=\s*)(.*?)(\s*;.*?)$/", $raw[$i], $matches)) + { + # quoted? + if (preg_match("/^[\"'].*[\"']$/", $matches[2])) $val = "'$val'"; + $matches[2] = $val; + $raw[$i] = join('', array_slice($matches,1)); + } + } + } + } + + // fallback if config file is empty or invalid + if (count($raw) < 4) + { + $raw = array(' $val) + { + $line = "\$config['$name'] = "; + $line .= (is_numeric($val)) ? "$val;" : "'$val';"; + $raw[] = $line; + } + $raw[] = '?>'; + } + + return join("\n", $raw); +} + +/** + * Parse database upgrade SQL file and + * build associate array of upgrade sql steps per version + * + * @return array associative array of upgrade steps + */ +function parse_upgrades($upgrade_file) +{ + $cfg = file_get_contents($upgrade_file); + $raw = preg_split('/# changes in DB version /i', $cfg); + + // loop through list of db upgrades split by comments + foreach ($raw as $str) + { + // upgrade version comment found? + if (preg_match('/(\d+)\s*#\s*(.+)/s', $str, $m)) + { + $key = $m[1]; + $str = $m[2]; + } + else + { + if (empty($upgrades['3'])) + { + // this is the first supported version + $key = 3; + } + else + { + // unexpected first db upgrade version found + trigger_error('Could not parse '.$upgrade_file.', please fix!', E_USER_ERROR); + } + } + $upgrades["$key"] = $str; + } + return $upgrades; +} + +/** + * Callback function for adding prefix to table name in FROM clauses- extended to include sub queries + * + * $match[2] is table + */ +function sql_add_prefix($match) +{ + global $db_prefix; + + $match[2] = preg_replace('/(\'?\w+\'?)(\s*\w*)(,?)/', "`$db_prefix$1`$2$3", $match[2]); + return join(array_slice($match, 1, 3)); +} + +/** + * Prefix table names with table name prefix + * This will only work for simple queries with known structure (createtables.sql, updatedb.sql) + * + * @param string SQL command + * @return string SQL command with new table names + */ +function prefix_query($query) +{ + global $db_prefix; + + $query = preg_replace('/((CREATE|ALTER)\s+TABLE\s+(IF\s+NOT\s+EXISTS\s+)?)`?(\w+)`?/', "$1`$db_prefix$4`", $query); + $query = preg_replace('/((INSERT(\s+IGNORE)?|REPLACE)\s+INTO\s+)`?(\w+)`?/', "$1`$db_prefix$4`", $query); + + $query = preg_replace('/(DROP\s+TABLE\s+(IF\s+EXISTS\s+)?)`?(\w+)`?(;?)/', "$1`$db_prefix$3`$4", $query); + // changed to allow only replace if sql update to row + $query = preg_replace('/(UPDATE\s+)`?(\w+)`?(\s+SET\s+)`?/', "$1`$db_prefix$2`$3", $query); + + // FROM matches at beginning of string or subquery opened by left bracket + $query = preg_replace_callback( "/(\s+FROM\s+)(.*?)((\s+WHERE|ORDER|LEFT|RIGHT|OUTER|JOIN)|\)|$)/msi", 'sql_add_prefix', $query); + + return $query; +} + +/** + * Run multiple comma-separated queries + * + * @todo Split SQL queries more cleverly (currently not needed) + * + * @param string SQL commands + * @param ressource database handle + * @return mixed result array or false + */ +function runSQL($sql, $dbh, $verify = false) +{ + $result = true; + foreach (explode(';', $sql) as $query) + { + $query = trim($query); + if (empty($query)) continue; + + $query = prefix_query($query); + $result = mysqli_query($dbh, $query); + + // error running SQL? + if ($result === false) + { + $sql = $query; + break; + } + } + + // error running SQL? + if ($result === false) + { + if ($verify) + { + error("Error in SQL statement:
". + "$sql
".mysqli_error($dbh)); + } + } + + // result set returned? + elseif ($result !== true) + { + $res = array(); + + for ($i=0; $i < mysqli_num_rows($result); $i++) + { + $res[] = mysqli_fetch_assoc($result); + } + mysqli_free_result($result); + + return $res; + } + + return $result; +} + +/** + * Run all upgrade scripts passed as array step by step + * + * @param array $upgrade_steps Associative array of upgrade sql steps + */ +function db_upgrade($upgrade_steps) +{ + global $dbh, $version; + global $step; + + foreach ($upgrade_steps as $ver => $sql) + { + #info("Upgrading to database version: $ver"); + $sql = preg_replace('/#.*\n/m','',$sql); + + if (runSQL($sql, $dbh) === false) + { + error('Error upgrading database, try full install instead of upgrade:
'.mysqli_error($dbh). + '

'.$sql.'
'); + return false; + } + + // perform additional upgrade steps + $upgrade_file = "./install/upgrade_v$ver.php"; + if (file_exists($upgrade_file)) + { + $result = include_once($upgrade_file); + if (!$result) return($false); + } + + // add DB version information- this will make the separate update statement in upgrade.sql obsolete + runSQL("REPLACE INTO config (opt,value) VALUES ('dbversion', ".$ver.");", $dbh); +# runSQL("update config set value=25 where opt='dbversion'"); + + $version = $ver; + } + + // perform generic upgrade validation + $upgrade_file = "./install/upgrade.php"; + if (file_exists($upgrade_file)) + { + $result = include_once($upgrade_file); + if (!$result) return($false); + } + + return $version; +} + +?> \ No newline at end of file diff --git a/videodb/core/output.php b/videodb/core/output.php new file mode 100644 index 0000000..6b20cd6 --- /dev/null +++ b/videodb/core/output.php @@ -0,0 +1,278 @@ + + * @author Andreas Goetz + * @version $Id: output.php,v 1.29 2013/04/19 07:55:58 andig2 Exp $ + */ + +require_once './core/functions.php'; + +/** + * Return list of valid genres from db + */ +function getGenres() +{ + $SELECT = 'SELECT id, name + FROM '.TBL_GENRES.' + ORDER BY name'; + $result = runSQL($SELECT); + + return $result; +} + +/** + * Display genre checkboxes + * + * @param array $selected selected genre IDs + * @return string HTML for genre checkboxes + */ +function out_genres($selected) +{ + global $config; + + $result = getGenres(); + $out = ''; + + // get list of adult genres + $adultgenres = array(); + if ($config['multiuser'] && !check_permission(PERM_ADULT)) + { + $adultgenres = get_adult_genres(); + } + + $row = 0; + foreach ($result as $res) + { + // don't show adult genres if no permissions + if (in_array($res['id'], $adultgenres)) continue; + + $out .= ''; + if ((++$row % 5) == 0) + { + $out .= ''; + } + } + $out .= '
'; + $out .= ''.$res['name'].''; + $out .= '
'; + + return $out; +} + +/** + * Generate genres array for use with genre checkboxes + * + * @param array $selected selected genre IDs + * @return string HTML for genre checkboxes + */ +function out_genres2($item_genres = null) +{ + global $config; + // get detailed genres + $all_genres = getGenres(); + $adultgenres = array(); + if ($config['multiuser'] && !check_permission(PERM_ADULT)) { + $adultgenres = get_adult_genres(); + } + + $genres = array(); + foreach ($all_genres as $gen) { + // don't show adult genres if no permissions + if (in_array($gen['id'], $adultgenres)) continue; + + // selected? + if ($item_genres) $gen['checked'] = (@in_array($gen['id'], $item_genres)) ? 1 : 0; + + $genres[] = $gen; + } + + return($genres); +} + +/** + * Display selectbox with available Mediatypes + * + * @todo is this still used? can it be replaced by template code? + * @author + * @return string HTML of selectbox + */ +function out_mediatypes($prefix = null) +{ + global $config; + + // select mediatypes + $SELECT = 'SELECT id, name + FROM '.TBL_MEDIATYPES.' + ORDER BY name'; + $result = runSQL($SELECT); + + // build associative array + # array('0' => '') + + $mediatypes = is_array($prefix) ? $prefix : array(); + $mediatypes = $mediatypes + array_associate($result, 'id', 'name'); + + return $mediatypes; +} + +/** + * All available language flags for config screen + * + * @param array $flags selected flags + * @return string HTML of Languageflags + */ +function out_languageflags($flags) +{ + global $config; + + $out = ''; + $count = 1; + + if (($dh = @opendir('./'.$config['templatedir'].'images/flags')) || ($dh = opendir('./images/flags'))) + { + while (($file = readdir($dh)) !== false) + { + if (preg_match("/(.*)\.gif$/", $file, $matches)) + { + $CHECK= (in_array($matches[1], $flags)) ? 'checked="checked"' : ''; + $out .= ''; + $out .= ' '; + if ($count++%4 == 0) $out.='
'; + } + } + closedir($dh); + } + return $out; +} + +/** + * List of owners names/ids with valid permissions for use in edit/index/search templates + * + * @author + * @author Chinamann + * @param string $prefix Predefined additional Array entries + * @param string $permission Honor permissions for selectbox + * @return array Array with keys=ownernames and values=ownerids + */ +function out_owners($prefix = null, $permission = false, $keyIsId = false) +{ + global $config; + + // all permissions available if admin + if (check_permission(PERM_ADMIN)) $permission = false; + + // hide guest if he/she can't login + $WHERES = ($config['denyguest']) ? " AND B.id != ".$config['guestid'] : ''; + + // select user ids- if permissions are required and no all access given, this is done against xrefs + if ($permission && !check_permission($permission)) + { + // xref permissions + // TODO use cached permission table instead + $SELECT = 'SELECT DISTINCT(B.name) AS name, B.id + FROM '.TBL_PERMISSIONS.' A, '.TBL_USERS.' B + WHERE A.to_uid = B.id + AND A.from_uid = '.get_current_user_id().' + AND (A.permissions & '.$permission.') = '.$permission.$WHERES.' + ORDER BY name'; + } + else + { + // all users +/- guest + $SELECT = 'SELECT B.id, B.name + FROM '.TBL_USERS.' B + WHERE 1=1 '.$WHERES.' + ORDER BY B.name'; + } + $result = runSQL($SELECT); + + $key = ($keyIsId) ? 'id' : 'name'; + + // build associative array + $owners = is_array($prefix) ? $prefix : array(); + $owners = $owners + array_unique(array_associate($result, $key, 'name')); + + return $owners; +} + +/** + * MySQL-compatible list of owner ids with required access permission + * + * @author Andreas Goetz + */ +function get_owner_ids($permission) +{ + foreach($_SESSION['vdb']['permissions']['to_uid'] as $to_uid => $perm) + { + if ($permission & $perm) $ids[] = $to_uid; + } + + return (count($ids)) ? join(',', $ids) : -1; +} + +/** + * Present a size (in bytes) as a human-readable value + * + * @author http://php.net + * + * @param int $size size (in bytes) + * @param int $precision number of digits after the decimal point + * @return string + */ +function sizetostring($size, $precision = 0) +{ + $sizes = array('YB', 'ZB', 'EB', 'PB', 'TB', 'GB', 'MB', 'kB', 'B'); + $total = count($sizes); + + while($total-- && $size > 1024) $size /= 1024; + return round($size, $precision).$sizes[$total]; +} + +// @todo unused +function img_avg_color($filename, $format=0) +{ + // networked file + if (preg_match('/^http/i', $imgurl)) return(FALSE); + + // not a valid image + if (!list($width, $height) = @getimagesize($filename)) return(FALSE); + + // resample + switch (exif_imagetype($filename)) { + case 2: + $img = imagecreatefromjpeg($filename); + break; + case 3: + $img = imagecreatefrompng($filename); + break; + case 1: + $img = imagecreatefromgif($filename); + break; + } + + $tmp = imagecreatetruecolor(1, 1); + if (!imagecopyresampled($tmp, $img, 0, 0, 0, 0, 1, 1, $width, $height)) return(FALSE); + + $rgb = imagecolorat($tmp, 0, 0); + $r = dechex($rgb >> 16); + $g = dechex($rgb >> 8 & 0xFF); + $b = dechex($rgb & 0xFF); + + return('#'.$r.$g.$b); +} + +?> \ No newline at end of file diff --git a/videodb/core/pdf.php b/videodb/core/pdf.php new file mode 100644 index 0000000..d2f9246 --- /dev/null +++ b/videodb/core/pdf.php @@ -0,0 +1,457 @@ + + * @version $Id: pdf.php,v 1.36 2013/03/15 16:42:46 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/cache.php'; +require_once './core/export.core.php'; +require_once './engines/engines.php'; +require_once './core/VariableStream.class.php'; + +define('FPDF', './vendor/setasign/fpdf'); +define('FPDF_FONTPATH', FPDF.'/font/'); + +require_once FPDF.'/fpdf.php'; +require_once './lib/fpdf2file/fpdf2file.php'; + +/** + * Copied from FPDF tutorial 3 + * enhanced with memory image creation for gif->png conversion + * + * @link http://www.fpdf.org/?go=script&id=45 + */ +class PDF extends FPDF2File +{ + var $B; + var $I; + var $U; + var $HREF; + var $GDCount = 0; + var $Scale = 0; // dont rescale images + + function FPDF2File($orientation='P', $unit='mm', $format='A4') + { + //Call parent constructor + $this->FPDF($orientation,$unit,$format); + //Initialization + $this->B=0; + $this->I=0; + $this->U=0; + $this->HREF=''; + } + + function GDImage($file, $x, $y, $im, $w=0, $h=0, $link='', $type='png') + { + // ouput the GD image $im + ob_start(); + $func = 'image'.$type; // image creation function according to type + $func($im); + $data = ob_get_contents(); + ob_end_clean(); + + // create file-unique variable name to not duplicate images for pdf + $file = (empty($file)) ? $this->GDCount++ : preg_replace('/[\s\/\.]/', '_', $file); + $name = 'pdf_image_'.$file; + $GLOBALS[$name] = $data; + + // call Image using in-memory PNG file + parent::Image('var://'.$name, $x, $y, $w, $h, $type, $link); + } + + function Image($file, $x=null, $y=null, $w=0, $h=0, $ext='', $link='') + { + global $config; + + $image_types = array(1 => 'gif', 2 => 'jpg', 3 => 'png', 6 => 'bmp'); + + list($width, $height, $ext, $attr) = getimagesize($file); + $ext = $image_types[$ext]; + + // find image loading function + switch($ext) + { + case 'jpg': $func = 'jpeg'; break; + case 'bmp': $func = 'wbmp'; break; + default: $func = $ext; + } + $func = 'imagecreatefrom'.$func; + + // check if loading functions exists (especially for gif support) + $im = (function_exists($func)) ? $func($file) : imagecreatetruecolor(1,1); + + // scaling requested? + if ($this->Scale) + { + $this->max_width = round($this->Scale * $config['pdf_image_max_width']); + $this->max_height = round($this->Scale * $config['pdf_image_max_height']); + + $scale = min($this->max_width/$width, $this->max_height/$height); + $thumb_x = round($width * $scale); + $thumb_y = round($height * $scale); + } + + // scaling requied? + if (($this->Scale) && (($thumb_x != $width) || ($thumb_y != $height))) + { + // create white truecolor image (in case original is transparent) + $target = imagecreatetruecolor($thumb_x, $thumb_y); + $white = imagecolorallocate($target, 255, 255, 255); + imagefilledrectangle($target, 0, 0, $thumb_x, $thumb_y, $white); + imagecopyresampled($target, $im, 0,0, 0,0, $thumb_x,$thumb_y, $width,$height); + $this->GDImage($file, $x, $y, $target, $w, $h, $link, 'jpeg'); // change to png if you receive acrobat errors + imagedestroy($target); + } + elseif ($ext == 'gif') { + // pdf doesn't support interlaced images + if (imageinterlace($im)) + { + // claim non-interlaced image + imageinterlace($im, false); + } + + $this->GDImage($file, $x, $y, $im, $w, $h, $link); + } + else { + parent::Image($file, $x, $y, $w, $h, $ext, $link); + } + + imagedestroy($im); + } + + function VerifyFont($font, $mode = '') + { + $default_fonts = array('Arial', 'Courier', 'Helvetica', 'Times'); + + if (!in_array($font, $default_fonts)) + { + if ($mode) + $this->AddFont($font, 'B', strtolower($font).'b.php'); + else + $this->AddFont($font); + } + } + + function WriteHTML($html) + { + global $config; + + //HTML parser + $html = str_replace("\n",' ',$html); + $a = preg_split('/<(.*)>/U',$html,-1,PREG_SPLIT_DELIM_CAPTURE); + foreach($a as $i=>$e) + { + if($i%2==0) + { + //Text + if($this->HREF) + $this->PutLink($this->HREF,$e); + else + $this->Write((int)$config['pdf_font_size'] / 2.5, $e); + } + else + { + //Tag + if($e[0]=='/') + $this->CloseTag(strtoupper(substr($e,1))); + else + { + //Extract attributes + $a2=explode(' ',$e); + $tag=strtoupper(array_shift($a2)); + $attr=array(); + foreach($a2 as $v) + if(ereg('^([^=]*)=["\']?([^"\']*)["\']?$',$v,$a3)) + $attr[strtoupper($a3[1])]=$a3[2]; + $this->OpenTag($tag,$attr); + } + } + } + } + + function OpenTag($tag, $attr) + { + global $config; + + //Opening tag + if($tag=='B' or $tag=='I' or $tag=='U') + $this->SetStyle($tag,true); + if($tag=='A') + $this->HREF=$attr['HREF']; + if($tag=='BR') + $this->Ln((int)$config['pdf_font_size'] / 2); + } + + function CloseTag($tag) + { + //Closing tag + if($tag=='B' or $tag=='I' or $tag=='U') + $this->SetStyle($tag,false); + if($tag=='A') + $this->HREF=''; + } + + function SetStyle($tag, $enable) + { + //Modify style and select corresponding font + $this->$tag+=($enable ? 1 : -1); + $style=''; + foreach(array('B','I','U') as $s) + if($this->$s>0) + $style.=$s; + $this->SetFont('',$style); + } + + function PutLink($URL, $txt) + { + global $config; + + //Put a hyperlink + $this->SetTextColor(0,0,255); + $this->SetStyle('U',true); + $this->Write((int)$config['pdf_font_size'] / 2, $txt, $URL); + $this->SetStyle('U',false); + $this->SetTextColor(0); + } + + /** + * @author Olivier + * @license Freeware + */ + function WordWrap(&$text, $maxwidth) + { + $text = trim($text); + if ($text==='') + return 0; + $space = $this->GetStringWidth(' '); + $lines = explode("\n", $text); + $text = ''; + $count = 0; + + foreach ($lines as $line) + { + $words = preg_split('/ +/', $line); + $width = 0; + + foreach ($words as $word) + { + $wordwidth = $this->GetStringWidth($word); + if ($width + $wordwidth <= $maxwidth) + { + $width += $wordwidth + $space; + $text .= $word.' '; + } + else + { + $width = $wordwidth + $space; + $text = rtrim($text)."\n".$word.' '; + $count++; + } + } + $text = rtrim($text)."\n"; + $count++; + } + $text = rtrim($text); + return $count; + } + + function SaveFile($filename) + { + FPDF::Output('D', 'videoDB.pdf', $isUTF8=false); + readfile($filename); + } +} + +/** + * Return image name for representing the media type + */ +function getMediaImage($mediatype) +{ + if (preg_match("/^(DVD([+-]R)?|DivX|CD|VCD|SVCD|VHS|BLU-RAY|AVCHD|HDD|HD-DVD)/i", $mediatype, $matches)) + { + $type_image = strtolower($matches[1]).'.png'; + } + else $type_image = ''; + + return $type_image; +} + +/** + * Export PDF document + * + * @param string $where WHERE clause for SQL statement + */ +function pdfexport($WHERE) +{ + global $config; + + $ypos = $config['pdf_font_size']; // Match the font size for proper vertical offset + $page_width = $config['pdf_page_width']; + $margin = $config['pdf_margin']; + $left_margin = $config['pdf_left_margin']; + $right_margin = $config['pdf_right_margin']; + $mediaimg_width = $config['pdf_image_media_width']; + $font_size = $config['pdf_font_size']; + + $image_height = $config['pdf_image_height']; + $image_width = $config['pdf_image_width']; + + $font_title = $config['pdf_font_title']; + $font_plot = $config['pdf_font_plot']; + + $text_length = $config['pdf_text_length']; + + $tempfolder = cache_get_folder(''); + if ($config['cache_pruning']) cache_prune_folder($tempfolder, 3600, false, false, 'videodb*.pdf'); + $filename = $tempfolder.'videodb'.date('His',time()).'.pdf'; + + // setup pdf class + $pdf = new PDF(); + $pdf->Open($filename); + $pdf->VerifyFont($font_title); + $pdf->VerifyFont($font_title, 'B'); + $pdf->VerifyFont($font_plot); + $pdf->AddPage(); + $pdf->SetRightMargin($right_margin); + + // add downscaling + if (array_key_exists('pdf_scale', $config) && $config['pdf_scale']) + { + $pdf->Scale = $config['pdf_scale']; + $pdf->max_width = $config['pdf_image_max_width']; + $pdf->max_height= $config['pdf_image_max_height']; + } + + // get data + $result = iconv_array('utf-8', 'iso-8859-1', exportData($WHERE)); + + $tech = array(); + + foreach ($result as $row) + { + set_time_limit(300); // rise per movie execution timeout limit if safe_mode is not set in php.ini + + $title = $row['title']; + if ($row['subtitle']) $title .= ' - '.$row['subtitle']; + if ($row['diskid'] || $row['mediatype']) + { + $title .= ' ['; + if ($row['mediatype']) $title .= $row['mediatype'] . ', '; + if ($row['diskid']) $title .= $row['diskid']; + $title = preg_replace('/, $/', '', $title) . ']'; + } + + // get drilldown url for image + $imdb = $row['imdbID']; + $link = ($imdb) ? engineGetContentUrl($imdb, engineGetEngine($imdb)) : ''; + + // title + $pdf->SetFont($font_title, 'B', $font_size); + $pdf->SetXY($left_margin + $image_width + $margin, $ypos); + $pdf->Cell(0, 0, $title, 0,1, 'L',0,$link); + + // [muddle] technical details + unset($tech['Y']); + if ($row['year']) { + $tech['Y'] = "Year: ".$row['year']; + } + + unset($tech['V']); + if ($row['video_width'] and $row['video_height']) + { + $vw = $row['video_width']; + $vh = $row['video_height']; + $tech['V'] = "Video: "; + + if ($vw>1920) { + $tech['V'] .= "UHD ".$vw."x".$vh; + } elseif ($vw>1280) { + $tech['V'] .= "HD 1080p"; + } elseif ($vw==1280 or $vh==720) { + $tech['V'] .= "HD 720p"; + } elseif ($vw==720 or $vw==704) { + $tech['V'] .= "SD "; + if ($vh==480) { + $tech['V'] .= "NTSC"; + } elseif ($vh==576) { + $tech['V'] .= "PAL"; + } else { + $tech['V'] .= $vw."x".$vh; + } + } else { + $tech['V'] .= "LORES ".$vw."x".$vh; + } + } + + unset($tech['A']); + if ($row['audio_codec']) { + $tech['A'] = "Audio: ".$row['audio_codec']; + } + + unset($tech['D']); + if ($row['created']) { + $tech['D'] = "Date: ".$row['created']; + } + + $techinfo = implode(", ", $tech); + + $pdf->SetFont($font_title, 'B', $font_size-3); + $pdf->SetXY($left_margin + $image_width + $margin, $ypos+ 4); + $pdf->Cell(0, 0, $techinfo, 0,1, 'L',0); + + // plot + $plot = leftString($row['plot'], $text_length); + $pdf->SetFont($font_plot, '', $font_size-1); + $pdf->SetXY($left_margin + $image_width + $margin, $ypos+3 +3); + $pdf->SetLeftMargin($left_margin + $image_width + $margin); + $pdf->WriteHTML($plot); + + // image + $file = getThumbnail($row['imgurl']); + if (preg_match('/^img.php/', $file)) $file = img(); + + // image file present? + if ($file) + { + $pdf->Image($file, $left_margin, $ypos-2, $image_width, $image_height, '', $link); + } + + // add mediatype image + if ($type_image = getMediaImage($row['mediatype'])) + { + $pdf->Image('./images/media/'.$type_image, $page_width - $mediaimg_width - $right_margin, $ypos - 2, $mediaimg_width, 0, '', ''); + } + + // new position + $ypos += $margin; + if ($file or $plot) + { + $ypos += max($image_height, $font_size); + } + else + { + $ypos += $font_size; + } + + if ($ypos > 250) + { + $ypos = $config['pdf_font_size']; + $pdf->AddPage(); + } + } + + $pdf->SaveFile($filename); + + // get rid of temp file + @unlink($filename); +} + +?> diff --git a/videodb/core/queryparser.php b/videodb/core/queryparser.php new file mode 100644 index 0000000..9b438d3 --- /dev/null +++ b/videodb/core/queryparser.php @@ -0,0 +1,182 @@ + + * @author Andreas Götz + * @version $Id: queryparser.php,v 1.9 2007/12/19 18:42:11 andig2 Exp $ + */ + + +/** + * Querystringparser + * + * Parses a querystring into a datastructure + * + * @param string $query Querystring + * @param string &$errors Stringreference to write errors back + * @return array parsed querytokens + */ +function queryparser($query, &$errors) +{ + $query = trim($query); +/* + // filter forbidden characters + if (preg_match('/[^\w \.\(\)"\'*]/',$query)) + { + $errors .= "Nicht erlaubte Zeichen wurden ignoriert\n"; + $query = preg_replace('/[^\w \.\(\)"\'*]/','',$query); + } +*/ + + $ops = array(); + $struct = array(); + $tokens = tokenizer($query); + + // look through tokens + while ($current = array_shift($tokens)) + { + if (preg_match('/^(AND|OR|NOT)$/i', $current)) + { + // token is operator + $ops[] = strtoupper($current); + } + else + { + // token is searchword + if (!count($ops)) + { + // empty operator counts as AND + $ops[] = 'AND'; + } + + // clean invalid operators + $cleanops = cleanoperators($ops, $errors); + + // check wildcards + $wild = ''; + if (substr($current,0,1) == '*') $wild .= 'l'; + if (substr($current, -1) == '*') $wild .= 'r'; + + $current = str_replace('*', '', $current); + $struct[] = array('ops' => $cleanops, + 'token' => $current, + 'wildcard' => $wild); + $ops = array(); + } + } + return $struct; +} + +/** + * Querystring tokenizer + * + * Parse string into array of tokens. + * Honors literal expressions enclosed by "", + * converts +/- to AND and NOT + * + * @param string Querystring + * @return array All tokens of the Strings + */ +function tokenizer($qstring) +{ + // replace +/- with AND and NOT + $qstring = ' '.$qstring; // for following regexps + $qstring = preg_replace('/(\s)-(\S)/', '\1NOT \2', $qstring); + $qstring = preg_replace('/(\s)\+(\S)/', '\1AND \2', $qstring); + $qstring = trim($qstring); + + $tokens = array(); + $current = ''; + $sep = '\s'; + + for ($i=0; $i < strlen($qstring); $i++) + { + $char = $qstring[$i]; + + // match current separator? + if (preg_match("/$sep/", $char)) + { + $current = trim($current); + if (!empty($current) AND ((str_replace('*', '', $current)) != '')) + { + // add non-empty token + $tokens[] = $current; + } + $current = ''; + $sep = '\s'; + } + + // begin literal expression? + elseif ($char == '"') + { + $sep = '"'; + } + + // normal token character + else + { + $current .= $char; + } + } + + // add remaining token + $current = trim($current); + if (!empty($current) AND ((str_replace('*','',$current))!='')) + { + $tokens[] = $current; + } + + return $tokens; +} + +/** + * Operator cleaning + * + * removes illogical operator combinations... + * + * @param array Operators + * @param string Stringreference to write errors back + * @return string cleaned Operators + */ +function cleanoperators($ops, &$errors) +{ + $newops = array(); + + // make unique + $ops = array_unique($ops); + + // sort + if (in_array('AND', $ops)) $newops[] = 'AND'; + if (in_array('OR', $ops)) $newops[] = 'OR'; + if (in_array('NOT', $ops)) $newops[] = 'NOT'; + + // join + $opstr = join(' ', $newops); + + // clean unnormal conditions + if (strstr($opstr, 'AND OR')) + { + $errors .= "Die logische Verknüpfung 'AND OR' ist nicht erlaubt und wurde in 'OR' umgewandelt.\n"; + $opstr = str_replace('AND OR', 'OR', $opstr); + } + if (strstr($opstr, 'OR NOT')) + { + $errors .= "Die logische Verknüpfung 'OR NOT' ist nicht erlaubt und wurde in 'AND' umgewandelt.\n"; + $opstr = str_replace('OR NOT', 'AND', $opstr); + } + if ($opstr == 'NOT') + { + $opstr = 'AND NOT'; + } + + return $opstr; +} + + diff --git a/videodb/core/security.php b/videodb/core/security.php new file mode 100644 index 0000000..94e3c6c --- /dev/null +++ b/videodb/core/security.php @@ -0,0 +1,59 @@ + + * @author tREXX + * @version $Id: security.php,v 1.2 2008/01/05 13:50:58 andig2 Exp $ + */ + +/** + * Allow these tags + */ +$allowedTags = '


    • '; + +/** + * Disallow these attributes/prefix within a tag + */ +$stripAttrib = 'javascript:|onclick|ondblclick|onmousedown|onmouseup|onmouseover|'. + 'onmousemove|onmouseout|onkeypress|onkeydown|onkeyup'; + +/** + * @return string + * @param string + * @desc Strip forbidden attributes from a tag + */ +function removeEvilAttributes($tagSource) +{ + global $stripAttrib; + return stripslashes(preg_replace("/$stripAttrib/i", 'forbidden', $tagSource)); +} + +/** + * @return string + * @param string + * @desc Strip forbidden attributes from an array of matches for an expression like (<)(.*?)(>) + */ +function _callbackRemoveEvilAttributes($matches) +{ + return $matches[1] . removeEvilAttributes($matches[2]) . $matches[3]; +} + +/** + * @return string + * @param string + * @desc Strip forbidden tags and delegate tag-source check to removeEvilAttributes() + */ +function removeEvilTags($source) +{ + global $allowedTags; + if (!is_null($source)) + { + $source = strip_tags($source, $allowedTags); + return preg_replace_callback('/(<)(.*?)(>)/i', "_callbackRemoveEvilAttributes", $source); + } + return $source; +} + +?> \ No newline at end of file diff --git a/videodb/core/session.php b/videodb/core/session.php new file mode 100644 index 0000000..6b89fcf --- /dev/null +++ b/videodb/core/session.php @@ -0,0 +1,69 @@ + + * @version $Id: session.php,v 1.13 2008/02/28 20:01:17 andig2 Exp $ + */ + +// start session +session_start(); + +/** + * Get session value or specified default + */ +function session_get($varname, $default=null) +{ + if (isset($_SESSION['vdb'][$varname])) + { + return $_SESSION['vdb'][$varname]; + } + return $default; +} + +/** + * Set session value or specified default + */ +function session_set($varname, $value) +{ + $_SESSION['vdb'][$varname] = $value; +} + +/** + * Upsert session value with current value of global variable or specified default + */ +function session_default($varname, $default=null) +{ + global $$varname; + + if (!isset($$varname)) + { + $$varname = (isset($_SESSION['vdb'][$varname])) ? $_SESSION['vdb'][$varname] : $default; + } + $_SESSION['vdb'][$varname] = $$varname; +} + +/** + * get session_default for owner + * + * basically this only executes the extra query when the global $owner is not set and also + * not available in session data. only then we need the hasAny check. if the global is set + * or the global is not set but the session is then session_default() will fix both those + * cases. put into a single function because it gets called from multiple files. + */ +function session_default_owner() +{ + global $owner, $lang; + if (!isset($owner) && !isset($_SESSION['vdb']['owner'])) { + $hasAny = runSQL('SELECT COUNT(*) AS num FROM '.TBL_DATA.' WHERE '.TBL_DATA.'.owner_id = ' . get_current_user_id()); + $hasAny = ($hasAny && isset($hasAny[0]['num']) && $hasAny[0]['num'] > 0); + $default = ($hasAny ? get_username(get_current_user_id()) : $lang['filter_any']); + return session_default('owner', $default); + } + return session_default('owner'); +} + diff --git a/videodb/core/setup.core.php b/videodb/core/setup.core.php new file mode 100644 index 0000000..e90ca29 --- /dev/null +++ b/videodb/core/setup.core.php @@ -0,0 +1,356 @@ + + * @author Andreas Götz + * @version $Id: setup.core.php,v 1.12 2013/03/16 10:10:07 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/custom.php'; +require_once './core/output.php'; + +$SETUP_GLOBAL = array('language', 'autoid', 'mediadefault', 'langdefault', 'acclangbrowser', + 'filterdefault', 'showtv', 'orderallbydisk', 'removearticles', + 'localnet', 'IMDBage', 'thumbnail', + 'castcolumns', 'template', 'languageflags', 'custom1', + 'custom2', 'custom3', 'custom4', 'custom1type', + 'custom2type', 'custom3type', 'custom4type', 'enginedefault', + 'proxy_host', 'proxy_port', 'actorpics', 'thumbAge', 'listcolumns', + 'shownew', 'imdbBrowser', 'multiuser', 'denyguest', 'adultgenres', + 'pageno', 'showtools', 'showcasttoggle', 'browse_include_title'); + +$SETUP_QUICK = array('template'); + +$SETUP_USER = array('language', 'mediadefault', 'langdefault', 'acclangbrowser', 'filterdefault', + 'showtv', 'orderallbydisk', 'template', 'languageflags', + 'listcolumns', 'castcolumns', 'shownew', 'pageno', 'removearticles', + 'showcasttoggle', 'browse_include_title'); + +/** + * Build config options array + * + * @param boolean $isprofile Determines if user-specific options are to be displayed + * + * @return array associative array of config options + */ +function setup_mkOptions($isprofile = false) +{ + global $config, $lang; + + // built list of setup options + $setup = array(); + + // isprofile, name, type (text|boolean|dropdown|special|link), data, set, helphl, helptxt + $setup[] = setup_addSection('opt_general'); + $setup[] = setup_addOption($isprofile, 'language', 'dropdown', setup_getLanguages(), null, $lang['help_langn'], $lang['help_lang']); + $option = setup_addOption($isprofile, 'template', 'dropdown', setup_getTemplates($thumbs)); + $option['thumbs'] = $thumbs; + $setup[] = $option; + + $setup[] = setup_addOption($isprofile, 'listcolumns', 'text'); + $setup[] = setup_addOption($isprofile, 'castcolumns', 'text'); + + $setup[] = setup_addOption($isprofile, 'autoid', 'boolean'); + $setup[] = setup_addOption($isprofile, 'orderallbydisk', 'boolean'); + + $setup[] = setup_addOption($isprofile, 'mediadefault', 'dropdown', setup_getMediatypes()); + $setup[] = setup_addOption($isprofile, 'langdefault', 'text'); + $setup[] = setup_addOption($isprofile, 'acclangbrowser', 'boolean'); + $setup[] = setup_addOption($isprofile, 'filterdefault', 'dropdown', array('all'=>$lang['radio_all'], 'unseen'=>$lang['radio_unseen'], 'new'=>$lang['radio_new'], 'wanted'=>$lang['radio_wanted'])); + $setup[] = setup_addOption($isprofile, 'showtv', 'boolean'); + $setup[] = setup_addOption($isprofile, 'shownew', 'text'); + $setup[] = setup_addOption($isprofile, 'pageno', 'text'); + $setup[] = setup_addOption($isprofile, 'languageflags', 'special', out_languageflags($config['languages'])); + $setup[] = setup_addOption($isprofile, 'removearticles', 'boolean'); + $setup[] = setup_addOption($isprofile, 'adultgenres', 'multi', setup_getGenres(), @explode('::', $config['adultgenres'])); + $setup[] = setup_addOption($isprofile, 'showtools', 'boolean'); + $setup[] = setup_addOption($isprofile, 'showcasttoggle', 'boolean'); + $setup[] = setup_addOption($isprofile, 'browse_include_title', 'dropdown', array('none' => $lang['none'], 'top' => $lang['top'], 'bottom' => $lang['bottom'], 'both' => $lang['both'])); + + if (!$isprofile) $setup[] = setup_addSection('opt_custom'); + $setup[] = setup_addOption($isprofile, 'custom', 'special', setup_mkCustoms(), 'custom', $lang['help_customn'], $lang['help_custom']); + + if (!$isprofile) $setup[] = setup_addSection('opt_engines'); + $setup[] = setup_addOption($isprofile, 'enginedefault', 'dropdown', setup_getEngines($config['engines']), null , $lang['help_defaultenginen'], $lang['help_defaultengine']); + + foreach ($config['engines'] as $engine => $meta) + { + $title = $meta['name']; + if (array_key_exists($engine,$config['engine'])) {$enabled = $config['engine'][$engine];} else {$enabled = null;} + $helptext = sprintf($lang['help_engine'], $title); + if (array_key_exists('help_engine' . $engine, $lang)) {$helptext .= ' ' . $lang['help_engine' . $engine];} + if (!array_key_exists('stable', $meta) || $meta['stable'] == 0) {$helptext .= ' ' . $lang['help_engexperimental'];} + + $setup[] = setup_addOption($isprofile, 'engine'.$engine, 'boolean', null, $enabled, $title, $helptext); + + // add engine-specific options + if (array_key_exists('config', $meta) && is_array($meta['config'])) + { + foreach ($meta['config'] as $setting) + { + // NOTE: check setup_additionalSettings if you change the option naming + if (is_array($setting['values'])) + $setup[] = setup_addOption($isprofile, $engine.$setting['opt'], + 'dropdown', $setting['values'], null, $setting['name'], $setting['desc']); + else + $setup[] = setup_addOption($isprofile, $engine.$setting['opt'], + 'text', null, null, $setting['name'], $setting['desc']); + } + } + } + + if (!$isprofile) {$setup[] = setup_addSection('opt_security');} + $setup[] = setup_addOption($isprofile, 'localnet', 'text'); + $setup[] = setup_addOption($isprofile, 'multiuser', 'boolean'); + $setup[] = setup_addOption($isprofile, 'denyguest', 'boolean'); + $setup[] = setup_addOption($isprofile, 'usermanager', 'link', 'users.php', 'usermanager'); + $setup[] = setup_addOption($isprofile, 'proxy_host', 'text'); + $setup[] = setup_addOption($isprofile, 'proxy_port', 'text'); + + if (!$isprofile) {$setup[] = setup_addSection('opt_caching');} + $setup[] = setup_addOption($isprofile, 'thumbnail', 'boolean'); + $setup[] = setup_addOption($isprofile, 'imdbBrowser', 'boolean'); + $setup[] = setup_addOption($isprofile, 'IMDBage', 'text'); + $setup[] = setup_addOption($isprofile, 'actorpics', 'boolean'); + $setup[] = setup_addOption($isprofile, 'thumbAge', 'text'); + + // clean empty entries + for ($i = count($setup); $i > 0; $i--) + { + if (empty($setup[$i]['name']) && empty($setup[$i]['group'])) unset($setup[$i]); + } + + return $setup; +} + +/** + * Add engine-specific config options for saving + */ +function setup_additionalSettings() +{ + global $config, $SETUP_GLOBAL; + + foreach ($config['engines'] as $engine => $meta) + { + // add engine-specific options + if (array_key_exists('config', $meta) && is_array($meta['config'])) + { + foreach ($meta['config'] as $setting) + { + $SETUP_GLOBAL[] = $engine.$setting['opt']; + } + } + } +} + +/** + * Add a new section to the config options array + * + * @param array $setup The config array + * @param string $section Name of the new section + */ +function setup_addSection($section) +{ + $option['group'] = $section; + return $option; +} + +/** + * Adds an entry for the config option array + * + * returns NULL on global options if $isprofile is true + * so global options will not be added to user profile settings + * + * @param array $setup The config array + * @param boolean $isprofile Do we prepare a profile array? + * @param string $name Name of the config option + * @param string $type Type of option (text|boolean|dropdown|special|link) + * @param string $data Current value of this option + * @param string $set Default value of this option + * @param string $hl Help text headline + * @param string $help Help text + */ +function setup_addOption($isprofile, $name, $type, + $data='', $set=NULL, $hl=NULL, $help=NULL) +{ + global $config, $lang; + global $SETUP_USER; + + // user-specific setting? + $isuser = in_array($name, $SETUP_USER); + + if ($isprofile and !$isuser) return; + + $option['isuser'] = $isuser; + $option['name'] = $name; + $option['type'] = $type; + $option['data'] = $data; + + $option['set'] = ($set) ? $set : $config[$name]; + $option['hl'] = ($hl) ? $hl : $lang['help_'.$name.'n']; + $option['help'] = ($help) ? $help : $lang['help_'.$name]; + + return $option; +} + +/** + * Find available languages + */ +function setup_getLanguages() +{ + if ($dh = opendir('language')) + { + while (($file = readdir($dh)) !== false) + { + if (preg_match("/(.*)\.php$/", $file, $matches)) + { + $languages[$matches[1]] = $matches[1]; + } + } + closedir($dh); + } + return $languages; +} + +/** + * Find available templates/styles + * Extended to search for template screenshots + * + * @author Andreas Götz + */ +function setup_getTemplates(&$screenshots) +{ + $screenshots = array(); + + if ($dh = @opendir('templates')) + { + while (($file = readdir($dh)) !== false) + { + if (preg_match("/^\./", $file)) continue; + if (is_dir('templates/'.$file)) + { + $template = 'templates/'.$file; + if ($dh2 = opendir($template)) + { + $style_name = ''; + + while (($style = readdir($dh2)) !== false) + { + if (preg_match("/(.*)\.css$/", $style, $matches)) + { + $thumb = $template.'/screenshot_'.$matches[1].'.jpg'; + if (file_exists($thumb)) + { + $screenshots[] = array('name' => "$file::".$matches[1], 'img' => $thumb); + } + elseif (empty($style_name)) + { + // remember first style found + $style_name = $matches[1]; + } + $templates[$file.'::'.$matches[1]] = $file.' ('.$matches[1].')'; + } + } + closedir($dh2); + + if ($style_name) + { + $thumb = $template.'/screenshot.jpg'; + if (file_exists($thumb)) + { + $screenshots[] = array('name' => "$file::$style_name", 'img' => $thumb); + } + } + + } + } + } + closedir($dh); + } + return $templates; +} + +/** + * Mediatypes + */ +function setup_getMediatypes() +{ + $SELECT = 'SELECT id, name + FROM '.TBL_MEDIATYPES.' + ORDER BY name'; + $result = runSQL($SELECT); + + return array_associate($result, 'id', 'name'); +} + +/** + * Genres + */ +function setup_getGenres() +{ + $SELECT = 'SELECT id, name + FROM '.TBL_GENRES.' + ORDER BY name'; + $result = runSQL($SELECT); + + return array_associate($result, 'id', 'name'); +} + +/** + * Get list of engines for default engine selection + */ +function setup_getEngines($engines_ary) +{ + $engines = array(); + + foreach ($engines_ary as $engine => $meta) + { + if (engine_get_capability($engine, 'movie')) $engines[$engine] = $meta['name']; + } + + return $engines; +} + +/** + * Prepare customfields + */ +function setup_mkCustoms() +{ + global $config; + global $allcustomtypes; + + $setup_custom = ''; + + for ($i=1; $i<5; $i++) + { + $setup_custom .= $i.'. '; + $setup_custom .= ''; + $setup_custom .= "
      \n"; + } + + return $setup_custom; +} + +/** + * Update session variables with configuration values + * + * @author Andreas Goetz + */ +function update_session() +{ + global $listcolumns, $showtv; + + if ($listcolumns) $_SESSION['vdb']['listcolumns'] = $listcolumns; + if ($showtv) $_SESSION['vdb']['showtv'] = $showtv; +} + diff --git a/videodb/core/template.php b/videodb/core/template.php new file mode 100644 index 0000000..ffd841f --- /dev/null +++ b/videodb/core/template.php @@ -0,0 +1,612 @@ + + * @author Andreas Goetz + * @author Chinamann + * @version $Id: template.php,v 1.70 2013/03/21 16:27:57 andig2 Exp $ + */ + +require_once './core/session.php'; +require_once './core/output.php'; +require_once './core/genres.php'; +require_once './core/functions.php'; +require_once './engines/engines.php'; + +/** + * Display template with standard header and footer + */ +function tpl_display_show($template, $flush = true) +{ + smarty_display('header.tpl'); + + if ($flush) flush(); + smarty_display($template); + smarty_display('footer.tpl'); +} + +/** + * Display page using templates + * If page content is unmodified, return HTTP 304 Not modified + * + * @param string $template Template name for main content + */ +function tpl_display($template) +{ + global $config; + + // caching enabled? + if ($config['http_caching']) + { + require_once('./core/httpcache.php'); + httpCacheCaptureStart(); + } + + tpl_display_show($template, !$config['http_caching']); + + if ($config['http_caching']) + { + httpCacheOutput($template, httpCacheCaptureEnd()); + } +} + +/** + * Prepare standard page templates + */ +function tpl_page($help = '', $title = '') +{ + tpl_language(); + tpl_header($help, $title); + tpl_footer(); +} + +/** + * Assigns language strings and config options to the smarty engine + */ +function tpl_language() +{ + global $smarty, $lang, $config; + + $smarty->assign('lang', $lang); + $smarty->assign('config', $config); +} + +/** + * Assigns the header urls to the smarty engine + * + * @param string $help The helpfile to display (optional, without extension) + * @param string $title The text to add to html tag (optional, will be html-encoded) + */ +function tpl_header($help = '', $title = '') +{ + global $smarty, $lang, $config; + global $id, $diskid; + + // viewing is only availble if autorized or public access + if (auth_check(false)) + { + $header['browse'] = 'index.php'; + if (check_permission(PERM_READ, PERM_ANY)) + { + $header['random'] = 'show.php'; + $header['search'] = 'search.php'; + } + $header['stats'] = 'stats.php'; + if ($config['imdbBrowser']) $header['trace'] = 'trace.php'; + $header['help'] = 'help.php'; + if ($help) $header['help'] .= '?page='.$help.'.html'; + } + + // editing is only available in local network + if (localnet()) + { + if (check_permission(PERM_WRITE, PERM_ANY)) + { + $header['new'] = 'edit.php'; + if ($config['showtools']) $header['contrib'] = 'contrib.php'; + } + if (check_permission(PERM_ADMIN)) $header['setup'] = 'setup.php'; + + // edit or show? + if ($id) + { + if (check_videopermission(PERM_WRITE, $id)) $header['edit'] = 'edit.php?id='.$id; + if (!preg_match('/show.php$/', $_SERVER['PHP_SELF'])) + { + $header['view'] = 'show.php?id='.$id; + } + if (check_videopermission(PERM_WRITE, $id)) $header['del'] = 'delete.php?id='.$id; + } + if (check_permission(PERM_WRITE, PERM_ANY)) + { + $header['borrow'] = 'borrow.php'; + if (isset($diskid)) $header['borrow'] .= '?diskid='.$diskid; + } + } + + // multiuser settings + if ($config['multiuser']) + { + $header['login'] = 'login.php'; + + // logged in? + if (!empty($_COOKIE['VDBusername']) && $_COOKIE['VDBuserid'] != $config['guestid']) + { + $header['profile'] = 'profile.php'; + $smarty->assign('loggedin', $_COOKIE['VDBusername']); + } + else + { + // make sure anonymous users don't get access to trace for security reasons + unset($header['trace']); + } + + if (check_permission(PERM_ADMIN)) $header['users'] = 'users.php'; + } + + // determine active tab + if (preg_match('/(\w+)\.php/', $_SERVER['PHP_SELF'], $m)) + { + $tab = strtolower($m[1]); + switch ($tab) + { + case 'show': + $header['request_uri'] = $_SERVER['REQUEST_URI']; + case 'edit': + if (!empty($id)) $header['active'] = $tab; + // uncomment this if you want the 'Browse' tab to remember last visited movie + // { ... $smarty->assign('browseid', $_REQUEST['id']); } + else $header['active'] = ($tab == 'show') ? 'random' : 'new'; + break; + default: + /* legacy version + $translate = array('index' => 'browse', 'users' => 'setup', 'permissions' => 'setup', 'delete' => 'show'); + */ + $translate = array('index' => 'browse', 'permissions' => 'users', 'delete' => 'show'); + if (in_array($tab, array_keys($translate))) + { + $tab = $translate[$tab]; + } + $header['active'] = $tab; + } + } + + // breadcrumbs + $breadcrumbs = session_get('breadcrumbs', array()); + $smarty->assign('breadcrumbs', $breadcrumbs); + + if (!is_null($title)) + { + $smarty->assign('title', htmlspecialchars($title)); + } + else + { + $smarty->assign('title', ""); + } + $smarty->assign('header', $header); + $smarty->assign('style', $config['style']); + $smarty->assign('langcode', $config['language']); +} + +/** + * Assigns the filter options to the smarty engine + */ +function tpl_filters($filter, $showtv) +{ + global $smarty, $lang; + global $filter_expr; + global $owner, $mediatype; + global $config; + + // build filter array + foreach ($filter_expr as $flt => $regex) + { + $filters[$flt] = ($flt == "NUM") ? "#" : $flt; + } + $filters['all'] = $lang['radio_all']; + $filters['unseen'] = $lang['radio_unseen']; + $filters['new'] = $lang['radio_new']; +/* + # removed as of 4.0 in favour of media type filter + $filters['wanted'] = $lang['radio_wanted']; +*/ + $smarty->assign('filters', $filters); + $smarty->assign('filter', $filter); + $smarty->assign('showtv', $showtv); + + // create owner selectbox + $smarty->assign('owners', out_owners(array($lang['filter_any'] => $lang['filter_any']), PERM_READ)); + if (!$owner) $owner = $lang['filter_any']; //!! default owner hack + $smarty->assign('owner', $owner); + + // create mediatype selectbox + $smarty->assign('mediafilter', out_mediatypes(array(-2 => $lang['filter_any'], -1 => $lang['filter_available']))); + if (!$mediatype) $mediatype = session_get('mediafilter'); //!! default media type hack + $smarty->assign('mediatype', $mediatype); + + // create sorting selectbox + // Sorting is disabled when ordering by diskid is enabled + if(!$config['orderallbydisk']) { + $smarty->assign('order_options', array(-1 => $lang['title'], 1 => $lang['rating'], 2 => $lang['date'])); + if(!isset($order)) $order = session_get('order'); + $smarty->assign('order', $order); + } + + + // enable dynamic columns in list view + $smarty->assign('listcolumns', session_get('listcolumns')); +} + +function adultcheck_for_video($video) +{ + return adultcheck($video["id"]); +} + +/** + * Assigns the searchresults/browselist to the smarty engine + * + * @param array indexed array containing the item data + */ +function tpl_list($list) +{ + global $smarty, $config; + global $listcolumns; + if(!$list) + { + $smarty->assign('totalresults', 0); + return; + } + for ($i=0; $i < count($list); $i++) + { + // setup imgurls + $list[$i]['imgurl'] = ($config['thumbnail']) ? getThumbnail($list[$i]['imgurl']) : ''; + + // check for flagfile + $languages = $list[$i]['language']; + $flagfile = img('flags/'.$languages.'.gif'); + if (file_exists($flagfile)) + { + // one langage + $list[$i]['flagfile'][$languages] = $flagfile; + $list[$i]['language'] = array($list[$i]['language']); + } + else + { + // multiple languages + $langary = preg_split('/,\s*/', $languages); + $list[$i]['language'] = $langary; + + // assign them all + foreach ($langary as $languagepart) + { + $flagfile = img('flags/'.$languagepart.'.gif'); + if (file_exists($flagfile)) + { + $list[$i]['flagfile'][$languagepart] = $flagfile; + } + } + } + + // is this file editable? + if (localnet()) + { + $list[$i]['editable'] = ($config['multiuser']) ? + check_permission(PERM_WRITE, $list[$i]['owner_id']) : true; + } + else + { + $list[$i]['editable'] = false; + } +/* + uncomment this to allow display of rating in the 'Browse' tab + require_once 'custom.php'; + customfields($list[$i], 'out'); +*/ + } + + // do adultcheck + if (is_array($list)) + { + $list = array_filter($list, "adultcheck_for_video"); + } + + // enable dynamic columns in list view + $smarty->assign('listcolumns', session_get('listcolumns')); + $smarty->assign('list', $list); + + // show total number of movies in footer + $smarty->assign('totalresults', count($list)); +} + +/** + * Assigns debug infos and version to the smarty engine + */ +function tpl_footer() +{ + global $smarty, $config, $SQLtrace; + + if ($config['debug']) + { + $out = $config; + $out['db_password'] = '***'; + $session = $_SESSION['vdb']; + $session['db_password'] = '***'; + + ob_start(); + print '<pre>'; + dump($SQLtrace); + dump($out); + dump($session); + print '</pre>'; +# phpinfo(); + $debug = ob_get_contents(); + ob_end_clean(); + + $smarty->assign('DEBUG', $debug); + } + $smarty->assign('version', VERSION); +} + +/** + * Function combines multiple actor thumbnail queries into single SQL query + */ +function get_actor_thumbnails_batched(&$actors) +{ + if (!count($actors)) return; + + $ids = "'".join("','", array_map('escapeSQL', array_column($actors, 'id')))."'"; + + $SQL = 'SELECT actorid, name, imgurl, UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(checked) AS cacheage + FROM '.TBL_ACTORS.' WHERE actorid IN ('.$ids.')'; + $result = runSQL($SQL); + + $result = array_associate($result, 'actorid'); + + // loop over actors from full-text field + foreach ($actors as $idx => $actor) + { + // check for actor thumbnail + $batch_result = null; + if (array_key_exists($actor['id'], $result)) + { + $batch_result = $result[$actor['id']]; + } + + if ($batch_result) + { + $actors[$idx]['imgurl'] = get_actor_image_from_cache($batch_result, $actor['name'], $actor['id']); + } + else + { + $actors[$idx]['imgurl'] = getActorThumbnail($actor['name'], $actor['id'], false); + } + } +} + +/** + * Convert textbox/db presentation into actors array + */ +function split_cast_array(&$actor, $key) +{ + $ary = explode('::', $actor); + + $actor = array(); + $actor['name'] = $ary[0]; + $actor['id'] = $ary[2]; + $actor['roles'] = preg_split('[^</]', $ary[1]); +} + +/** + * Converts plain cast data into array of actors with thumbnails + * + * @author Andreas Goetz <cpuidle@gmx.de> + */ +function prepare_cast($cast) +{ + global $config; + + // convert text represenatation into array + $actors = array_filter(preg_split("/\r?\n/", trim($cast))); + + // reformat roles + $actors = preg_replace('/\((.*?)\)/', '<small>($1)</small>', $actors); + + array_walk($actors, 'split_cast_array'); + + // check for actor thumbnails + if ($config['actorpics']) get_actor_thumbnails_batched($actors); + + // loop over actors from full-text field + foreach ($actors as $idx => $actor) + { + $actors[$idx]['imdburl'] = engineGetActorUrl($actor['name'], $actor['id'], engineGetActorEngine($actor['id'])); + + // check for actor thumbnail +# if ($config['actorpics']) $actor['imgurl'] = getActorThumbnail($actor['name'], $actor['id']); + } + + return $actors; +} + +/** + * Assigns the videoinfos to the smarty engine + * + * @param array associative array containing the item data + */ +function tpl_show($video) +{ + global $smarty, $config; + + // imageurl + $video['imgurl'] = getThumbnail($video['imgurl'], $video['title']); + + // make soft linebreaks: + $video['filename'] = preg_replace('/(_|\.|-)/', '$1<wbr />', $video['filename']); + + // split comma-separated countries, prevent empty array + $video['country'] = preg_split('/,\s*/', $video['country'], -1, PREG_SPLIT_NO_EMPTY); + + // split comma-separated multiple languages, prevent empty array + $video['language'] = preg_split('/,\s*/', $video['language'], -1, PREG_SPLIT_NO_EMPTY); + + // humanreadable filesize: + $video['filesize'] = round($video['filesize']/(1024*1024), 2); + + // break plot and comment + $video['plot'] = nl2br($video['plot']); + $video['comment'] = nl2br($video['comment']); + + // cast + $smarty->assign('cast_toggle', $config['showcasttoggle']); + $show_cast = true; + if ($config['showcasttoggle']) + { + $show_cast = (isset($_GET['show_cast']) && $_GET['show_cast'] == '1'); + } + $smarty->assign('show_cast', $show_cast); + $video['cast'] = []; + if ($show_cast) + { + $video['cast'] = prepare_cast($video['actors']); + } + + // prepare the custom fields + customfields($video, 'out'); + + // hide owner if not using multi-user + if (!$config['multiuser']) unset($video['owner']); + + // get drilldown url for image + if ($video['imdbID']) + { + require_once './engines/engines.php'; + $smarty->assign('link', engineGetContentUrl($video['imdbID'], engineGetEngine($video['imdbID']))); + } + + // add episodes information + if (array_key_exists('episodes', $video) && is_array($video['episodes'])) + { + // allow multiple columns + $smarty->assign('listcolumns', session_get('listcolumns')); + } + + $smarty->assign('castcolumns', $config['castcolumns']); + $smarty->assign('video', $video); + + // get genre ids and names + $smarty->assign('genres', getItemGenres($video['id'], true)); + + // make engines available + $smarty->assign('engines', $config['engine']); + + // allow XML export + foreach (array('xls','pdf','xml') as $export) + { + if ($config[$export]) $smarty->assign($export, 'show.php?id='.$video['id'].'&'); + } + // new-style way of exporting + // $smarty->assign('exports', listExports('show.php?id='.$video['id'].'&')); +} + +/** + * Assigns the videoinfos to the smarty engine + */ +function tpl_edit($video) +{ + global $smarty, $config, $lang; + + // create a form ready quoted version for each value + foreach (array_keys($video) as $key) + { + $video['q_'.$key] = formvar($video[$key]); + } + + // use custom function for language + $video['f_language'] = custom_language_input('language', $video['language']); + + // create mediatype selectbox + $smarty->assign('mediatypes', out_mediatypes()); + if (!isset($video['mediatype'])) $video['mediatype'] = $config['mediadefault']; + + // prepare the custom fields + customfields($video, 'in'); + + if ($config['multiuser']) + { + $smarty->assign('owners', out_owners(array('0' => ''), (check_permission(PERM_ADMIN)) ? false : PERM_WRITE, true)); + } + + // item genres + if (array_key_exists('id', $video)) + { + $item_genres = getItemGenres($video['id']); + } + else + { + $item_genres = array(); + } + // new-style + $smarty->assign('genres', out_genres2($item_genres)); +#dlog(out_genres2($item_genres)); +#dlog($item_genres); + // classic + $smarty->assign('genreselect', out_genres($item_genres)); + + // assign data + $smarty->assign('video', $video); + + // get drilldown url for visit link + if (array_key_exists('imdbID', $video)) + { + require_once './engines/engines.php'; + $engine = engineGetEngine($video['imdbID']); + $smarty->assign('link', engineGetContentUrl($video['imdbID'], $engine)); + $smarty->assign('engine', $engine); + } + +/* + // populate autocomplete boxes + $smarty->assign('audio_codecs', array_column(runSQL('SELECT DISTINCT audio_codec FROM '.TBL_DATA.' WHERE audio_codec IS NOT NULL'), 'audio_codec')); + $smarty->assign('video_codecs', array_column(runSQL('SELECT DISTINCT video_codec FROM '.TBL_DATA.' WHERE video_codec IS NOT NULL'), 'video_codec')); +*/ + $smarty->assign('lookup', array('0' => $lang['radio_look_ignore'], + '1' => $lang['radio_look_lookup'], + '2' => $lang['radio_look_overwrite'])); + + // needed for ajax image lookup + $smarty->assign('engines', $config['engines']); +} + +/** + * Prepare lookup template + */ +function tpl_lookup($find, $engine, $searchtype) +{ + global $smarty, $config; + + $find = trim($find); + $smarty->assign('find', $find); + $smarty->assign('q_find', formvar($find)); + + $smarty->assign('engine', $engine); + + $tpl = array(); + foreach (engine_get_capable_engines($searchtype) as $eng => $enabled) + { + // url- make sure this is non-unicode + $tpl[$eng]['url'] = 'lookup.php?find='.urlencode(utf8_smart_decode($find)).'&engine='.$eng.'&searchtype='.$searchtype; + + // title + $tpl[$eng]['name'] = $config['engines'][$eng]['name']; + } + + $smarty->assign('engines', $tpl); +} + +?> diff --git a/videodb/core/xls.php b/videodb/core/xls.php new file mode 100644 index 0000000..ae91c94 --- /dev/null +++ b/videodb/core/xls.php @@ -0,0 +1,459 @@ +<?php +/** + * XLS Export functions + * + * Allows exporting movies to an Excel list + * Requires Spreadsheet_Excel_Writer libaray (http://pear.php.net) + * + * @package Core + * @link http://pear.php.net/package/Spreadsheet_Excel_Writer + * @author Chinamann <chinamann@users.sourceforge.net> + * @author Andreas Götz <cpuidle@gmx.de> + * @version $Id: xls.php,v 1.8 2008/01/05 13:50:29 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/export.core.php'; +require_once './engines/engines.php'; + +#error_reporting(E_ALL^E_NOTICE); +require_once 'vendor/autoload.php'; + +/** + * Export PDF document + * + * @param string $where WHERE clause for SQL statement + */ +function xlsexport($WHERE) +{ + global $config, $lang; + + $text_length = 256-3; + + // videodb context dir + $context_dir = preg_replace('/^(.*)\/.*?$/','\\1',$_SERVER["SCRIPT_FILENAME"]); + + // array of temp files wich have to be deleted if workbook is closed + $del_list = array(); + + // make shure we have list with extra fields, even if empty + $extra_fields = array_map('trim', explode(",", $config['xls_extra_fields'])); + + // Creating a workbook + $workbook = new Spreadsheet_Excel_Writer(); + $workbook->setCustomColor(12, 192,192,192); // Headline + $workbook->setCustomColor(13, 255,255,200); // Seen + $workbook->setCustomColor(14, 255,220,220); // Lent + //$workbook->setCustomColor(15, 0,0,0); // Test + + // sending HTTP headers + $outputFilename = ($config['xls_output_filename']) ? $config['xls_output_filename'] : 'VideoDB'; + $workbook->send($outputFilename.'.xls'); + + // Creating a worksheet + $sheetTitle = ($config['xls_sheet_title']) ? $config['xls_sheet_title'] : 'VideoDB'; + $worksheet =& $workbook->addWorksheet($sheetTitle); + + // format templates + $alignLeftFormatNormal =& $workbook->addFormat(); + $alignLeftFormatLent =& $workbook->addFormat(array('Pattern' => 1)); + $alignLeftFormatLent -> setFGColor(14); + $alignRightFormatNormal =& $workbook->addFormat(array('Align' => 'right')); + $alignRightFormatLent =& $workbook->addFormat(array('Align' => 'right', 'Pattern' => 1)); + $alignRightFormatLent -> setFgColor(14); + $alignCenterFormatNormal =& $workbook->addFormat(array('Align' => 'center')); + $alignCenterFormatLent =& $workbook->addFormat(array('Align' => 'center', 'Pattern' => 1)); + $alignCenterFormatLent -> setFgColor(14); + $titleFormatNormal =& $workbook->addFormat(array('Bold' => 1)); + $titleFormatUnseen =& $workbook->addFormat(array('Bold' => 1, 'Pattern' => 1)); + $titleFormatUnseen -> setFgColor(13); + $titleFormatLent =& $workbook->addFormat(array('Bold' => 1, 'Pattern' => 1)); + $titleFormatLent -> setFgColor(14); + + $plotFormatNormal =& $workbook->addFormat(array('Align' => 'top')); + $plotFormatNormal -> setTextWrap(); + $plotFormatLent =& $workbook->addFormat(array('Align' => 'top','Pattern' => 1)); + $plotFormatLent -> setTextWrap(); + $plotFormatLent -> setFgColor(14); + + $headlineFormat =& $workbook->addFormat(array('Bold' => 1, 'Align' => 'center', 'Pattern' => 1)); + $headlineFormat ->setFgColor(12); + + $rowindex = 0; + $columnindex = 0; + + if ($config['xls_show_headline']) + { + $worksheet->setRow(0, 30); + $rowindex++; + } + + // get data (see http://pear.php.net/bugs/bug.php?id=1572) + $result = iconv_array('utf-8', 'iso-8859-1', exportData($WHERE)); + + foreach ($result as $row) + { + $columnindex = 0; + set_time_limit(300); // rise per movie execution timeout limit if safe_mode is not set in php.ini + + if (!empty($row['lentto']) && $config['xls_mark_lent']) { + $alignLeftFormat = $alignLeftFormatLent; + $alignCenterFormat = $alignLeftFormatLent; + $alignRightFormat = $alignLeftFormatLent; + } + else + { + $alignLeftFormat = $alignLeftFormatNormal; + $alignCenterFormat = $alignLeftFormatNormal; + $alignRightFormat = $alignLeftFormatNormal; + } + $worksheet->setRow($rowindex, 15, $alignLeftFormat); + + foreach ($extra_fields as $field) + { + $isNote = false; + $walks = 1; + if (preg_match('/(.+)\((.+)\)/',$field,$matches)) + { + $field = trim($matches[1]); + $note = trim($matches[2]); + $walks = 2; + } + + for ($walk = 0;$walk < $walks; $walk++) + { + if ($walk == 1) + { + $isNote = true; + $field = $note; + $columnindex--; + } + + // title + if ($field == "title") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['title'], $headlineFormat); + + $title = $row['title']; + if ($row['subtitle']) $title .= ' - '.$row['subtitle']; + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['title'].":\n".html_entity_decode($title)); + else { + if ($row['seen'] == '0' && $config['xls_mark_unseen']) $format = $titleFormatUnseen; + elseif (!empty($row['lentto']) && $config['xls_mark_lent']) $format = $titleFormatLent; + else $format = $titleFormatNormal; + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 50); + $imdb = $row['imdbID']; + $link = ($imdb) ? engineGetContentUrl($imdb, engineGetEngine($imdb)) : ''; + if($link <> '') $worksheet->writeUrl($rowindex, $columnindex, $link, html_entity_decode($title), $format); + else $worksheet->writeString($rowindex, $columnindex, leftString(html_entity_decode($row['title']),$text_length), $format); + } + $columnindex++; + } + + // plot + elseif ($field == "plot") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['plot'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, leftString(html_entity_decode($row['plot']),$text_length)); + else + { + if (!empty($row['lentto']) && $config['xls_mark_lent']) $format = $plotFormatLent; + else $format = $plotFormatNormal; + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 50); + $worksheet->writeString($rowindex, $columnindex++, leftString(html_entity_decode($row['plot']),$text_length), $format); + } + } + + // DiskId + elseif ($field == "diskid") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['diskid'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['diskid'].":\n".html_entity_decode($row['diskid'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row['diskid']), $alignCenterFormat); + } + } + + // add language + elseif ($field == "language") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['language'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['language'].":\n".html_entity_decode($row['language'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 30); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row['language']), $alignLeftFormat); + } + } + + // add mediatype + elseif ($field == "mediatype") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['mediatype'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['mediatype'].":\n".html_entity_decode($row['mediatype'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row['mediatype']), $alignLeftFormat); + } + } + + // genres + elseif ($field == "genres") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['genres'], $headlineFormat); + + if (count($row['genres'])) + { + $output_genres = array(); + foreach ($row['genres'] as $genre) + { + $output_genres[]= html_entity_decode($genre['name']); + } + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['genres'].":\n".join(", ", $output_genres)); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 20); + $worksheet->writeString($rowindex, $columnindex, join(", ", $output_genres), $alignCenterFormat); + } + } + $columnindex++; + } + + // runtime + elseif ($field == "runtime") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['runtime'], $headlineFormat); + + if ($row['runtime']) + { + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['runtime'].":\n".html_entity_decode($row['runtime']).' min'); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex, html_entity_decode($row['runtime']).' min', $alignRightFormat); + } + } + $columnindex++; + } + + // year + elseif ($field == "year") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['year'], $headlineFormat); + + if ($row['year'] != '0000') + { + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['year'].":\n".html_entity_decode($row['year'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeNumber($rowindex, $columnindex, html_entity_decode($row['year']), $alignCenterFormat); + } + } + $columnindex++; + } + + // owner + elseif ($field == "owner") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['owner'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['owner'].":\n".html_entity_decode($row['owner'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 15); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row['owner']), $alignCenterFormat); + } + } + + // lent + elseif ($field == "lent") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['lentto'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['lentto'].":\n".html_entity_decode($row['lentto'])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 15); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row['lentto']), $alignCenterFormat); + } + } + // seen + elseif ($field == "seen") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['seen'], $headlineFormat); + + if ($isNote) { + if ($row['seen'] == 1) $worksheet->writeNote($rowindex, $columnindex++, html_entity_decode($lang['seen'])); + else $columnindex++; + } + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 2); + if ($row['seen'] == 1) $worksheet->writeString($rowindex, $columnindex++, "X", $alignCenterFormat); else $columnindex++; + } + } + // insertdate + elseif ($field == "insertdate") + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $lang['date'], $headlineFormat); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $lang['date'].":\n".html_entity_decode(preg_replace('/^([0-9]{4}\-[0-9]{2}\-[0-9]{2}).*/','$1',$row['created']))); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 10); + $worksheet->write($rowindex, $columnindex++, html_entity_decode(preg_replace('/^([0-9]{4}\-[0-9]{2}\-[0-9]{2}).*/','$1',$row['created'])), $alignCenterFormat); + } + } + + // custom fields + elseif(preg_match("/^custom[0-4]$/",$field)) + { + // headline + if ($config['xls_show_headline'] && $rowindex == 1 && !$isNote) + $worksheet->writeString( 0, $columnindex, $config[$field], $headlineFormat); + + //$row[$field] = html_entity_decode($row[$field]); + + switch ($config[$field.'type']) + { + case 'ed2k': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 12); + $worksheet->writeUrl($rowindex, $columnindex++, html_entity_decode($row[$field]), 'ED2K-Link', $alignCenterFormat); + } + break; + case 'language': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 30); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignLeftFormat); + } + break; + case 'rating': + if ($row[$field]) $rating = html_entity_decode($row[$field]).'/10'; else $rating = ""; + if ($isNote) + { + if ($row[$field]) $worksheet->writeNote($rowindex, $columnindex, $config[$field].":\n".$rating); + $columnindex++; + } + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + if ($row[$field]) $worksheet->writeString($rowindex, $columnindex, $rating,$alignCenterFormat); + $columnindex++; + } + break; + case 'fsk': + if (preg_match("/[0-9]+/",$row[$field]) && !preg_match("/[^0-9]+/",$row[$field])) + { + $fskstr = 'FSK'.html_entity_decode($row[$field]); + } + else $fskstr = html_entity_decode($row[$field]); + + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".$fskstr); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex++, $fskstr, $alignCenterFormat); + } + break; + case 'barcode': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 15); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignCenterFormat); + } + break; + case 'orgtitle': + if ($isNote) + { + if (!empty($row[$field])) $worksheet->writeNote($rowindex, $columnindex, $config[$field]. ":\n".html_entity_decode($row[$field])); + $columnindex++; + } + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 50); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignLeftFormat); + } + break; + case 'movix': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n ".html_entity_decode($row[$field])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignCenterFormat); + } + break; + case 'mpaa': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field]), $alignCenterFormat); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 12); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignCenterFormat); + } + break; + case 'bbfc': + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field])); + else + { + if ($rowindex == 1) $worksheet->setColumn($columnindex, $columnindex, 7); + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignCenterFormat); + } + break; + default: // unknown + if ($isNote) $worksheet->writeNote($rowindex, $columnindex++, $config[$field].":\n".html_entity_decode($row[$field])); + else + { + $worksheet->writeString($rowindex, $columnindex++, html_entity_decode($row[$field]), $alignLeftFormat); + } + break; + } + } + + } //End of walk + + } + $rowindex++; + } + // Let's send the file + $workbook->close(); +} +?> \ No newline at end of file diff --git a/videodb/core/xml.core.php b/videodb/core/xml.core.php new file mode 100644 index 0000000..3fb56dd --- /dev/null +++ b/videodb/core/xml.core.php @@ -0,0 +1,107 @@ +<?php +/** + * XML support functions + * + * @package Core + * @author Andreas Götz <cpuidle@gmx.de> + * @version $Id: xml.core.php,v 1.8 2013/04/26 15:09:35 andig2 Exp $ + */ + +/** + * See http://feedvalidator.org/docs/error/InvalidRFC2822Date.html + */ +$GLOBALS['rss_timestamp_format'] = 'D, j M Y H:i:s O'; + +/** + * Encodes HTML entities into XML character entities + * this avoids problems with unknown entities in XML + * + * @param string $string HTML string to encode + * @return string encoded string containing XML character entities + */ +function encode_character_entities($string) +{ + return strtr($string, get_html_translation_table(HTML_SPECIALCHARS)); +# return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS))); +} + +/** + * Create an XML tag + * + * @param string $tag XML tag name + * @param string $value value for XML tag + * @param boolean $encode require encoding of tag value + * @return string XML tag + */ +function createTag($tag, $value, $encode = true) +{ + if ($encode) $value = encode_character_entities($value); + return "<$tag>".$value."</$tag>\n"; +} + +function createContainer($tag, $value = '') +{ + return createTag($tag, $value, false); +} + +/** + * Convert MySQL Date to RSS timestamp + * + * @return string RFC 2822 Date (http://feedvalidator.org/docs/error/InvalidRFC2822Date.html) + */ +function rss_timestamp($timestamp) +{ + global $rss_timestamp_format; + + // Lets sort this nasty timestamp nonsense out ;D + $y = substr($timestamp, 0, 4); + $m = substr($timestamp, 5, 2); + $d = substr($timestamp, 8, 2); + $h = substr($timestamp, 11, 2); + $min = substr($timestamp, 14, 2); + $s = substr($timestamp, 17, 2); + + $timestamp = mktime($h, $min, $s, $m, $d, $y); + + return date($rss_timestamp_format, $timestamp); +} + +/** + * Load XML into SimpleXML object + * Fixes potential encoding issue + */ +function load_xml($data) +{ + $xml = simplexml_load_string($data, $root = 'SimpleXMLElement', LIBXML_NOCDATA); + + // character encoding warning- hack + if ($xml === false) + { + $error = error_get_last(); +# dump($error); + + // this is nasty- sometimes simplexml_load_string fails but doesn't raise an error + if (preg_match('/simplexml_load_string/i', $error['message'])) + { + $xml = simplexml_load_string(utf8_encode($data), $root, LIBXML_NOCDATA); + } + } + + return $xml; +} + +/** + * Concatenate string-converted xml entities + */ +function xml_join($items, $str = "\n") +{ + foreach ($items as $item) + { + if ($data) $data .= $str; + $data .= (string) $item; + } + + return $data; +} + +?> diff --git a/videodb/core/xml.php b/videodb/core/xml.php new file mode 100644 index 0000000..10b025b --- /dev/null +++ b/videodb/core/xml.php @@ -0,0 +1,186 @@ +<?php +/** + * XML export functions + * + * Lets you browse through your movie collection + * + * @package Core + * @author Andreas Götz <cpuidle@gmx.de> + * @author Kokanovic Branko <branko.kokanovic@gmail.com> + * @version $Id: xml.php,v 1.34 2013/03/10 16:25:35 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/export.core.php'; +require_once './core/xml.core.php'; + +/** + * Export XML data + * + * @param string $where WHERE clause for SQL statement + */ +function xmlexport($WHERE) +{ + global $config; + + // get data + $result = exportData($WHERE); + + // do adultcheck + // this may not be needed as same check is done in exportData in previous statement + if (is_array($result)) + { + $result = array_filter($result, function($video) {return adultcheck($video["id"]);}); + } + + $xml = ''; + + // loop over items + foreach ($result as $item) + { + $xml_item = ''; + + // loop over attributes + foreach ($item as $key => $value) + { + if (!empty($value)) + { + if (($key != 'owner_id') && ($key != 'actors') && ($key != 'genres')) + { + $tag = strtolower($key); + $xml_item .= createTag($tag, trim(html_entity_decode_all($value))); + } + } + } + + // this is a hack for exporting thumbnail URLs + if ($item['imgurl'] && $config['xml_thumbnails']) + { + $thumb = getThumbnail($item['imgurl']); + if (preg_match('/cache/', $thumb)) + $xml_item .= createTag('thumbnail', trim($thumb)); + } + + // genres + if (count($item['genres'])) + { + $xml_genres = ''; + foreach ($item['genres'] as $genre) + { + $xml_genres .= createTag('genre', $genre['name']); + } + $xml_item .= createContainer('genres', $xml_genres); + } + + // actors + $actors = explode ("\n",$item['actors']); + if (count($actors)) + { + $xml_actors = ''; + foreach ($actors as $actor) + { + $xml_actor_data = ''; + $actor_data = explode("::",$actor); + if (array_key_exists('1', $actor_data)) + { + $xml_actor_data .= createTag('name', $actor_data[0]); + } + else + { + $xml_actor_data .= createTag('name', ''); + } + if (array_key_exists('1', $actor_data)) + { + $xml_actor_data .= createTag('role', $actor_data[1]); + } + else + { + $xml_actor_data .= createTag('role', ''); + } + if (array_key_exists('2', $actor_data)) + { + $xml_actor_data .= createTag('imdbid', $actor_data[2]); + } + else + { + $xml_actor_data .= createTag('imdbid', ''); + } + $xml_actors .= createContainer('actor', $xml_actor_data); + } + $xml_item .= createContainer('actors', $xml_actors); + } + $xml .= createContainer('item', $xml_item); + } + + $xml = '<?xml version="1.0" encoding="utf-8"?>'. + "\n".createContainer('catalog', $xml); + +// header('Content-type: text/xml'); + $mime = (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? 'application/force-download' : 'application/octet-stream'; + header('Content-type: '.$mime); + header('Content-length: '.strlen($xml)); + header('Content-disposition: attachment; filename=videoDB.xml'); + + echo $xml; +} + +/** + * Update RSS File + * + * @author Mike Clark <mike.clark@cinven.com> + */ +function rssexport($WHERE) +{ + global $config, $rss_timestamp_format, $filter; + + // make sure server doesn't specify something else + header('Content-type: text/xml; charset=utf-8'); + + if ($filter) + { + $result = exportData($WHERE); + } + else + { + // get the latest items from the DB according to config setting + $SQL = 'SELECT id, title, plot, created + FROM '.TBL_DATA.' + ORDER BY created DESC LIMIT '.$config['shownew']; + $result = runSQL($SQL); + } + + // script root + $base = 'http://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']); + + // setup the RSS Feed + $rssfeed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + $rssfeed .= '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">'; + $rssfeed .= '<channel>'; + $rssfeed .= '<atom:link href="'.$base.'/index.php?export=rss" rel="self" type="application/rss+xml" />'; + $rssfeed .= createTag('title', 'VideoDB'); + $rssfeed .= createTag('link', $base.'/index.php?export=rss'); + $rssfeed .= createTag('description', 'New items posted on VideoDB'); + $rssfeed .= createTag('language', 'en-us'); + $rssfeed .= createTag('lastBuildDate', date($rss_timestamp_format)); + + // build the <item></item> section of the Feed + foreach ($result as $item) + { + $xml_item = createTag('title', $item['title']); + $xml_item .= createTag('link', $base.'/show.php?id='.$item['id']); + $xml_item .= createTag('description', $item['plot']); + $xml_item .= createTag('guid', $base.'/show.php?id='.$item['id']); + $xml_item .= createTag('pubDate', rss_timestamp($item['created'])); + + $rssfeed .= createTag('item', $xml_item, false); + } + $rssfeed .= '</channel>'; + $rssfeed .= '</rss>'; + + header('Content-type: text/xml'); +# header('Content-length: '.rssfeed($xml)); +# header('Content-disposition: filename=rss.xml'); + echo $rssfeed; +} + +?> diff --git a/videodb/delete.php b/videodb/delete.php new file mode 100644 index 0000000..b9b3f9f --- /dev/null +++ b/videodb/delete.php @@ -0,0 +1,78 @@ +<?php +/** + * Delete a video + * + * Handles the deletion of a video + * + * @package videoDB + * @author Andreas Gohr <a.gohr@web.de> + * @version $Id: delete.php,v 2.22 2013/03/10 16:20:31 andig2 Exp $ + */ + +require_once './core/functions.php'; + +/** + * input + */ +$id = req_int('id'); +$redirect = req_int('redirect'); + +/** + * Remove image from cache + * + * @author Andreas Goetz <cpuidle@gmx.de> + */ +function removeCacheFile($url) +{ + // get extension + if (preg_match("/\.(jpe?g|gif|png)$/i", $url, $matches)) + { + // check if file exists + if (cache_file_exists($url, $cache_file, CACHE_IMG, $matches[1])) + { + @unlink($cache_file); + } + } +} + +// check for localnet +localnet_or_die(); + +// @todo check if post, fail if not? + +// multiuser permission check +permission_or_die(PERM_WRITE, get_owner_id($id)); + +/* +// remove old cover image from cache +$SQL = 'SELECT imgurl FROM '.TBL_DATA.' WHERE id = '.$id; +$res = runSQL($SQL); +if (count($res)) +{ + removeCacheFile($res[0]['imgurl']); +} +*/ + +// remove actual data +runSQL('DELETE FROM '.TBL_DATA.' WHERE id = '.$id); +runSQL('DELETE FROM '.TBL_VIDEOGENRE.' WHERE video_id = '.$id); + +// clear smarty cache for this item +#!! this does not work- at least not with Smarty3 +#$smarty->cache->clear($id); + +// goto index instead of delete template +if ($redirect) +{ + header("Location: index.php?deleteid=$id"); + exit; +} + +// prepare templates +tpl_page(); + +// display templates +$smarty->assign('delete_meta', '<meta http-equiv="refresh"; content="1; url='.session_get('listview', 'index.php').'?'.strip_tags(SID).'">'); +tpl_display('delete.tpl'); + + diff --git a/videodb/doc/CHANGES b/videodb/doc/CHANGES new file mode 100644 index 0000000..8c198ad --- /dev/null +++ b/videodb/doc/CHANGES @@ -0,0 +1,731 @@ +# Version History + +## videoDB Rel. 4.0 + +### 13.03.13 +- Created nexgen template based on Foundation CSS library +- Added Foundation4 library +- Upgraded to Smarty 3.1.10 +- Released videoDB 4.0pre + +### 04.03.12 +- Upgraded to Smarty 3.1.8 +- Various PHP 5.4 compatibility fixes (Andreas Gtz) + +## videoDB Rel. 3.0 + +### 13.02.11 (3.1) +- Loads of engine fixes, especially but not limited to IMDB +- All bundled libraries upgraded + +### 01.04.10 +- Various PHP 5.3 compatibility fixes (Andreas Gtz) +- Enhanced PDF output (Muddle) +- Upgraded to Smarty 3 (Andreas Gtz) + +### 19.01.10 +- Added support for mod_fastcgi Basic authentication (Muddle) + +### 25.03.09 +- Replaced outdated amazon engine with new Amazon Web Service (Andreas Gtz) +- Replaced partically broken google images engine with new google REST api (Andreas Gtz) + +### 05.03.09 +- Released v3.0.3 + +### 25.02.09 +- Fixed dvdb engine (Chinamann) +- Fixed ofdb engine (Chinamann) +- Fixed test classes for dvdb and dvdpalace engines (Chinamann) +- Added test class for ofdb engine (Chinamann) +- Fixed allocine engine and test (Martin Vauchel & Andreas Gtz) +- Fixed various character encoding issues (Andreas Gtz) +- Rewrote basic authentication code- should now provide seamless integration (Andreas Gtz) + +### 21.08.08 +- Fixed dvdb engine (Chinamann) +- Fixed add_dvdb_barcode contrib (Chinamann) +- Fixed OFDB engine (Chinamann) + +### 20.01.08 (3.0a1) +- Upgraded database schema to utf8- you can now have videos of various languages in one db +- Revamped elegant template to support AJAX +- Out-of-the-box thumbnail generation for better image quality and performance +- Out-of-the-box support for PDF export +- Various performance fixes +- Added cantonese (Hong Kong) flag + +### 03.12.07 (pre v3.0) +- Added bundled fpdf and phpthumb libraries (Andreas Gtz) + +## videoDB Rel. 2.0 + +### 01.12.07 +- Fixed IMDB engine (Andreas Gtz) +- Upgraded xajax library to 0.5 (Andreas Gtz) +- Improved setup screen by calculating cache size in background (Andreas Gtz) +- Fixed various test cases (Andreas Gtz) + +### 08.09.07 +- Fixed PHP short tags (Andreas Gtz) +- Moved Smarty to /libs folder (Andreas Gtz) + +### 18.08.07 +- Fixed tv.com engine (Victor) +- Fixed allocine.fr engine (tedemo) + +### 10.08.07 +- Added support for youTube trailers (youtube developer key required) +- More engine test cases (Sebastien) + +### 15.07.07 +- Released videoDB 2.3.0 +- Upgraded Smarty to 2.6.18 (Andreas Gtz) +- Zillions of engine fixes +- Added automated test cases for engines (Andreas Gtz) + +### 06.02.07 +- Completely rewritten OFDB engine (beta state) (Chinamann) + +### 05.02.07 +- Fixed less privileged users can't use random, search, borrow and new/edit views even if the should (Chinamann) + +### 17.01.07 +- Fixed security hole in show.php which enabled users to see movies they don't have access to (Chinamann) +- Bugfix in stats.php: Don't count movies which are on the wishlist (Chinamann) + +### 16.01.07 +- Fixed dvdb engine and improved fetching performance (Chinamann) +- Fixed dvdpalace engine (Chinamann) +- Fixed JavaScript error in elegant.css (Chinamann) +- Added session status restore in httpClient (Chinamann) + +### 11.11.06 +- Added template screenshots for selected templates (Andreas Gtz) +- Fixed an installer issue when upgrading (Andreas Gtz) +- Fixed [ 1596452 ] displaying movie takes a long time (Andreas Gtz) + +### 10.10.06 +- Upgraded Smarty to 2.6.14 (Andreas Gtz) + +### 04.10.06 +- Updated dvdfr engine (Seb) +- Updated release scripts for new SF CVS settings (Andreas Gtz) + +### 03.10.06 +- Fixed IMDB cast issue (Andreas Gtz) +- Fixed include security issue Secunia advisory SA22184 (Andreas Gtz) + +### 05.07.06 +- Updated google image search engine according to google website updates (Andreas Gtz) + +### 28.03.06 +- added Aka search for IMDB (Andreas Gtz) +- split xml.tpl from header scripts for advanced and jeckyl template (Andreas Gtz) + +### 25.01.06 +- upgraded Smarty to 2.6.12 (Andreas Gtz) + +### 27.12.05 +- upgraded Smarty to 2.6.11 (Andreas Gtz) + +### 27.09.05 +- added dvdb user login support to acess FSK18 movies (Chinamann) + +### 23.09.05 +- added favicons by Paul Leclair (Andreas Gtz) +- added previous/next navigation to show template (Andreas Gtz) +- added new auto DiskId calculation. Now: "next free" before: "highest+1" (Chinamann) +- fixed external search (Chinamann) +- added convert_engines contrib to switch between dvdpalace and dvdb engine (Chinamann) +- added user and engine filter to refetchAllInfos contrib (Chinamann) +- added imdbID prefix history functions to dvdb and dvdpalace engine (Chinamann) +- fixed dvdb engine to avoid bad results if httpClient sends "Request Timeout" (Chinamann) + +### 21.09.05 +- added DVDB.de engine (Chinamann) +- changed barcode based adding contrib to use DVDB.de engine (Chinamann) +- changed LableWriter Export to use DVDB.de engine (Chinamann) +- added UTF8 support to httpClient (Chinamann) +- added imdb-id-prefix also to imdb and changed it in dvdpalace engine (Chinamann) + +### 01.09.05 +- PDF title formatting fix (Justin Pasher) +- Excel export (Chinamann) +- added Amazon.com engine (Andreas Gtz) +- added more IMDB genres (Andreas Gtz) +- added HTTP caching support (Andreas Gtz) +- upgraded Smarty to latest 2.6.10 (Andreas Gtz) + +### 31.07.05 +- Image max width/height values (pdf config file) (Justin Pasher) +- Image width/height values (in the generated PDF) (pdf config file) (Justin Pasher) +- Overall font size (pdf config file) (Justin Pasher) +- Display mediatype next to diskid in the title for the movies (Justin Pasher) +- Search form now display "Video Codec" in the "Search Fields" box (Justin Pasher) + +### 15.06.05 +- fixed db prefix problems in create- and upgradescripts (Chinamann) +- fixed "database not empty" problem even if different prefix is selected (Chinamann) +- fixed bug [ 1199833 ] Database prefix not added when MySQL database is not empty (Chinamann) + +### 24.05.05 +- added user to user permissions (Chinamann) +- updated dvdpalace engine (Chinamann) +- fixed bug [ 1122052 ] Automatic DiskID generation problem (Chinamann) + +### 01.04.05 +- upgraded Smarty to latest 2.6.8 (Andreas Gtz) + +### 23.02.05 +- added engine config options (Andreas Gtz) +- added prelimenary improved episodes support (Andreas Gtz) +- added add_tv_episodes + +### 10.02.05 +- added support for common table prefix for hosting environments (Andreas Gtz) +- added content drilldown to DvdInside engine (Andreas Gtz) +- added support for regrouping of articles (Andreas Gtz) +- added default values for list- and castcolumns to createtables.sql (Andreas Gtz) +- upgraded Smarty to latest 2.6.7 (Andreas Gtz) +- moved some language flags to images/flags/additional (Andreas Gtz) + +### 25.01.05 +- added HTTP basic auth support for use by RSS feed readers (Andreas Gtz) +- disabled IMDB browser access for anonymous users for security reasons +- added new language flags (Constantinos Neophytou) + +### 21.01.05 +- added langcheck tooltips update (Constantinos Neophytou) +- added greek language files (Constantinos Neophytou) +- applied multi-language patch (Constantinos Neophytou) + +### 17.01.05 +- added https patch (Andreas Gtz, thanks to Robert Hendricks) +- added removal of html tags in title and subtitle (Andreas Gtz) + +### 05.01.05 +- security fix for javascript attacks (Andreas Gtz, thanks to Holger van Lengerich) + +### 28.12.04 +- fixed phpthumb code sometimes generating duplicate thumbnails +- fixed custom fields not being copied from IMDB + +### 24.12.04 +- added cache-less mode to avoid caching of IMDB queries (Andreas Gtz) +- various template fixes (bug 1084553, etc) +- added swedish translations (Nisse Hellberg) +- several security fixes (Andreas Gtz, Steve Kemp) + +### 15.11.04 +- even more PDF export fixes (Andreas Gtz) + +### 05.11.04 +- PDF export fixes +- removed 'Adult' genres for users without permissions (Andreas Gtz) +- added default adult genre to the setup SQL (Andreas Gtz) + +### 27.10.04 +- added PDF export (Andreas Gtz) +- added advanced template (Elkin) + +### 15.10.04 +- added thumbnail scaling using phpThumb for saving bandwidth (Andreas Gtz) + +### 20.09.04 +- moved javascripts to separate files for re-use (Andreas Gtz) +- moved xml headers to separate template file for re-use (Andreas Gtz) +- added xml import/export (Andreas Gtz) +- separated search field names in templates from physical columns names (Andreas Gtz) +- upgraded smarty to 2.6.5 (Andreas Gtz) +- added custom smarty function html_checkbox to get rid of the {if $video.seen}{else}{/if} (Andreas Gtz) +- added tool to decode html entities into ANSI characters (contrib/decode_entities.php) +- fixed user dropdown (Mike Clark) + +### 10.09.04 +- added hierarchical language files (Idea by Elkin) +- added tool for finding recommendations (contrib/add_recommended_movies.php) +- added owner dropdown for multiuser mode (PimpGoblin) + +### 24.08.2004 +- added support for google image lookup (edit.tpl) +- allowed to store engine as id prefix (experimental: imdb:081547) +- upgraded search functionality to search any external site +- added tool to remove orphans from image cache (contrib/clean_unused_images.php) +- updated delete functionality to remove cover of deleted movie + +### 16.08.2004 +- updated to allow drilldown on individual countries (if separated by ,) +- major overhaul of installer (Andreas Gtz) + +### 15.08.2004 +- major overhaul of internal database structure +- improved developer documentation + +### 01.08.2004 +- Added indian language flag +- small updates to the manual + +### 03.07.2004 +- Not working search IMDB button fixed in default template + +### 29.06.2004 +- Bugfix for PHP installation with missing '.' in include_path + +### 28.06.2004 +- Bugfix for SQL error on user insert (Kris Senden) + +### 27.06.2004 +- install.php (Andreas Gtz) +- upgrade accross multiple versions at once +- jumps directly into upgrade step + +### 21.06.2004 +- french update by Cyril Duveau +- bugfix for german language file + +### 19.06.2004 +- fixed support for admin to change owner +- added per user seen value (statistics not updated yet) +- bugfix for default lookup.tpl (no engine support yet) + +### 11.06.2004 +- added amazon webservice (experimental) (Andreas Gtz) +- added compact.css (Andreas Gtz) +- modified imdboverwrite to be more flexible (Andreas Gtz) + +### 09.06.2004 +- small bugfix for contrib/tvtome.php +- added access keys to default template + +### 05.06.2004 +- Andreas Gtz: +- upgraded smarty 2.5 to 2.6.3 to ensure php5 compatibility +- modified html_image to allow max_width and max_height parameters for +modern template +- Andreas Gohr: +- fixed permission problems when logging out and in within the same +browser session +- added profile support to modern template +- fetching gueststars in contrib/tvtome.php + +### 03.06.2004 +- added <title> support for speaking html page titles (Andreas Gtz) + +### 01.06.2004 +- added installer support to write configuration back to +config.inc.php (Andreas Gtz) + +### 28.05.2004 +- Various changes by Andreas Gtz +- added httpClient support for gzip-encoding +- added httpClient support to remember redirects +- reformatted error messages and added links to install.php +- added preliminary multi-engine support +- added improved video copy support +- added referer patch by Mike Clark + +### 16.05.2004 +- added user profiles + +### 13.05.2004 +- bugfix for names containing quotes +- bugfix for modern template CSS by Justin Pasher + +### 10.05.2004 +- swedish language file by Nisse added +- spanish update by Elkin Fricke +- bugfix for tvtome.php by Elkin Fricke + +### 09.05.2004 +- Multiuser Bug on deletion fixed by Stephan Hesmer +- added a index.html (thx to Merle Reine) +- added dvdadd.pl to contrib (based upon sample script by Elkin +Fricke) +- Request to Borrow Feature added some time in between ;-) +- multiple minor changes and fixes by Andreas Gtz and some +others including some fixes on the IMDB integration + +### 04.02.2004 +- language updates + +### 29.12.2003 +- videoadd.pl supports the owner field +- default template now uses the full screen width + +### 21.12.2003 +- minor code cleanup +- manual additions +- use of security mechanisms for tvtome.php +- all images are tried in the template directory first +- VideoDB now checks for a loaded mysql PHP extension +- videoadd.pl now sets the created timestamp +- the # filter now matches foreign characters and special +chars, too (thx to Justin Lim) +- Now all fields from the videodata table are selected for list +views (index and search) - more freedom for template designers + +### 19.12.2003 +- bugfix for russian language file by Eugene Surov +- Optional adult check for certain genres + +### 29.11.2003 +- bugfix for IMDB Problem (thx to Andreas Gtz and others) +- more effective session init (Andreas Gtz) + +### 05.11.2003 +- no edit links on missing permissions in modern template +- admin can change owner (thx to Mike Clark) + +### 02.11.2003 +- small doc updates + +### 26.10.2003 +- New custom type for MPAA ratings by Tim M. Sanders +- bulgarian language update by drJekyll +- polish update by Maciej Witkowiak + +### 25.10.2003 +- french language update by Cyril Duveau +- minor tweaks for modern template + +### 23.10.2003 +- dutch language update by Kees de Bruin +- german language updated + +### 21.10.2003 +- small update for french language by Daniel Caujolle-Bert + +### 19.10.2003 +- added new cutom type 'movix' by Antonio Giungato +- added new default style 'metal' + +### 15.10.2003 +- new Multiplayer code based on many code contributions by Mike +Clark and some by "weber" + +### 03.10.2003 +- added new stylesheet for the default template by Joakim Sandn + +### 21.09.2003 +- spanish update by Elkin Fricke +- DB versioning + +### 17.09.2003 +- Added missing <?xml ?> header + +### 16.09.2003 +- Dutch update by Kees de Bruin + +### 15.09.2003 +- Manual and Helpbrowser added +- random view does no longer show wishlist items +- French update by Cyril Duveau +- Bulgarian Update by drJekyll +- movies are only marked as new when added not when changed anymore + +### 14.09.2003 +- Portuguese update by Tiago Fonseca, Brasilian Portuguese +by Marcus Leandro + +### 08.09.2003 +- Italian update by Antonio Giungato + +### 07.09.2003 +- language checker added to the contrib directory + +### 04.09.2003 +- all IMDB functions now use the new link formats +- imdbData now returns a hash + +### 30.08.2003 +- bugfix for new imdb link format by Charles Morgan + +### 29.08.2003 +- french update by Cyril Duveau +- patch to enable upload from an URL in Konquerer by Stephan Zalewski +- Userdefinable config table (by Stephan Zalewski) +- Brazillian portuguese Language by Marcus Leandro added + +### 27.08.2003 +- custom type for original title by Stephan Zalewski +- better cachedirectory checks + +### 26.08.2003 +- POST support for HTTP client by Andreas Gtz +- IMDB-Browser by Andreas Gtz + +### 25.08.2003 +- bulgarian language update by drJekyll + +### 23.08.2003 +- spanish update by Elkin Fricke +- bugfix for HTTP caching on windows +- lots of comments added its now possible to generate a API doc with phpDocumentor + +### 21.08.2003 +- small wishlist bugfix by Andreas Gtz +- Number of shown movies in new filter is cutomizable now (Patch by Stephan Zalewski) +- improvements to the year statistic + +### 18.08.2003 +- minor template tweaks (default) +- updated spanish translation by Elkin Fricke +- idea for year statistic implemented not finished yet + +### 17.08.2003 +- setup.php uses REPLACE now +- merged multi template with default +- new template by Andreas Gtz: modern +- Fixed bug with interfering environment variables (Thanks to +Raymond Scholz pointing that out) +- nocover image is now a transparent gif - Templates can use +their own. +- Added a new rating custom type which uses the IMDB rating as +a default +- Search now works with ' in queries (Fixes Ed O'Neill) +- Fixed error when selecting random on an empty database +- New Mediatype 'wanted' for building a wishlist of movies (Idea by +Andreas Gtz) +- Cover-Upload by Stephan Zalewski +- some template tuning + +### 11.08.2003 +- bugfix by Harald Leithner for mediadefault in setup.php +- bulgarian update by drJekyll +- minor template fixes + +### 10.08.2003 +- Enhancement of the imdbActor function +- Updated french language by Cyril Duveau +- local files in the cover field now work again +- setup.tpl is now independent from the available setup options +- changed the random function again please send me a mail if it +isn't random enough + +### 09.08.2003 +- New template (multi) and some minor codechanges (Andreas Gtz) +- Fix for missing director and country fields in show.php +- Not found actorthumbnails are retried after specified age + +### 07.08.2003 +- Patches by Andreas Gtz: +- fix for chunked encoding which makes http/1.1 possible +- enhanced imdbActor function +- bugfix for IMDB caching function +- Bulgarian language update by drJekyll + +### 05.08.2003 +- Spanish language updates by Elkin Fricke + +### 03.08.2003 +- IMDB cache reimplemented +- small fix for imdbActors by Andreas Gtz +- Translation update for russian language by Konstantin Boudnik + +### 31.07.2003 +- Dutch translation updated by Kees de Bruin + +### 30.07.2003 +- update for the portuguese languagefile by Nuno Nunes +- Some updates to the new proxy enabled download function +- Experimental support for actor fotos by Andreas Gtz + +### 29.07.2003 +- Bulgarian languagefile by drJekyll added +- spanish language update by ctRl +- Proxy support by Andreas Gtz + +### 28.07.2003 +- Bugfix for the ID assigning problem which made adding new videos impossible (sorry folks) +- image cacheing is now handled seperately by img.php + +### 26.07.2003 +- Small fix by Andreas Gtz for autoDiskID calculation + +### 20-25.07.2003 +- Everything is better now! :-) I like to call it Version 2 +- Templates with Smarty +- Webbased, database stored config +- SQL function now returns associative arrays +- much code cleanup +- display bug in Linux Opera fixed +- a TODO file was added +- Style by drJeckyll added + +### 20.07.2003 +- Patch by Andreas Gtz to fix the genre links in show.php +- The TV-Episode switch is now stored in the session (Patch by Thomas Zajik) + +### 17.07.2003 +- Now checkboxlabels are clickable (patch by Kjetil Thuen) + +### 16.07.2003 +- Fixed the random function to produce more random values + +### 15.07.2003 +- Unavailable movies are now shown in red on the browsing view (Thanks to Alexandre Thomas for the idea) +- The french translation was corrected by Alexandre Thomas + +### 14.07.2003 +- Added russian language file by Konstantin Boudnik +- bugfix for orderallbydiskid + +### 05.07.2003 +- Incorporated lots of changes by Nicola Asuni (www.tecnick.com +info@tecnick.com) +- The output is in XHTML 1.0 Transitional +- some PHP bugs has been removed +- code cleanup +- directory hierachy was redesigned +- Added portuguese languagefile by Nuno Nunes +- The style can be changed from the config file + +### 21.06.2003 +- changed error_reporting to show fatal errors and parse errors this +should make debugging of custom function easier + +### 20.06.2003 +- added polish language file by Maciej Witkowiak +- charsetencoding is now set with the languagefile +- czech and polish flags added + +### 19.06.2003 +- You can now define your own input/output functions for the custom fields. see custom.php +- videoadd.pl now uses mplayer for getting video informations, it can create md4 sums for edonkey links, too (thx to Frank Schubert for the idea) +- The recursion bug in videoadd now really is fixed + +### 12.06.2003 +- added missing top.gif + +### 09.06.2003 +- some more language flags and a new config option for language quick select buttons were added +- cosmetic changes +- infinite recursion fixed in videoadd.pl + +### 01.06.2003 +- Cast is displayed in multiple columns now (Idea by Andreas Gtz) +- videoadd.pl now scans recursivly for files - this should work with (S)VCDs can anyone confirm that? + +### 30.05.2003 +- fixed a small bug in the IMDB cast parser + +### 28.05.2003 +- Updated the README a little bit + +### 25.05.2003 +- back to top link on every page +- Patches by Andreas Gtz +- addslashes for delete JavaScript (fixes problems with "Charlie's Angels") +- The filtersetting is now saved in a session + +### 24.05.2003 +- added french language file by Cyril Duveau +- disabled error_reporting in nondebugmode + +### 09.05.2003 +- Patches by Andreas Gtz +- bug fix for the new Cast IMDB parser (bad HREFs removed) +- you can set a default language for new movies +- thumbnails when browsing - sweet :-) + +### 22.04.2003 +- added patches by Raymond Scholz: +- little bug on custom fields fixed +- IMDB parser now gets the runtime right on movies >999 min +- The cast is now fetched from the extended credits page + +### 17.04.2003 +- videoadd.pl now always sets the mediatype to CD-R and a bug in the titelguesser was fixed + +### 23.03.2003 +- more statistics + +### 18.03.2003 +- Dutch translation by Kees de Bruin added +- IMDB parser now splits Title from Subtitle at " - " instead of "-" (patch by Kees de Bruin) + +### 17.03.2003 +- added some JavaScript to enable the IMDB lookup by default when the IMBD-ID is changed +- added an option to let IMDB lookups replace existing data + +### 16.03.2003 +- various patches by "unknown": +- saving new items without a genre with no errors from functions.php line 87 +- stop imdb lookup when searching on empty title/subtitle (window still shows but it is faster as it does nothing) +- stop imdb lookup when selected checkbox "lookup missing data from imdb" and there is no imdb item value before clicking save +- option to show listing entries sorted by diskid +- allow editing seen option from inside edit screen +- show total hours seen (watched) in statistics +- added "Adult" genre +- IMDB caching now works on redirects, too + +### 15.03.2003 +- added spanish translation contributed by "Orko" + +### 06.03.2003 +- linked the categories from the statistics page to the search + +### 02.03.2003 +- applied some Patches by Cameron W. Johnston to add support for +various writable DVD formats + +### 04.02.2003 +- fixed a problem with duplicate keys on genre inserts + +### 02.02.2003 +- The default filter can be set in the config (thx to Dale Blount) +- removed the useless 'Edit' and 'View' link on the respective pages (thx to Stephen Tomkinson) +- only nonempty fields are shown in the movie view (thx to Stephen Tomkinson) +- Category names are linked to the search (thx to Stephen again) +- empty years default to 0000 instead 2002 now +- Version tag added + +### 30.01.2003 +- Useragent of IMDB grabber is now IE and IMDB queries are now cached locally (thx to Marc Schoenefeld and Andreas Gtz) + +### 25.01.2003 +- new Perl script direxport.pl added +- splitbrain link at the bottom now links directly to the videodb page + +### 24.01.2003 +- fixed another small bugs in the IMDBgrabber and added a new search.gif (again: all the fame goes to Antonio :-)) + +### 23.01.2003 +- made search DISTINCT (thx to Antonio Giungato) +- IMDBfetcher now grabs 'Runtime: USA: 110 min' correctly (thx to Antonio again) +- added mediatype dropdown (thx to Rob Vonk) +- added custom fields +- limit edit to localnet only (thx to Jens Oberender) + +### 20.01.2003 +- "column 'comment' specified twice" error removed (ARGG! STUPID ME!) +- languages with no flag available don't result in a broken image anymore + +### 19.01.2003 +- started a changefile ;-) +- you may add a comment to each movie (currently 255 chars - mail me if that is a problem) +- "lent tofred" solved +- diskid '0' is possible now +- actors and director is linked to the search (works pretty well) +- changed the link color +- borrow manager links to the first movie on disk +- magic quote problems hopfully solved forever + +### 16.01.2003 +- better borrowmanager, various smaller bugfixes and improvements + +### 12.01.2003 +- It now works with register_globals=off. +- Title and subtilefetching added. +- Problems with gpc_magicquotes solved. + +### 11.01.2003 +- Much better IMDB Search :-) + +### 06.01.2003 17:00 +- Fixed a typo in the english language file +- Grabbing the plot and image URL from IMDB now works again. + +### 06.01.2003 +- Thanks to Marcel Spitau <marcel at spitau dot de> VideoDB now supports multiple languages. Currently english and german is available. But adding your own language is easy. If you do so please send in your language file! diff --git a/videodb/doc/COPYING b/videodb/doc/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/videodb/doc/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/videodb/doc/INSTALL b/videodb/doc/INSTALL new file mode 100644 index 0000000..f066b0f --- /dev/null +++ b/videodb/doc/INSTALL @@ -0,0 +1 @@ +See README \ No newline at end of file diff --git a/videodb/doc/README b/videodb/doc/README new file mode 100644 index 0000000..5c99eec --- /dev/null +++ b/videodb/doc/README @@ -0,0 +1,66 @@ +INSTALL + + - Be sure to have a recent PHP version! At least 5.3 with GD library is required + - copy all files in a directory somewhere below your webserver root + + Do one of these steps: + + - point your browser the install.php and follow the instructions + + or (not recommended since v2.0) + + - create a MySQL-Database using the table definitions in install/install.sql + - edit the config.inc.php to match your database + + - point your browser to your new VideoDB and add some movies + - customize everything in the config screen + +UPDATING FROM A PRIOR VERSION + + - extract the new videoDB archive into the same folder as your old installation + - open videoDB- the system will recognize that upgrade is required and start install.php + + or + + - manually point your browser to install.php + + - follow the instructions + + or (not recommended since v2.0) + + - run doc/updatedb.sql over your existing database with the -f option to + force mysql to ignore errors. This *should* not delete any of your data. + e.g. + mysql -p -f VideoDB < doc/updatedb.sql + + - check out new config options in the config screen + +CUSTOMIZING + + - You can define up to four custom fields in the config screen + - You can add input and output functions for them in custom.php + - You can add additional stylesheets by dropping them into the template + directory (e.g. templates/default/) + - You can create your own templates by putting them into their own + directory in templates/ + - Whenever you do anything of the above please send it to me + +PROBLEMS/FEEDBACK + + - Use the mailinglist to contact the developers at + http://lists.sourceforge.net/mailman/listinfo/videodb-devel + +THANKS + + - all the people mentioned in the CHANGELOG + - all th people who made the wonderful free software this project is + based on (e.g. Developers of PHP, Smarty, Apache, MySQL, Linux) + +LICENSE + + VideoDB is released under the GNU General Public License (GPL) + See COPYING for more Info + + VideoDB comes with the Smarty Template Engine + Smarty is released under the GNU Lesser General Public License (LGPL) + See COPYING.lib in the smarty directory for more Info diff --git a/videodb/doc/TODO b/videodb/doc/TODO new file mode 100644 index 0000000..e4e4fd0 --- /dev/null +++ b/videodb/doc/TODO @@ -0,0 +1,30 @@ +Here are some things that still have to be done. I add my thoughts +about them, too. So you have an idea how likely you will see them +in the next version. +If you want to do some of them *please* do so! I'm drowning in work. + +This file is always slightly out of date - to have full insight in +the ongoing development process join the mailing list! + +* Template-Creation HOWTO + - which variables are available in which template and so on + +* Update/Add language files + - Yes please do so! You may even correct my bad english! + +* Export-Function + - There were some user with ideas for this + - export current shown list (browse, search) or all to txt, + pdf, csv ... + - Is this really needed? what for? + - there is mklist.pl in the contrib dir... + +* Platform-Independent CD-Scanner to add files + - I currently use videoadd.pl for that but it depends on perl + and mplayer + - a small JAVA tool would be really cool I could write one but I + don't know of any java libs to extract video infos from files + - No, there will be no PHP Disk browser in VideoDB - my webserver + does not even have a CDROM drive + - There is some Database for DVD like CDDB for AudioCDs it should + be used, too diff --git a/videodb/doc/VERSION b/videodb/doc/VERSION new file mode 100644 index 0000000..02d2381 --- /dev/null +++ b/videodb/doc/VERSION @@ -0,0 +1 @@ +4_0_0 diff --git a/videodb/doc/development/README.txt b/videodb/doc/development/README.txt new file mode 100644 index 0000000..3f40c16 --- /dev/null +++ b/videodb/doc/development/README.txt @@ -0,0 +1,5 @@ +The database design was modeled with FabForce DBModeler + +To work with this model download from http://www.fabforce.net/downloads.php + +cpuidle@gmx.de diff --git a/videodb/doc/development/datamodel.png b/videodb/doc/development/datamodel.png new file mode 100644 index 0000000..75af87b Binary files /dev/null and b/videodb/doc/development/datamodel.png differ diff --git a/videodb/doc/development/videoDB Data Model v2.xml b/videodb/doc/development/videoDB Data Model v2.xml new file mode 100644 index 0000000..988dad7 --- /dev/null +++ b/videodb/doc/development/videoDB Data Model v2.xml @@ -0,0 +1,803 @@ +<?xml version="1.0" standalone="yes" ?> +<DBMODEL Version="4.0"> +<SETTINGS> +<GLOBALSETTINGS ModelName="videoDB v2" IDModel="3" IDVersion="4" VersionStr="1.0.0.1" Comments="" UseVersionHistroy="1" AutoIncVersion="1" DatabaseType="MySQL" ZoomFac="91.00" XPos="151" YPos="310" DefaultDataType="5" DefaultTablePrefix="0" DefSaveDBConn="dbdesigner" DefSyncDBConn="videodb_localhost" DefQueryDBConn="" Printer="" HPageCount="4.0" PageAspectRatio="1.440892512336408" PageOrientation="1" PageFormat="A4 (210x297 mm, 8.26x11.7 inches)" SelectedPages="" UsePositionGrid="1" PositionGridX="20" PositionGridY="20" TableNameInRefs="0" DefaultTableType="0" ActivateRefDefForNewRelations="1" FKPrefix="" FKPostfix="" CreateFKRefDefIndex="0" DBQuoteCharacter="`" CreateSQLforLinkedObjects="0" DefModelFont="Tahoma" CanvasWidth="4096" CanvasHeight="2842" /> +<DATATYPEGROUPS> +<DATATYPEGROUP Name="Numeric Types" Icon="1" /> +<DATATYPEGROUP Name="Date and Time Types" Icon="2" /> +<DATATYPEGROUP Name="String Types" Icon="3" /> +<DATATYPEGROUP Name="Blob and Text Types" Icon="4" /> +<DATATYPEGROUP Name="User defined Types" Icon="5" /> +<DATATYPEGROUP Name="Geographic Types" Icon="6" /> +</DATATYPEGROUPS> +<DATATYPES> +<DATATYPE ID="1" IDGroup="0" TypeName="TINYINT" Description="A very small integer. The signed range is -128 to 127. The unsigned range is 0 to 255." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="1" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="2" IDGroup="0" TypeName="SMALLINT" Description="A small integer. The signed range is -32768 to 32767. The unsigned range is 0 to 65535." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="1" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="3" IDGroup="0" TypeName="MEDIUMINT" Description="A medium-size integer. The signed range is -8388608 to 8388607. The unsigned range is 0 to 16777215." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="1" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="4" IDGroup="0" TypeName="INT" Description="A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="1" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="0" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="5" IDGroup="0" TypeName="INTEGER" Description="A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="1" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="1" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="6" IDGroup="0" TypeName="BIGINT" Description="A large integer. The signed range is -9223372036854775808 to 9223372036854775807. The unsigned range is 0 to 18446744073709551615." ParamCount="1" OptionCount="2" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="UNSIGNED" Default="0" /> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="7" IDGroup="0" TypeName="FLOAT" Description="A small (single-precision) floating-point number. Cannot be unsigned. Allowable values are -3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to 3.402823466E+38." ParamCount="1" OptionCount="1" ParamRequired="1" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="precision" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="8" IDGroup="0" TypeName="FLOAT" Description="A small (single-precision) floating-point number. Cannot be unsigned. Allowable values are -3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to 3.402823466E+38." ParamCount="2" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="9" IDGroup="0" TypeName="DOUBLE" Description="A normal-size (double-precision) floating-point number. Cannot be unsigned. Allowable values are -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and 2.2250738585072014E-308 to 1.7976931348623157E+308." ParamCount="2" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="2" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="10" IDGroup="0" TypeName="DOUBLE PRECISION" Description="This is a synonym for DOUBLE." ParamCount="2" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="2" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="11" IDGroup="0" TypeName="REAL" Description="This is a synonym for DOUBLE." ParamCount="2" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="2" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="12" IDGroup="0" TypeName="DECIMAL" Description="An unpacked floating-point number. Cannot be unsigned. Behaves like a CHAR column." ParamCount="2" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="3" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="13" IDGroup="0" TypeName="NUMERIC" Description="This is a synonym for DECIMAL." ParamCount="2" OptionCount="1" ParamRequired="1" EditParamsAsString="0" SynonymGroup="3" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +<PARAM Name="decimals" /> +</PARAMS> +<OPTIONS> +<OPTION Name="ZEROFILL" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="14" IDGroup="1" TypeName="DATE" Description="A date. The supported range is \a1000-01-01\a to \a9999-12-31\a." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="15" IDGroup="1" TypeName="DATETIME" Description="A date and time combination. The supported range is \a1000-01-01 00:00:00\a to \a9999-12-31 23:59:59\a." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="16" IDGroup="1" TypeName="TIMESTAMP" Description="A timestamp. The range is \a1970-01-01 00:00:00\a to sometime in the year 2037. The length can be 14 (or missing), 12, 10, 8, 6, 4, or 2 representing YYYYMMDDHHMMSS, ... , YYYYMMDD, ... , YY formats." ParamCount="1" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +</DATATYPE> +<DATATYPE ID="17" IDGroup="1" TypeName="TIME" Description="A time. The range is \a-838:59:59\a to \a838:59:59\a." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="18" IDGroup="1" TypeName="YEAR" Description="A year in 2- or 4-digit format (default is 4-digit)." ParamCount="1" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +</DATATYPE> +<DATATYPE ID="19" IDGroup="2" TypeName="CHAR" Description="A fixed-length string (1 to 255 characters) that is always right-padded with spaces to the specified length when stored. values are sorted and compared in case-insensitive fashion according to the default character set unless the BINARY keyword is given." ParamCount="1" OptionCount="1" ParamRequired="1" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="BINARY" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="20" IDGroup="2" TypeName="VARCHAR" Description="A variable-length string (1 to 255 characters). Values are sorted and compared in case-sensitive fashion unless the BINARY keyword is given." ParamCount="1" OptionCount="1" ParamRequired="1" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="length" /> +</PARAMS> +<OPTIONS> +<OPTION Name="BINARY" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="21" IDGroup="2" TypeName="BIT" Description="This is a synonym for CHAR(1)." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="22" IDGroup="2" TypeName="BOOL" Description="This is a synonym for CHAR(1)." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="23" IDGroup="3" TypeName="TINYBLOB" Description="A column maximum length of 255 (2^8 - 1) characters. Values are sorted and compared in case-sensitive fashion." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="24" IDGroup="3" TypeName="BLOB" Description="A column maximum length of 65535 (2^16 - 1) characters. Values are sorted and compared in case-sensitive fashion." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="25" IDGroup="3" TypeName="MEDIUMBLOB" Description="A column maximum length of 16777215 (2^24 - 1) characters. Values are sorted and compared in case-sensitive fashion." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="26" IDGroup="3" TypeName="LONGBLOB" Description="A column maximum length of 4294967295 (2^32 - 1) characters. Values are sorted and compared in case-sensitive fashion." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="27" IDGroup="3" TypeName="TINYTEXT" Description="A column maximum length of 255 (2^8 - 1) characters." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="28" IDGroup="3" TypeName="TEXT" Description="A column maximum length of 65535 (2^16 - 1) characters." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="29" IDGroup="3" TypeName="MEDIUMTEXT" Description="A column maximum length of 16777215 (2^24 - 1) characters." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="30" IDGroup="3" TypeName="LONGTEXT" Description="A column maximum length of 4294967295 (2^32 - 1) characters." ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="31" IDGroup="3" TypeName="ENUM" Description="An enumeration. A string object that can have only one value, chosen from the list of values." ParamCount="1" OptionCount="0" ParamRequired="1" EditParamsAsString="1" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="values" /> +</PARAMS> +</DATATYPE> +<DATATYPE ID="32" IDGroup="3" TypeName="SET" Description="A set. A string object that can have zero or more values, each of which must be chosen from the list of values." ParamCount="1" OptionCount="0" ParamRequired="1" EditParamsAsString="1" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<PARAMS> +<PARAM Name="values" /> +</PARAMS> +</DATATYPE> +<DATATYPE ID="33" IDGroup="4" TypeName="Varchar(20)" Description="" ParamCount="0" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<OPTIONS> +<OPTION Name="BINARY" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="34" IDGroup="4" TypeName="Varchar(45)" Description="" ParamCount="0" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<OPTIONS> +<OPTION Name="BINARY" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="35" IDGroup="4" TypeName="Varchar(255)" Description="" ParamCount="0" OptionCount="1" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +<OPTIONS> +<OPTION Name="BINARY" Default="0" /> +</OPTIONS> +</DATATYPE> +<DATATYPE ID="36" IDGroup="5" TypeName="GEOMETRY" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="38" IDGroup="5" TypeName="LINESTRING" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="39" IDGroup="5" TypeName="POLYGON" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="40" IDGroup="5" TypeName="MULTIPOINT" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="41" IDGroup="5" TypeName="MULTILINESTRING" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="42" IDGroup="5" TypeName="MULTIPOLYGON" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +<DATATYPE ID="43" IDGroup="5" TypeName="GEOMETRYCOLLECTION" Description="Geographic Datatype" ParamCount="0" OptionCount="0" ParamRequired="0" EditParamsAsString="0" SynonymGroup="0" PhysicalMapping="0" PhysicalTypeName="" > +</DATATYPE> +</DATATYPES> +<COMMON_DATATYPES> +<COMMON_DATATYPE ID="5" /> +<COMMON_DATATYPE ID="8" /> +<COMMON_DATATYPE ID="20" /> +<COMMON_DATATYPE ID="15" /> +<COMMON_DATATYPE ID="22" /> +<COMMON_DATATYPE ID="28" /> +<COMMON_DATATYPE ID="26" /> +<COMMON_DATATYPE ID="33" /> +<COMMON_DATATYPE ID="34" /> +<COMMON_DATATYPE ID="35" /> +</COMMON_DATATYPES> +<TABLEPREFIXES> +<TABLEPREFIX Name="Default (no prefix)" /> +</TABLEPREFIXES> +<REGIONCOLORS> +<REGIONCOLOR Color="Red=#FFEEEC" /> +<REGIONCOLOR Color="Yellow=#FEFDED" /> +<REGIONCOLOR Color="Green=#EAFFE5" /> +<REGIONCOLOR Color="Cyan=#ECFDFF" /> +<REGIONCOLOR Color="Blue=#F0F1FE" /> +<REGIONCOLOR Color="Magenta=#FFEBFA" /> +</REGIONCOLORS> +<POSITIONMARKERS> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +<POSITIONMARKER ZoomFac="-1.0" X="0" Y="0" /> +</POSITIONMARKERS> +</SETTINGS> +<METADATA> +<REGIONS> +<REGION ID="1368" RegionName="Actors" XPos="680" YPos="20" Width="320" Height="220" RegionColor="5" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="23" /> +<REGION ID="1366" RegionName="Attributes" XPos="320" YPos="260" Width="680" Height="260" RegionColor="3" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="21" /> +<REGION ID="1357" RegionName="User" XPos="320" YPos="540" Width="300" Height="380" RegionColor="2" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="20" /> +<REGION ID="1341" RegionName="Configuration" XPos="680" YPos="540" Width="320" Height="380" RegionColor="0" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="16" /> +<REGION ID="1342" RegionName="Base data" XPos="20" YPos="20" Width="280" Height="900" RegionColor="1" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="17" /> +<REGION ID="1367" RegionName="Borrow" XPos="320" YPos="20" Width="300" Height="220" RegionColor="4" TablePrefix="0" TableType="0" OverwriteTablePrefix="0" OverwriteTableType="0" Comments="" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="22" /> +</REGIONS> +<TABLES> +<TABLE ID="1216" Tablename="actors" PrevTableName="" XPos="740" YPos="60" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="2" > +<COLUMNS> +<COLUMN ID="1228" ColName="name" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1229" ColName="actorid" PrevColName="" Pos="3" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1230" ColName="imgurl" PrevColName="" Pos="4" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1231" ColName="checked" PrevColName="" Pos="5" idDatatype="16" DatatypeParams="(14)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<INDICES> +<INDEX ID="1232" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1228" LengthParam="0" /> +<INDEXCOLUMN idColumn="1229" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1234" IndexName="actorid" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1229" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1217" Tablename="config" PrevTableName="" XPos="760" YPos="600" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="" TableOptions="" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="3" > +<COLUMNS> +<COLUMN ID="1235" ColName="opt" PrevColName="" Pos="1" idDatatype="20" DatatypeParams="(50)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1236" ColName="value" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<INDICES> +<INDEX ID="1237" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1235" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1218" Tablename="genres" PrevTableName="" XPos="660" YPos="300" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="" TableOptions="" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="4" > +<COLUMNS> +<COLUMN ID="1238" ColName="id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1239" ColName="name" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_START> +<RELATION_START ID="1345" /> +</RELATIONS_START> +<INDICES> +<INDEX ID="1240" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1238" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1219" Tablename="lent" PrevTableName="" XPos="380" YPos="60" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="5" > +<COLUMNS> +<COLUMN ID="1241" ColName="diskid" PrevColName="" Pos="1" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1243" ColName="who" PrevColName="" Pos="3" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1244" ColName="dt" PrevColName="" Pos="4" idDatatype="16" DatatypeParams="(14)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_START> +<RELATION_START ID="1369" /> +</RELATIONS_START> +<INDICES> +<INDEX ID="1245" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1241" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1220" Tablename="mediatypes" PrevTableName="" XPos="360" YPos="420" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="6" > +<COLUMNS> +<COLUMN ID="1359" ColName="id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10))" Width="-1" Prec="-1" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1247" ColName="name" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_START> +<RELATION_START ID="1365" /> +</RELATIONS_START> +<INDICES> +<INDEX ID="1360" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1359" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1221" Tablename="userconfig" PrevTableName="" XPos="760" YPos="740" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="" TableOptions="" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="7" > +<COLUMNS> +<COLUMN ID="1249" ColName="user_id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(11)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1250" ColName="opt" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(50)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1251" ColName="value" PrevColName="" Pos="3" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_END> +<RELATION_END ID="1339" /> +</RELATIONS_END> +<INDICES> +<INDEX ID="1252" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1249" LengthParam="0" /> +<INDEXCOLUMN idColumn="1250" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1222" Tablename="users" PrevTableName="" XPos="360" YPos="700" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="" TableOptions="" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="8" > +<COLUMNS> +<COLUMN ID="1253" ColName="id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(11)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1254" ColName="name" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1255" ColName="passwd" PrevColName="" Pos="3" idDatatype="20" DatatypeParams="(100)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1256" ColName="cookiecode" PrevColName="" Pos="4" idDatatype="20" DatatypeParams="(100)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1257" ColName="permissions" PrevColName="" Pos="5" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1258" ColName="timestamp" PrevColName="" Pos="6" idDatatype="16" DatatypeParams="(14)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1259" ColName="email" PrevColName="" Pos="7" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_START> +<RELATION_START ID="1330" /> +<RELATION_START ID="1339" /> +</RELATIONS_START> +<INDICES> +<INDEX ID="1260" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1253" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1261" IndexName="name" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1254" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1224" Tablename="videodata" PrevTableName="" XPos="40" YPos="60" TableType="0" TablePrefix="0" nmTable="0" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="10" > +<COLUMNS> +<COLUMN ID="1265" ColName="id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1266" ColName="md5" PrevColName="" Pos="2" idDatatype="20" DatatypeParams="(32)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1267" ColName="title" PrevColName="" Pos="3" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1268" ColName="subtitle" PrevColName="" Pos="4" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1269" ColName="language" PrevColName="" Pos="5" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1270" ColName="diskid" PrevColName="" Pos="6" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1271" ColName="comment" PrevColName="" Pos="7" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1272" ColName="disklabel" PrevColName="" Pos="8" idDatatype="20" DatatypeParams="(32)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1273" ColName="imdbID" PrevColName="" Pos="9" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1274" ColName="year" PrevColName="" Pos="10" idDatatype="18" DatatypeParams="(4)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1275" ColName="imgurl" PrevColName="" Pos="11" idDatatype="27" DatatypeParams="" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1276" ColName="director" PrevColName="" Pos="12" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1277" ColName="actors" PrevColName="" Pos="13" idDatatype="28" DatatypeParams="" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1278" ColName="runtime" PrevColName="" Pos="14" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1279" ColName="country" PrevColName="" Pos="15" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1280" ColName="plot" PrevColName="" Pos="16" idDatatype="28" DatatypeParams="" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1281" ColName="filename" PrevColName="" Pos="17" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1282" ColName="filesize" PrevColName="" Pos="18" idDatatype="5" DatatypeParams="(16)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1283" ColName="filedate" PrevColName="" Pos="19" idDatatype="15" DatatypeParams="" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1284" ColName="audio_codec" PrevColName="" Pos="20" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1285" ColName="video_codec" PrevColName="" Pos="21" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1286" ColName="video_width" PrevColName="" Pos="22" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1287" ColName="video_height" PrevColName="" Pos="23" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1288" ColName="istv" PrevColName="" Pos="24" idDatatype="1" DatatypeParams="(1)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1290" ColName="seen" PrevColName="" Pos="26" idDatatype="1" DatatypeParams="(1)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1291" ColName="mediatype" PrevColName="" Pos="27" idDatatype="5" DatatypeParams="(10))" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1292" ColName="custom1" PrevColName="" Pos="28" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1293" ColName="custom2" PrevColName="" Pos="29" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1294" ColName="custom3" PrevColName="" Pos="30" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1295" ColName="custom4" PrevColName="" Pos="31" idDatatype="20" DatatypeParams="(255)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1289" ColName="lastupdate" PrevColName="" Pos="25" idDatatype="16" DatatypeParams="(14)" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1296" ColName="created" PrevColName="" Pos="32" idDatatype="15" DatatypeParams="" Width="0" Prec="0" PrimaryKey="0" NotNull="0" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1297" ColName="owner_id" PrevColName="" Pos="33" idDatatype="5" DatatypeParams="(11)" Width="0" Prec="0" PrimaryKey="0" NotNull="1" AutoInc="0" IsForeignKey="0" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_START> +<RELATION_START ID="1329" /> +<RELATION_START ID="1344" /> +</RELATIONS_START> +<RELATIONS_END> +<RELATION_END ID="1365" /> +<RELATION_END ID="1369" /> +</RELATIONS_END> +<INDICES> +<INDEX ID="1298" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1265" LengthParam="0" /> +<INDEXCOLUMN idColumn="1270" LengthParam="0" /> +<INDEXCOLUMN idColumn="1291" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1299" IndexName="seen" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1290" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1300" IndexName="title_idx" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1267" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1301" IndexName="diskid_idx" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1270" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1302" IndexName="mediatype" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1291" LengthParam="0" /> +<INDEXCOLUMN idColumn="1288" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1303" IndexName="actors_idx" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1277" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +<INDEX ID="1304" IndexName="comment" IndexKind="1" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1271" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1328" Tablename="userseen" PrevTableName="videodata_has_users" XPos="360" YPos="580" TableType="0" TablePrefix="0" nmTable="1" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="13" > +<COLUMNS> +<COLUMN ID="1336" ColName="video_id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1337" ColName="user_id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(11)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1370" ColName="diskid" PrevColName="" Pos="1" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_END> +<RELATION_END ID="1329" /> +<RELATION_END ID="1330" /> +</RELATIONS_END> +<INDICES> +<INDEX ID="1332" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1336" LengthParam="0" /> +<INDEXCOLUMN idColumn="1337" LengthParam="0" /> +<INDEXCOLUMN idColumn="1370" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +<TABLE ID="1343" Tablename="videogenre" PrevTableName="videodata_has_genres" XPos="360" YPos="300" TableType="0" TablePrefix="0" nmTable="1" Temporary="0" UseStandardInserts="0" StandardInserts="\n" TableOptions="DelayKeyTblUpdates=0\nPackKeys=0\nRowChecksum=0\nRowFormat=0\nUseRaid=0\nRaidType=0\n" Comments="" Collapsed="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="18" > +<COLUMNS> +<COLUMN ID="1352" ColName="genre_id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1354" ColName="video_id" PrevColName="" Pos="1" idDatatype="5" DatatypeParams="(10)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="1" /> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +<COLUMN ID="1371" ColName="diskid" PrevColName="" Pos="1" idDatatype="20" DatatypeParams="(15)" Width="0" Prec="0" PrimaryKey="1" NotNull="1" AutoInc="0" IsForeignKey="1" DefaultValue="" Comments=""> +<OPTIONSELECTED> +<OPTIONSELECT Value="0" /> +</OPTIONSELECTED> +</COLUMN> +</COLUMNS> +<RELATIONS_END> +<RELATION_END ID="1344" /> +<RELATION_END ID="1345" /> +</RELATIONS_END> +<INDICES> +<INDEX ID="1347" IndexName="PRIMARY" IndexKind="0" FKRefDef_Obj_id="-1"> +<INDEXCOLUMNS> +<INDEXCOLUMN idColumn="1352" LengthParam="0" /> +<INDEXCOLUMN idColumn="1354" LengthParam="0" /> +<INDEXCOLUMN idColumn="1371" LengthParam="0" /> +</INDEXCOLUMNS> +</INDEX> +</INDICES> +</TABLE> +</TABLES> +<RELATIONS> +<RELATION ID="1329" RelationName="was seen by" Kind="1" SrcTable="1224" DestTable="1328" FKFields="id=video_id\ndiskid=diskid\nmediatype=mediatype\n" FKFieldsComments="\n\n\n" relDirection="2" MidOffset="-21" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="14" /> +<RELATION ID="1330" RelationName="has seen" Kind="1" SrcTable="1222" DestTable="1328" FKFields="id=user_id\n" FKFieldsComments="\n" relDirection="1" MidOffset="10" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="15" /> +<RELATION ID="1339" RelationName="has config options" Kind="2" SrcTable="1222" DestTable="1221" FKFields="id=user_id\n" FKFieldsComments="\n" relDirection="2" MidOffset="0" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="15" /> +<RELATION ID="1344" RelationName="is of genres" Kind="1" SrcTable="1224" DestTable="1343" FKFields="id=video_id\ndiskid=diskid\nmediatype=mediatype\n" FKFieldsComments="\n\n\n" relDirection="2" MidOffset="-20" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="19" /> +<RELATION ID="1345" RelationName="genres are used by" Kind="1" SrcTable="1218" DestTable="1343" FKFields="id=genre_id\n" FKFieldsComments="\n" relDirection="4" MidOffset="0" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="20" /> +<RELATION ID="1365" RelationName="has type" Kind="0" SrcTable="1220" DestTable="1224" FKFields="id=mediatype\n" FKFieldsComments="\n" relDirection="4" MidOffset="-12" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="21" /> +<RELATION ID="1369" RelationName="is lent by" Kind="0" SrcTable="1219" DestTable="1224" FKFields="diskid=diskid\n" FKFieldsComments="\n" relDirection="4" MidOffset="-32" OptionalStart="0" OptionalEnd="0" CaptionOffsetX="0" CaptionOffsetY="0" StartIntervalOffsetX="0" StartIntervalOffsetY="0" EndIntervalOffsetX="0" EndIntervalOffsetY="0" CreateRefDef="1" Invisible="0" RefDef="Matching=0\nOnDelete=3\nOnUpdate=3\n" Comments="" FKRefDefIndex_Obj_id="-1" Splitted="0" IsLinkedObject="0" IDLinkedModel="-1" Obj_id_Linked="-1" OrderPos="24" /> +</RELATIONS> +<NOTES> +</NOTES> +<IMAGES> +</IMAGES> +</METADATA> +<PLUGINDATA> +<PLUGINDATARECORDS> +</PLUGINDATARECORDS> +</PLUGINDATA> +<QUERYDATA> +<QUERYRECORDS> +</QUERYRECORDS> +</QUERYDATA> +<LINKEDMODELS> +</LINKEDMODELS> +</DBMODEL> diff --git a/videodb/doc/manual/browse.html b/videodb/doc/manual/browse.html new file mode 100644 index 0000000..2b8f9b0 --- /dev/null +++ b/videodb/doc/manual/browse.html @@ -0,0 +1,36 @@ +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr"> + <head> + <title>VideoDB - Documentation + + + +
      Table of Contents + + +

      Usage

      + +

      Browsing the Database

      + +

      The browse view allows you to list the content of your database by applying filters to the available data. On the top of the screen you'll find a row of radio buttons to list the available titles sorted by the first letter of the title.
      This means if you select the DEF filter only movies beginning with a D, E or F are shown in the list.

      + +

      When adding movies it is a good practice to move common words like the or a to the end of the title to have the sorting make more sense.

      + +

      Example: A Clockwork Orange becomes Clockwork Orange, A + +

      VideoDB treats TV episodes special, which means they are not shown by default. To show them, show TV episodes must be enabled. You can change this behavior in the configuration.

      + +

      When selecting the all filter all movies are shown - please note that this can take some time if you have a very large database!
      +Usually the list is always sorted lexically, you may change the sort order of the all filter to sort by the DiskID in the configuration.

      + +

      The unseen filter just shows you the movies that are not marked as seen yet by you.

      + +

      When using the new filter only the 15 newest additions to your database are shown. You can change the number of shown files here in the configuration. + + +

      The last filter is the Wishlist. Movies shown here are not yet owned by you and will not show up in any of the other views (not even with the all filter). To add a movie to your wishlist just add like every other movie but set its mediatype to wanted.

      + + + + + + \ No newline at end of file diff --git a/videodb/doc/manual/configview.html b/videodb/doc/manual/configview.html new file mode 100644 index 0000000..0e5f5b6 --- /dev/null +++ b/videodb/doc/manual/configview.html @@ -0,0 +1,59 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      The Configuration Screen

      + +

      The Cache Control Bar

      + +

      Changing the configuration

      + +

      The custom types

      + +

      There are the following customtypes available:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      textThis is a simple textfield nothing special about it
      urlThis shows a clickable link in the show view
      ed2kYou may enter a MD4 sum and a clickable eDonkey link will be generated from this field, the filesize and the filename
      languageThis is another language field. It will display the same languageflags for quickentry as the standard one.
      ratingYou can rate a movie with this field. It can be filled from IMDB.
      orgtitleThis is the same as the standard title field, it gets filled from IMDB. This is useful if you store translated title of the movie in the title field.
      movixIf you use Movix on your CDs you can choose the version with this field.
      mpaaThis field can hold the movie rating of the Motion Picture Association of Amerika. It gets filled from the IMDB.
      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/contrib.html b/videodb/doc/manual/contrib.html new file mode 100644 index 0000000..e5eccb7 --- /dev/null +++ b/videodb/doc/manual/contrib.html @@ -0,0 +1,57 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Additional Scripts

      + +

      General

      + +

      VideoDB comes with some additional Scripts to do certain tasks, using them is completely optional and not needed for a functional VideoDB. All these files are stored in the contrib directory.
      +Some of them are written in Perl and you have to edit them to match your database auth options. Some knowledge of Perl and SQL may be helpful.

      + +

      If you wrote something you think others could find useful just send it to the developers mailing list (videodb-devel@lists.sourceforge.net) or join the Developer Mailinglist and post it there.

      + + +

      mklist.pl

      + +

      This generates a plaintextlist of all files in the database. Edit the variables at the top

      + + +

      setGenre.pl

      + +

      This script is useful to set the same genres for a bunch of entries based on their title. For example setting all Simpsons episodes to Animation and Comedy. You have to edit some variables at top of the file to make it do what you want.

      + + +

      videoadd.pl

      + +

      This tool is very useful to automatically add some videofiles to the database. It mounts your CD-ROM drive, searches for video files, tries to guess the title and some other infos from the filename and adds them to the database. It uses mPlayer to extract video informations from the file. It accepts the path to your CD-ROM drive as argument.

      + + +

      direxport.pl

      + +

      This creates single html pages from the movies in the database. It allows me to browse the database content with my IR File Chooser tool.

      + + +

      langcheck.php

      + +

      This PHP tool checks all language files for missing translations and displays the missing keys. This is useful for Translators. Just call it from your webbrowser like this: http://myserver.com/videodb/contrib/langcheck.php

      + + +

      tvtome.php

      + +

      To use this PHP tool call it from your webbrowser like this: http://myserver.com/videodb/contrib/twtome.php. This tool can fetch episode infos from www.tvtome.com and add the data to the episodes in your VideoDB.

      + +

      You need to enter the complete URL to the TV shows episode guide at tvtome (eg. http://www.tvtome.com/Futurama/guide.html for Futurama) and some Searchstring to select the episodes of this show stored in your VideoDB (eg. Futurama* to find all movies with their titles beginning with Futurama)

      + +

      After you press the "submit query" button the tool fetches the episode guide and lists all episodes. For each episode you have to choose the matching movie in your VideoDB. The tool tries to suggest the matching title based upon an similarity comparison. For each episodes data you want to import you need to check the checkbox. Then pressing thefinal submit button the selected data is imported.

      + +

      You need to have Admin rights to use this tool in multiuser mode.

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/detailview.html b/videodb/doc/manual/detailview.html new file mode 100644 index 0000000..aebd682 --- /dev/null +++ b/videodb/doc/manual/detailview.html @@ -0,0 +1,21 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      The Detail View

      + +

      When clicking on a title in the Browse View, you're presented with the movies detail view. It's pretty self-explanatory. You just see all the inforamation thats stored in the database about the selected movie.
      +Clicking the cover image opens the IMDB page for the movie. Clicking an actors name will search for movies with that actor. The same applies to all other clickable labels.

      + +

      You can click the random link in the top menu to get a random view

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/devel.html b/videodb/doc/manual/devel.html new file mode 100644 index 0000000..a945506 --- /dev/null +++ b/videodb/doc/manual/devel.html @@ -0,0 +1,42 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Development

      + + +

      Developer Ressources

      + +

      If you want to join the development of VideoDB the first you should do is joining the Developer Mailinglist.

      + +

      To stay up to date you should also use the current CVS version of VideoDB to avoid writing something that maybe is already included there. You can get the current development version from the CVS Server either by browsing the Web-Interface or accessing the repository directly via anonymous CVS: + +

      +  cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/videodb login
      +  cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/videodb co videodb
      +  
      + +

      When prompted for a password just hit enter.

      + +

      Please note that the webbased access is up to 24 hours behind the real CVS Server so a real checkout is always to prefer.

      + + +

      Coding guidelines

      + +

      Here are some simple guidelines when contributing to VideoDB:

      + +
        +
      • Use an indention width of 4
      • +
      • Don't use tabs! Use spaces instead. Tabs look different in different editors, that makes problems...
      • +
      • Comment your code!
      • +
      • Use phpDocumentor tags to explain your functions
      • +
      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/faq.html b/videodb/doc/manual/faq.html new file mode 100644 index 0000000..a06e37b --- /dev/null +++ b/videodb/doc/manual/faq.html @@ -0,0 +1,74 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      Frequently Asked Questions

      + +

      Here are some frequently asked questions:

      + +
      +
      I installed the database and changed the config.inc.php but when I try to browse the videodb it doesn't work.
      +
      Make sure your PHP installation has 'short_open_tag' enabled (should be by default).
      + +
      I want more Inputfields!
      +
      There are four custom fields! Use them! Read the comments in custom.php for informations how to change the output and input of them.
      + +
      My videos are not in english, german, french or spanish! What should I put into the language box?
      +
      Just put in your desired language it is just a freeform textfield. If you want to have a country flag displayed for it, place a GIF image named like the language into the flags directory. It should be 30x15 pixels in size. There is a README in the images/flags directory - read it.
      + +
      I can't borrow a video! How is this done?
      +
      You have to assign a diskid to it! If you have done so, select the video and click on [ borrow ] in the top row.
      + +
      My movies are stored on some really weird media! How can I add some new mediatypes?
      +
      Use your favourite MySQL tool to add them into the mediatypes table. Does anyone own LaserDisks? Should they be in by default?
      + +
      The Adult genre never get's filled by an IMDB lookup!
      +
      Yes - there is no such category in IMDB - You have to mark your pr0n yourself ;-)
      + +
      How does the wishlist work?
      +
      To add a movie to wishlist find the wanted movie on IMDB, add the movie as usual but choose wanted as mediatype - the movie will appear on your wishlist. When you finally get the movie you can change the mediatype setting to appropriated in the edit screen.
      + +
      I enabled the multiuser support. But now I can't login to add users!
      +
      There is a default user named admin created on database creation. An his password is - you guessed it - admin. BTW: Be sure to change the password once you logged in.
      + +
      Some of my movies are spread about multiple disks. How do I add them?
      +
      There are two ways people handle this. What I do is adding them twice (or how many disks you have) with some label like "CD2" or something in the title or subtitle field. Others use a custom field to store the number of disks - but this makes the file information fields a little bit useless. All other solutions would require drastic database changes.
      + +
      I have multiple movies on one disk. Is that a problem?
      +
      No. Just add any movie and give each the same diskID - All movies on the same disk become unavailable if you borrow the disk.
      + +
      Can I add some more genres?
      +
      Just add them with your favourite MySQL tool to the genres table.
      + +
      I tried to translate the english language file but when I select my newly created language everything stays in english!?
      +
      If there is a bug in the language file the default one is used. Try to load your file directly in the browser. This will parse the file and display an error message which should help you. Another hint: You have to escape doublequotes with a backslash.
      + +
      My cache/imdb directory is getting large - Can I delete the files in it?
      +
      Yes. Or just use the cache controlbar in the Config Screen
      + +
      Arghh! I locked my self out from the config screen by making a mistake in the localnet option.
      +
      Use your favourite MySQL tool to delete the option from the config table: DELETE FROM config WHERE opt='localnet';
      + +
      Hey your tool is great! Can I do anything for you?
      +
      Read, inform yourself and think before voting your next president.
      + +
      I want to support you and the development of VideoDB - can I donate something?
      +
      Of course you can :-D Use the PayPal Account
      + +
      Are all these questions really asked frequently or do you just make them up?
      +
      Yes :-)
      + +
      + +

      your question isn't answered here? just mail me :-)

      + + + + diff --git a/videodb/doc/manual/imdbbrowser.html b/videodb/doc/manual/imdbbrowser.html new file mode 100644 index 0000000..210cd65 --- /dev/null +++ b/videodb/doc/manual/imdbbrowser.html @@ -0,0 +1,20 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      The IMDB Browser

      + +

      This feature is disabled by default! Enable it in the configuration screen to use it.

      + +

      The IMDB Browser integrates the Internet Movie Database directly into VideoDB. When enabled you can access the IMDB website from the menubar. You can use their site as usual, but after every mentioned movie title you'll find a little + button. By pressing this button you can add this movie to your VideoDB. This feature is very useful to fill your wishlist.

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/index.html b/videodb/doc/manual/index.html new file mode 100644 index 0000000..6bc8e71 --- /dev/null +++ b/videodb/doc/manual/index.html @@ -0,0 +1,62 @@ + + + VideoDB - Documentation + + + + +

      VideoDB Online Manual

      +

      Table of Contents

      + + + +
      + Your Help is needed!
      +

      This manual is far from complete. Please help completing it by contributing to the documentation Wiki at + http://www.splitbrain.org/dokuwiki/vdb:videodb. Just visit the site and contribute.

      +

      The videoDB development team can be reached at videodb-devel@lists.sourceforge.net +

      +
      + + + + diff --git a/videodb/doc/manual/installation.html b/videodb/doc/manual/installation.html new file mode 100644 index 0000000..f2609ae --- /dev/null +++ b/videodb/doc/manual/installation.html @@ -0,0 +1,65 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Installation

      + +

      The examples in this document assumes you're using Linux and Apache. The Apaches document root is in /var/www and VideoDB is to be installed in /var/www/videodb. But it shouldn't be difficult for you to follow the steps with any other environment.

      + + +

      Requirements

      + +

      VideoDB works with the typical LAMP and WAMP environments. Here are the requirements in detail:

      + +
        +
      • A Webserver wich supports PHP (Apache recommended)
      • +
      • A recent version of PHP 4, at least 4.1.0 is required
      • +
      • A MySQL Database
      • +
      • A WebBrowser (something new is probably better)
      • +
      + + + +

      First time installation

      + +

      Unzip the tarball into a directory below your webserver document root and make the cache directories writable.

      + +

      Example:

      +
      +  $> cd /var/www
      +  $> tar -xzvf ~/videodb-*.tgz
      +  $> chmod 777 videodb/cache/*
      +  
      + +

      Then create a new database using the install.sql file in the install directory

      + +

      Example:

      +
      +  $> mysqladmin -p create videodb
      +  $> mysql -p videodb < /var/www/videodb/install/install.sql
      +  
      + + +

      Now edit the config.inc.php file to match your database settings.

      + +

      Thats it. Now point your webbrowser to your new videodb and add some movies

      + + + +

      Update from a previous version

      + +

      For updating from a previous version just delete all files but the cache directory and keep your database. Then unzip the new tarball as described above. Then apply the upgrade.sql to your database using the the -f parameter to ignore errors.

      + +

      Example:

      +
      +  $> mysql -p -f videodb < /var/www/videodb/install/upgrade.sql
      +  
      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/keys.html b/videodb/doc/manual/keys.html new file mode 100644 index 0000000..2dce6e4 --- /dev/null +++ b/videodb/doc/manual/keys.html @@ -0,0 +1,31 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      Access Keys

      + +

      The default template implements the use of access keys to speed up accessing certain parts +of VideoDB. Here is a list of currently assigned Keys.

      + + + + + + + + + + + +
      Key
      Alt+iBrings you back to the index page
      Alt+fOpens the search page to find a movie
      Alt+nThis creates a new entry in the database
      Alt+eUsing this combination you can edit the current movie
      Alt+bThis opens the borrowmanager
      Alt+hOpens the help with the user manual
      Alt+lUse this for llogging in or out
      Alt+sPressing this key lets you save entered formdata
      + + + + diff --git a/videodb/doc/manual/multiuser.html b/videodb/doc/manual/multiuser.html new file mode 100644 index 0000000..6b210a6 --- /dev/null +++ b/videodb/doc/manual/multiuser.html @@ -0,0 +1,30 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      Multiuser Options

      + +

      VideoDB can work with multiple user as an option. To enable this go to the Configuration Screen and check the Multiuser Support checkbox.

      + +

      Logging in

      + +

      When the Multiusermode is enabled a new menu item Login appears. Use your Username and Password to login. You must have cookies enabled or it won't work!! When you check the stay logged in checkbox the cookie will not expire after the actual browser session.

      + +

      On database creation an initial user with administration rights is created: Username: admin Password: admin

      + +

      Add and modify users

      + +

      Follow the link Usermanagement from the Configuration Screen.

      + +

      Be sure to change the password of the admin user!

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/profile.html b/videodb/doc/manual/profile.html new file mode 100644 index 0000000..dbe9cbd --- /dev/null +++ b/videodb/doc/manual/profile.html @@ -0,0 +1,18 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      User Profile

      + +needs to be written + + + + \ No newline at end of file diff --git a/videodb/doc/manual/search.html b/videodb/doc/manual/search.html new file mode 100644 index 0000000..76176df --- /dev/null +++ b/videodb/doc/manual/search.html @@ -0,0 +1,29 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      Searching the Database

      + +

      The search screen allows you to search for one or more movies that match your criteria.

      + +

      The simplest thing is to search for one or more keywords. Just put into the textfield and hit Search. The search is caseinsensitive and matches word parts, too. To search phrases you can enclose them in quotes ".

      + +

      Examples:
      + wood matches Ed Wood as well as Clint Eastwood
      + "Ed wood" only matches Ed Wood but Ed Wood matches Eddy got lost in Sherwood Forest., too
      +

      + +

      To refine your search you can use the boolean operators AND, OR and NOT

      + +

      You can restrict your search to some genres by selecting the wanted genres and to some database fields by selecting them in the selectbox. To select multiple fields hold the Ctrl-Key while selecting.

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/stylesheets.html b/videodb/doc/manual/stylesheets.html new file mode 100644 index 0000000..b635f5f --- /dev/null +++ b/videodb/doc/manual/stylesheets.html @@ -0,0 +1,19 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Development

      + +

      Stylesheets

      + +

      Instead of designing a whole new Template you can add new Stylesheets to existing templates. Just copy an exisisting stylesheet to a new name and adjust it to your match your ideas. All .css files become automatically available in the Configuration Screen.

      + + + + + \ No newline at end of file diff --git a/videodb/doc/manual/templates.html b/videodb/doc/manual/templates.html new file mode 100644 index 0000000..8752baa --- /dev/null +++ b/videodb/doc/manual/templates.html @@ -0,0 +1,142 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Development

      + +

      Templates

      + +

      General

      + +

      VideoDB uses the Smarty Template engine. Thus everybody interested in writing his own templates should have a look at the Smarty Documentation

      + +

      What follows is a list of all templates used by VideoDB and the variables assigned to them. Of course you can split your templates by using Smarty's {include} statement.

      + +

      All Templates

      + +

      The following variables are available in all templates:

      + + + + + + + + + + + + + + + + + + + +
      nametypedescription
      $langassociative arrayContains all language specific strings for the current language. Have a look at the files in the language directory for available keys.
      $configassociative arrayAll config options as set in the Configuration Screen
      + +

      header.tpl

      + +

      This template is included at the top of every page and should set up the needed HTML headers and include the stylesheet

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      nametypedescription
      $headerassociative arrayContains the URLs for the top navigation. The available keys are: browse, random, search, stats, new, setup, edit, view, del, borrow, imdbBrowser, help. You should check if the key is empty and only display a link when an URL was given.
      $stylestringDeprecated - use $config.style instead.
      $langcodestringDeprecated - use $config.language instead.
      $localnetbooleanTrue if VideoDB is accessed from the localnet else false.
      + +

      footer.tpl

      + +

      The footer is included at the bottom of every page

      + +

      filter.tpl

      + +

      This template displays the row of available filters in the browsing view.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      nametypedescription
      $filtersassociative arrayContains all available filters as keys and their Names (translations) as values
      $filterstringThe currently selected filter in the browsing view
      $showtvbooleanTrue if TV-Episodes are to be shown else false.
      $listcolumnsintegerThis is currently only used in the modern template and determines how many columns are to be used in the browsing view.
      + +

      browse.tpl

      + + + + + + + + + + + + + +
      nametypedescription
      $
      + + +

      Needs to be continued...

      + + + + + \ No newline at end of file diff --git a/videodb/doc/manual/translate.html b/videodb/doc/manual/translate.html new file mode 100644 index 0000000..a912eab --- /dev/null +++ b/videodb/doc/manual/translate.html @@ -0,0 +1,30 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Development

      + +

      Languages

      + +

      VideoDB already comes with a lot of languages, but if your's isn't supported why don't you become a translator?

      + +

      The first thing you should do is to join the Developer Mailinglist at SourceForge to stay informed when new language strings are added.

      + +

      To create a new language file just copy the en.php file in the language directory and translate the all the contained strings to your language. I think you'll get the system when you have a look at it ;-).
      +However pay attention to the $lang[encoding] field. This contains the character encoding you used in the file. You may use another encoding than iso-8859-1. See the bg.php as an example.
      +You should adjust the the header at the top of the file to reflect your authorship, too. ;-). When updating or correcting a file of someone else just add another @author line with your name.

      + +

      After translation you should send the file to the mailing list to be included in the next release.

      + +

      Sometimes translations get slightly behind the english version. Missing translations will are always replaced by the english strings.

      + +

      Hint: The langcheck.php tool can be very helpful to see a list of missing translations

      + + + + \ No newline at end of file diff --git a/videodb/doc/manual/usermanager.html b/videodb/doc/manual/usermanager.html new file mode 100644 index 0000000..aa40ef2 --- /dev/null +++ b/videodb/doc/manual/usermanager.html @@ -0,0 +1,37 @@ + + + VideoDB - Documentation + + + + Table of Contents + + +

      Usage

      + +

      Usermanagement

      + +

      In the usermanagement Screen you can add, delete and modify users for multiuser mode.

      + + + + + + + + + + + + + + + + + + +
      Modify own moviesUsers with this flag may add, delete and change their own movies. However they can not change movies of other users.
      Modify others moviesUsers with this flag may change details of movies belonging to other users, too.
      AdministrationUsers with this flag may do everything. Setting this implies all other flags.
      See adult moviesYou can define movie genres which are not suitable for minors and should be hidden from all users which do not have this permission flag set.
      + + + + \ No newline at end of file diff --git a/videodb/doc/screenshots/0.png b/videodb/doc/screenshots/0.png new file mode 100644 index 0000000..dd25bae Binary files /dev/null and b/videodb/doc/screenshots/0.png differ diff --git a/videodb/doc/screenshots/1.png b/videodb/doc/screenshots/1.png new file mode 100644 index 0000000..0d00094 Binary files /dev/null and b/videodb/doc/screenshots/1.png differ diff --git a/videodb/doc/screenshots/2.png b/videodb/doc/screenshots/2.png new file mode 100644 index 0000000..7e4a81e Binary files /dev/null and b/videodb/doc/screenshots/2.png differ diff --git a/videodb/doc/screenshots/3.png b/videodb/doc/screenshots/3.png new file mode 100644 index 0000000..2025d09 Binary files /dev/null and b/videodb/doc/screenshots/3.png differ diff --git a/videodb/doc/screenshots/4.png b/videodb/doc/screenshots/4.png new file mode 100644 index 0000000..68baaa6 Binary files /dev/null and b/videodb/doc/screenshots/4.png differ diff --git a/videodb/docker-entrypoint.sh b/videodb/docker-entrypoint.sh new file mode 100644 index 0000000..526ecce --- /dev/null +++ b/videodb/docker-entrypoint.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -e + +DB_HOST="${DB_HOST:-db}" +DB_USER="${DB_USER:-videodb}" +DB_PASSWORD="${DB_PASSWORD:-videodb_secret}" +DB_NAME="${DB_NAME:-videodb}" +DB_PREFIX="${DB_PREFIX:-videodb_}" + +CONFIG_FILE="/var/www/html/config.inc.php" + +# ── Write config.inc.php from environment variables ────────────────────────── +cat > "$CONFIG_FILE" </dev/null; do + printf "." + sleep 2 +done +echo "" +echo "[entrypoint] MySQL is ready." + +# ── Initialize DB schema (idempotent) ───────────────────────────────────────── +php /usr/local/bin/init-db.php + +# ── Ensure cache dirs exist and are writable ────────────────────────────────── +mkdir -p \ + /var/www/html/cache/smarty \ + /var/www/html/cache/imdb \ + /var/www/html/cache/img \ + /var/www/html/cache/thumbs \ + /var/www/html/cache/javascript \ + /var/www/html/cache/local +chown -R www-data:www-data /var/www/html/cache /var/www/html/images + +echo "[entrypoint] Starting Apache..." +exec "$@" diff --git a/videodb/edit.php b/videodb/edit.php new file mode 100644 index 0000000..2633bc7 --- /dev/null +++ b/videodb/edit.php @@ -0,0 +1,430 @@ + + * @author Chinamann + * @version $Id: edit.php,v 2.90 2013/03/11 19:00:26 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/genres.php'; +require_once './core/custom.php'; +require_once './core/edit.core.php'; +require_once './engines/engines.php'; + +// check for localnet +localnet_or_die(); + +global $CLIENTERROR; + +/** + * input id + */ +$id = req_int('id'); + +// multiuser permission check +permission_or_die(PERM_WRITE, ($id) ? get_owner_id($id) : PERM_ANY); + +/** + * input + */ +$save = req_int('save'); +$ajax_type = req_string('ajax_type'); +$ajax_prefetch_id = req_string('ajax_prefetch_id'); // elegant only +$ajax_autocomplete_title = req_string('ajax_autocomplete_title'); // elegent and nexgen +$ajax_autocomplete_subtitle = req_string('ajax_autocomplete_subtitle'); // not used +$ajax_check_duplicate = req_string('ajax_check_duplicate'); // not used +$import = req_string('import'); +$lookup = req_int('lookup'); +$engine = req_string('engine'); +$genres = req_array('genres'); +$copy = req_int('copy'); +$copyid = req_int('copyid'); +// $imdb_set_fields +$md5 = req_string('md5'); +$title = req_string('title'); +$subtitle = req_string('subtitle'); +$language = req_string('language'); +$diskid = req_string('diskid'); +$mediatype = req_string('mediatype'); +$comment = req_string('comment'); +$disklabel = req_string('disklabel'); +$imdbID = req_string('imdbID'); +$year = req_string('year'); +$imgurl = req_string('imgurl'); +$director = req_string('director'); +$actors = req_string('actors'); +$runtime = req_string('runtime'); +$country = req_string('country'); +$plot = req_string('plot'); +$filename = req_string('filename'); +$filesize = req_string('filesize'); +$filedate = req_string('filedate'); +$audio_codec = req_string('audio_codec'); +$video_codec = req_string('video_codec'); +$video_width = req_string('video_width'); +$video_height = req_string('video_height'); +$istv = req_int('istv'); +$rating = req_string('rating'); +$custom1 = req_string('custom1'); +$custom2 = req_string('custom2'); +$custom3 = req_string('custom3'); +$custom4 = req_string('custom4'); +// from engineGetData (specific imdb) +$mpaa = req_string('mpaa'); // if customXtype == mpaa => imdbdata[mpaa] +$origtitle = req_string('origtitle'); // ? +$tvseries_id = req_string('tvseries_id'); // ? +// from edit.tpl +$autoid = req_string('autoid'); +$oldmediatype = req_string('oldmediatype'); +$owner_id = req_int('owner_id'); +$seen = req_int('seen'); +$add_flag = req_int('add_flag'); + +// clean input data +$genres = (is_array($genres)) ? array_filter($genres) : array(); + +// ajax autocomplete? +if ($ajax_prefetch_id || $ajax_autocomplete_title || $ajax_autocomplete_subtitle) +{ + // add some delay for debugging + if ($config['debug'] && $_SERVER['SERVER_ADDR'] == '127.0.0.1') usleep(rand(200,1000)*1000); + + // prefetch external data + if ($ajax_prefetch_id) + { + $data = engineGetData($ajax_prefetch_id, engineGetEngine($ajax_prefetch_id)); + if (count($data)) + { + $data['imdbID'] = $ajax_prefetch_id; + $data['actors'] = $data['cast']; + $data['imgurl'] = $data['coverurl']; + } +/* + // load languages and config into Smarty + tpl_language(); + tpl_edit($data); + $content = $smarty->fetch('edit.tpl'); +# file_append(LOG_FILE, $content); + + echo $content; +*/ + exit; + } + + // use subtitle for aka search + $data = ($ajax_autocomplete_title) ? + engineSearch($ajax_autocomplete_title, engineGetDefault()) : + engineSearch($ajax_autocomplete_subtitle, engineGetDefault(), true); + + if ($ajax_type == 'json') { +# file_append(LOG_FILE, $res); + header("Content-Type: application/json"); + echo(json_encode($data)); + exit; + } + + foreach ($data as $item) + { + $text = preg_replace('/('.$ajax_autocomplete_title.')/i', '\1', $item['title']); + $text.= (($item['year']) ? " (".$item['year'].")" : ''); + $ret .= "
    • ".$text."
    • "; + } + $ret = "
        $ret
      "; + + exit($ret); +} + +// duplicate check +if ($ajax_check_duplicate) +{ + $q = escapeSQL($ajax_check_duplicate); + $res = runSQL("SELECT id, title FROM ".TBL_DATA." WHERE imdbid='".$q."' OR title LIKE '%".$q."%' AND owner_id=".get_current_user_id()); + + header('X-JSON: '.json_encode($res)); + exit; +} + +// XML import +if ($config['xml'] && ($import == 'xml')) +{ + require_once './core/xml.php'; + + // xml file upload + if (isset($_FILES['xmlfile']) && is_uploaded_file($_FILES['xmlfile']['tmp_name'])) + { + $file = $_FILES['xmlfile']['tmp_name']; + $xmldata = file_get_contents($file); + unlink($file); + } + + // uploading XML data directly or loaded from file + if (!empty($xmldata)) + { + $error = ''; + $item_id = 0; + + require_once './core/xmlimport.php'; // @todo 404 + + if (function_exists('xmlimport') && ($xmlitems = xmlimport($xmldata, $error)) !== false) + { + // multiple items imported + if ($xmlitems === true) + { + redirect('index.php?filter=new'); + } + // exactly one movie imported? + else + { + redirect('show.php?id='.$xmlitems); + } + } + $smarty->assign('xmlerror', $error); + } + + // prepare templates + tpl_page(); + + // display templates + tpl_display('xmlimport.tpl'); + exit; +} + +// legacy +if (isset($imdb) && $imdb) {$lookup = 1;} + +// get default lookup mode (0=ignore, 1=lookup, 2=overwrite) if not set +if (!isset($lookup)) $lookup = (empty($id)) ? $lookup = $config['lookupdefault_new'] : $config['lookupdefault_edit']; + +// preload old data for refresh all mechanism +if ($lookup > 2) +{ + // get a list of movies in DB + $video = runSQL('SELECT * FROM '.TBL_DATA.' WHERE id = '.$id); + + // get fields (according to list) from db to be saved later + foreach ($video[0] as $name => $val) + { + if (in_array($name, $imdb_set_fields)) $$name = $val; + } + $owner_id = $video[0]['owner_id']; + + // Build a list of all fields which are allowed to be overwritten + $overwrites = array(); + foreach ($imdb_set_fields as $field) + { + $tempFieldName = 'update_'.$field; + if (isset($$tempFieldName) && $$tempFieldName == 1) $overwrites[] = $field; + } + $imdb_set_fields = $overwrites; + + // valid input values for lookup > 2 are either 5 (add missing) or 6 (overwrite) + $lookup -= 4; +} + +// lookup imdb +if ($lookup && $imdbID) +{ + // get engine from id + if (empty($engine)) $engine = engineGetEngine($imdbID); + + // get external data + $imdbdata = engineGetData($imdbID, $engine); + + // lookup cover + if (empty($imgurl) || ($lookup > 1)) + { + $imgurl = $imdbdata['coverurl']; + } + + // lookup genres + if (count($genres) == 0 || ($lookup > 1)) + { + $genres = array(); + $gnames = $imdbdata['genres']; + if (isset($gnames)) + { + foreach ($gnames as $gname) + { + // check if genre is found- otherwise fail silently + if (is_numeric($genre = getGenreId($gname))) + { + $genres[] = $genre; + } + } + } + } + + // lookup actors + if (empty($actors) || ($lookup > 1)) + { + $actors = $imdbdata['cast']; + } + + // lookup all other fields + foreach (array_keys($imdbdata) as $name) + { + if (in_array($name, array('coverurl', 'genres', 'cast', 'id'))) continue; + + // use !$$ as empty($$) doesn't seem to work + if (!$$name || ($lookup > 1)) + { + $$name = $imdbdata[$name]; + } + } + + // custom fields + for ($i=1; $i<=4; $i++) + { + $custom = 'custom'.$i; + $type = $config[$custom.'type']; + if (!empty($type) && isset($$type)) + { + // copy imdb data into corresponding custom field + $$custom = $$type; + } + } +} + +// get fields from db if copying +if ($copy && $copyid) +{ + $video = runSQL('SELECT * FROM '.TBL_DATA.' WHERE id = '.$copyid); + + // get fields (according to list) from db to be saved later + foreach ($video[0] as $name => $val) + { + // don't copy diskid + if ($name == 'diskid') + { + if ($config['autoid']) + { + $$name = getDiskId(); + } + } + else + { + if (in_array($name, $imdb_set_fields)) $$name = $val; + } + } + + $genres = getItemGenres($copyid); +} + + +// save data +if ($save) +{ + // uncomment the following line to provide simple protection for your own public access videoDB + //if (!preg_match('/[0-9]{2+}/', $id)) break; + + // implicit owner id if not set + if (!$owner_id) $owner_id = get_current_user_id(); + + // generate diskid + if (empty($diskid) && $config['autoid'] && $mediatype != MEDIA_WISHLIST) $diskid = getDiskId(); + + // write videodata table + $SETS = prepareSQL($GLOBALS); + $id = updateDB($SETS, $id, ($oldmediatype == MEDIA_WISHLIST) && ($mediatype != MEDIA_WISHLIST)); + + // save genres + setItemGenres($id, $genres); + + // set seen for currently logged in user + set_userseen($id, $seen); + + // uploaded cover? + if (isset($_FILES['coverupload']['tmp_name']) && + isset($_FILES['coverupload']['type']) && + isset($_FILES['coverupload']['name'])) + { + processUpload($id, $_FILES['coverupload']['tmp_name'], $_FILES['coverupload']['type'], $_FILES['coverupload']['name']); + } + + // make sure no artifacts + $smarty->clearCache('list.tpl'); + $smarty->clearCache('show.tpl', get_current_user_id().'|'.$id); + + // add another? + if ($add_flag) + { + // remove id to prevent edit mode instead of new + $id = ''; + $smarty->assign('add_flag', $add_flag); + } + else + { + // show the saved movie + redirect('show.php?id='.$id); + } +} + + +// load existing data +if ($id) +{ + $SELECT = ''; + // select all fields according to list, plus id + foreach ($imdb_set_fields as $name) + { + if ($SELECT) $SELECT .= ', '; + $SELECT .= $name; + } + + $SELECT = 'SELECT '.TBL_DATA.'.id, '.TBL_DATA.'.owner_id, '.TBL_USERS.'.name AS owner, + !ISNULL('.TBL_USERSEEN.'.video_id) AS seen, '.$SELECT.' + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + LEFT JOIN '.TBL_USERSEEN.' + ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id().' + WHERE '.TBL_DATA.'.id = '.$id; + $video = runSQL($SELECT); + + // diskid to global scope: + $diskid = $video[0]['diskid']; +} +else +{ + $video[0]['language'] = $config['langdefault']; +} + + +// assign automatic disk id +if ($config['autoid'] && (empty($diskid) || $add_flag) && $mediatype != MEDIA_WISHLIST) +{ + $video[0]['diskid'] = getDiskId(); + + // Fix for Bugreport [1122052] Automatic DiskID generation problem + $smarty->assign('autoid', $result[0]['max']); +} +else + { + $smarty->assign('autoid', ""); + } + +if (empty($video[0]['owner_id']) && !empty($owner_id)) +{ + $video[0]['owner_id'] = $owner_id; +} + + +// prepare templates +tpl_page(); +tpl_edit($video[0]); + +$smarty->assign('lookup_id', $lookup); +$smarty->assign('http_error', $CLIENTERROR); + +// allow XML import +if ($config['xml'] && empty($id)) $smarty->assign('xmlimport', true); + +// display templates +tpl_display('edit.tpl'); + diff --git a/videodb/engines/allocine.php b/videodb/engines/allocine.php new file mode 100755 index 0000000..c8bea92 --- /dev/null +++ b/videodb/engines/allocine.php @@ -0,0 +1,602 @@ + + * @author Andreas Gohr + * @author tedemo + * @link http://www.allocine.fr Internet Movie Database + * @version $Id: allocine.php,v 1.17 2011/06/24 23:08:06 robelix Exp $ + */ + +$GLOBALS['allocineServer'] = 'https://www.allocine.fr'; +$GLOBALS['allocineIdPrefix'] = 'allocine:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ + +function allocineMeta() +{ + return array('name' => 'Allocine (fr)'); +} + +/** + * Encode title search to allow results with accentued characters + * @author Martin Vauchel + * @param string The search string + * @return string The search string with no accents + */ +function removeAccents($title) +{ + $accentued = array("à","á","â","ã","ä","ç","è","é","ê","ë","ì", + "í","î","","ï","ñ","ò","ó","ô","õ","ö","ù","ú","û","ü","ý","ÿ", + "À","Á","Â","Ã","Ä","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","Ñ","Ò", + "Ó","Ô","Õ","Ö","Ù","Ú","Û","Ü","Ý"); + $nonaccentued = array("a","a","a","a","a","c","e","e","e","e","i","i", + "i","i","n","o","o","o","o","o","u","u","u","u","y","y","A","A","A", + "A","A","C","E","E","E","E","I","I","I","I","N","O","O","O","O","O", + "U","U","U","U","Y"); + + $title = str_replace($accentued, $nonaccentued, $title); + + return $title; +} + +/** + * Get Url to search Allocine for a movie + * + * @author Douglas Mayle + * @author Andreas Goetz + * @param string The search string + * @return string The search URL (GET) + */ +function allocineSearchUrl($title) +{ + global $allocineServer; + // The removeAccents function is added here + return $allocineServer.'/recherche/?q='.urlencode(removeAccents($title)); +} + +/** + * Get Url to visit Allocine for a specific movie + * + * @author Douglas Mayle + * @author Andreas Goetz + * @param string $id The movie's external id + * @return string The visit URL + */ +function allocineContentUrl($id) +{ + global $allocineServer; + global $allocineIdPrefix; + + $allocineID = preg_replace('/^'.$allocineIdPrefix.'/', '', $id); + return $allocineServer.'/film/fichefilm_gen_cfilm='.$allocineID.'.html'; +} + + +/** + * Search a Movie + * + * Searches for a given title on Allocine and returns the found links in + * an array + * + * @author Douglas Mayle + * @author Tiago Fonseca + * @author Charles Morgan + * @param string The search string + * @return array Associative array with id and title + */ +function allocineSearch($title) +{ + global $allocineServer; + global $CLIENTERROR; + + // The removeAccents function is added here + $resp = httpClient(allocineSearchUrl($title), 1); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $data = array(); + + #echo '
      ';
      +    #dump(htmlspecialchars($resp['data']));
      +    #echo '
      '; + + // add encoding + $data['encoding'] = $resp['encoding']; + + // direct match (redirecting to individual title)? + // no longer needed?? + $single = array(); + if (preg_match('#^'.preg_quote($allocineServer,'/').'/film/fichefilm_gen_cfilm=(\d+)\.html#', $resp['url'], $single)) + { + $data[0]['id'] = 'allocine:'.$single[2]; + $data[0]['title']= $title; + return $data; + } + + // multiple matches + // We remove all the multiples spaces and line breakers + $resp['data'] = preg_replace('/[\s]{2,}/','',$resp['data']); + // To have the result zone + #$debutr = strpos($resp['data'], '')+strlen('
      '); + #$finr = strpos($resp['data'], '
      ', $debutr); + #$chaine = substr($resp['data'], $debutr, $finr-$debutr); + + preg_match('#

      \s*?Films\s*?

      (.*?)

      #si',$resp['data'],$ary); + + $chaine = $ary[1]; + # contains some pretty random + $chaine = preg_replace('//','',$chaine); + $chaine = preg_replace('/<\/b>/','',$chaine); + + /* + + Clerks II +
      + + Clerks II +
      + + 2006
      + de Kevin Smith
      + avec Brian O'Halloran, Jeff Anderson
      +
      +
      + + */ + + preg_match_all('#\s*?(.*?)\s*?
      \s*?\s*?(\d+)
      \s*?de (.*?)\s*?/#si', $chaine, $m, PREG_SET_ORDER); + + foreach ($m as $row) + { + $info['id'] = 'allocine:'.$row[1]; + + $info['title'] = html_clean_utf8(strip_tags($row[2])); + $info['title'] = str_replace("(", " (", $info['title']); + + // add year (helpful in case of multiple matches) + if (isset($row[3])) {$info['year'] = html_clean_utf8($row[3]);} + + // add director (helpful in case of multiple matches) + if (isset($row[4])) { + $info['director'] = html_clean_utf8($row[4]); + $info['director'] = preg_replace("/^de\s/", "", $info['director']); + } + + $data[] = $info; + } + + return $data; +} + +/** + * Fetches the data for a given Allocine-ID + * + * @author Douglas Mayle + * @author Tiago Fonseca + * @param int imdb-ID + * @return array Result data + */ +function allocineData($imdbID) +{ + global $allocineServer; + global $allocineIdPrefix; + global $CLIENTERROR; + + $allocineID = preg_replace('/^'.$allocineIdPrefix.'/', '', $imdbID); + + // fetch mainpage + $resp = httpClient($allocineServer.'/film/fichefilm_gen_cfilm='.$allocineID.'.html', 1); // added trailing / to avoid redirect + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $data = array(); // result + $ary = array(); // temp + + // add encoding + $data['encoding'] = $resp['encoding']; + + // Allocine ID + $data['id'] = "allocine:".$allocineID; + + // We remove all the multiples spaces and line breakers + $resp['data'] = preg_replace('/[\s]{2,}/','',$resp['data']); + + /* + Title and subtitle + */ + preg_match('#(.*?)

      #si', $resp['data'], $ary); + list($t, $s) = explode(" - ",trim($ary[1]),2); + // Some bugs when using html_clean function --> using html_clean_utf8 + $data['title'] = html_clean_utf8($t); + $data['subtitle'] = html_clean_utf8($s); + + + /* + Year + */ + preg_match('/(\d+)<\/a>/i', $resp['data'], $ary); + if (!empty($ary[1])) {$data['year'] = trim($ary[1]);} + + + /* + Release Date + added to the comments + */ + preg_match('#(.*)#i',$resp['data'], $ary); + $release_date = ""; + if (!empty($ary[1])) {$release_date = "\r\nDate de sortie cinéma : ".html_clean_utf8($ary[1]);} + + /* + Cover URL + */ + preg_match('#
      \s*?
      \s*?
      \s*?\s*?\s*?#si', $resp['data'], $ary); + $data['coverurl'] = trim($ary[1]); + + + /* + Runtime + */ + #Durée : 02h13min + + preg_match('/Durée :\s*?(\d+)h(\d+)\s*?min/i', $resp['data'], $ary); + $hours = preg_replace('/,/', '', trim($ary[1])); + $minutes = preg_replace('/,/', '', trim($ary[2])); + $data['runtime'] = $hours * 60 + $minutes; + + + /* + Director + */ + preg_match('#Réalisé par\s*(.*)#i', $resp['data'], $ary); + $data['director'] = trim($ary[1]); + + + /* + Rating + */ + preg_match('#

      \((.*)\)

      #i', $resp['data'], $ary); + $data['rating'] = trim($ary[1]); + $data['rating'] = str_replace(",", ".", $data['rating']); + // Allocine rating is based on 5, imdb is based on 10 + $data['rating'] = $data['rating'] * 2; + + + /* + Countries + */ + // Countries in English + $map_countries = array( + 'allemand' => 'Germany', + 'américain' => 'USA', + 'arménien' => 'Armenia', + 'argentin' => 'Argentina', + 'sud-africain' => 'South Africa', + 'australien' => 'Australia', + 'belge' => 'Belgium', + 'britannique' => 'UK', + 'bulgare' => 'Bulgaria', + 'canadien' => 'Canada', + 'chinois' => 'China', + 'coréen' => 'South Korea', + 'danois' => 'Denmark', + 'espagnol' => 'Spain', + 'français' => 'France', + 'grec' => 'Greece', + 'hollandais' => 'Netherlands', + 'hong-kongais' => 'Hong-Kong', + 'hongrois' => 'Hungary', + 'indien' => 'India', + 'irlandais' => 'Republic of Ireland', + 'islandais' => 'Iceland', + 'israëlien' => 'Israel', + 'italien' => 'Italy', + 'japonais' => 'Japan', + 'luxembourgeois' => 'Luxembourg', + 'mexicain' => 'Mexico', + 'norvégien' => 'Norge', + 'néo-zélandais' => 'New Zealand', + 'polonais' => 'Poland', + 'portugais' => 'Portugal', + 'roumain' => 'Romania', + 'russe' => 'Russia', + 'serbe' => 'Serbia', + 'suédois' => 'Sweden', + 'taïwanais' => 'Taiwan', + 'tchèque' => 'Czech Republic', + 'thaïlandais' => 'Thailand', + 'turc' => 'Turkey', + 'ukrainien' => 'Ukraine', + 'vietnamien' => 'Vietnam'); + + if (preg_match_all('#Long\-métrage\s*?(.*?)#si', $resp['data'], $ary, PREG_PATTERN_ORDER) > 0) + { + $originlist = explode(",",trim(join(', ', $ary[1]))); + foreach ($originlist as $origin) + { + $mapped_country_found = ''; + + foreach ($map_countries as $pattern_c => $mapped_country) + { + if (preg_match_all('/'.$pattern_c.'/i', $origin, $junk, PREG_PATTERN_ORDER) > 0) + { + $mapped_country_found = $mapped_country; + break; + } + } + + if($data['country'] == '') {$data['country'] = $mapped_country_found;} + elseif(stristr($data['country'], $mapped_country_found) == TRUE) + { + $data['country'] = $data['country']; + } + else + { + $data['country'] = $data['country'] . ', ' . $mapped_country_found; + } + } + } + + /* + Plot + */ + preg_match('#
      \s*?

      \s*?Synopsis \: \s*?(.*?)#is', $resp['data'], $ary); + if (!empty($ary[1])) { + $data['plot'] = $ary[1]; + $data['plot']= html_clean_utf8($data['plot']); + + // And cleanup + $data['plot'] = trim($data['plot']); + $data['plot'] = preg_replace('/[\n\r]/',' ', $data['plot']); + $data['plot'] = preg_replace('/ /',' ', $data['plot']); + } + + /* + Genres (as Array) + */ + $map_genres = array( + 'Action' => 'Action', + 'Animation' => 'Animation', + 'Arts Martiaux' => 'Action', + 'Aventure' => 'Adventure', + 'Biopic' => 'Biography', + 'Bollywood' => 'Musical', + 'Classique' => '-', + 'Comédie Dramatique' => 'Drama', + 'Comédie musicale' => 'Musical', + 'Comédie' => 'Comedy', + 'Dessin animé' => 'Animation', + 'Divers' => '-', + 'Documentaire' => 'Documentary', + 'Drame' => 'Drama', + 'Epouvante-horreur' => 'Horror', + 'Erotique' => 'Adult', + 'Espionnage' => '-', + 'Famille' => 'Family', + 'Fantastique' => 'Fantasy', + 'Guerre' => 'War', + 'Historique' => 'History', + 'Horreur' => 'Horror', + 'Musique' => 'Musical', + 'Policier' => 'Crime', + 'Péplum' => 'History', + 'Romance' => 'Romance', + 'Science fiction' => 'Sci-Fi', + 'Thriller' => 'Thriller', + 'Western' => 'Western'); + + if (preg_match_all('#Genre :(.*?)\s*? 0) + { + $genrelist = explode(",", trim(join(', ', $ary[1]))); + + foreach ($genrelist as $genre) + { + $mapped_genre_found = ''; + foreach ($map_genres as $pattern => $mapped_genre) + { + if (preg_match_all('/'.$pattern.'/i', $genre, $junk, PREG_PATTERN_ORDER) > 0) + { + $mapped_genre_found = $mapped_genre; + break; + } + } + $data['genres'][] = ($mapped_genre_found != '-') ? $mapped_genre_found : trim($genre); + } + } + + /* + Original Title + */ + preg_match('#Titre original : (.*)#', $resp['data'], $ary); + $data['origtitle'] = trim($ary[1]); + + /* + Title and Subtitle + If sub-title is blank, we'll try to fill in the original title for foreign films. + */ + if (empty($data['subtitle'])) + { + if ($data['origtitle']) + { + $data['subtitle'] = $data['title']; + $data['title'] = $data['origtitle']; + } + } + + /* + CREDITS AND CAST + */ + // fetch credits + // Another HTML page + $resp = httpClient($allocineServer.'/film/casting_gen_cfilm='.$allocineID.'.html', 1); + if (!$resp['success']) {$CLIENTERROR .= $resp['error']."\n";} + + // We remove all the multiples spaces and line breakers + $resp['data'] = preg_replace('/[\s]{2,}/','',$resp['data']); + + if (preg_match('#

      Acteurs, rôles, personnages

      (.*?)
      \s*?\s*?

      #is', $resp['data'], $Section)) + { + + # the big ones with image + /* +
      +

      + Liam Neeson +

      +
      +

      + Rôle : Qui-Gon Jinn +

      +
      + */ + preg_match_all('#
      \s*?

      \s*?(.*?)\s*?

      \s*?
      \s*?

      \s*Rôle : (.*?)\s*

      #is', $Section[1], $ary, PREG_PATTERN_ORDER); + + $count = 0; + $cast = ''; + while (isset($ary[1][$count])) + { + $cast .= $ary[2][$count]."::".$ary[3][$count]."::allocine:".$ary[1][$count]."\n"; + $count++; + } + + # extended cast - without image + /* + + + Shmi Skywalker + + + Pernilla August + + + */ + preg_match_all('#\s*?\s*(.*?)\s*\s*?\s*?(.*?)\s*?#si', $Section[1], $ary, PREG_PATTERN_ORDER); + + $count = 0; + while (isset($ary[1][$count])) + { + $cast .= $ary[3][$count]."::".$ary[1][$count]."::allocine:".$ary[2][$count]."\n"; + $count++; + } + $data['cast'] = trim($cast); + + } + + + + /* + Comments + */ + // By default + $data['language'] = 'french'; + + // Another HTML page + $resp = httpClient($allocineServer.'/film/fichefilm-'.$allocineID.'/technique/', 1); + if (!$resp['success']) {$CLIENTERROR .= $resp['error']."\n";} + + // We remove all the multiples spaces and line breakers + $resp['data'] = preg_replace('/[\s]{2,}/','',$resp['data']); + + // Technical informations as comment + preg_match('#
      \s*?
      \s*(.*?)\s*
      \s*?
      #si', $resp['data'], $ary); + if (!empty($ary[1])) + { + $data['comment'] = $ary[1]; + + $data['comment'] = str_replace("Tourné en :", "Tourné en : ", $data['comment']); + + // Adding the release date in theater + $data['comment'] = $data['comment'] . $release_date; + + // Search the language + // Default language + $data['language'] = "french"; + + if (preg_match('#

      \s*?Tourné en :\s*(.*?)\s*

      #si', $resp['data'], $ary)) + { + $data['language'] = $ary[1]; + + // Converting languages from french to english + $map_languages = array( + 'Anglais' => 'english', + 'Français' => 'french', + 'Allemand' => 'german', + 'Italien' => 'italian', + 'Espagnol' => 'spanish', + 'Coréen' => 'Korean', + 'Roumain' => 'romanian', + 'Autre' => 'french', + 'Hindi' => 'hindi', + 'Arabe' => 'arabic', + 'Thaï' => 'thai', + 'Danois' => 'danish', + 'Suédois' => 'swedish', + 'Tchèque' => 'czech', + 'Japonais' => 'japanese', + 'Portugais' => 'portuguese', + 'Norvégien' => 'norwegian', + 'Bulgare' => 'bulgarian', + 'Grec' => 'greek', + 'Hongrois' => 'hungarian', + 'Turc' => 'turkish', + 'Islandais' => 'icelandic', + 'Polonais' => 'polish', + 'Russe' => 'russian', + 'Ukrainien' => 'ukrainian', + 'Serbe' => 'serbian', + 'Vietnamien' => 'vietnamese', + 'Afrikaans' => 'afrikaans' + ); + + foreach($map_languages as $pattern => $map_lang) + { + $data['language'] = str_replace($pattern, $map_lang, $data['language']); + } + } + } + + // Return the data collected + return $data; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor, not sure if this can be made + * a one-step process? Completion waiting on update of actor + * functionality to support more than one engine. + * + * @author Douglas Mayle + * @author Andreas Goetz + * @param string $name Name of the Actor + * @return array array with Actor-URL and Thumbnail + */ +function allocineActor($name, $actorid) +{ + global $allocineServer; + + if (empty ($actorid)) { + return; + } + + $url = 'https://www.allocine.fr/personne/fichepersonne_gen_cpersonne='.urlencode($actorid).'.html'; + $resp = httpClient($url, 1); + + $single = array(); + if (preg_match ('/src="([^"]+allocine.fr\/acmedia\/medias\/nmedia\/[^"]+\/[0-9]+\.jpg)[^>]+width="120"/', $resp['data'], $single)) { + $ary[0][0]=$url; + $ary[0][1]=$single[1]; + return $ary; + } else { + return null; + } +} + diff --git a/videodb/engines/dvdfr.php b/videodb/engines/dvdfr.php new file mode 100755 index 0000000..b2371bb --- /dev/null +++ b/videodb/engines/dvdfr.php @@ -0,0 +1,331 @@ + + * + * @package Engines + * @author tedemo + * @link http://www.dvdfr.com + * @version $Id: dvdfr.php,v 1.7 2011/06/23 12:27:28 robelix Exp $ + */ + +require_once './core/compatibility.php'; + +$GLOBALS['dvdfrServer'] = 'https://www.dvdfr.com'; +$GLOBALS['dvdfrIdPrefix'] = 'dvdfr:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function dvdfrMeta() +{ + return array('name' => 'Dvdfr (fr)', 'stable' => 0); +} + + +/** + * Clean a string + * + * @param string The string to clean + * @return string The cleaned string + */ +function dvdfrCleanStr($str) +{ + // Remove spaces + $str = trim($str); + + // Translate strange (MS-Word?) quotes + $str = preg_replace( '/'/', '\'', $str ); + + // Remove HTML entities + $str = html_entity_decode($str); + + return $str; +} + +/** + * Get Url to search Dvdfr for a movie + * + * @param string The search string + * @return string The search URL (GET) + */ +function dvdfrSearchUrl($title) +{ + global $dvdfrServer; + return $dvdfrServer.'/api/search.php?title='.urlencode(mb_convert_encoding($title,'ISO-8859-15','UTF-8')); +} + +/** + * Get Url to visit Dvdfr for a specific movie + * + * @param string $id The movie's external id + * @return string The visit URL + */ +function dvdfrContentUrl($id) +{ + list($engineword, $dvdfrID) = explode(':',$id,2); + global $dvdfrServer; + return $dvdfrServer.'/api/dvd.php?id='.$dvdfrID; + #return 'http://koocotte.org/DVDMARK'; +} + +/** + * Search a Movie + * + * Searches for a given title on Dvdfr and returns the found links in + * an array + * + * @return array Associative array with id and title + */ +function dvdfrSearch($title) +{ + global $dvdfrServer; + global $CLIENTERROR; + + $para['useragent'] = 'VideoDB (http://www.videodb.net/)'; + + $resp = httpClient(dvdfrSearchUrl($title), 1, $para); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Encoding + $ary['encoding'] = $resp['encoding']; + +/* No more direct match with XLM API + + // direct match (redirecting to individual title)? + $single = array(); + if (preg_match('/\/dvd\/dvd\.php\?id=(\d+)/', $resp['url'], $single)) + { + $ary[0]['id'] = 'dvdfr:'.$single[1]; + preg_match('/
      ([^<]+)<\/div>[^<]*
      ([^<]+)<\/div>[^<]*
      ([^<]+) + 16892 <= $1 + DVD <= $2 + + Star Wars - Clone Wars - Vol. 1 <= $3 + Star Wars: Clone Wars <= $4 + + + + 2003 <= $5 + <= $6 + 20th Century Fox <= $7 + + Genndy Tartakovsky + + + */ + + preg_match_all('#\s*(\d+)\s*(\w+)\s*\s*(.+?)\s*(.*?).*?(.*?)\s*(.*?)\s*(.*?)\s*.*?#is', $resp['data'], $data, PREG_SET_ORDER); + foreach ($data as $row) + { + $info['id'] = 'dvdfr:'.$row[1]; + $title = dvdfrCleanStr($row[3]); + // add native title + if( !empty($row[4]) ) $title .= " / " . dvdfrCleanStr($row[4]); + + if( !empty($row[5]) and !empty($row[6]) and !empty($row[7]) ) { + $title .= ' ('; + // add year (helpful in case of multiple matches) + if( !empty($row[5]) ) $title .= dvdfrCleanStr($row[5]); + $title .= '/'; + // add edition and editor + if( !empty($row[6]) ) $title .= dvdfrCleanStr($row[6]); + $title .= '/'; + if( !empty($row[7]) ) $title .= dvdfrCleanStr($row[7]); + $title .=')'; + } + + // Add record + $info['title'] = $title; + $ary[] = $info; + } + + return $ary; +} + +/** + * Fetches the data for a given Dvdfr-ID + * + * @param int IMDB-ID + * @return array Result data + */ +function dvdfrData($imdbID) +{ + global $dvdfrServer; + global $CLIENTERROR; + + $data= array(); // result + $ary = array(); // temp + + $para['useragent'] = 'VideoDB (http://www.videodb.net/)'; + + // fetch mainpage + $resp = httpClient(dvdfrContentUrl($imdbID), 1, $para); // added trailing / to avoid redirect + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // add encoding + $data['encoding'] = $resp['encoding']; + + // See http://www.dvdfr.com/api/dvd.php?id=2869 for output + + // Titles + preg_match('#\s*(.+?)\s*(.+?)#is', $resp['data'], $ary); + $data['title'] = mb_convert_case(dvdfrCleanStr($ary[1]), MB_CASE_TITLE, $data['encoding']); + $data['subtitle'] = mb_convert_case(dvdfrCleanStr($ary[2]), MB_CASE_TITLE, $data['encoding']); + + // I found:
      USA, Royaume-Uni , 2004
      R&D TV, Sky TV, USA Cable Entertainment
      + preg_match('#\s*(.+?)#is', $resp['data'], $ary); + $data['country'] = dvdfrCleanStr($ary[1]); + preg_match('#(\d+)#is', $resp['data'], $ary); + $data['year'] = dvdfrCleanStr($ary[1]); + + // Cover URL + preg_match('#(.*?)#i', $resp['data'], $ary); + $data['coverurl'] = trim($ary[1]); + + // Runtime + preg_match('#(\d+)#i', $resp['data'], $ary); + $data['runtime'] = $ary[1]; + + // Director (only the first one) + preg_match('#(.*?)#i', $resp['data'], $ary); + $data['director'] = dvdfrCleanStr($ary[1]); + + // Plot + preg_match('#(.*?)#is', $resp['data'], $ary); + if (!empty($ary[1])) { + $data['plot'] = $ary[1]; + // And cleanup + $data['plot'] = preg_replace('/[\n\r]/',' ', $data['plot']); + $data['plot'] = preg_replace('/\s+/',' ', $data['plot']); + $data['plot'] = dvdfrCleanStr($data['plot']); + } + + // maps dvdfr category ids to videodb category names + $category_map = array + ( + "1" => "Action", + "2" => "Animation", + "61" => "", // "Autres séries" + "3" => "Adventure", + "72" => "", //"Beaux-Arts" + "81" => "Musical", //"Bollywood" + "4" => "Comedy", + "5" => "Drama", // "Comédie dramatique" + "6" => "Musical", //"Comédie musicale" + "74" => "Romance", // "Comédie romantique" + "7" => "Music", //"Concert" + "8" => "" , //"Conte" + "9" => "Short", //"Court-Métrage" + "10" => "Documentary", //"Culture" + "78" => "Documentary", //"Culture Gay" + "11" => "Music", //"Danse" + "12" => "", //"Divers" + "13" => "Documentary", //"Documentaire" + "14" => "Drama", //"Drame" + "73" => "Drama", //"Emotion" + "15" => "Adult", //"Erotique" + "16" => "Action", //"Espionnage" + "17" => "Sci-Fi", //"Fantastique" + "30" => "Musical", //"Film musical" + "83" => "Sport", //"Freefight" + "18" => "War", //"Guerre" + "19" => "Musical", //"Hard-rock" + "20" => "History", //"Historique" + "21" => "Horror", //"Horreur" + "22" => "Comedy", //"Humour" + "23" => "Animation", //"Japanimation" + "24" => "Adult", //"Japanimation érotique" + "25" => "Music", //"Jazz & Blues" + "79" => "", //"Jeux" + "26" => "Music", //"Karaoke" + "27" => "Action", //"Kung Fu" + "28" => "", //"Méthode" + "57" => "", //"Mini-series / Feuilletons" + "29" => "Documentary", //"Muet" + "32" => "Music", //"Musique Classique" + "71" => "Music", //"Musiques du monde" + "31" => "Music", //"Opéra" + "33" => "War", //"Péplum" + "34" => "Crime", //"Policier" + "54" => "", //"Pour enfants" + "76" => "Music", //"R&B & Soul" + "55" => "Music", //"Rap" + "56" => "Sci-Fi", //"Science Fiction" + "60" => "", //"Série Anime / OAV" + "75" => "", //"Série d'animation enfants" + "58" => "", //"Série TV" + "59" => "", //"Sitcom" + "62" => "", //"Spectacle" + "63" => "Sport", + "82" => "Sport", //"Sports mécaniques" + "64" => "Music", //"Techno / Electro" + "65" => "", //"Theatre" + "66" => "Thriller", + "67" => "Music", //"Variété française" + "68" => "Music", //"Variété internationale" + "69" => "Documentary", //"Voyages" + "70" => "Western", + "Science Fiction" => "Sci-Fi", + ); + + // Genres (as Array) + if (preg_match_all('#(.*?)#i', $resp['data'], $ary, PREG_PATTERN_ORDER) > 0) + { + $count = 0; + while (isset($ary[1][$count])) + { + $data['genres'][] = $category_map[dvdfrCleanStr($ary[1][$count])]; + $count ++; + } + } + + // Cast + if( preg_match('#(.*)#is', $resp['data'], $Section) ) { + preg_match_all('#(.*?)#i', $Section[1], $ary,PREG_PATTERN_ORDER); + $cast = ''; + for ($i=0; $i < sizeof($ary[0]); $i++) + { + $cast .= dvdfrCleanStr($ary[2][$i]) . '::::dvdfr' . dvdfrCleanStr($ary[1][$i]) . "\n"; + #$cast .= "$actor::$character::$imdbIdPrefix$actorid\n"; + } + $data['cast'] = dvdfrCleanStr($cast); + } + + #// Convert ISO to UTF8 + #$encoding = $data['encoding']; + #foreach( $data as $k => $v ) { + # $data[$k] = mb_convert_encoding(trim($v),'UTF-8',$encoding); + #} + + return $data; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor, not sure if this can be made + * a one-step process? Completion waiting on update of actor + * functionality to support more than one engine. + * + * @param string $name Name of the Actor + * @return array array with Actor-URL and Thumbnail + */ +function dvdfrActor($name, $actorengineid) +{ + global $dvdfrServer; + + return; +} + diff --git a/videodb/engines/engines.php b/videodb/engines/engines.php new file mode 100644 index 0000000..dac607b --- /dev/null +++ b/videodb/engines/engines.php @@ -0,0 +1,452 @@ + + * @version $Id: engines.php,v 1.45 2010/10/15 08:13:01 andig2 Exp $ + */ + +require_once './core/httpclient.php'; // include for all engines +require_once './core/encoding.php'; + +/** + * Determine the default engine + * + * @author Andreas Goetz + * @return string engine name + */ +function engineGetDefault() +{ + global $config; + + if (!empty($config['enginedefault'])) + { + $engine = $config['enginedefault']; + } + elseif (count($engine_list = array_keys($config['engines']))) + { + // first valid engine from list + $engine = $engine_list[0]; + } + else $engine = 'imdb'; // last resort + + return $engine; +} + +/** + * Determine engine from id + * + * @todo Enhance DB schema to store engine type explicitly + * + * @author Andreas Goetz + * @param string item id + * @return string engine name + */ +function engineGetEngine($id) +{ + global $config; + + // recognize engine from id + if ($id) + { + // engine prefixed (imdb:081547) + // currently working for imdb, amazon, amazoncom and tvcom + if (preg_match('/^(\w+):/', $id, $match)) $engine = $match[1]; + elseif (preg_match('/^[0-9A-Z]{10,}$/', $id)) $engine = 'amazonaws'; // Amazon + } + if (empty($engine)) $engine = 'imdb'; + return $engine; +} + +/** + * Include engine file and retrieve item data + * + * @author Andreas Goetz + * @param string item id + * @param string engine name + * @return array item data + */ +function engineGetData($id, $engine = 'imdb') +{ + global $lang, $cache; + + if (!engine_load_engine($engine)) return array(); + $func = $engine.'Data'; + + $result = array(); + if (function_exists($func)) + { + $cache = true; + $result = $func($id); + } + + // make sure all engines properly return the encoding type + if (empty($result['encoding'])) errorpage('Engine Error', 'Engine '.$engine.' does not properly return encoding'); + + // set default encoding iso-8859-1 + $source_encoding = ($result['encoding']) ? $result['encoding'] : $lang['encoding']; + $target_encoding = 'utf-8'; + unset($result['encoding']); + + // convert to unicode + if ($source_encoding != $target_encoding) + { + $result = iconv_array($source_encoding, $target_encoding, $result); + } + engine_clean_input($result); + + return $result; +} + +/** + * Include engine file and execute item search + * + * @author Andreas Goetz + * @param string search string + * @param string engine name + * @return array list of item data + */ +function engineSearch($find, $engine = 'imdb', $para1 = null, $para2 = null) +{ + global $lang, $cache; + + if (!engine_load_engine($engine)) return array(); + $func = $engine.'Search'; + + $result = array(); + if (function_exists($func)) + { + $cache = true; + // check if additional parameters given to avoid overriding default values + $result = (isset($para1)) ? $func($find, $para1, $para2) : $func($find); + } + + // make sure all engines properly return the encoding type +# if (empty($result['encoding'])) errorpage('Engine Error', 'Engine does not properly return encoding'); + + // set default encoding iso-8859-1 + $source_encoding = ($result['encoding']) ? $result['encoding'] : $lang['encoding']; + $target_encoding = 'utf-8'; + unset($result['encoding']); + + // convert to unicode + if ($source_encoding != $target_encoding) + { + #dump("Converting from $source_encoding to $target_encoding"); + $result = iconv_array($source_encoding, $target_encoding, $result); + } + + // obtain unique entries + $result = engine_deduplicate_result($result); + + engine_clean_input($result); + + return $result; +} + +/** + * Get item details URL in external site + * + * @author Andreas Goetz + * @param string item id + * @param string engine name + * @return string item details url + */ +function engineGetContentUrl($id, $engine = 'imdb') +{ + if (empty($id)) return ''; + if (!engine_load_engine($engine)) return ''; + + $func = $engine.'ContentUrl'; + + $result = ''; + if (function_exists($func)) + { + $result = $func($id); + } + + return $result; +} + +/** + * Get recommendations for a specific movie that meets the requirements + * of rating and release year. + * + * @author Klaus Christiansen + * @param int $id The external movie id. + * @param float $rating The minimum rating for the recommended movies. + * @param int $year The minimum year for the recommended movies. + * @return array Associative array with: id, title, rating, year. + */ +function engineGetRecommendations($id, $rating, $year, $engine = 'imdb') +{ + if (empty($id)) return ''; + + if (!engine_load_engine($engine)) return ''; + $func = $engine.'Recommendations'; + + if (function_exists($func)) + { + return $func($id, $rating, $year); + } + + return ''; +} + +/** + * Get complete search URL for external site + * + * @author Andreas Goetz + * @param string search string + * @param string engine name + * @return string item search url + */ +function engineGetSearchUrl($find, $engine = 'imdb') +{ + if (!engine_load_engine($engine)) return ''; + $func = $engine.'SearchUrl'; + + $result = ''; + if (function_exists($func)) + { + $result = $func($find); + } + + return $result; +} + +/** + * This function is used internally by setup and engines to add meta-engine of the engine's capability type + * e.g. if the youtube engine provides 'trailer' capability, this will add $config[engine][trailer] = (youtube) + */ +function engine_setup_meta($engine, $meta) +{ + global $config; + + if (array_key_exists('capabilities', $meta) && is_array($meta['capabilities'])) { + foreach ($meta['capabilities'] as $caps) { + $config['engine'][$caps][] = $engine; + } + } +} + +/** + * Retrieve meta information about all available engines + * + * @author Andreas Goetz + * @return array engines array containing engine names + */ +function engineMeta() +{ + $engines = array(); + + if ($dh = @opendir(__DIR__)) + { + while (($file = readdir($dh)) !== false) + { + if ((preg_match("/(.*)\.php$/", $file, $matches)) && ($matches[1] != 'engines')) + { + // engine file + $engine = $matches[1]; + + // get meta data + engine_load_engine($engine); + + $func = $engine.'Meta'; + + if (function_exists($func)) + { + $meta = $func(); + $engines[$engine] = $meta; + + // required php version present? + if (array_key_exists( 'php', $engines[$engine] ) && (version_compare(phpversion(), $engines[$engine]['php']) < 0)) + { + unset($engines[$engine]); + } + } + } + } + closedir($dh); + } + + return $engines; +} + +/** + * Determine actor engine from actor id, defaults to imdb + * + * @author Michael Kollmann + * @param string actor id + * @return string engine name + */ +function engineGetActorEngine($id) +{ + // recognize engine from id + if ($id) + { + // actor engine prefixed, too? (imdb:nm0347149) + if (preg_match('/^(\w+):/', $id, $match)) $engine = $match[1]; + elseif (preg_match('/^tv\d+$/', $id)) $engine = 'tvcom'; + } + if (empty($engine)) $engine = 'imdb'; + + return $engine; +} + +/** + * Get actors details URL in external site + * + * @author Michael Kollmann + * @param string actor name + * @param string actor id + * @param string engine name + * @return string actor details url + */ +function engineGetActorUrl($name, $id, $engine = 'imdb') +{ + if (!engine_load_engine($engine)) return ''; + $func = $engine.'ActorUrl'; + + $result = ''; + if (function_exists($func)) + { + $id = preg_replace('|^'.$engine.':|', '', $id); + $result = $func($name, $id); + } + + return $result; +} + +/** + * Include engine file and execute item search + * + * @author Michael Kollmann + * @param string actor name + * @param string actor id + * @param string engine name + * @return array array with Actor-URL and Thumbnail + */ +function engineActor($name, $id, $engine = 'imdb') +{ + global $cache; + + if (!engine_load_engine($engine)) return array(); + $func = $engine.'Actor'; + + $result = array(); + if (function_exists($func)) + { + $id = preg_replace('|^'.$engine.':|', '', $id); + + $cache = true; + $result = $func($name, $id); + } + + return $result; +} + +/** + * Callback function for validating if an engine has a certain capability + */ +function engine_get_capability($engine, $searchtype) +{ + global $config; + + // get the meta information + $engine = $config['engines'][$engine]; + + if (array_key_exists( 'capabilities', $engine) && is_array($engine['capabilities'])) + { + return in_array($searchtype, $engine['capabilities']); + } + else + { + return $searchtype == 'movie'; + } +} + +/** + * Get list of engines which have certain capability + * + * 'movie' search capability is assumed as default, either if + * $searchtype is empty or engine does not maintain specific capability + * + * @return array list of capable engines + */ +function engine_get_capable_engines($searchtype) +{ + global $config; + + if (!$searchtype) $searchtype = 'movie'; + + $engines = array(); + foreach ($config['engines'] as $engine => $meta) + { + $enabled = $config['engine'][$engine]; + if ($enabled && engine_get_capability($engine, $searchtype)) $engines[$engine] = $enabled; + } + + return $engines; +} + +/** + * Clean HTML tags from hierarchical associative array + * + * @param array $data string or hierarchical array to convert + */ +function engine_clean_input(&$data) +{ + if (is_array($data)) foreach ($data as $key => $val) + { + if (is_array($val)) + engine_clean_input($data[$key]); + else + { + $val = html_to_text($val); + $data[$key] = html_clean_utf8($val); + } + } +} + +/** + * Filter result set for unique engine ids. + * This avoids deduplication of search results inside every single engine. + */ +function engine_deduplicate_result($data) +{ + $keys = array(); + for ($i=0; $i + * @author Victor La + * @desc Original filmweb.php by Marek Domaniuk, rewritten by Victor La + * @desc using Andreas Goetz's imdb.php as a template + * @link https://www.filmweb.com Internet Movie Database + * @version $Id: filmweb.php,v 1.4 2007/08/08 18:28:15 andig2 Exp $ + */ + +$GLOBALS['filmwebServer'] = 'https://www.filmweb.pl'; +$GLOBALS['filmwebIdPrefix'] = 'filmweb:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function filmwebMeta() +{ + return array('name' => 'FilmWeb (pl)', 'stable' => 1); +} + +/** + * Get Url to search FilmWeb for a movie + * + * @author Marek Domaniuk + * @param string The search string + * @return string The search URL (GET) + */ +function filmwebSearchUrl($title) +{ + global $filmwebServer; + return $filmwebServer.'/Find?query='.urlencode($title).'&category=1&submit=szukaj'; +} + +/** + * Get Url to visit FilmWeb for a specific movie + * + * @author Andreas Goetz + * @author Marek Domaniuk + * @author Victor La + * @param string $id The movie's external id + * @return string The visit URL + */ +function filmwebContentUrl($id) +{ + global $filmwebServer; + global $filmwebIdPrefix; + $id = preg_replace('/^'.$filmwebIdPrefix.'/', '', $id); + return $filmwebServer.'/Film?id='.$id; +} + +/** + * Get Url for actor on FilmWeb + * + * @author Victor La + * @param string $name Name of the Actor + * @return string The actor URL (GET) + */ +function filmwebActorUrl($name, $actorid) +{ + global $filmwebServer; + $url = ($actorid) ? '/Person,id='.urlencode($actorid) : '/szukaj?q='.urlencode($name).'&alias=person'; + return $filmwebServer.$url; +} + +/** + * Search a Movie + * + * Searches for a given title on the FilmWeb and returns the found links in + * an array + * + * @author Marek Domaniuk + * @author Victor La + * @param string The search string + * @return array Associative array with id and title + */ +function filmwebSearch($title) +{ + global $filmwebServer; + global $filmwebIdPrefix; + global $CLIENTERROR; + + $resp = httpClient(filmwebSearchUrl($title), 1); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + preg_match_all('/(.+?)<\/a>.+?\((.+?)\)/si', $resp['data'], $data, PREG_SET_ORDER); + foreach ($data as $row) + { + $info['id'] = trim($row[1]); + + $row[2] = preg_replace('//','', $row[2]); + $row[2] = preg_replace('/<\/b>/','', $row[2]); + $info['title'] = trim($row[2]); + + // add year (helpful in case of multiple matches) + $info['title'] = $info['title'].' ('.$row[3].')'; + + //Check URL to see if the movie ID is in it, if not, load the page and grab it from that new page + if (preg_match('/id=(\d+)/', $info['id'], $single)) + { + $info['id'] = $single[1]; + } + elseif ((strpos($info['id'], 'id=')) != true) + { + $subResp = httpClient($info['id'],1); + if (!$subResp['success']) $CLIENTERROR .= $subResp['error']."\n"; + //dodaj do ulubionych + preg_match('/,id=(\d+)">/i', $subResp['data'], $single); + $info['id'] = $single[1]; + } + $info['id'] = $filmwebIdPrefix.$info['id']; + + $ary[] = $info; + } + + return $ary; +} + +/** + * Fetches the data for a given FilmWeb-ID + * + * @author Marek Domaniuk + * @author Victor La + * @param int FilmWeb-ID + * @return array Result data + */ +function filmwebData($filmwebID) +{ + global $filmwebServer; + global $filmwebIdPrefix; + global $CLIENTERROR; + + $filmwebID = preg_replace('/^'.$filmwebIdPrefix.'/', '', $filmwebID); + $data= array(); // result + $ary = array(); // temp + + // fetch mainpage + $resp = httpClient(filmwebContentUrl($filmwebID), 1); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Titles - Fixed + preg_match('/
      (.*?)(.+?)<\/span>/is', $resp['data'], $ary); + $data['title'] = trim($ary[1]); + + //DOUBLE CHECK THIS SECTION OF CODE FOR THE SUBTITLE! + if (!preg_match('/.+?Kerry Conran + preg_match('/re.yseria.+?title="(.+?)- filmografia.+?"/i', $resp['data'], $ary); + $data['director'] = trim($ary[1]); + + // Rating - Fixed + preg_match('/(.+?)<\/b>\/10/i', $resp['data'], $ary); + $data['rating'] = trim($ary[1]); + + // Countries - Fixed + preg_match_all('/(.+?)<\/A>/i', $resp['data'], $ary, PREG_PATTERN_ORDER); + $data['country'] = trim(join(', ', $ary[1])); + + // Languages - DOES THIS NEED TO BE FIXED? + //$data['language'] = ''; + + // Plot (movies in their early stages have the plot here but not yet in plotsummary?) - Fixed 4-5-07 + // Not necessary for FilmWeb? + + // Genres (as Array) - Fixed + $genres = array( + 'Przygodowy' => 'Adventure', + 'Akcja' => 'Action', + 'Komedia' => 'Comedy', + 'Familijny' => 'Family', + 'Muzyka' => 'Music', + 'Western' => 'Western', + 'Dla doros³ych' => 'Adult', + 'Krymina³' => 'Crime', + 'Fantasy' => 'Fantasy', + 'Musical' => 'Musical', + 'Muzyczny' => 'Musical', + 'Krótkometra¿owy' => 'Short', + 'Dokumentalny' => 'Documentary', + 'Film-Noir' => 'Film-Noir', + 'Mystery' => 'Mystery', + 'Thriller' => 'Thriller', + 'Dreszczowiec' => 'Thriller', + 'Animacja' => 'Animation', + 'Dramat' => 'Drama', + 'Melodramat' => 'Drama', + 'Dramat historyczny' => 'History', + 'Historyczny' => 'History', + 'Dramat obyczajowy' => 'Drama', + 'Dramat s±dowy' => 'Drama', + 'Dramat spo³eczny' => 'Drama', + 'Horror' => 'Horror', + 'Romans' => 'Romance', + 'Wojenny' => 'War', + 'Biograficzny' => 'Biographic', + 'Erotyczny' => 'Adult', + 'Komedia kryminalna' => 'Comedy', + 'Komedia obycz.' => 'Comedy', + 'Komedia rom.' => 'Comedy', + 'Komedia' => 'Comedy', + 'Czarna komedia' => 'Comedy', + 'Dla dzieci' => '', + 'Obyczajowy' => '', + 'Bibilijny' => '', + 'Sensacja' => 'Action', + 'Sensacyjny' => 'Action', + 'Fabularyzowany dok.' => 'Documentary', + 'Psychologiczny' => '' + ); + preg_match_all('/genreIds=\d.+?">(.+?)(.+?)<\/a>.+?(().|(
      :<\/div>.+?
      (.+?)<\/div>))/si', $resp['data'], $ary,PREG_PATTERN_ORDER); + $count = 0; + $cast = ''; + while (isset($ary[1][$count])) + { + $actor = trim(strip_tags($ary[2][$count])); + $role = trim(strip_tags($ary[6][$count])); + $role = preg_replace('/ /',' ', $role); + $role = trim($role); + + //$actorid= $ary[1][$count]; //, $m)) ? $m[1] : ''; + preg_match('/.+?Person\,id=(\d+)/i', $ary[1][$count], $ary2); + + if (!empty($ary2[1])) + { + $actorid= $ary2[1]; + } + else + { + $resp = httpClient($ary[1][$count], 1); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + preg_match('/,Person.+?,id=(\d+)">/i', $resp['data'], $ary2); + if (!empty($ary2[1])) $actorid = $ary2[1]; + } + + + $cast .= "$actor::$role::$filmwebIdPrefix$actorid\n"; + $count++; + } + $data['cast'] = trim($cast); + + // fetch Plot + $resp = httpClient($filmwebServer.'/FilmDescriptions?id='.$filmwebID, 1); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Plot + preg_match('/
    • (.+?)<\/div><\/li>/is', $resp['data'], $ary); + if (!empty($ary[1])) $data['plot'] = trim($ary[1]); + $data['plot'] = preg_replace('/[\n\r]/',' ', $data['plot']); + $data['plot'] = preg_replace('/ /',' ', $data['plot']); + $data['plot'] = trim($data['plot']); + + return $data; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor, not sure if this can be made + * a one-step process? + * + * @author Victor La + * @param string $name Name of the Actor + * @return array array with Actor-URL and Thumbnail + */ +function filmwebActor($name, $actorid) +{ + global $filmwebServer; + + // search directly by id or via name? + $resp = httpClient(filmwebActorUrl($name, $actorid), 1); + + $ary = array(); + + if (preg_match('//i', $resp['data'], $m)) { + $resp = httpClient($m[1], true); + } + + // now we should have loaded the best match + if (preg_match('/ + * + * @link http://images.google.com Google image search + * @link http://code.google.com/apis/ajaxsearch/documentation/ API doc + * + * @version $Id: google.php,v 1.13 2013/03/16 14:29:47 andig2 Exp $ + */ + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function googleMeta() +{ + return array('name' => 'Google', 'stable' => 1, 'capabilities' => array('image')); +} + +/** + * Search an image on Google + * + * Searches for a given title on the google and returns the found links in + * an array + * + * @param string The search string + * @return array Associative array with id and title + */ +function googleSearch($title) +{ + global $CLIENTERROR; + global $cache; + global $config; + global $savedata_for_errorpage; + + $page = 1; + $data = array(); + $data['encoding'] = 'utf-8'; + + do + { + $url = "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=large&q=".urlencode($title)."&start=".count($data); + + if ( $config['debug'] ) + { + // save data to pass to functions.php - erropage + // if url fails in httpclient it can go directly to errorpage which loses + // message set in httpCLient + // this is a cause of not finding cover url + $savedata_for_errorpage = 'Module->google.php, Message->'; + } + + $resp = httpClient($url, $cache); + + if ( $config['debug'] ) + { + unset($savedata_for_errorpage); + } + + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $json = json_decode($resp['data']); +# dump($resp['data']); +# dump($page); +# dump($json); + + // prevent caching invalid responses + if ($json->responseStatus != 200 && $cache) { + $cache_file = cache_get_filename($url, CACHE_HTML); + @unlink($cache_file); + } + + foreach ($json->responseData->results as $row) + { + # dump($row); + $res = array(); + $res['title'] = $row->width.'x'.$row->height; // width x height + $res['imgsmall']= $row->tbUrl; // small thumbnail url + $res['coverurl']= $row->url; // resulting target url + $data[] = $res; + } + } + // Google does not return more than 4 pages of results. Limiting to 2 for performance + while ($page++ < 3); + +# dump($data); + + return $data; +} + +?> \ No newline at end of file diff --git a/videodb/engines/imdb.php b/videodb/engines/imdb.php new file mode 100644 index 0000000..c3889a8 --- /dev/null +++ b/videodb/engines/imdb.php @@ -0,0 +1,804 @@ + + * @link http://www.imdb.com Internet Movie Database + * @version $Id: imdb.php,v 1.76 2013/04/10 18:11:43 andig2 Exp $ + */ + +$GLOBALS['imdbServer'] = 'https://www.imdb.com'; +$GLOBALS['imdbIdPrefix'] = 'imdb:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function imdbMeta() +{ + return array('name' => 'IMDB', 'stable' => 1); +} + + +/** + * Get Url to search IMDB for a movie + * + * @author Andreas Goetz + * @param string The search string + * @return string The search URL (GET) + */ +function imdbSearchUrl($title) +{ + global $imdbServer; + return $imdbServer.'/find?s=all&q='.urlencode($title); +} + +/** + * Get Url to visit IMDB for a specific movie + * + * @author Andreas Goetz + * @param string $id The movie's external id + * @return string The visit URL + */ +function imdbContentUrl($id) +{ + global $imdbServer; + global $imdbIdPrefix; + $id = preg_replace('/^'.$imdbIdPrefix.'/', '', $id); + return $imdbServer.'/title/tt'.$id.'/'; +} + +/** + * Get IMDB recommendations for a specific movie that meets the requirements + * of rating and release year. + * + * @author Klaus Christiansen + * @param int $id The external movie id. + * @param float $rating The minimum rating for the recommended movies. + * @param int $year The minimum year for the recommended movies. + * @return array Associative array with: id, title, rating, year. + * If error: $CLIENTERROR contains the http error and blank is returned. + */ +// Only used in contrib/add_recommended_movies.php +function imdbRecommendations($id, $required_rating, $required_year) +{ + global $CLIENTERROR; + + $url = imdbContentUrl($id); + $resp = httpClient($url, true); + + $recommendations = array(); + preg_match_all('/
      /si', $resp['data'], $ary, PREG_SET_ORDER); + + foreach ($ary as $recommended_id) { + $rec_resp = getRecommendationData($recommended_id[1]); + $imdbId = $recommended_id[1]; + $title = $rec_resp['title']; + $year = $rec_resp['year']; + $rating = $rec_resp['rating']; + + // matching at least required rating? + if (empty($required_rating) || (float) $rating < $required_rating) continue; + + // matching at least required year? + if (empty($required_year) || (int) $year < $required_year) continue; + + $data = array(); + $data['id'] = $imdbId; + $data['rating'] = $rating; + $data['title'] = $title; + $data['year'] = $year; + + $recommendations[] = $data; + } + return $recommendations; +} + +function getRecommendationData($imdbID) { + global $imdbServer; + global $imdbIdPrefix; + global $CLIENTERROR; + + $imdbID = preg_replace('/^'.$imdbIdPrefix.'/', '', $imdbID); + + // fetch mainpage + $resp = httpClient($imdbServer.'/title/tt'.$imdbID.'/', true); // added trailing / to avoid redirect + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Titles and Year + // See for different formats. https://contribute.imdb.com/updates/guide/title_formats + if ($data['istv']) { // @todo this is always false + if (preg_match('/"(.+?)"(.+?)\(TV Episode (\d+)\) - IMDb<\/title>/si', $resp['data'], $ary)) { + # handles one episode of a TV serie + $data['title'] = trim($ary[1]); + $data['year'] = $ary[3]; + } else if (preg_match('/<title>(.+?)\(TV Series (\d+).+?<\/title>/si', $resp['data'], $ary)){ + $data['title'] = trim($ary[1]); + $data['year'] = trim($ary[2]); + } + } else { + preg_match('/<title>(.+?)\((\d+)\).+?<\/title>/si', $resp['data'], $ary); + $data['title'] = trim($ary[1]); + $data['year'] = trim($ary[2]); + } + + // Rating + preg_match('/<span class="AggregateRatingButton__RatingScore-.+?">(.+?)<\/span>/si', $resp['data'], $ary); + $data['rating'] = trim($ary[1]); + + return $data; +} + +/** + * Search a Movie + * + * Searches for a given title on the IMDB and returns the found links in + * an array + * + * @author Tiago Fonseca <t_r_fonseca@yahoo.co.uk> + * @author Charles Morgan <cmorgan34@yahoo.com> + * @param string title The search string + * @param boolean aka Use AKA search for foreign language titles + * @return array Associative array with id and title + */ +function imdbSearch($title, $aka=null) +{ + global $imdbServer; + global $imdbIdPrefix; + global $CLIENTERROR; + global $cache; + + $url = $imdbServer.'/find?q='.urlencode($title); + if ($aka) $url .= ';s=tt;site=aka'; + + $resp = httpClient($url, $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $data = array(); + + // add encoding + $data['encoding'] = $resp['encoding']; + + // direct match (redirecting to individual title)? + // @todo i don't think this gets called anymore, investigate + if (preg_match('/^'.preg_quote($imdbServer,'/').'\/[Tt]itle(\?|\/tt)([0-9?]+)\/?/', $resp['url'], $single)) + { + $info = array(); + $info['id'] = $imdbIdPrefix.$single[2]; + + // Title + preg_match('/<title>(.*?) \([1-2][0-9][0-9][0-9].*?\)<\/title>/i', $resp['data'], $m); + list($t, $s) = explode(' - ', trim($m[1]), 2); + $info['title'] = trim($t); + $info['subtitle'] = trim($s); + + $data[] = $info; + } + + // multiple matches + else if (preg_match_all('#div class="ipc-metadata-list-summary-item__tc".*href="/title/tt(\d+)/.*>([^\<]+)</a>.*<ul.*>(.*)</ul>.*</div>#Uism', $resp['data'], $multi, PREG_SET_ORDER)) + { + foreach ($multi as $row) + { + $info = [ + 'id' => $imdbIdPrefix.$row[1], + 'title' => $row[2], + 'year' => null + ]; + if (preg_match_all('#<label.*>([^\<]+)</label>#Uism', $row[3], $labels, PREG_PATTERN_ORDER)) + { + foreach ($labels[1] as $label) + { + if (preg_match('#^(\d{4})$#i', $label)) $info['year'] = $label; + if (preg_match('#^.*(episode|series)$#i', $label)) $info['title'] .= ' ('.$label.')'; + } + } + $data[] = $info; + } + } elseif (preg_match_all('/<div class="col-title">.+?<a href="\/title\/tt(\d+)\/\?ref_=adv_li_tt".+?>(.+?)<\/a>.+?<span .+?>\((\d+).*?\)<\/span>/is', $resp['data'], $ary, PREG_SET_ORDER)) { + foreach ($ary as $row) { + $info = array(); + $info['id'] = $imdbIdPrefix.$row[1]; + $info['title'] = $row[2]; + $info['year'] = $row[3]; + $data[] = $info; + } + } + + return $data; +} + +/** + * Fetches the data for a given IMDB-ID + * + * @author Tiago Fonseca <t_r_fonseca@yahoo.co.uk> + * @author Victor La <cyridian@users.sourceforge.net> + * @author Roland Obermayer <robelix@gmail.com> + * @param int IMDB-ID + * @return array Result data + */ +function imdbData($imdbID) +{ + global $imdbServer; + global $imdbIdPrefix; + global $CLIENTERROR; + global $cache; + + $imdbID = preg_replace('/^'.$imdbIdPrefix.'/', '', $imdbID); + $data= array(); // result + $ary = array(); // temp + + // fetch mainpage + $resp = httpClient($imdbServer.'/title/tt'.$imdbID.'/', $cache); // added trailing / to avoid redirect + #testing code save resp data from imdb + #file_put_contents('./cache/httpclient-php_imdbData_title.html', $resp['data']); // write page data to file + + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // extract json data from page + if (preg_match('#(\<script id\="__NEXT_DATA__".*?\>)(.*?)(\</script\>)#',$resp['data'],$matches)) + { + #file_put_contents('./cache/nextdata.json', $matches[2]); // write json data to file + $json_data = json_decode($matches[2],true); + #file_put_contents('./cache/nextdata-decoded.json', print_r($json_data, true)); // write formated json data to file + } + + // add encoding + $data['encoding'] = $resp['encoding']; + + // Check if it is a TV series episode + if (preg_match('/<title>.+?\(TV (Episode|Series|Mini-Series).*?<\/title>/si', $resp['data'])) { + $data['istv'] = 1; + + # find id of Series + preg_match('/<meta property="imdb:pageConst" content="tt(\d+)"\/>/si', $resp['data'], $ary); + $data['tvseries_id'] = trim($ary[1]); + } + + // Titles and Year + // See for different formats. https://contribute.imdb.com/updates/guide/title_formats + if ($data['istv']) { + if (preg_match('/<title>"(.+?)"(.+?)\(TV Episode (\d+)\) - IMDb<\/title>/si', $resp['data'], $ary)) { + # handles one episode of a TV serie + $data['title'] = trim($ary[1]); + $data['subtitle'] = trim($ary[2]); + $data['year'] = $ary[3]; + } else if (preg_match('/<title>(.+?)\(TV (?:Series|Mini-Series) (\d+).+?\) - IMDb<\/title>/si', $resp['data'], $ary)) { + # handles a TV series. + # split title - subtitle + list($t, $s) = explode(' - ', $ary[1], 2); + # no dash, lets try colon + if ($s == false) { + list($t, $s) = explode(': ', $ary[1], 2); + } + $data['title'] = trim($t); + $data['subtitle'] = trim($s); + $data['year'] = trim($ary[2]); + } + } else { + preg_match('/<title>(.+?)\(.*?(\d+)\).+?<\/title>/si', $resp['data'], $ary); + $data['year'] = trim($ary[2]); + # split title - subtitle + list($t, $s) = explode(' - ', $ary[1], 2); + # no dash, lets try colon + if ($s == false) { + list($t, $s) = explode(': ', $ary[1], 2); + } + $data['title'] = trim($t); + $data['subtitle'] = trim($s); + } + # orig. title + preg_match('/<div class="originalTitle">(.+?)<span class="description"> \(original title\)<\/span><\/div>/si', $resp['data'], $ary); + $data['origtitle'] = trim($ary[1]); + + // Cover URL + $data['coverurl'] = imdbGetCoverURL($resp['data'], $json_data); + + // MPAA Rating + $data['mpaa'] = ""; + $data['mpaa'] = $json_data["props"]["pageProps"]["aboveTheFoldData"]["certificate"]["rating"]; + + // Runtime + if (filter_var($json_data["props"]["pageProps"]["aboveTheFoldData"]["runtime"]["seconds"], FILTER_SANITIZE_NUMBER_INT) > 0) { + # use the runtime from the next_data json data + $data['runtime'] = filter_var($json_data["props"]["pageProps"]["aboveTheFoldData"]["runtime"]["seconds"], FILTER_SANITIZE_NUMBER_INT) / 60; + } else if (preg_match('/<li role="presentation" class="ipc-inline-list__item">(\d+)(?:<!-- --> ?)+(?:h|s).*?(?:(?:<!-- --> ?)+(\d+)(?:<!-- --> ?)+.+?)?<\/li>/si', $resp['data'], $ary)) { + # handles Hours and maybe minutes. Some movies are exactly 1 hours. + $minutes = intval($ary[2]); + if (is_numeric($ary[1])) { + $minutes += intval($ary[1]) * 60; + } + + $data['runtime'] = $minutes; + } else if (preg_match('/<li role="presentation" class="ipc-inline-list__item">(\d+)(?:<!-- --> ?)+m.*?<\/li>/si', $resp['data'], $ary)) { + # handle only minutes + $data['runtime'] = $ary[1]; + } else if (preg_match('/<div class="ipc-metadata-list-item__content-container">(\d+)(?:<!-- --> ?)+m.*?<\/div>/si', $resp['data'], $ary)) { + # handle only minutes + # Handles the case where runtime is only in the technical spec section. + $data['runtime'] = $ary[1]; + } + + // Rating + preg_match('/<div data-testid="hero-rating-bar__aggregate-rating__score" class="sc-.+?"><span class="sc-.+?">(.+?)<\/span><span>\/<!-- -->10<\/span><\/div>/si', $resp['data'], $ary); + $data['rating'] = trim($ary[1]); + + // Countries + preg_match_all('/href="\/search\/title\/\?country_of_origin.+?>(.+?)<\/a>/si', $resp['data'], $ary, PREG_PATTERN_ORDER); + $data['country'] = trim(join(', ', $ary[1])); + + // Languages + $data['language'] = ''; + if (isset( $json_data["props"]["pageProps"]["mainColumnData"]["spokenLanguages"]["spokenLanguages"]) && + is_array($json_data["props"]["pageProps"]["mainColumnData"]["spokenLanguages"]["spokenLanguages"])) + { + foreach ($json_data["props"]["pageProps"]["mainColumnData"]["spokenLanguages"]["spokenLanguages"] as $languagedata) + { + $languagearray[] = trim($languagedata["text"]); + } + $data['language'] = trim(strtolower(join(', ',$languagearray))); + } + + // Genres (as Array) + preg_match_all('/class="ipc-chip__text">(.+?)<\/span><\/a>/si', $resp['data'], $ary, PREG_PATTERN_ORDER); + foreach($ary[1] as $genre) { + $data['genres'][] = trim($genre); + } + + // for Episodes - try to get some missing stuff from the main series page + if ( $data['istv'] and (!$data['runtime'] or !$data['country'] or !$data['language'] or !$data['coverurl'])) { + $sresp = httpClient($imdbServer.'/title/tt'.$data['tvseries_id'].'/', $cache); + if (!$sresp['success']) $CLIENTERROR .= $resp['error']."\n"; + + # runtime + if (preg_match('/<li role="presentation" class="ipc-inline-list__item">(\d+)(?:<!-- --> ?)+(?:h|s).*?(?:(?:<!-- --> ?)+(\d+)(?:<!-- --> ?)+.+?)?<\/li>/si', $resp['data'], $ary)) { + # handles Hours and maybe minutes. Some movies are exactly 1 hours. + $minutes = intval($ary[2]); + if (is_numeric($ary[1])) { + $minutes += intval($ary[1]) * 60; + } + + $data['runtime'] = $minutes; + } else if (preg_match('/<li role="presentation" class="ipc-inline-list__item">(\d+)(?:<!-- --> ?)+m.*?<\/li>/si', $resp['data'], $ary)) { + # handle only minutes + $data['runtime'] = $ary[1]; + } else if (preg_match('/<div class="ipc-metadata-list-item__content-container">(\d+)(?:<!-- --> ?)+m.*?<\/div>/si', $resp['data'], $ary)) { + # handle only minutes + # Handles the case where runtime is only in the technical spec section. + $data['runtime'] = $ary[1]; + } + + # country + if (!$data['country']) { + preg_match_all('/href="\/search\/title\/\?country_of_origin.+?>(.+?)<\/a>/si', $sresp['data'], $ary, PREG_PATTERN_ORDER); + $data['country'] = trim(join(', ', $ary[1])); + } + + # language + if (!$data['language']) { + preg_match_all('/<a class=".+?" rel="" href="\/search\/title\?title_type=feature&primary_language=.+?&sort=moviemeter,asc&ref_=tt_dt_ln">(.+?)<\/a>/', $sresp['data'], $ary, PREG_PATTERN_ORDER); + $data['language'] = trim(strtolower(join(', ', $ary[1]))); + } + + # cover + if (!$data['coverurl']) { + $data['coverurl'] = imdbGetCoverURL($sresp['data']); + } + } + + // Plot + if (array_key_exists('plainText', $json_data["props"]["pageProps"]["aboveTheFoldData"]["plot"]["plotText"]) ) + { + $data['plot'] = stripslashes($json_data["props"]["pageProps"]["aboveTheFoldData"]["plot"]["plotText"]["plainText"]); + } + + // Fetch credits + $resp = imdbFixEncoding($data, httpClient($imdbServer.'/title/tt'.$imdbID.'/fullcredits', $cache)); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Cast + // Directors + #testing code save resp data from imdb + #file_put_contents('./cache/httpclient-php_imdbData_cast.html', $resp['data']); // write page data to file + + // Increase the PCRE backtrack limit for a potentially large regex operation + $origBacktrackLimit = ini_get('pcre.backtrack_limit'); + $newBacktrackLimit = '10000000'; + ini_set('pcre.backtrack_limit', $newBacktrackLimit); + + // extract json data from page + if (preg_match('#(\<script id\="__NEXT_DATA__".*?\>)(.*?)(\</script\>)#s',$resp['data'],$matches)) + { + #file_put_contents('./cache/nextdata.json-cast', $matches[2]); // write json data to file + $json_data_cast = json_decode($matches[2],true); + #file_put_contents('./cache/nextdata-decoded.json-cast', print_r($json_data_cast, true)); // write formated json data to file + } + //revert the PCRE limits back to their original values after regex operation, + ini_set('pcre.backtrack_limit', $origBacktrackLimit); + + // cast and directors + $data['cast'] = ""; + $data['director'] = ""; + $cast_done = false; + $directors_done = false; + + if (isset($json_data_cast['props']['pageProps']['contentData']['categories']) && + is_array($json_data_cast['props']['pageProps']['contentData']['categories'])) + { + foreach ($json_data_cast['props']['pageProps']['contentData']['categories'] as $category) + { + if (!isset($category['name'])) + { + continue; + } + switch (strtolower($category['name'])) + { + case "cast": + $cast = imdbGetCast($category, $imdbID); + $data['cast'] = $cast; + $cast_done = true; + break; + case "directors": + case "director": + $dirs = imdbGetDirectors($category); + $data['director'] = $dirs; + $directors_done = true; + break; + default: + // Other categories can be handled here if needed + break; + } + if ($cast_done && $directors_done) + { + break; + } + } + } + + // Fetch plot + $resp = $resp = imdbFixEncoding($data, httpClient($imdbServer.'/title/tt'.$imdbID.'/plotsummary', $cache)); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Plot + //<li class="ipl-zebra-list__item" id="summary-ps0695557"> + // <p>A nameless first person narrator (<a href="/name/nm0001570/">Edward Norton</a>) attends support groups in attempt to subdue his emotional state and relieve his insomniac state. When he meets Marla (<a href="/name/nm0000307/">Helena Bonham Carter</a>), another fake attendee of support groups, his life seems to become a little more bearable. However when he associates himself with Tyler (<a href="/name/nm0000093/">Brad Pitt</a>) he is dragged into an underground fight club and soap making scheme. Together the two men spiral out of control and engage in competitive rivalry for love and power. When the narrator is exposed to the hidden agenda of Tyler's fight club, he must accept the awful truth that Tyler may not be who he says he is.</p> + // <div class="author-container"> + // <em>—<a href="/search/title?plot_author=Rhiannon&view=simple&sort=alpha&ref_=ttpl_pl_0">Rhiannon</a></em> + // </div> + //</li> + preg_match('/<li class="ipl-zebra-list__item" id="summary-p.\d+">\s+<p>(.+?)<\/p>/is', $resp['data'], $ary); + if ($ary[1]) + { + $data['plot'] = trim($ary[1]); + $data['plot'] = preg_replace('/"/', '"', $data['plot']); //Replace HTML " with " + + // removed linked actors like: <a href="/name/nm0001570?ref_=tt_stry_pl">Edward Norton</a> + $data['plot'] = preg_replace('/<a href="\/name\/nm\d+.+?">/', '', $data['plot']); + $data['plot'] = preg_replace('/<\/a>/', '', $data['plot']); + $data['plot'] = preg_replace('/\s+/s', ' ', $data['plot']); + } + + $data['plot'] = html_clean_utf8($data['plot']); + + return $data; +} + +/** + * At the moment - oct 2010 - most imdb-pages were changed to utf8, + * but e.g. fullcredits are still iso-8859-1 + * so data is recoded here + */ +function imdbFixEncoding($data, $resp) +{ + $result = $resp; + $pageEncoding = $resp['encoding']; + + if ($pageEncoding != $data['encoding']) + { + $result['data'] = iconv($pageEncoding, $data['encoding'], html_entity_decode_all($resp['data'])); + } + + return $result; +} + +/** + * Get Url of Cover Image + * + * @author Roland Obermayer <robelix@gmail.com> + * @param string $data IMDB Page data + * @param string $jsondata IMDB json Data + * @return string Cover Image URL + */ +function imdbGetCoverURL($data, $jsondata = null) { + global $imdbServer; + global $CLIENTERROR; + global $cache; + + if ($jsondata !== null) + { + $url = ''; + if (isset($jsondata["props"]["pageProps"]["aboveTheFoldData"]["primaryImage"])) + { + $url = $jsondata["props"]["pageProps"]["aboveTheFoldData"]["primaryImage"]["url"]; + // If you want the image to scaled to a certain size you can do this. + // UX800 sets the width of the image to 800 with correct aspect ratio with regard to height. + // UY800 set the height to 800 with correct aspect ratio with regard to width. + // $url= str_replace('.jpg', 'UY800_.jpg', $url); + } + return $url; + } + +// find cover image url + if (preg_match('/<a class="ipc-lockup-overlay ipc-focusable.*?" href="(\/title\/tt\d+\/mediaviewer\/\??rm.+?)" aria-label=".*?Poster.*?"><div class="ipc-lockup-overlay__screen"><\/div><\/a>/s', $data, $ary)) + { + // Fetch the image page + $resp = httpClient($imdbServer.$ary[1], $cache); + + if ($resp['success']) + { + // get big cover image. + preg_match('/<div style=".+?" class=".+?"><img src="(.+?)"/si', $resp['data'], $ary); + // If you want the image to scaled to a certain size you can do this. + // UX800 sets the width of the image to 800 with correct aspect ratio with regard to height. + // UY800 set the height to 800 with correct aspect ratio with regard to width. + // return str_replace('.jpg', 'UY800_.jpg', $ary[1]); + return trim($ary[1]); + } + $CLIENTERROR .= $resp['error']."\n"; + return ''; + } + // src look somthing like: src="https://images-na.ssl-images-amazon.com/images/M/MV5BMTc0MDMyMzI2OF5BMl5BanBnXkFtZTcwMzM2OTk1MQ@@._V1_UX214_CR0,0,214,317_AL_.jpg" + // The last part ._V1_UX214.....jpg seams to be an function that scales the image. Just remove that we want the full size. + else if (preg_match('/<div.*?class="poster".*?<img.*?src="(.*?\.)_v.*?"/si', $data, $ary)) + { + $img_url = $ary[1]."jpg"; + // Replace the https wtih http. + $img_url = str_replace("https://images-na.ssl-images-amazon.com", "http://ecx.images-amazon.com", $img_url); + return $img_url; + } + else + { + # no image + return ''; + } +} + + +/** + * Get Url to visit IMDB for a specific actor + * + * @author Michael Kollmann <acidity@online.de> + * @param string $name The actor's name + * @param string $id The actor's external id + * @return string The visit URL + */ +function imdbActorUrl($name, $id) +{ + global $imdbServer; + + $path = ($id) ? 'name/'.urlencode($id).'/' : 'Name?'.urlencode(html_entity_decode_all($name)); + + return $imdbServer.'/'.$path; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor, not sure if this can be made + * a one-step process? + * + * @author Andreas Goetz <cpuidle@gmx.de> + * @param string $name Name of the Actor + * @return array array with Actor-URL and Thumbnail + */ +function imdbActor($name, $actorid) +{ + global $imdbServer; + global $cache; + + // search directly by id or via name? + $resp = httpClient(imdbActorUrl($name, $actorid), $cache); + //testing code save resp data from imdb + //$file_path = './cache/httpclient-php_imdbActor_call_1.html'; + //file_put_contents($file_path, $resp['data']); + + // if not direct match load best match + if (preg_match('#<b>Popular Names</b>.+?<a\s+href="(.*?)">#i', $resp['data'], $m) || + preg_match('#<b>Names \(Exact Matches\)</b>.+?<a\s+href="(.*?)">#i', $resp['data'], $m) || + preg_match('#<b>Names \(Approx Matches\)</b>.+?<a\s+href="(.*?)">#i', $resp['data'], $m)) + { + if (!preg_match('/http/i', $m[1])) + { + $m[1] = $imdbServer.$m[1]; + } + $resp = httpClient($m[1], true); + //testing code save resp data from imdb + //$file_path = './cache/httpclient-php_/_imdbActor_call_2.html'; + //file_put_contents($file_path, $resp['data']); + } + + // now we should have loaded the best match + + // only search in img_primary <td> - or we get far to many useless images + preg_match('/<div class="ipc-poster.*?>(.*?)<\/a><\/div>/si', $resp['data'], $match); + + $ary = array(); + if (preg_match('/.+?src="(.+?)".+?<a.*?href="(\/name\/nm\d+\/).+?/si', $match[1], $m)) + { + $ary[0][0] = $m[2]; + $ary[0][1] = $m[1]; + } + + return $ary; +} + +function imdbGetCast(array $category, string $imdbID) +{ + $cast = []; + if (isset($category['section']['items']) && is_array($category['section']['items'])) { + $pageSize = $category['pagination']['queryVariables']['first']; + $total_cast = $category['section']['total']; + + if ($total_cast > $pageSize) { + $cast = imdbCastExtra($imdbID); + } else { + $cast = imdbCast($category['section']['items']); + } + } + return $cast; +} + +function imdbGetDirectors(array $category) +{ + $directors = []; + if (isset($category['section']['items']) && is_array($category['section']['items'])) { + foreach ($category['section']['items'] as $item) { + if (isset($item['rowTitle'])) { + $directors[] = $item['rowTitle']; + } + } + } + $dirs = implode(', ', $directors); + $dirs = substr($dirs, 0, 250); + + return $dirs; +} + +function imdbCast(array $items) +{ + global $imdbIdPrefix; + + // Loop through each item in the items array + foreach ($items as $item) + { + // Check if the required keys exist. + $actorid = isset($item['id']) ? $item['id'] : ""; + $actor = isset($item['rowTitle']) ? $item['rowTitle'] : ""; + // Build the $character string from characters and attributes + if (isset($item['characters']) && is_array($item['characters']) && !empty($item['characters'])) + { + // Join all characters if available + $character = implode(" / ", $item['characters']); + // Append attributes if present + if (isset($item['attributes']) && !empty($item['attributes'])) + { + $character .= " " . $item['attributes']; + } + } + elseif (isset($item['attributes']) && !empty($item['attributes'])) + { + // Use only attributes if characters are not set or empty + $character = $item['attributes']; + } + else + { + // Default to an empty string if neither field is available + $character = ""; + } + // Append episodic credit data if available + if (isset($item['episodicCreditData']) && is_array($item['episodicCreditData'])) + { + $episodicParts = []; + if (isset($item['episodicCreditData']['episodesText']) && !empty($item['episodicCreditData']['episodesText'])) { + $episodicParts[] = $item['episodicCreditData']['episodesText']; + } + if (isset($item['episodicCreditData']['tenureText']) && !empty($item['episodicCreditData']['tenureText'])) { + $episodicParts[] = $item['episodicCreditData']['tenureText']; + } + if (!empty($episodicParts)) { + $character .= " " . implode(", ", $episodicParts); + } + } + // Append the current actor's details + $cast .= "$actor::$character::$imdbIdPrefix$actorid\n"; + } + + return $cast; +} + +function imdbCastExtra($imdbID) +{ + global $imdbIdPrefix; + global $CLIENTERROR; + global $cache; + + $param = ['header' => ['Accept' => 'application/json', + 'User-Agent' => 'Mozilla/5.0', + 'Content-Type' => 'application/json', + ] + ]; + $after = ''; + $cast = ''; + + do + { + $url = 'https://caching.graphql.imdb.com/?operationName=TitleCreditSubPagePagination&variables={"after":"'.$after.'","category":"cast","const":"tt'.$imdbID.'","first":250,"locale":"en-US","originalTitleText":false,"tconst":"tt'.$imdbID.'"}&extensions={"persistedQuery":{"sha256Hash":"716fbcc1b308c56db263f69e4fd0499d4d99ce1775fb6ca75a75c63e2c86e89c","version":1}}'; + + $resp = httpClient($url, $cache, $param); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // Cast + #testing code save resp data from imdb + #file_put_contents('./cache/httpclient-php_imdbData_castextra.html', $resp['data']); // write page data to file + #file_put_contents('./cache/json-castextra', $resp['data']); // write json data to file + $json_data_castextra = json_decode( $resp['data'],true); + #file_put_contents('./cache/jsonDecoded-castextra', print_r($json_data_castextra, true)); // write formated json data to file + + if (isset($json_data_castextra['data']['title']['credits']) && + is_array($json_data_castextra['data']['title']['credits'])) + { + $credits = $json_data_castextra['data']['title']['credits']; + // Loop through each item in the items array + foreach ($credits['edges'] as $edge) + { + // Check if the required keys exist. + $actorId = isset($edge['node']['name']['id']) ? $edge['node']['name']['id'] : ""; + $actor = isset($edge['node']['name']['nameText']['text']) ? $edge['node']['name']['nameText']['text'] : ""; + // Build the $character string from characters and attributes + + if (is_array($edge['node']['characters'])) + { + $characterNames = array_map(function ($char) + { + return $char['name']; + }, $edge['node']['characters']); + $role = implode(' / ', $characterNames); + + if ($edge['node']['attributes']) + { + foreach($edge['node']['attributes'] as $attr) + { + $role .= " (" . $attr['text'] . ")"; + } + } + } + else + { + $role = $edge['node']['attributes']['text']; + } + if ($edge['node']['episodeCredits'] && $edge['node']['episodeCredits']['total'] > 0) + { + $total = $edge['node']['episodeCredits']['total']; + $from = $edge['node']['episodeCredits']['yearRange']['year']; + $to = $edge['node']['episodeCredits']['yearRange']['endYear']; + + $role .= ", $total episodes, $from"; + if ($to) + { + $role .= "-$to"; + } + } + // Append the current actor's details + $cast .= "$actor::$role::$imdbIdPrefix$actorId\n"; + } + } + + $after = $credits['pageInfo']['endCursor']; + } while ($credits['pageInfo']['hasNextPage']); + + return $cast; +} diff --git a/videodb/engines/ofdb.php b/videodb/engines/ofdb.php new file mode 100755 index 0000000..c880915 --- /dev/null +++ b/videodb/engines/ofdb.php @@ -0,0 +1,698 @@ +<?php +/** + * OFDB Parser + * + * Parses data from the OFDB + * + * @package Engines + * @author Chinamann <chinamann@users.sourceforge.net> + * @link http://www.ofdb.de + * @version $Id: ofdb.php,v 1.27 2013/03/16 14:29:47 andig2 Exp $ + */ + +require_once './core/xml.core.php'; + +$GLOBALS['ofdbServer'] = 'https://www.ofdb.de'; +$GLOBALS['ofdbGW'] = 'http://www.ofdbgw.org'; // defunct +$GLOBALS['ofdbIdPrefix'] = 'ofdb:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function ofdbMeta() +{ + return array( + 'name' => 'OFDB (de)' + , 'stable' => 1 + , 'supportsEANSearch' => 1 + ); +} + +/** + * Get search Url for OfDB + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string The search string + * @return string The search URL (GET) + */ +function ofdbSearchUrl($title, $searchType = 'title') +{ + global $ofdbServer; + + // auto switch to ean Mode if title is exactly 13 digits + if (preg_match('#^\s*[0-9]{13}\s*$#',$title)) $searchType = 'ean'; + + $url = $ofdbServer.'/view.php?page=suchergebnis&SText='.urlencode($title); + switch($searchType) + { + default : + case 'text': { + $url = $url.'&Kat=All'; break; + } + case 'ean' : { + $url = $url.'&Kat=EAN'; break; + } + } + + return $url; +} + +/** + * Get content overview URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @return string The visit URL + */ +function ofdbContentUrl($id) +{ + global $ofdbServer; + global $ofdbIdPrefix; + + $id = preg_replace('/^'.$ofdbIdPrefix.'/', '', $id); + list($id, $vid) = explode("-", $id, 2); + return $ofdbServer.'/view.php?page=film&fid='.$id; +} + +/** + * Get content detail URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @return string The visit URL + */ +function ofdbDetailUrl($id) +{ + global $ofdbServer; + return $ofdbServer.'/view.php?page=film_detail&fid='.$id; +} + +/** + * Get explicit version URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @param string $vid The movie's version id + * @return string The visit URL + */ +function ofdbVersionUrl($id, $vid) +{ + global $ofdbServer; + return $ofdbServer.'/view.php?page=fassung&fid='.$id.'&vid='.$vid; +} + +/** + * Get content description URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @param string $sid The movie's description id + * @return string The visit URL + */ +function ofdbDescriptionUrl($id, $sid) +{ + global $ofdbServer; + return $ofdbServer.'/view.php?page=inhalt&fid='.$id.'&sid='.$sid; +} + +/** + * Search a Movie + * + * Searches for a given title on the OfDB and returns the found links in + * an array + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string The search string + * @return array Associative array with id and title + */ +function ofdbSearch($title, $searchType = 'title') +{ + global $CLIENTERROR; + global $cache; + global $ofdbServer; + global $ofdbGW; + global $ofdbIdPrefix; + + $url = $ofdbGW.'/search/'.$title; + $resp = httpClient($url, $cache); +# dump($resp); + + if (!$resp['success']) { + $CLIENTERROR .= $resp['error']."\n"; + return(false); + } + + $xml = load_xml($resp['data']); +# dump($xml); + + if ((int) $xml->status->rcode > 0) { + // prevent caching bad data + if ($cache) { + $cache_file = cache_get_filename($url, CACHE_HTML); + @unlink($cache_file); + + if ($resp['source']) { + // TODO make sure redirects are deleted as well + $url = $resp['source']; + $cache_file = cache_get_filename($url, CACHE_HTML); + @unlink($cache_file); + } + } + $CLIENTERROR .= ((string) $xml->status->rcodedesc)."\n"; + return(false); + } + + $data = array(); + $data['encoding'] = 'utf-8'; + + foreach($xml->resultat->eintrag as $item) + { + $data = array(); + + // Id + $data['id'] = $ofdbIdPrefix.((string) $item->id); + + // Title + $data['title'] = (string) $item->titel; + $data['orgtitle'] = (string) $item->titel_orig; + list($data['title'], $data['subtitle']) = explode(" - ", $data['title'], 2); + + // Year + $data['year'] = (string) $item->jahr; + + // cover url for image lookup + $data['coverurl'] = (string) $item->bild; + + $result[] = $data; + } +# dump($data); + + return($result); +} + +/** + * Fetches the data for a given OfDB id + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param int OfDB id + * @return array Result data + */ +function ofdbData($id) +{ + global $CLIENTERROR; + global $cache; + global $ofdbServer; + global $ofdbGW; + global $ofdbIdPrefix; + + // Languages + $map_laguages = array( + 'arabisch' => 'arabic', + 'bulgarisch' => 'bulgarian', + 'chinesisch' => 'chinese', + 'tschechisch' => 'czech', + 'dänisch' => 'danish', + 'holländisch' => 'dutch', + 'englisch' => 'english', + 'französisch' => 'french', + 'deutsch' => 'german', + 'griechisch' => 'greek', + 'ungarisch' => 'hungarian', + 'isländisch' => 'icelandic', + 'indisch' => 'indian', + 'israelisch' => 'israeli', + 'italienisch' => 'italian', + 'japanisch' => 'japanese', + 'koreanisch' => 'korean', + 'norwegisch' => 'norwegian', + 'polnisch' => 'polish', + 'portugisisch' => 'portuguese', + 'rumänisch' => 'romanian', + 'russisch' => 'russian', + 'serbisch' => 'serbian', + 'spanisch' => 'spanish', + 'schwedisch' => 'swedish', + 'thailändisch' => 'thai', + 'türkisch' => 'turkish', + 'vietnamesisch' => 'vietnamese', + 'kantonesisch' => 'cantonese', + 'katalanisch' => 'catalan', + 'zypriotisch' => 'cypriot', + 'zyprisch' => 'cypriot', + 'esperanto' => 'esperanto', + 'gälisch' => 'gaelic', + 'hebräisch' => 'hebrew', + 'hindi' => 'hindi', + 'jüdisch' => 'jewish', + 'lateinisch' => 'latin', + 'mandarin' => 'mandarin', + 'serbokroatisch' => 'serbo-croatian', + 'somalisch' => 'somali' + ); + + // Genres + $map_genres = array( + 'Amateur' => '', + 'Eastern' => '', + 'Experimentalfilm' => '', + 'Mondo' => '', + 'Kampfsport' => 'Sport', + 'Biographie' => 'Biography', + 'Katastrophen' => 'Thriller', + 'Krimi' => 'Crime', + 'Science-Fiction' => 'Sci-Fi', + 'Kinder-/Familienfilm' => 'Family', + 'Dokumentation' => 'Documentary', + 'Action' => 'Action', + 'Drama' => 'Drama', + 'Abenteuer' => 'Adventure', + 'Historienfilm' => 'History', + 'Kurzfilm' => 'Short', + 'Liebe/Romantik' => 'Romance', + 'Heimatfilm' => 'Romance', + 'Grusel' => 'Horror', + 'Horror' => 'Horror', + 'Erotik' => 'Adult', + 'Hardcore' => 'Adult', + 'Sex' => 'Adult', + 'Musikfilm' => 'Musical', + 'Animation' => 'Animation', + 'Fantasy' => 'Fantasy', + 'Trash' => 'Horror', + 'Komödie' => 'Comedy', + 'Krieg' => 'War', + 'Mystery' => 'Mystery', + 'Thriller' => 'Thriller', + 'Tierfilm' => 'Documentary', + 'Western' => 'Western', + 'TV-Serie' => '', + 'TV-Mini-Serie' => '', + 'Sportfilm' => 'Sport', + 'Splatter' => 'Horror', + 'Manga/Anime' => 'Animation' + ); + + $data = array(); + $data['encoding'] = 'utf-8'; + $data['imdbID'] = $id; + + $id = preg_replace('/^'.$ofdbIdPrefix.'/', '', $id); + list($id, $vid) = explode("-", $id, 2); + + $url = $ofdbGW.'/movie/'.$id; +# dump($url); + $resp = httpClient($url, $cache); + + if (!$resp['success']) { + $CLIENTERROR .= $resp['error']."\n"; + return(false); + } + + $xml = load_xml($resp['data']); +# dump($xml); + + if ((int) $xml->status->rcode > 0) { + // prevent caching bad data + if ($cache) { + $cache_file = cache_get_filename($url, CACHE_HTML); + @unlink($cache_file); + } + $CLIENTERROR .= ((string) $xml->status->rcodedesc)."\n"; + return(false); + } + + // set root + $item = $xml->resultat; + + // Title + $data['title'] = (string) $item->titel; + $data['orgtitle'] = (string) $item->alternativ; + list($data['title'], $data['subtitle']) = explode(" - ", $data['title'], 2); + + // Year + $data['year'] = (string) $item->jahr; + + // Cover url for image lookup + $data['coverurl'] = (string) $item->bild; + + // Plot + $data['plot'] = (string) $item->beschreibung; + if (!$data['plot']) $data['plot'] = (string) $item->kurzbeschreibung; + + // Rating + $data['rating'] = round((float) $item->bewertung->note); + + // Cast + foreach ($item->besetzung->person as $cast) { + $data['cast'] .= "\n"; + $data['cast'] .= ((string) $cast->name); + if ((string) $cast->rolle) $data['cast'] .= '::'.((string) $cast->rolle); + if ((string) $cast->id) $data['cast'] .= '::'.((string) $cast->id); + } + + // Director + foreach ($item->regie->person as $director) { + if ($data['director']) $data['director'] .= ', '; + $data['director'] .= (string) $director->name; + } + + // Country + foreach ($item->produktionsland as $country) { + if ($data['country']) $data['country'] .= ', '; + $data['country'] .= (string) $country->name; + } + + // Genre + $data['genres'] = array(); + foreach ($item->genre->titel as $genre) { + $genre = (string) $genre; + // mapping + if ($map_genres[$genre]) $data['genres'][] = $map_genres[$genre]; + } + + + // Fetch first VID if none already selected + if (!$vid) { + foreach ($item->fassungen->titel as $fassung) { + $vid = (string) $fassung->id; // 1545;210858 + break; + } + if ($vid) { + // IMDB ID + $data['imdbID'] = $ofdbIdPrefix.preg_replace('/;/', '-', $vid); + } + } + +# dump($data); + return($data); +/* + $data = array(); //result + $ary = array(); //temp + $ary2 = array(); //temp2 + + // Fetch Mainpage + $resp = httpClient(ofdbContentUrl($id), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // add encoding + $data['encoding'] = $resp['encoding']; + + // add engine ID -> important for non edit.php refetch + $data['imdbID'] = $ofdbIdPrefix.$id; + + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // Titles / Year + preg_match('/<title>(.*?)<\/title>/i', $resp['data'], $ary); + $ary[1] = preg_replace('/^OFDb[\s-]*!!!!!!!!!!!/', '', $ary[1]); + $ary[1] = preg_replace('/\[.*\]/', ' ', $ary[1]); + if (preg_match('/\(([0-9]*)\)/i',$ary[1],$ary2)) + { + $data['year'] = trim($ary2[1]); + } + $ary[1] = preg_replace('/\([0-9]*\)/', ' ', $ary[1]); + $ary[1] = preg_replace('/\s{2,}/s', ' ', $ary[1]); + + // check if there is a comma sperated article at the end + if (preg_match('#(.*),\s*(A|The|Der|Die|Das|Ein|Eine|Einer)\s*$#i',$ary[1],$subRes)) { + $ary[1] = $subRes[2].' '.$subRes[1]; + } + + list($t,$s) = explode(" - ",trim($ary[1]),2); + $data['title'] = trim($t); + $data['subtitle'] = trim($s); + + // Original Title + if (preg_match('/Originaltitel.*?<b>(.*?)</i', $resp['data'], $ary)) + { + $data['orgtitle'] .= trim($ary[1]); + } + + // Country + if (preg_match('/>Herstellungsland:.*?<b><a.*?>(.*?)<\/a>/i', $resp['data'], $ary)) + { + $data['country'] .= trim($ary[1]); + } + + // Rating + if (preg_match('/<br>Note:\s*([0-9\.]+)/', $resp['data'], $ary)) { + $data['rating'] = $ary[1]; + } + + // Cover URL + if (preg_match('#<img src="(http://img.ofdb.de/film/.*?\.jpg)"#i', $resp['data'], $ary)) + { + $data['coverurl'] = trim($ary[1]); + } + + // Fetch first VID if none already selected + if (!$vid) + { + if (preg_match_all('/view\.php\?page=fassung&fid='.$id.'&vid=([0-9]+)".*?class="Klein">(.*?)</i', $resp['data'], $ary, PREG_SET_ORDER)) + { + foreach($ary as $row) + { + if (trim($row[2]) == "K" || trim($row[2]) == "KV") // Check if there is a good result + { + $vid=$row[1]; + break; + } + } + if (!$vid) // Still empty -> Take the first one + { + $vid=$ary[1][1]; + } + } + } + + // IMDB ID + $data['imdbID'] = $ofdbIdPrefix."$id-$vid"; + + // Fetch Plot + if (preg_match('#href="(plot/[^"]+)"#i', $resp['data'], $ary)) + { + $subresp = httpClient($ofdbServer.'/'.$ary[1], $cache); + + if (!$resp['success']) $CLIENTERROR .= $subresp['error']."\n"; + $subresp['data'] = preg_replace('/[\r\n\t]/',' ', $subresp['data']); + + if (preg_match('#</b><br><br>(.*?)</font></p>#i', $subresp['data'], $ary)) + { + + $ary[1] = preg_replace('/\s{2,}/s', ' ', $ary[1]); + $ary[1] = preg_replace('#<(br|p)[ /]*>#i', "\n", $ary[1]); + $data['plot'] = trim($ary[1]); + //$data['plot'] = "aeääääaaaä"; + } + } + + // Fetch Details + $resp = httpClient(ofdbDetailUrl($id), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // Director + if (preg_match('/<b><i>Regie<\/i><\/b>.*?<table.*?>(.*?)<\/table>/i', $resp['data'], $ary)) + { + if (preg_match_all('/class="Daten"><a.*?>(.*?)<\/a>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach ($ary2 as $row) + { + $data['director'] .= trim($row[1]).', '; + } + $data['director'] = preg_replace('/, $/', '', $data['director']); + } + } + + // Cast + if (preg_match('/<b><i>Darsteller<\/i><\/b>.*?<table.*?>(.*)<\/table>/', $resp['data'], $ary)) + { + // dirty workaround for (.*?) failed on very long match groups issue (tested at PHP 5.2.5.5) + // e.g.: ofdb:7749-111320 (Angel - Jäger der Finsternis) + $ary[1] = preg_replace('#</table.*#','',$ary[1]); + + if (preg_match_all('/class="Daten"><a(.*?)">(.*?)<\/a>.*?<\/td> <td.*?<\/td> <td[^>]*>(.*?)<\/td>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach ($ary2 as $row) + { + $actor = trim(strip_tags($row[2])); + + $actorid = ""; + if (!empty($row[1])) + { + if (preg_match('#href="view.php\?page=person&id=([0-9]*)#i', $row[1], $idAry)) + { + $actorid = $ofdbIdPrefix.$idAry[1]; + } + } + + $character = ""; + if (!empty($row[3])) + { + if (preg_match('#class="Normal">... ([^<]*)<#i', $row[3], $charAry)) + { + $character = trim(strip_tags($charAry[1])); + } + } + $data['cast'] .= "$actor::$character::$actorid\n"; + } + } + } + + if (preg_match('/>Genre\(s\)\:.*?<b>(.*?)<\/b>/i', $resp['data'], $ary)) + { + if (preg_match_all('/<a.*?>(.*?)<\/a>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach($ary2 as $row) { + $genre = trim(html_entity_decode($row[1])); + $genre = strip_tags($genre); + if (!$genre) continue; + if (isset($map_genres[$genre])) $data['genres'][] = $map_genres[$genre]; + } + } + } + + // Fetch Version + $resp = httpClient(ofdbVersionUrl($id, $vid), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // FSK + $fsks = array( + 'FSK o.A.' => '0', + 'FSK 6' => '6', + 'FSK 12' => '12', + 'FSK 16' => '16', + 'FSK 18' => '18', + 'Keine Jugendfreigabe' => '18', + 'SPIO/JK' => '18', + 'juristisch geprüft' => '', + 'ungeprüft' => '' + ); + if (preg_match('/>Freigabe:<.*?<b>(.*?)<\/tr>/i', $resp['data'], $ary)) + { + $fsk = trim(html_entity_decode($ary[1])); + $fsk = strip_tags($fsk); + if (isset($fsks[$fsk])) $data['fsk'] = $fsks[$fsk]; + } + + $lang_list = array(); + if (preg_match('/>Tonformat:(.*?)<\/tr>/i', $resp['data'], $ary) && + preg_match_all('/<a.*?>(\w+?)(\s\().*?a>/si', $ary[1], $langs, PREG_PATTERN_ORDER)) + { + foreach($langs[1] as $language) { + $language = trim(strtolower($language)); + $language = html_entity_decode(strip_tags($language)); + $language = preg_replace('/\s+$/','',$language); + if (!$language) continue; + if (isset($map_laguages[$language])) $language = $map_laguages[$language]; + else continue; + if (!$language) continue; + $lang_list[] = $language; + } + $data['language'] = trim(join(', ', array_unique($lang_list))); + } + + // Runtime + if (preg_match('/>Laufzeit:<.*?<b>(.*?)\s*Min/i', $resp['data'], $ary)) + { + $ary[1] = preg_replace('/:.*!!!!!!!/','', $ary[1]); + $data['runtime'] = trim($ary[1]); + } + + // EAN-Code + if (preg_match('/>EAN\/UPC<\/a>:.*?<b>\s*([0-9]+)\s*<\/b>/i', $resp['data'], $ary)) + { + $data['barcode'] = $ary[1]; + } + + return $data; +*/ +} + + +/** + * Get Url to visit OFDB for a specific actor + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $name The actor's name + * @param string $id The actor's external id + * @return string The visit URL + */ +function ofdbActorUrl($name, $id) +{ + global $ofdbServer; + global $ofdbIdPrefix; + + if ($id) { + $id = preg_replace('/^'.$ofdbIdPrefix.'/', '', $id); + } else { + $id = ofdbGetActorId($name); + } + + // now we have for shure an id + return ($id) ? $ofdbServer.'/view.php?page=person&id='.$id : ''; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor. + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $name Name of the actor + * @param string $id Prefixed ofdb actor id + * @return array array with Actor-URL and Thumbnail + */ +function ofdbActor($name, $id) +{ + global $ofdbServer; + global $ofdbIdPrefix; + + if ($id) { + $id = preg_replace('/^'.$ofdbIdPrefix.'/', '', $id); + } else { + $id = ofdbGetActorId($name); + } + + // now we have for shure an id + $folderId = ($id < 1000) ? 0 : substr($id,0,strlen($id)-3); + + $imgUrl = $ofdbServer.'/images/person/'.$folderId.'/'.$id.'.jpg'; + + $ary = array(); + $ary[0][0] = ofdbActorUrl($name, $id); + $ary[0][1] = $imgUrl; + return $ary; +} + +function ofdbGetActorId($name) +{ + global $ofdbServer; + global $CLIENTERROR; + + // try to guess the id -> first actor found with this name + $url = $ofdbServer.'/view.php?page=liste&Name='.urlencode(html_entity_decode_all($name)); + $resp = httpClient($url, $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + return (preg_match('#view.php?page=person&id=([0-9]+)#i', $resp['data'], $ary)) ? $ary[1] : 0; +} + +/** + * Get an array of all previous prefixes for the ImdbId + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @return array Associative array with ImdbId prefixes + */ +function ofdbImdbIdPrefixes() +{ + global $ofdbIdPrefix; + return array($ofdbIdPrefix); +} + +?> diff --git a/videodb/engines/ofdbscraper.php b/videodb/engines/ofdbscraper.php new file mode 100755 index 0000000..567a206 --- /dev/null +++ b/videodb/engines/ofdbscraper.php @@ -0,0 +1,564 @@ +<?php +/** + * OFDB Parser + * + * Parses data from the OFDB + * + * @package Engines + * @author Chinamann <chinamann@users.sourceforge.net> + * @link http://www.ofdb.de + * @version $Id: ofdb.php,v 1.24 2011/06/18 21:28:01 robelix Exp $ + */ + +$GLOBALS['ofdbscraperServer'] = 'https://www.ofdb.de'; +$GLOBALS['ofdbscraperIdPrefix'] = 'ofdbscraper:'; + +/** + * Get meta information about the engine + * + * @todo Include image search capabilities etc in meta information + */ +function ofdbscraperMeta() +{ + return array( + 'name' => 'OFDB (de) Scraper' + , 'stable' => 1 + , 'supportsEANSearch' => 0 + ); +} + +/** + * Get search Url for OfDB + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string The search string + * @return string The search URL (GET) + */ +function ofdbscraperSearchUrl($title, $searchType = 'title') +{ + global $ofdbscraperServer; + + // auto switch to ean Mode if title is exactly 13 digits + if (preg_match('#^\s*[0-9]{13}\s*$#',$title)) $searchType = 'ean'; + + $url = $ofdbscraperServer.'/view.php?page=suchergebnis&SText='.urlencode($title); + switch($searchType) + { + default : + case 'text': { + $url = $url.'&Kat=All'; break; + } + case 'ean' : { + $url = $url.'&Kat=EAN'; break; + } + } + + return $url; +} + +/** + * Get content overview URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @return string The visit URL + */ +function ofdbscraperContentUrl($id) +{ + global $ofdbscraperServer; + global $ofdbscraperIdPrefix; + + $id = preg_replace('/^'.$ofdbscraperIdPrefix.'/', '', $id); + list($id, $vid) = explode("-", $id, 2); + return $ofdbscraperServer.'/view.php?page=film&fid='.$id; +} + +/** + * Get content detail URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @return string The visit URL + */ +function ofdbscraperDetailUrl($id) +{ + global $ofdbscraperServer; + return $ofdbscraperServer.'/view.php?page=film_detail&fid='.$id; +} + +/** + * Get explicit version URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @param string $vid The movie's version id + * @return string The visit URL + */ +function ofdbscraperVersionUrl($id, $vid) +{ + global $ofdbscraperServer; + return $ofdbscraperServer.'/view.php?page=fassung&fid='.$id.'&vid='.$vid; +} + +/** + * Get content description URL + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $id The movie's external id + * @param string $sid The movie's description id + * @return string The visit URL + */ +function ofdbscraperDescriptionUrl($id, $sid) +{ + global $ofdbscraperServer; + return $ofdbscraperServer.'/view.php?page=inhalt&fid='.$id.'&sid='.$sid; +} + + +/** + * Search a Movie + * + * Searches for a given title on the OfDB and returns the found links in + * an array + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string The search string + * @return array Associative array with id and title + */ +function ofdbscraperSearch($title, $searchType = 'title') +{ + global $ofdbscraperServer; + global $ofdbscraperIdPrefix; + global $CLIENTERROR; + global $cache; + + // auto switch to ean Mode if title is exactly 13 digits + if (preg_match('#^\s*[0-9]{13}\s*$#',$title)) $searchType = 'ean'; + + // search for series + $resp = httpClient(ofdbscraperSearchUrl($title, $searchType), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; +# dump($resp); + + // add encoding + $ary['encoding'] = $resp['encoding']; + + if (preg_match_all('/<br>[0-9]+\.\s*<a href="film\/([0-9]+),[^"]*" onmouseover="[^"]*"[^>]*>([^<]*)<font.*?\/font> \(([\/\-0-9]+)\)<\/a>/', $resp['data'], $data, PREG_SET_ORDER)) + { + foreach ($data as $row) { + $info['id'] = $ofdbscraperIdPrefix.$row[1]; + $info['title'] = trim($row[2]).' ('.$row[3].')'; + $ary[] = $info; + } + } + if (preg_match_all('/<br>[0-9]+\.\s*<a href="film\/([0-9]+),[^"]*" onmouseover="[^"]*"><b>([^<]*)<.*?<a href="view\.php\?page=fassung.*?fid=[0-9]+.*?vid=([0-9]+)" onmouseover="[^"]*">([^<]*)</i', $resp['data'], $data, PREG_SET_ORDER)) + { + foreach ($data as $row) { + $info['id'] = $ofdbscraperIdPrefix.$row[1]."-".$row[3]; + $info['title'] = trim($row[2]).' - '.$row[4]; + $ary[] = $info; + } + } + // do not return an array which contains only an encoding attribute + if (count($ary) < 2) return array(); + + return $ary; +} + +/** + * Fetches the data for a given OfDB id + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param int OfDB id + * @return array Result data + */ +function ofdbscraperData($id) +{ + global $CLIENTERROR; + global $ofdbscraperServer; + global $ofdbscraperIdPrefix; + global $cache; + global $config; + + $id = preg_replace('/^'.$ofdbscraperIdPrefix.'/', '', $id); + list($id, $vid) = explode("-", $id, 2); + + $data = array(); //result + $ary = array(); //temp + $ary2 = array(); //temp2 + + // Fetch Mainpage + $resp = httpClient(ofdbscraperContentUrl($id), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + // add encoding + $data['encoding'] = $resp['encoding']; + + // add engine ID -> important for non edit.php refetch + $data['imdbID'] = $ofdbscraperIdPrefix.$id; + + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // Titles / Year + preg_match('/<title>(.*?)<\/title>/i', $resp['data'], $ary); + $ary[1] = preg_replace('/^OFDb[\s-]*/', '', $ary[1]); + $ary[1] = preg_replace('/\[.*\]/', ' ', $ary[1]); + if (preg_match('/\(([0-9]*)\)/i',$ary[1],$ary2)) + { + $data['year'] = trim($ary2[1]); + } + $ary[1] = preg_replace('/\([0-9]*\)/', ' ', $ary[1]); + $ary[1] = preg_replace('/\s{2,}/s', ' ', $ary[1]); + + // check if there is a comma sperated article at the end + if (preg_match('#(.*),\s*(A|The|Der|Die|Das|Ein|Eine|Einer)\s*$#i',$ary[1],$subRes)) { + $ary[1] = $subRes[2].' '.$subRes[1]; + } + + list($t,$s) = explode(" - ",trim($ary[1]),2); + $data['title'] = trim($t); + $data['subtitle'] = trim($s); + + // Original Title + if (preg_match('/Originaltitel.*?<b>(.*?)</i', $resp['data'], $ary)) + { + $data['orgtitle'] .= trim($ary[1]); + } + + // Country + if (preg_match('/>Herstellungsland:.*?<b><a.*?>(.*?)<\/a>/i', $resp['data'], $ary)) + { + $data['country'] .= trim($ary[1]); + } + + // Rating + if (preg_match('/Note: <span itemprop="ratingValue">([0-9\.]+)/', $resp['data'], $ary)) { +// if (preg_match('/<br>Note:\s*([0-9\.]+)/', $resp['data'], $ary)) { + $data['rating'] = $ary[1]; + } + + // Cover URL + if (preg_match('#<img src="(http://img.ofdb.de/film/na.gif)"#i', $resp['data'], $ary)) + { + $data['coverurl'] = ""; + } + else if (preg_match('#<img src="(http://img.ofdb.de/film/.*?\.jpg)"#i', $resp['data'], $ary)) + { + $data['coverurl'] = trim($ary[1]); + } + + // Fetch first VID if none already selected + if (!$vid) + { + if (preg_match_all('/view\.php\?page=fassung&fid='.$id.'&vid=([0-9]+)".*?class="Klein">(.*?)</i', $resp['data'], $ary, PREG_SET_ORDER)) + { + foreach($ary as $row) + { + if (trim($row[2]) == "K" || trim($row[2]) == "KV") // Check if there is a good result + { + $vid=$row[1]; + break; + } + } + if (!$vid) // Still empty -> Take the first one + { + $vid=$ary[1][1]; + } + } + } + + // IMDB ID + $data['imdbID'] = $ofdbscraperIdPrefix."$id-$vid"; + + // Fetch Plot + if (preg_match('#href="(plot/[^"]+)"#i', $resp['data'], $ary)) + { + $subresp = httpClient($ofdbscraperServer.'/'.$ary[1], $cache); + if (!$resp['success']) $CLIENTERROR .= $subresp['error']."\n"; + $subresp['data'] = preg_replace('/[\r\n\t]/',' ', $subresp['data']); + //ofdbDbg($subresp['data'],false); + if (preg_match('#</b><br><br>(.*?)</font></p>#i', $subresp['data'], $ary)) + { + + $ary[1] = preg_replace('/\s{2,}/s', ' ', $ary[1]); + $ary[1] = preg_replace('#<(br|p)[ /]*>#i', "\n", $ary[1]); + $data['plot'] = trim($ary[1]); + //$data['plot'] = "aeääääaaaä"; + } + } + + // Fetch Details + $resp = httpClient(ofdbscraperDetailUrl($id), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // Director + if (preg_match('/<b><i>Regie<\/i><\/b>.*?<table.*?>(.*?)<\/table>/i', $resp['data'], $ary)) + { + if (preg_match_all('/class="Daten"><a.*?>(.*?)<\/a>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach ($ary2 as $row) + { + $data['director'] .= trim($row[1]).', '; + } + $data['director'] = preg_replace('/, $/', '', $data['director']); + } + } + + // Cast + if (preg_match('/<b><i>Darsteller<\/i><\/b>.*?<table.*?>(.*)<\/table>/', $resp['data'], $ary)) + { + // dirty workaround for (.*?) failed on very long match groups issue (tested at PHP 5.2.5.5) + // e.g.: ofdb:7749-111320 (Angel - Jäger der Finsternis) + $ary[1] = preg_replace('#</table.*#','',$ary[1]); + + if (preg_match_all('/class="Daten"><a(.*?)">(.*?)<\/a>.*?<\/td> <td.*?<\/td> <td[^>]*>(.*?)<\/td>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach ($ary2 as $row) + { + $actor = trim(strip_tags($row[2])); + + $actorid = ""; + if (!empty($row[1])) + { + if (preg_match('#href="view.php\?page=person&id=([0-9]*)#i', $row[1], $idAry)) + { + $actorid = $ofdbscraperIdPrefix.$idAry[1]; + } + } + + $character = ""; + if (!empty($row[3])) + { + if (preg_match('#class="Normal">... ([^<]*)<#i', $row[3], $charAry)) + { + $character = trim(strip_tags($charAry[1])); + } + } + $data['cast'] .= "$actor::$character::$actorid\n"; + } + } + } + + // Genres + $genres = array( + 'Amateur' => '', + 'Eastern' => '', + 'Experimentalfilm' => '', + 'Mondo' => '', + 'Kampfsport' => 'Sport', + 'Biographie' => 'Biography', + 'Katastrophen' => 'Thriller', + 'Krimi' => 'Crime', + 'Science-Fiction' => 'Sci-Fi', + 'Kinder-/Familienfilm' => 'Family', + 'Dokumentation' => 'Documentary', + 'Action' => 'Action', + 'Drama' => 'Drama', + 'Abenteuer' => 'Adventure', + 'Historienfilm' => 'History', + 'Kurzfilm' => 'Short', + 'Liebe/Romantik' => 'Romance', + 'Heimatfilm' => 'Romance', + 'Grusel' => 'Horror', + 'Horror' => 'Horror', + 'Erotik' => 'Adult', + 'Hardcore' => 'Adult', + 'Sex' => 'Adult', + 'Musikfilm' => 'Musical', + 'Animation' => 'Animation', + 'Fantasy' => 'Fantasy', + 'Trash' => 'Horror', + 'Komödie' => 'Comedy', + 'Krieg' => 'War', + 'Mystery' => 'Mystery', + 'Thriller' => 'Thriller', + 'Tierfilm' => 'Documentary', + 'Western' => 'Western', + 'TV-Serie' => '', + 'TV-Mini-Serie' => '', + 'Sportfilm' => 'Sport', + 'Splatter' => 'Horror', + 'Manga/Anime' => 'Animation' + ); + if (preg_match('/>Genre\(s\)\:.*?<b>(.*?)<\/b>/i', $resp['data'], $ary)) + { + if (preg_match_all('/<a.*?>(.*?)<\/a>/i',$ary[1],$ary2, PREG_SET_ORDER)) + { + foreach($ary2 as $row) { + $genre = trim(html_entity_decode($row[1])); + $genre = strip_tags($genre); + if (!$genre) continue; + if (isset($genres[$genre])) $data['genres'][] = $genres[$genre]; + } + } + } + + // Fetch Version + $resp = httpClient(ofdbscraperVersionUrl($id, $vid), $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + // FSK + $fsks = array( + 'FSK o.A.' => '0', + 'FSK 6' => '6', + 'FSK 12' => '12', + 'FSK 16' => '16', + 'FSK 18' => '18', + 'Keine Jugendfreigabe' => '18', + 'SPIO/JK' => '18', + 'juristisch geprüft' => '', + 'ungeprüft' => '' + ); + if (preg_match('/>Freigabe:<.*?<b>(.*?)<\/tr>/i', $resp['data'], $ary)) + { + $fsk = trim(html_entity_decode($ary[1])); + $fsk = strip_tags($fsk); + if (isset($fsks[$fsk])) $data['fsk'] = $fsks[$fsk]; + } + + // Languages + // Languages (as Array) + $laguages = array( + 'arabisch' => 'arabic', + 'bulgarisch' => 'bulgarian', + 'chinesisch' => 'chinese', + 'tschechisch' => 'czech', + 'dänisch' => 'danish', + 'holändisch' => 'dutch', + 'englisch' => 'english', + 'französisch' => 'french', + 'deutsch' => 'german', + 'griechisch' => 'greek', + 'ungarisch' => 'hungarian', + 'isländisch' => 'icelandic', + 'indisch' => 'indian', + 'israelisch' => 'israeli', + 'italienisch' => 'italian', + 'japanisch' => 'japanese', + 'koreanisch' => 'korean', + 'norwegisch' => 'norwegian', + 'polnisch' => 'polish', + 'portugisisch' => 'portuguese', + 'rumänisch' => 'romanian', + 'russisch' => 'russian', + 'serbisch' => 'serbian', + 'spanisch' => 'spanish', + 'schwedisch' => 'swedish', + 'thailändisch' => 'thai', + 'türkisch' => 'turkish', + 'vietnamesisch' => 'vietnamese', + 'kantonesisch' => 'cantonese', + 'katalanisch' => 'catalan', + 'zypriotisch' => 'cypriot', + 'zyprisch' => 'cypriot', + 'esperanto' => 'esperanto', + 'gälisch' => 'gaelic', + 'hebräisch' => 'hebrew', + 'hindi' => 'hindi', + 'jüdisch' => 'jewish', + 'lateinisch' => 'latin', + 'mandarin' => 'mandarin', + 'serbokroatisch' => 'serbo-croatian', + 'somalisch' => 'somali' + ); + $lang_list = array(); + + // Runtime + if (preg_match('/>Laufzeit:<.*?<b>(.*?)\s*Min/i', $resp['data'], $ary)) + { + $ary[1] = preg_replace('/:.*/','', $ary[1]); + $data['runtime'] = trim($ary[1]); + } + + return $data; +} + + +/** + * Get Url to visit OFDB for a specific actor + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $name The actor's name + * @param string $id The actor's external id + * @return string The visit URL + */ +function ofdbscraperActorUrl($name, $id) +{ + global $ofdbscraperServer; + global $ofdbscraperIdPrefix; + + if ($id) { + $id = preg_replace('/^'.$ofdbscraperIdPrefix.'/', '', $id); + } else { + $id = ofdbscraperGetActorId($name); + } + + // now we have for shure an id + return ($id!=0) ? $ofdbscraperServer.'/view.php?page=person&id='.$id : ''; +} + +/** + * Parses Actor-Details + * + * Find image and detail URL for actor. + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @param string $name Name of the actor + * @param string $id Prefixed ofdb actor id + * @return array array with Actor-URL and Thumbnail + */ +function ofdbscraperActor($name, $id) +{ + global $ofdbscraperServer; + + if ($id) { + $id = preg_replace('/^'.$ofdbscraperIdPrefix.'/', '', $id); + } else { + $id = ofdbscraperGetActorId($name); + } + + // now we have for shure an id + $folderId = ($id < 1000) ? 0 : substr($id,0,strlen($id)-3); + + $imgUrl = $ofdbscraperServer.'/images/person/'.$folderId.'/'.$id.'.jpg'; + + $ary = array(); + $ary[0][0] = ofdbscraperActorUrl($name, $id); + $ary[0][1] = $imgUrl; + return $ary; +} + +function ofdbscraperGetActorId($name) +{ + global $ofdbscraperServer; + + // try to guess the id -> first actor found with this name + $url = $ofdbscraperServer.'/view.php?page=liste&Name='.urlencode(html_entity_decode_all($name)); + $resp = httpClient($url, $cache); + if (!$resp['success']) $CLIENTERROR .= $resp['error']."\n"; + + $resp['data'] = preg_replace('/[\r\n\t]/',' ', $resp['data']); + + return (preg_match('#view.php?page=person&id=([0-9]+)#i', $resp['data'], $ary)) ? $ary[1] : 0; +} + + +/** + * Get an array of all previous prefixes for the ImdbId + * + * @author Chinamann <chinamann@users.sourceforge.net> + * @return array Associative array with ImdbId prefixes + */ +function ofdbscraperImdbIdPrefixes() +{ + global $ofdbscraperIdPrefix; + return array($ofdbscraperIdPrefix); +} + +function ofdbscraperDbg($text,$append = true) +{ + file_append('debug.txt', $text, $append); +} +?> diff --git a/videodb/engines/youtube.php b/videodb/engines/youtube.php new file mode 100644 index 0000000..3de14a1 --- /dev/null +++ b/videodb/engines/youtube.php @@ -0,0 +1,74 @@ +<?php +// DEFUNCT, maybe implement new api since current code doesn't work anymore: https://developers.google.com/youtube/v3/docs/search + +/** + * youtube.com trailer search + * + * Search trailers on youtube.com + * + * @package Engines + * @author Andreas Goetz <cpuidle@gmx.de> + * @author Adam Benson <precarious_panther@bigpond.com> + * @link http://www.youtube.com YouTube + * @version $Id: youtube.php,v 1.9 2012/08/10 16:07:53 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/httpclient.php'; + +define('YOUTUBE_CLIENT_ID', 'ytapi-AndreasGoetz-videodb-g7dk2dh6-0'); +define('YOUTUBE_DEVELOPER_KEY', 'AI39si7znfvxGu-6OfT-PIPHxUJbAy429l63_jnWSThlJ7Hitv_gmCpJ9cE_HCnH7PDvSLgthw4wEZ5wSrw139DPLbbmLb50GQ'); + +// http://gdata.youtube.com/feeds/api/videos?client=ytapi-AndreasGoetz-videodb-g7dk2dh6-0&key=AI39si7znfvxGu-6OfT-PIPHxUJbAy429l63_jnWSThlJ7Hitv_gmCpJ9cE_HCnH7PDvSLgthw4wEZ5wSrw139DPLbbmLb50GQ&v=2&start-index=1&max-results=10&q=alien + +/** + * Get meta information about the engine + */ +function youtubeMeta() +{ + return array('name' => 'YouTube', 'stable' => 1, 'php' => '5.0', 'capabilities' => array('trailer')); +} + +function youtubeHasTrailer($title) +{ + return count(youtubeSearch($title)) > 0; +} + +function normalize($str) +{ + return preg_replace('/[^a-zäöüA-ZÄÖÜ0-9\s]/', '', $str); +} + +function youtubeSearch($title) +{ + $trailers = array(); + $title = normalize($title); + $trailerquery = $title." trailer"; + + $youtubeurl = "http://gdata.youtube.com/feeds/api/videos?client=".YOUTUBE_CLIENT_ID."&key=".YOUTUBE_DEVELOPER_KEY."&v=2&". + "q=".urlencode($trailerquery)."&start-index=1&max-results=10"; + + $resp = httpClient($youtubeurl, true); + if (!$resp['success']) return $trailers; + + $xml = simplexml_load_string($resp['data']); + + // obtain namespaces + $namespaces = $xml->getNameSpaces(true); + + foreach ($xml->entry as $trailer) + { + $media = $trailer->children($namespaces['media']); + $yt = $media->group->children($namespaces['yt']); + $id = $yt->videoid; + + // API filtering code removed + $trailers[] = array('id' => (string) $id, + 'src' => (string) $trailer->content['src'], + 'title' => (string) $trailer->title); + if (count($trailers) >= 10) break; + } + + return $trailers; +} + diff --git a/videodb/help.php b/videodb/help.php new file mode 100644 index 0000000..ab47301 --- /dev/null +++ b/videodb/help.php @@ -0,0 +1,44 @@ +<?php +/** + * Help Page + * + * Browses the manual + * + * @package videoDB + * @author Andreas Gohr <a.gohr@web.de> + * @version $Id: help.php,v 1.10 2004/09/20 15:15:41 andig2 Exp $ + */ + +require_once './core/functions.php'; + +function _replace_anchors_callback($matches) +{ + if (!preg_match('=^https?://=',$matches[2])) + { + $matches[2] = 'help.php?page='.$matches[2]; + } + return $matches[1].$matches[2].$matches[3]; +} + +/** + * input + */ +$page = req_string('page'); + +if (empty($page) || !preg_match('#^([a-z]{3,20})\.html$#', $page, $match)) $page = 'index.html'; +$page = 'doc/manual/' . $page; +if (!file_exists($page)) $page = 'doc/manual/index.html'; + +$html = file_get_contents($page); +$html = preg_replace_callback("/(<a\s+.*?href\s*=\s*\")(.*?)(\".*?>)/is", '_replace_anchors_callback', $html); +preg_match('=<body.*?>(.*)</body>=is',$html,$matches); +$html = $matches[1]; + +// prepare templates +tpl_page(); + +$smarty->assign('helptext', $html); + +// display templates +tpl_display('help.tpl'); + diff --git a/videodb/images/.htaccess b/videodb/images/.htaccess new file mode 100644 index 0000000..483c9ae --- /dev/null +++ b/videodb/images/.htaccess @@ -0,0 +1,12 @@ +# +# Apache access control +# +# @author Andreas Goetz <cpuidle@gmx.de> +# $Id: .htaccess,v 1.2 2008/03/01 10:19:07 andig2 Exp $ +# + +# avoid image expiry +<IfModule mod_expires.c> + ExpiresActive On + ExpiresDefault "access plus 1 month" +</IfModule> diff --git a/videodb/images/add.gif b/videodb/images/add.gif new file mode 100644 index 0000000..55ab8d5 Binary files /dev/null and b/videodb/images/add.gif differ diff --git a/videodb/images/bar.gif b/videodb/images/bar.gif new file mode 100644 index 0000000..d141780 Binary files /dev/null and b/videodb/images/bar.gif differ diff --git a/videodb/images/bbfc-12.gif b/videodb/images/bbfc-12.gif new file mode 100644 index 0000000..721cfdb Binary files /dev/null and b/videodb/images/bbfc-12.gif differ diff --git a/videodb/images/bbfc-12A.gif b/videodb/images/bbfc-12A.gif new file mode 100644 index 0000000..a457dff Binary files /dev/null and b/videodb/images/bbfc-12A.gif differ diff --git a/videodb/images/bbfc-15.gif b/videodb/images/bbfc-15.gif new file mode 100644 index 0000000..656e626 Binary files /dev/null and b/videodb/images/bbfc-15.gif differ diff --git a/videodb/images/bbfc-18.gif b/videodb/images/bbfc-18.gif new file mode 100644 index 0000000..35ddba3 Binary files /dev/null and b/videodb/images/bbfc-18.gif differ diff --git a/videodb/images/bbfc-PG.gif b/videodb/images/bbfc-PG.gif new file mode 100644 index 0000000..c7bd2ec Binary files /dev/null and b/videodb/images/bbfc-PG.gif differ diff --git a/videodb/images/bbfc-U.gif b/videodb/images/bbfc-U.gif new file mode 100644 index 0000000..eb87bfe Binary files /dev/null and b/videodb/images/bbfc-U.gif differ diff --git a/videodb/images/close.gif b/videodb/images/close.gif new file mode 100644 index 0000000..0919465 Binary files /dev/null and b/videodb/images/close.gif differ diff --git a/videodb/images/flags/README b/videodb/images/flags/README new file mode 100644 index 0000000..db9ba39 --- /dev/null +++ b/videodb/images/flags/README @@ -0,0 +1,14 @@ +If you want to add a new language just put a flag image in this directory. + +I got all my flags from www.blueflash.de - Unfortunately the images are no +longer available there but you can still get them from the Webarchive at: +http://web.archive.org/web/20020205113643/www.blueflash.de/flaggen/landlist.html + +Please email videodb-devel@lists.sf.net if you want your flag to be included in +the standard distribution. + +Note: + +To limit the number of flags displayed in the options dialog, a number of flags +has been moved into the additional folder. To use these flags, they must be +copied to the images/flags folder. diff --git a/videodb/images/flags/additional/cantonese.gif b/videodb/images/flags/additional/cantonese.gif new file mode 100644 index 0000000..dcd24db Binary files /dev/null and b/videodb/images/flags/additional/cantonese.gif differ diff --git a/videodb/images/flags/additional/catalan.gif b/videodb/images/flags/additional/catalan.gif new file mode 100644 index 0000000..8a3636a Binary files /dev/null and b/videodb/images/flags/additional/catalan.gif differ diff --git a/videodb/images/flags/additional/cypriot.gif b/videodb/images/flags/additional/cypriot.gif new file mode 100644 index 0000000..3b0cb59 Binary files /dev/null and b/videodb/images/flags/additional/cypriot.gif differ diff --git a/videodb/images/flags/additional/esperanto.gif b/videodb/images/flags/additional/esperanto.gif new file mode 100644 index 0000000..f0db9a2 Binary files /dev/null and b/videodb/images/flags/additional/esperanto.gif differ diff --git a/videodb/images/flags/additional/gaelic.gif b/videodb/images/flags/additional/gaelic.gif new file mode 100644 index 0000000..03f5de8 Binary files /dev/null and b/videodb/images/flags/additional/gaelic.gif differ diff --git a/videodb/images/flags/additional/hebrew.gif b/videodb/images/flags/additional/hebrew.gif new file mode 100644 index 0000000..31314f3 Binary files /dev/null and b/videodb/images/flags/additional/hebrew.gif differ diff --git a/videodb/images/flags/additional/hindi.gif b/videodb/images/flags/additional/hindi.gif new file mode 100644 index 0000000..4e56b61 Binary files /dev/null and b/videodb/images/flags/additional/hindi.gif differ diff --git a/videodb/images/flags/additional/jewish.gif b/videodb/images/flags/additional/jewish.gif new file mode 100644 index 0000000..31314f3 Binary files /dev/null and b/videodb/images/flags/additional/jewish.gif differ diff --git a/videodb/images/flags/additional/latin.gif b/videodb/images/flags/additional/latin.gif new file mode 100644 index 0000000..fa96211 Binary files /dev/null and b/videodb/images/flags/additional/latin.gif differ diff --git a/videodb/images/flags/additional/mandarin.gif b/videodb/images/flags/additional/mandarin.gif new file mode 100644 index 0000000..dcd24db Binary files /dev/null and b/videodb/images/flags/additional/mandarin.gif differ diff --git a/videodb/images/flags/additional/serbo-croatian.gif b/videodb/images/flags/additional/serbo-croatian.gif new file mode 100644 index 0000000..9317507 Binary files /dev/null and b/videodb/images/flags/additional/serbo-croatian.gif differ diff --git a/videodb/images/flags/additional/sign language.gif b/videodb/images/flags/additional/sign language.gif new file mode 100644 index 0000000..cbf4600 Binary files /dev/null and b/videodb/images/flags/additional/sign language.gif differ diff --git a/videodb/images/flags/additional/somali.gif b/videodb/images/flags/additional/somali.gif new file mode 100644 index 0000000..d60f352 Binary files /dev/null and b/videodb/images/flags/additional/somali.gif differ diff --git a/videodb/images/flags/arabic.gif b/videodb/images/flags/arabic.gif new file mode 100644 index 0000000..13a0378 Binary files /dev/null and b/videodb/images/flags/arabic.gif differ diff --git a/videodb/images/flags/bulgarian.gif b/videodb/images/flags/bulgarian.gif new file mode 100644 index 0000000..8a0e69d Binary files /dev/null and b/videodb/images/flags/bulgarian.gif differ diff --git a/videodb/images/flags/cantonese.gif b/videodb/images/flags/cantonese.gif new file mode 100644 index 0000000..346a903 Binary files /dev/null and b/videodb/images/flags/cantonese.gif differ diff --git a/videodb/images/flags/chinese.gif b/videodb/images/flags/chinese.gif new file mode 100644 index 0000000..dcd24db Binary files /dev/null and b/videodb/images/flags/chinese.gif differ diff --git a/videodb/images/flags/czech.gif b/videodb/images/flags/czech.gif new file mode 100644 index 0000000..4216513 Binary files /dev/null and b/videodb/images/flags/czech.gif differ diff --git a/videodb/images/flags/danish.gif b/videodb/images/flags/danish.gif new file mode 100644 index 0000000..2c785f5 Binary files /dev/null and b/videodb/images/flags/danish.gif differ diff --git a/videodb/images/flags/dutch.gif b/videodb/images/flags/dutch.gif new file mode 100644 index 0000000..21d328e Binary files /dev/null and b/videodb/images/flags/dutch.gif differ diff --git a/videodb/images/flags/english.gif b/videodb/images/flags/english.gif new file mode 100644 index 0000000..c2eedfc Binary files /dev/null and b/videodb/images/flags/english.gif differ diff --git a/videodb/images/flags/french.gif b/videodb/images/flags/french.gif new file mode 100644 index 0000000..3886d93 Binary files /dev/null and b/videodb/images/flags/french.gif differ diff --git a/videodb/images/flags/german.gif b/videodb/images/flags/german.gif new file mode 100644 index 0000000..dbd8ea7 Binary files /dev/null and b/videodb/images/flags/german.gif differ diff --git a/videodb/images/flags/greek.gif b/videodb/images/flags/greek.gif new file mode 100644 index 0000000..98fdd08 Binary files /dev/null and b/videodb/images/flags/greek.gif differ diff --git a/videodb/images/flags/hungarian.gif b/videodb/images/flags/hungarian.gif new file mode 100644 index 0000000..be79fc2 Binary files /dev/null and b/videodb/images/flags/hungarian.gif differ diff --git a/videodb/images/flags/icelandic.gif b/videodb/images/flags/icelandic.gif new file mode 100644 index 0000000..42ec289 Binary files /dev/null and b/videodb/images/flags/icelandic.gif differ diff --git a/videodb/images/flags/indian.gif b/videodb/images/flags/indian.gif new file mode 100644 index 0000000..4e56b61 Binary files /dev/null and b/videodb/images/flags/indian.gif differ diff --git a/videodb/images/flags/israeli.gif b/videodb/images/flags/israeli.gif new file mode 100644 index 0000000..31314f3 Binary files /dev/null and b/videodb/images/flags/israeli.gif differ diff --git a/videodb/images/flags/italian.gif b/videodb/images/flags/italian.gif new file mode 100644 index 0000000..834d657 Binary files /dev/null and b/videodb/images/flags/italian.gif differ diff --git a/videodb/images/flags/japanese.gif b/videodb/images/flags/japanese.gif new file mode 100644 index 0000000..03cf54c Binary files /dev/null and b/videodb/images/flags/japanese.gif differ diff --git a/videodb/images/flags/klingon.gif b/videodb/images/flags/klingon.gif new file mode 100644 index 0000000..cb0b0ae Binary files /dev/null and b/videodb/images/flags/klingon.gif differ diff --git a/videodb/images/flags/korean.gif b/videodb/images/flags/korean.gif new file mode 100644 index 0000000..456b97e Binary files /dev/null and b/videodb/images/flags/korean.gif differ diff --git a/videodb/images/flags/latin.gif b/videodb/images/flags/latin.gif new file mode 100644 index 0000000..fa96211 Binary files /dev/null and b/videodb/images/flags/latin.gif differ diff --git a/videodb/images/flags/norwegian.gif b/videodb/images/flags/norwegian.gif new file mode 100644 index 0000000..b9171a8 Binary files /dev/null and b/videodb/images/flags/norwegian.gif differ diff --git a/videodb/images/flags/polish.gif b/videodb/images/flags/polish.gif new file mode 100644 index 0000000..007bb38 Binary files /dev/null and b/videodb/images/flags/polish.gif differ diff --git a/videodb/images/flags/portuguese.gif b/videodb/images/flags/portuguese.gif new file mode 100644 index 0000000..99e4b00 Binary files /dev/null and b/videodb/images/flags/portuguese.gif differ diff --git a/videodb/images/flags/romanian.gif b/videodb/images/flags/romanian.gif new file mode 100644 index 0000000..4767770 Binary files /dev/null and b/videodb/images/flags/romanian.gif differ diff --git a/videodb/images/flags/russian.gif b/videodb/images/flags/russian.gif new file mode 100644 index 0000000..f045dd9 Binary files /dev/null and b/videodb/images/flags/russian.gif differ diff --git a/videodb/images/flags/serbian.gif b/videodb/images/flags/serbian.gif new file mode 100644 index 0000000..9317507 Binary files /dev/null and b/videodb/images/flags/serbian.gif differ diff --git a/videodb/images/flags/spanish.gif b/videodb/images/flags/spanish.gif new file mode 100644 index 0000000..6fdede8 Binary files /dev/null and b/videodb/images/flags/spanish.gif differ diff --git a/videodb/images/flags/swedish.gif b/videodb/images/flags/swedish.gif new file mode 100644 index 0000000..130e7d9 Binary files /dev/null and b/videodb/images/flags/swedish.gif differ diff --git a/videodb/images/flags/thai.gif b/videodb/images/flags/thai.gif new file mode 100644 index 0000000..8bb3f15 Binary files /dev/null and b/videodb/images/flags/thai.gif differ diff --git a/videodb/images/flags/turkish.gif b/videodb/images/flags/turkish.gif new file mode 100644 index 0000000..f43ad73 Binary files /dev/null and b/videodb/images/flags/turkish.gif differ diff --git a/videodb/images/flags/vietnamese.gif b/videodb/images/flags/vietnamese.gif new file mode 100644 index 0000000..9c2b652 Binary files /dev/null and b/videodb/images/flags/vietnamese.gif differ diff --git a/videodb/images/fsk0.gif b/videodb/images/fsk0.gif new file mode 100644 index 0000000..a55a178 Binary files /dev/null and b/videodb/images/fsk0.gif differ diff --git a/videodb/images/fsk12.gif b/videodb/images/fsk12.gif new file mode 100644 index 0000000..fb79471 Binary files /dev/null and b/videodb/images/fsk12.gif differ diff --git a/videodb/images/fsk16.gif b/videodb/images/fsk16.gif new file mode 100644 index 0000000..b66faf8 Binary files /dev/null and b/videodb/images/fsk16.gif differ diff --git a/videodb/images/fsk18.gif b/videodb/images/fsk18.gif new file mode 100644 index 0000000..59a2f48 Binary files /dev/null and b/videodb/images/fsk18.gif differ diff --git a/videodb/images/fsk6.gif b/videodb/images/fsk6.gif new file mode 100644 index 0000000..9c9c524 Binary files /dev/null and b/videodb/images/fsk6.gif differ diff --git a/videodb/images/goldstar.gif b/videodb/images/goldstar.gif new file mode 100644 index 0000000..b8f5359 Binary files /dev/null and b/videodb/images/goldstar.gif differ diff --git a/videodb/images/greystar.gif b/videodb/images/greystar.gif new file mode 100644 index 0000000..8f9f85f Binary files /dev/null and b/videodb/images/greystar.gif differ diff --git a/videodb/images/icons/1-favicon.ico b/videodb/images/icons/1-favicon.ico new file mode 100644 index 0000000..1037610 Binary files /dev/null and b/videodb/images/icons/1-favicon.ico differ diff --git a/videodb/images/icons/2-favicon.ico b/videodb/images/icons/2-favicon.ico new file mode 100644 index 0000000..848e7d1 Binary files /dev/null and b/videodb/images/icons/2-favicon.ico differ diff --git a/videodb/images/icons/3-favicon.ico b/videodb/images/icons/3-favicon.ico new file mode 100644 index 0000000..64bc369 Binary files /dev/null and b/videodb/images/icons/3-favicon.ico differ diff --git a/videodb/images/kijkwijzer/12.png b/videodb/images/kijkwijzer/12.png new file mode 100644 index 0000000..047e11c Binary files /dev/null and b/videodb/images/kijkwijzer/12.png differ diff --git a/videodb/images/kijkwijzer/14.png b/videodb/images/kijkwijzer/14.png new file mode 100644 index 0000000..ec4b9bf Binary files /dev/null and b/videodb/images/kijkwijzer/14.png differ diff --git a/videodb/images/kijkwijzer/16.png b/videodb/images/kijkwijzer/16.png new file mode 100644 index 0000000..b02828d Binary files /dev/null and b/videodb/images/kijkwijzer/16.png differ diff --git a/videodb/images/kijkwijzer/18.png b/videodb/images/kijkwijzer/18.png new file mode 100644 index 0000000..5aaf6d5 Binary files /dev/null and b/videodb/images/kijkwijzer/18.png differ diff --git a/videodb/images/kijkwijzer/6.png b/videodb/images/kijkwijzer/6.png new file mode 100644 index 0000000..c139f16 Binary files /dev/null and b/videodb/images/kijkwijzer/6.png differ diff --git a/videodb/images/kijkwijzer/9.png b/videodb/images/kijkwijzer/9.png new file mode 100644 index 0000000..accd07c Binary files /dev/null and b/videodb/images/kijkwijzer/9.png differ diff --git a/videodb/images/kijkwijzer/al.png b/videodb/images/kijkwijzer/al.png new file mode 100644 index 0000000..9127eb6 Binary files /dev/null and b/videodb/images/kijkwijzer/al.png differ diff --git a/videodb/images/kijkwijzer/angst.png b/videodb/images/kijkwijzer/angst.png new file mode 100644 index 0000000..eedbe5a Binary files /dev/null and b/videodb/images/kijkwijzer/angst.png differ diff --git a/videodb/images/kijkwijzer/discriminatie.png b/videodb/images/kijkwijzer/discriminatie.png new file mode 100644 index 0000000..0a6c3af Binary files /dev/null and b/videodb/images/kijkwijzer/discriminatie.png differ diff --git a/videodb/images/kijkwijzer/drugs-en-alcohol.png b/videodb/images/kijkwijzer/drugs-en-alcohol.png new file mode 100644 index 0000000..4f07b68 Binary files /dev/null and b/videodb/images/kijkwijzer/drugs-en-alcohol.png differ diff --git a/videodb/images/kijkwijzer/geweld.png b/videodb/images/kijkwijzer/geweld.png new file mode 100644 index 0000000..78ae376 Binary files /dev/null and b/videodb/images/kijkwijzer/geweld.png differ diff --git a/videodb/images/kijkwijzer/seks.png b/videodb/images/kijkwijzer/seks.png new file mode 100644 index 0000000..0d99575 Binary files /dev/null and b/videodb/images/kijkwijzer/seks.png differ diff --git a/videodb/images/kijkwijzer/taal.png b/videodb/images/kijkwijzer/taal.png new file mode 100644 index 0000000..6990c82 Binary files /dev/null and b/videodb/images/kijkwijzer/taal.png differ diff --git a/videodb/images/media/avchd.png b/videodb/images/media/avchd.png new file mode 100644 index 0000000..51fee62 Binary files /dev/null and b/videodb/images/media/avchd.png differ diff --git a/videodb/images/media/blu-ray.png b/videodb/images/media/blu-ray.png new file mode 100644 index 0000000..4c43587 Binary files /dev/null and b/videodb/images/media/blu-ray.png differ diff --git a/videodb/images/media/cd.png b/videodb/images/media/cd.png new file mode 100644 index 0000000..9475fcc Binary files /dev/null and b/videodb/images/media/cd.png differ diff --git a/videodb/images/media/divx.png b/videodb/images/media/divx.png new file mode 100644 index 0000000..f4e28ad Binary files /dev/null and b/videodb/images/media/divx.png differ diff --git a/videodb/images/media/dvd+r.png b/videodb/images/media/dvd+r.png new file mode 100644 index 0000000..747fb69 Binary files /dev/null and b/videodb/images/media/dvd+r.png differ diff --git a/videodb/images/media/dvd-r.png b/videodb/images/media/dvd-r.png new file mode 100644 index 0000000..e003213 Binary files /dev/null and b/videodb/images/media/dvd-r.png differ diff --git a/videodb/images/media/dvd.png b/videodb/images/media/dvd.png new file mode 100644 index 0000000..86308da Binary files /dev/null and b/videodb/images/media/dvd.png differ diff --git a/videodb/images/media/hd-dvd.png b/videodb/images/media/hd-dvd.png new file mode 100644 index 0000000..7645a70 Binary files /dev/null and b/videodb/images/media/hd-dvd.png differ diff --git a/videodb/images/media/hdd.gif b/videodb/images/media/hdd.gif new file mode 100644 index 0000000..38e2879 Binary files /dev/null and b/videodb/images/media/hdd.gif differ diff --git a/videodb/images/media/svcd.png b/videodb/images/media/svcd.png new file mode 100644 index 0000000..ccc7465 Binary files /dev/null and b/videodb/images/media/svcd.png differ diff --git a/videodb/images/media/vcd.png b/videodb/images/media/vcd.png new file mode 100644 index 0000000..0ccc684 Binary files /dev/null and b/videodb/images/media/vcd.png differ diff --git a/videodb/images/media/vhs.png b/videodb/images/media/vhs.png new file mode 100644 index 0000000..354b668 Binary files /dev/null and b/videodb/images/media/vhs.png differ diff --git a/videodb/images/mpaa-G.gif b/videodb/images/mpaa-G.gif new file mode 100644 index 0000000..fa138ed Binary files /dev/null and b/videodb/images/mpaa-G.gif differ diff --git a/videodb/images/mpaa-NC-17.gif b/videodb/images/mpaa-NC-17.gif new file mode 100644 index 0000000..7002062 Binary files /dev/null and b/videodb/images/mpaa-NC-17.gif differ diff --git a/videodb/images/mpaa-PG-13.gif b/videodb/images/mpaa-PG-13.gif new file mode 100644 index 0000000..32febc8 Binary files /dev/null and b/videodb/images/mpaa-PG-13.gif differ diff --git a/videodb/images/mpaa-PG.gif b/videodb/images/mpaa-PG.gif new file mode 100644 index 0000000..1adc191 Binary files /dev/null and b/videodb/images/mpaa-PG.gif differ diff --git a/videodb/images/mpaa-R.gif b/videodb/images/mpaa-R.gif new file mode 100644 index 0000000..5d00804 Binary files /dev/null and b/videodb/images/mpaa-R.gif differ diff --git a/videodb/images/nocover.gif b/videodb/images/nocover.gif new file mode 100644 index 0000000..81cd60e Binary files /dev/null and b/videodb/images/nocover.gif differ diff --git a/videodb/images/pdfexport.png b/videodb/images/pdfexport.png new file mode 100644 index 0000000..06584c4 Binary files /dev/null and b/videodb/images/pdfexport.png differ diff --git a/videodb/images/r_0.gif b/videodb/images/r_0.gif new file mode 100644 index 0000000..a55a178 Binary files /dev/null and b/videodb/images/r_0.gif differ diff --git a/videodb/images/r_12.gif b/videodb/images/r_12.gif new file mode 100644 index 0000000..fb79471 Binary files /dev/null and b/videodb/images/r_12.gif differ diff --git a/videodb/images/r_16.gif b/videodb/images/r_16.gif new file mode 100644 index 0000000..b66faf8 Binary files /dev/null and b/videodb/images/r_16.gif differ diff --git a/videodb/images/r_18.gif b/videodb/images/r_18.gif new file mode 100644 index 0000000..59a2f48 Binary files /dev/null and b/videodb/images/r_18.gif differ diff --git a/videodb/images/r_6.gif b/videodb/images/r_6.gif new file mode 100644 index 0000000..9c9c524 Binary files /dev/null and b/videodb/images/r_6.gif differ diff --git a/videodb/images/rss.gif b/videodb/images/rss.gif new file mode 100644 index 0000000..c81ac37 Binary files /dev/null and b/videodb/images/rss.gif differ diff --git a/videodb/images/rssexport.png b/videodb/images/rssexport.png new file mode 100644 index 0000000..6fed979 Binary files /dev/null and b/videodb/images/rssexport.png differ diff --git a/videodb/images/search.gif b/videodb/images/search.gif new file mode 100644 index 0000000..acf3e86 Binary files /dev/null and b/videodb/images/search.gif differ diff --git a/videodb/images/top.gif b/videodb/images/top.gif new file mode 100644 index 0000000..10c46d0 Binary files /dev/null and b/videodb/images/top.gif differ diff --git a/videodb/images/xlsexport.png b/videodb/images/xlsexport.png new file mode 100644 index 0000000..1a0c1b2 Binary files /dev/null and b/videodb/images/xlsexport.png differ diff --git a/videodb/images/xml.gif b/videodb/images/xml.gif new file mode 100644 index 0000000..5ae365a Binary files /dev/null and b/videodb/images/xml.gif differ diff --git a/videodb/images/xmlexport.png b/videodb/images/xmlexport.png new file mode 100644 index 0000000..a85e527 Binary files /dev/null and b/videodb/images/xmlexport.png differ diff --git a/videodb/img.php b/videodb/img.php new file mode 100644 index 0000000..0f77f99 --- /dev/null +++ b/videodb/img.php @@ -0,0 +1,130 @@ +<?php +/** + * Image loader + * + * Loads an image from the net and creates a chachefile for it. + * + * @package videoDB + * @author Andreas Gohr <a.gohr@web.de> + * @version $Id: img.php,v 2.29 2013/03/10 16:24:32 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/httpclient.php'; + +/** + * input + */ +$name = req_string('name'); +$actorid = req_string('actorid'); +$url = req_url('url'); + +/* + * Note: + * + * We don't clear overage thumbnails. Instead, + * the table entries will be replaced when an image is finally available + */ + +// since we don't need session functionality, use this as workaround +// for php bug #22526 session_start/popen hang +session_write_close(); + +/** + * amazon workaround for 1 pixel transparent images + */ +function checkAmazonSmallImage($url, $ext, $file) +{ + global $config; + + if (preg_match('/^(.+)L(Z{7,}.+)$/', $url, $m)) + { + if (list($width, $height, $type, $attr) = getimagesize($file)) { + if ($width <= 1) { + $smallurl = $m[1].'M'.$m[2]; + if (cache_file_exists($smallurl, $cache_file, CACHE_IMG, $ext) || + download($smallurl, $cache_file)) { + copy($cache_file, $file); + } + } + } + } +} + + +// default - no url given or no image +$file = img(); + +// Get imgurl for the actor +if ($name) +{ + require_once './engines/engines.php'; + + // name given + $name = html_entity_decode($name); + + if ( $config['debug'] ) + { + // save data to pass to functions.php - erropage + // if engineActor fails in httpclient it goes directly to errorpage which loses + // message set in httpCLient + // this is a cause of broken actor images appearing + $savedata_for_errorpage = 'Module->img.php, Name->'.$name.', Actorid->'.$actorid; + } + + $result = engineActor($name, $actorid, engineGetActorEngine($actorid)); + + if ( $config['debug'] ) + { + unset($savedata_for_errorpage); + } + + if (!empty($result)) + { + $url = $result[0][1]; + if (preg_match('/nohs(-[f|m])?.gif$/', $url)) { + // imdb no-image picture + $url = ''; + } + } + + // write actor last checked record + // NOTE: this is only called if the template preparation has determined the actor record needs checking + { + // write only if HTTP lookup physically successful + $SQL = 'REPLACE '.TBL_ACTORS." (name, imgurl, actorid, checked) + VALUES ('".escapeSQL($name)."', '".escapeSQL($url)."', '".escapeSQL($actorid)."', NOW())"; + runSQL($SQL); + } +} + +// Get cached image for the given url +if (!is_null($url) && preg_match('/\.(jpe?g|gif|png)$/i', $url, $matches)) +{ + // calculate cache filename if we're not looking into the cache again- otherwise this is done by cache_file_exists + // $file is further needed for downloading the file + // This is only effective if function is enabled in getThumbnail function + # if ($cache_ignore) $file = cache_get_filename($url, CACHE_IMG, $matches[1])); + + // does the cache file exist? + if (cache_file_exists($url, $targetfile, CACHE_IMG, $matches[1])) { + // amazon workaround for 1 pixel transparent images + checkAmazonSmallImage($url, $matches[1], $targetfile); + } + // try to download and make sure it's really an image + else { + download($url, $targetfile); + } + + // double-check this is really an image + if (@exif_imagetype($targetfile)) { + // success- the result is an actual image + $file = $targetfile; + } +} + +// fix url for redirect +$file = preg_replace('/img\.php$/', $file, $_SERVER['PHP_SELF']); + +header('Location: '.$file); + diff --git a/videodb/index.php b/videodb/index.php new file mode 100644 index 0000000..0b05b6a --- /dev/null +++ b/videodb/index.php @@ -0,0 +1,345 @@ +<?php +/** + * Browse View + * + * Lets you browse through your movie collection + * + * @package videoDB + * @author Andreas Gohr <a.gohr@web.de> + * @author Andreas Götz <cpuidle@gmx.de> + * @author Chinamann <chinamann@users.sourceforge.net> + * @link http://videodb.sf.net + * @version $Id: index.php,v 2.102 2013/03/21 16:27:57 andig2 Exp $ + */ + +require_once './core/functions.php'; +require_once './core/output.php'; + +/** + * input + */ +$id = req_int('id'); +$diskid = req_string('diskid'); +// start session'd settings +$filter = req_string('filter'); +$showtv = req_int('showtv'); +$listcolumns = req_int('listcolumns'); +$mediafilter = req_int('mediafilter'); +$order = req_int('order'); +// end session'd settings +$owner = req_string('owner'); +$ajax_quicksearch = req_string('ajax_quicksearch'); // elegant template only +$quicksearch = req_string('quicksearch'); // elegant template only +$export = req_string('export'); +$pageno = (req_string('pageno') == 'all' ? 'all' : req_int('pageno')); +$ajax_render = req_int('ajax_render'); // elegant template only +$deleteid = req_int('deleteid'); + +/** + * Update item list asynchronously + * + * @author Andreas Goetz <cpuidle@gmx.de> + */ +function ajax_render() +{ + global $smarty, $result, $filter, $config; + global $pageno, $totalpages, $totalresults; + + // TODO Smarty caching would require further efforts $smarty->caching = 1; +# if (!$smarty->is_cached('list.tpl', get_current_user_id().'|'.$filter.$pageno)) { + // add some delay for debugging + if ($config['debug'] && $_SERVER['SERVER_ADDR'] == '127.0.0.1') usleep(rand(200,1000)*1000); + + // load languages and config into Smarty + tpl_language(); + tpl_list($result); + + // TODO consider pagination + $content = $smarty->fetch('list.tpl', get_current_user_id().'|'.$filter.$pageno); + + header('X-JSON: '.json_encode(array('totalresults' => $totalresults ? $totalresults : count($result), + 'maxpageno' => $totalpages))); + exit($content); +} + +function prepareOrder($m) { + switch($m) { + case 1: + $ORDER = "rating desc"; + break; + case 2: + $ORDER = "created DESC, lastupdate DESC"; + break; + default: + $ORDER = "title, subtitle asc"; + break; + } + return $ORDER; +} + +function get_mediatype_sql($m) +{ + switch ($m) { + case -2: + $mediatype = '1=1'; + break; + case -1: + $mediatype = 'mediatype != '.MEDIA_WISHLIST; + break; + default: + $mediatype = 'mediatype = '.$m; + } + + return $mediatype; +} + +// set defaults and update session +session_default('filter', $config['filterdefault']); +session_default('showtv', $config['showtv']); +session_default('listcolumns', $config['listcolumns']); +session_default('mediafilter', -1); +session_default('order', -1); + +// enable redirects to last list view for delete.php +session_set('listview', 'index.php'); + +// standard filters +$filter_expr = array( + 'NUM' => '^["\\\' ]*[^A-Za-zÄäÖöÜüß]', + 'ABC' => '^["\\\' ]*[ABCabcÄä]', + 'DEF' => '^["\\\' ]*[DEFdef]', + 'GHI' => '^["\\\' ]*[GHIghi]', + 'JKL' => '^["\\\' ]*[JKLjkl]', + 'MNO' => '^["\\\' ]*[MNOmnoÖö]', + 'PQRS' => '^["\\\' ]*[PQRSpqrsß]', + 'TUV' => '^["\\\' ]*[TUVtuvÜü]', + 'WXYZ' => '^["\\\' ]*[WXZwxy]' +); + +if ($filter == 'wanted') $mediafilter = MEDIA_WISHLIST; +$WHERES = get_mediatype_sql(($mediafilter) ? $mediafilter : -1); + +// create SQL according to selected filter +$JOINS = ''; +$LIMIT = ''; + +// create SQL according to selected filter +switch ($filter) +{ + case 'all': +# $WHERES = 'mediatype != '.MEDIA_WISHLIST; + if($config['orderallbydisk']) + { + $ORDER = 'diskid asc, title, subtitle'; + } + break; + case 'seen': + $WHERES .= ' AND !ISNULL('.TBL_USERSEEN.'.video_id)';# AND mediatype != '.MEDIA_WISHLIST; + break; + case 'unseen': + $WHERES .= ' AND ISNULL('.TBL_USERSEEN.'.video_id)';# AND mediatype != '.MEDIA_WISHLIST; + break; + case 'new': +# $WHERES = 'mediatype != '.MEDIA_WISHLIST; + $ORDER = 'created DESC, lastupdate DESC '; + $LIMIT = ' LIMIT '.$config['shownew']; + break; + case 'wanted': +# $WHERES = 'mediatype = '.MEDIA_WISHLIST; + break; + case 'full': // secret filter for exposing all data +# $WHERES = '1=1'; + break; + default: + // make sure filter is valid + if (!array_key_exists($filter, $filter_expr)) $filter = 'ABC'; + // apply filter + $WHERES .= ' AND title RLIKE \''.mb_convert_encoding($filter_expr[$filter], 'UTF-8', 'ISO-8859-1').'\'';# AND mediatype != '.MEDIA_WISHLIST; +} + +if(!isset($ORDER)) +{ + if(isset($order)) + { + $ORDER = prepareOrder($order); + session_set('order', $order); + } else { + $ORDER = prepareOrder(-1); + session_set('order', -1); + } +} + +if (!$showtv) $WHERES .= ' AND istv = 0'; + + +// owner selection for multiuser mode- by default this is the logged in user +// any user has automatically read permissions for his personal data +if ($config['multiuser']) +{ + // get owner from session- or use current user + session_default_owner(); + // if we don't have read all permissions, limit visibility using cross-user permissions + if (!check_permission(PERM_READ)) + { + $JOINS = ' LEFT JOIN '.TBL_PERMISSIONS.' ON '.TBL_DATA.'.owner_id = '.TBL_PERMISSIONS.'.to_uid'; + $WHERES .= ' AND '.TBL_PERMISSIONS.'.from_uid = '.get_current_user_id().' AND '.TBL_PERMISSIONS.'.permissions & '.PERM_READ.' != 0'; + } + // further limit to single owner + if (html_entity_decode($owner) != $lang['filter_any']) { + $WHERES .= " AND ".TBL_USERS.".name = '".escapeSQL($owner)."'"; + } +} + +// searching? +if ($ajax_quicksearch || $quicksearch) +{ + $qs = escapeSQL($ajax_quicksearch ? $ajax_quicksearch : $quicksearch); + $WHERES .= ' AND (title LIKE "%'.$qs.'%" OR subtitle LIKE "%'.$qs.'%")'; +} + +// async request for quick-searching within current spec +if ($ajax_quicksearch) +{ + // do hard work + $SQL = 'SELECT '.TBL_DATA.'.id, title, subtitle + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id()." + $JOINS + WHERE $WHERES + ORDER BY $ORDER + LIMIT 20"; + $result = runSQL($SQL); + + $ret = ''; + foreach ($result as $item) + { + $title = preg_replace('/('.$ajax_quicksearch.')/i', '<em>\1</em>', $item['title']); + if ($item['subtitle']) $title .= ' - '.$item['subtitle']; + $ret .= "<li id='".$item['id']."'>".$title."</li>"; + } + $ret = "<ul>$ret</ul>"; + + exit($ret); +} + +// XML / RSS / PDF export +if ($export && array_key_exists($export, $config) && $config[$export]) +{ + // either (xml|rss|pdf|xls)export + $func = $export.'export'; + if ($export == 'rss') $export = 'xml'; + require_once './core/'.$export.'.php'; + + if (function_exists($func)) $func("$JOINS WHERE $WHERES ORDER BY $ORDER $LIMIT"); + exit; +} + +/* + Calculate pagination + + Check to see if user has selected the New items tab. + This is seperately assigned as a LIMIT so, if this exists, + lets just skip page numbers and carry on +*/ +if ($LIMIT == '' && ($config['pageno'] > 0) &! ($pageno == 'all')) +{ + // start at first page + if (!$pageno) $pageno = ($deleteid) ? session_get('lastpageno', 1) : 1; + session_set('lastpageno', $pageno); + + // define Max Results Per Page + $maxresults = $config['pageno']; + + // define the Start Number + $from = (($pageno * $maxresults) - $maxresults); + + $LIMIT = ' LIMIT '.$from.', '.$maxresults; + + // get total amount of results from DB + $totalresults = runSQL('SELECT count(*) AS num + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id()." + $JOINS WHERE $WHERES"); + $totalresults = (count($totalresults) > 0) ? (int)$totalresults[0]['num'] : 0; + + // calculate total amount of pages + $totalpages = ceil($totalresults / $maxresults); + + $smarty->assign('pageno', $pageno); // assign current Page Number + $smarty->assign('maxpageno', $totalpages); // set Maximum Pages + $smarty->assign('totalresults', $totalresults); // set Total Records Returned +} + + +// do hard work +$SQL = 'SELECT '.TBL_DATA.'.id, '.TBL_DATA.'.diskid, + title, subtitle, language, year, + director, plot, imgurl, + owner_id, '.TBL_USERS.'.name AS owner, '.TBL_LENT.'.who, + md5, comment, disklabel, imdbID, actors, runtime, + country, filename, filesize, filedate, audio_codec, + video_codec, video_width, video_height, istv, + lastupdate, mediatype, rating, + custom1, custom2, custom3, custom4, + created, !ISNULL('.TBL_USERSEEN.'.video_id) AS seen, + '.TBL_MEDIATYPES.'.name AS mediatypename + FROM '.TBL_DATA.' + LEFT JOIN '.TBL_USERS.' ON '.TBL_DATA.'.owner_id = '.TBL_USERS.'.id + LEFT JOIN '.TBL_USERSEEN.' ON '.TBL_DATA.'.id = '.TBL_USERSEEN.'.video_id AND '.TBL_USERSEEN.'.user_id = '.get_current_user_id().' + LEFT JOIN '.TBL_LENT.' ON '.TBL_DATA.'.diskid = '.TBL_LENT.'.diskid + LEFT JOIN '.TBL_MEDIATYPES.' ON '.TBL_DATA.'.mediatype = '.TBL_MEDIATYPES.'.id'." + $JOINS + WHERE $WHERES + ORDER BY $ORDER + $LIMIT"; +$result = runSQL($SQL); + +// store query result in session for prev/next navigation +session_set('query_result', array_column($result, 'id')); + +// process asynchronous refresh +if ($ajax_render) +{ + ajax_render(); +} + +// prepare +tpl_page('browse'); +tpl_list($result); +tpl_filters($filter, $showtv); + +// caching enabled? +if ($config['http_caching']) +{ + require_once('./core/httpcache.php'); + httpCacheCaptureStart(); +} + +$smarty->assign('moreless', true); // show more/less control in list view + +// allow data export +foreach (array('xls','pdf','xml','rss') as $export) +{ + if (array_key_exists($export, $config) && $config[$export]) $smarty->assign($export, 'index.php?'); +} + +// display templates +smarty_display('header.tpl'); +smarty_display('filters.tpl'); +if (!$config['http_caching']) flush(); + +if ($deleteid) $smarty->assign('deleted', true); + +// TODO smarty caching would require further efforts +smarty_display('list.tpl', get_current_user_id().'|'.$WHERES); + +smarty_display('footer.tpl'); + +// caching enabled? +if ($config['http_caching']) +{ + httpCacheOutput('index', httpCacheCaptureEnd()); +} + diff --git a/videodb/init-db.php b/videodb/init-db.php new file mode 100644 index 0000000..fd80d37 --- /dev/null +++ b/videodb/init-db.php @@ -0,0 +1,48 @@ +<?php +/** + * Docker DB initializer — run via PHP CLI during container startup. + * Uses the app's own prefix_query() / runSQL() so table prefix is applied correctly. + */ + +$db_host = getenv('DB_HOST') ?: 'db'; +$db_user = getenv('DB_USER') ?: 'videodb'; +$db_password = getenv('DB_PASSWORD') ?: 'videodb_secret'; +$db_name = getenv('DB_NAME') ?: 'videodb'; +$db_prefix = getenv('DB_PREFIX') ?: 'videodb_'; + +// Load install helpers (prefix_query, runSQL) +require_once '/var/www/html/core/install.core.php'; + +// Connect +$dbh = @mysqli_connect($db_host, $db_user, $db_password, $db_name); +if (!$dbh) { + fwrite(STDERR, "[init-db] ERROR: Cannot connect to MySQL: " . mysqli_connect_error() . "\n"); + exit(1); +} + +// Idempotency check — skip if already initialized +$result = mysqli_query($dbh, "SHOW TABLES LIKE '{$db_prefix}videodata'"); +if ($result && mysqli_num_rows($result) > 0) { + echo "[init-db] Database already initialized — skipping.\n"; + mysqli_close($dbh); + exit(0); +} + +echo "[init-db] Initializing database schema (prefix: {$db_prefix})...\n"; + +$sql = file_get_contents('/var/www/html/install/install.sql'); +if (!$sql) { + fwrite(STDERR, "[init-db] ERROR: Cannot read install/install.sql\n"); + exit(1); +} + +// Strip comment lines (# …) — the SQL parser in runSQL() does not strip them +$sql = preg_replace('/^\s*#[^\n]*\n/m', "\n", $sql); + +if (runSQL($sql, $dbh) === false) { + fwrite(STDERR, "[init-db] ERROR: Schema install failed: " . mysqli_error($dbh) . "\n"); + exit(1); +} + +echo "[init-db] Database initialized successfully.\n"; +mysqli_close($dbh); diff --git a/videodb/install.php b/videodb/install.php new file mode 100644 index 0000000..3985061 --- /dev/null +++ b/videodb/install.php @@ -0,0 +1,547 @@ +<?php +/** + * Installer + * + * Create database, tables and config file + * + * @package Setup + * @author Andreas Goetz <cpuidle@gmx.de> + * @version $Id: install.php,v 1.49 2013/03/21 16:27:57 andig2 Exp $ + */ + +error_reporting(E_ALL ^ E_NOTICE); + +require_once './core/compatibility.php'; +require_once './core/install.core.php'; +require_once './core/constants.php'; + + +// required files +$install_sql = './install/install.sql'; +$upgrade_sql = './install/upgrade.sql'; + +// button definitions +$button_next = '   Next   >>'; +$button_prev = '<<   Previous   '; +$button_upgrade = 'Upgrade'; + + +// extract request parameters and disable warnings +extract($_REQUEST, EXTR_OVERWRITE); + +// create default config file if not existing +if (!file_exists(CONFIG_FILE)) +{ + @copy('./config.sample.php', CONFIG_FILE); +} + +// set default +$upgrading = false; + +// stepping back? +if (isset($formPrevious)) +{ + $step -= 2; +} + +// upgrading? +elseif (isset($action) && stristr($action, 'upgrade')) +{ + $upgrading = true; + + // load configuration + @include_once(CONFIG_FILE); + + // begin of upgrade? + if (empty($step)) + { + // set initial step for upgrading + $step = 2; + + // set default db configuration + if (empty($db_server)) + { + extract($config, EXTR_OVERWRITE, 'db_'); + } + + // remove files from previous version + foreach(array( + 'imdb.php', 'amazon.php', 'engines.php', 'functions.php', + 'setupfunctions.php', 'compatibility.php', 'template.php', + 'queryparser.php', 'genres.php', 'output.php', 'session.php', + './core/setupfunctions.php', './core/installfunctions.php', './core/xml_functions.php', + './doc/createtables.sql', './doc/updatedb.sql') + as $file) + { + if (file_exists($file)) @rename($file, $file.'.old'); + } + + // remove old xajax library + delete_files('lib/xajax/lib'); + + // remove old smarty cache + delete_files('cache/smarty'); + + // recursively delete old smarty folder (now lib/smarty) + delete_files('smarty', true); + + // obsolete templates + delete_files('templates/advanced', true); + delete_files('templates/downlord', true); + delete_files('templates/jeckyll', true); + } +} + +// or first installation step? +elseif (empty($step)) +{ + $step = 1; +} + + + +?> + +<html> +<head> + <title>videoDB - Installation + + + + + + + + + + + + +
      + Installer for +
      + + +
      + + + 0) + { + error("DB has already tables with this prefix!"); + break; + } + + // add root user warning + if ($db_user == 'root') + { + warn("You've used 'root' as database username. Root is often the master administration account. + For security reasons it is recommended that you choose a different username after + the installation."); + } + + // continue with table installation + $step++; + + + case 4: /* + * continue installation by upgrading or installing tables and (re)moving files (upgrade only) + */ + + if ($upgrading) + { + // re-connect if not continued from step 3 + if (!isset($dbh)) + { + $dbh = @mysqli_connect($db_server, $db_user, $db_password); + if (mysqli_connect_error()) trigger_error("Can't connect: ".mysqli_connect_error(), E_USER_ERROR); + mysqli_select_db($dbh, $db_database) or trigger_error("Can't select database: ".mysqli_error($dbh), E_USER_ERROR); + } + + // get version + $sql = "SELECT value FROM {$db_prefix}config WHERE opt = 'dbversion'"; + $rs = mysqli_query($dbh, $sql); + if ($rs) list($version) = mysqli_fetch_row($rs); + + // successfully retrieved installed version? + if (!($rs && $version)) + { + error("Error getting DB version, try full install instead of upgrade: ".mysqli_error($dbh)); + error("

      $sql
      "); + $step--; + break; + } + + // already correct db version? this might happen if just the username/ password were wrong + if ($version >= DB_REQUIRED) + { + info("Database upgrade not required."); + break; + } + + // perform actual upgrade + $upgrades = parse_upgrades($upgrade_sql); + if ($version >= max(array_keys($upgrades))) + { + info("Database upgrade not required."); + $step--; + break; + } + else + { + // upgrade + info("
      Upgrading tables..."); + info("Old database version: $version"); + + $sql_array = array(); + // select the relevant upgrades (> current version) + foreach ($upgrades as $ver => $value) + { + if ($ver > $version) $sql_array["$ver"] = $value; + } + + // upgrades successful? + $version = db_upgrade($sql_array); + if (is_numeric($version)) + { + info("New database version: $version"); + // dev-time sanity check + if ($version > DB_REQUIRED) warn("Upgraded database version $version is higher than required database version ".DB_REQUIRED); + } + else + { + // error + $step--; + } + } + } + else + { + // install + info("
      Installing tables..."); + + // re-connect if not continued from step 3 + if (!$dbh) { + $dbh = @mysqli_connect($db_server, $db_user, $db_password); + if (mysqli_connect_error()) trigger_error("Can't connect: ".mysqli_connect_error(), E_USER_ERROR); + mysqli_select_db($dbh, $db_database) or trigger_error("Can't select database: ".mysqli_error($dbh), E_USER_ERROR); + } + + // open SQL script from doc directory + $sql = file_get_contents($install_sql); + if (!$sql) trigger_error('Couldn\'t open SQL file: '.$install_sql, E_USER_ERROR); + + if (runSQL($sql, $dbh) === false) { + error('Error creating tables: '.mysqli_error($dbh)); + error('

      '.$sql.'
      '); + $step--; + } + else + { + $write_config_file = true; +/* + // create config file + info("
      Writing config file..."); + $config = parse_config(array( + 'db_server' => $db_server, + 'db_user' => $db_user, + 'db_password' => $db_password, + 'db_database' => $db_database, + 'db_prefix' => $db_prefix), true); + + if (!file_put_contents(CONFIG_FILE, $config)) + { + error('
      Could not write config file '.CONFIG_FILE.'! +
      Please make sure your config file contains the following lines:

      '. + highlight_string($config, 1)); + } +*/ + } + } + + break; +} + + +// determine next installation step +switch ($step) +{ + + case 4: // start videoDB + $action_target = 'index.php'; + break; + default: // continue installation + $action_target = 'install.php'; +} + +?> +
      + +"; +} + +switch ($step) +{ + + case 4: // start VideoDB + $installed = ($upgrading) ? 'upgraded' : 'installed'; + +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + Installation successful!

      + VideoDB database and tables have been successfully .
      +
      + Writing config file..."); + $config_file = parse_config(array( + 'db_server' => $db_server, + 'db_user' => $db_user, + 'db_password' => $db_password, + 'db_database' => $db_database, + 'db_prefix' => $db_prefix), true); + + if (!@file_put_contents(CONFIG_FILE, $config_file)) + { + error('
      Could not write config file '.CONFIG_FILE.'! +
      Please make sure your config file contains the following lines:

      '. + highlight_string($config_file, 1), true); + } +/* + warn('Your username/ password chosen for this upgrade do not match your config file. Please make sure + to update the config file.', true); +*/ + } + + warn('For security reasons this file (install.php) should be deleted after the installation. + You can later adjust the database settings by modifying the '.CONFIG_FILE.' file.', true); + ?> + Click Start to begin using videoDB...
      +
      +
      + + + +
      +
      database and tables.

      + +
      +
      +
      database and tables.

      + + You have selected a non-empty database. Installing into a non-empty database might lead to data loss and is only recommended for experienced users. +
      +
      +
      + Choose Upgrade to upgrade the existing installation (recommended). + Before upgrading, please make sure to backup your database!
      +
      +
      + +
      +
      +
      + Click Next to install the tables into the existing non-empty database...
      +
      +
      + + + + + + + + + + +
      + +
      Database Upgrade
      +
      Your database does not match the current version of VideoDB and needs to be upgraded.
      +
      + + Select database and user.
      +
      + +
      Select database and user.
      +
      + Note: unless you're installing into an existing database, the database login must have 'Create Database' rights. +

      + + +
      Server:
      User:
      Password:
      Database:
      Table prefix: (only required for new installations)
      +
      + Click Next to database and tables...
      +
      +
      + + + + +
      +
      This is the installer for videoDB. You will require: +
        +
      1. PHP >= 5.5.0 with GD library and session support configured.


      2. +
      3. A MySQL database, login (username and password) with create/drop table rights.


      4. +
      5. If you want this installer to create the config file for you, it needs permission to write to web server's the videoDB root directory.


      6. +
      + The installer will create a database in your MySQL installation, create and populate the required tables, and generate the videoDB configuration file.

      + + Click Next to setup the database connection...

      + + + + +
      +
      + +
      Step: +
      +
      + + + diff --git a/videodb/install/install.sql b/videodb/install/install.sql new file mode 100644 index 0000000..80065f3 --- /dev/null +++ b/videodb/install/install.sql @@ -0,0 +1,233 @@ +# +# Database installation script +# +# This script should not be run directly, but rather be executed by using install.php +# +# @package Setup +# @author Andreas Goetz +# @version $Id: install.sql,v 1.22 2013/03/13 15:26:50 andig2 Exp $ +# + +# genres +CREATE TABLE genres ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +) CHARACTER SET UTF8; + +# lent +CREATE TABLE lent ( + diskid VARCHAR(15) NOT NULL, + who VARCHAR(255) NOT NULL, + dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (diskid) +) CHARACTER SET UTF8; + +# mediatypes +CREATE TABLE mediatypes ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(15) NULL, + PRIMARY KEY (id) +) CHARACTER SET UTF8; + +# videodata +CREATE TABLE videodata ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + md5 VARCHAR(32) DEFAULT NULL, + title VARCHAR(255) DEFAULT NULL, + subtitle VARCHAR(255) DEFAULT NULL, + language VARCHAR(255) DEFAULT NULL, + diskid VARCHAR(15) DEFAULT NULL, + comment VARCHAR(255) DEFAULT NULL, + disklabel VARCHAR(32) DEFAULT NULL, + imdbID VARCHAR(30) DEFAULT NULL, + year INT(4) UNSIGNED NOT NULL DEFAULT '0', + imgurl VARCHAR(255) DEFAULT NULL, + director VARCHAR(255) DEFAULT NULL, + actors MEDIUMTEXT, + runtime INT(10) UNSIGNED DEFAULT NULL, + country VARCHAR(255) DEFAULT NULL, + plot TEXT, + rating VARCHAR(15) DEFAULT NULL, + filename VARCHAR(255) DEFAULT NULL, + filesize BIGINT UNSIGNED DEFAULT NULL, + filedate DATETIME DEFAULT NULL, + audio_codec VARCHAR(255) DEFAULT NULL, + video_codec VARCHAR(255) DEFAULT NULL, + video_width INT(10) UNSIGNED DEFAULT NULL, + video_height INT(10) UNSIGNED DEFAULT NULL, + istv tinyINT(1) UNSIGNED NOT NULL DEFAULT '0', + lastupdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + mediatype INT(10) UNSIGNED NOT NULL DEFAULT '0', + custom1 VARCHAR(255) DEFAULT NULL, + custom2 VARCHAR(255) DEFAULT NULL, + custom3 VARCHAR(255) DEFAULT NULL, + custom4 VARCHAR(255) DEFAULT NULL, + created DATETIME DEFAULT NULL, + `owner_id` INT(11) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `title_idx` (`title`), + KEY `diskid_idx` (`diskid`), + KEY `mediatype` (`mediatype`,`istv`), + FULLTEXT KEY `plot_idx` (`plot`), + FULLTEXT KEY `actors_idx` (`actors`), + FULLTEXT KEY `comment` (`comment`) +) CHARACTER SET UTF8, ENGINE=MyISAM; + +# videogenre +CREATE TABLE videogenre ( + video_id INT(10) UNSIGNED NOT NULL, + genre_id INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (video_id, genre_id) +) CHARACTER SET UTF8; + +# add genres + +INSERT INTO genres (id, name) VALUES (1,'Action'); +INSERT INTO genres (id, name) VALUES (2,'Adventure'); +INSERT INTO genres (id, name) VALUES (3,'Animation'); +INSERT INTO genres (id, name) VALUES (4,'Comedy'); +INSERT INTO genres (id, name) VALUES (5,'Crime'); +INSERT INTO genres (id, name) VALUES (6,'Documentary'); +INSERT INTO genres (id, name) VALUES (7,'Drama'); +INSERT INTO genres (id, name) VALUES (8,'Family'); +INSERT INTO genres (id, name) VALUES (9,'Fantasy'); +INSERT INTO genres (id, name) VALUES (10,'Film-Noir'); +INSERT INTO genres (id, name) VALUES (11,'Horror'); +INSERT INTO genres (id, name) VALUES (12,'Musical'); +INSERT INTO genres (id, name) VALUES (13,'Mystery'); +INSERT INTO genres (id, name) VALUES (14,'Romance'); +INSERT INTO genres (id, name) VALUES (15,'Sci-Fi'); +INSERT INTO genres (id, name) VALUES (16,'Short'); +INSERT INTO genres (id, name) VALUES (17,'Thriller'); +INSERT INTO genres (id, name) VALUES (18,'War'); +INSERT INTO genres (id, name) VALUES (19,'Western'); +INSERT INTO genres (id, name) VALUES (20,'Adult'); +INSERT INTO genres (id, name) VALUES (21,'Music'); +INSERT INTO genres (id, name) VALUES (22,'Biography'); +INSERT INTO genres (id, name) VALUES (23,'History'); +INSERT INTO genres (id, name) VALUES (24,'Sport'); + +# add mediatypes + +INSERT INTO mediatypes (id, name) VALUES (1,'DVD'); +INSERT INTO mediatypes (id, name) VALUES (2,'SVCD'); +INSERT INTO mediatypes (id, name) VALUES (3,'VCD'); +INSERT INTO mediatypes (id, name) VALUES (4,'CD-R'); +INSERT INTO mediatypes (id, name) VALUES (5,'CD-RW'); +INSERT INTO mediatypes (id, name) VALUES (6,'VHS'); +INSERT INTO mediatypes (id, name) VALUES (7,'DVD-R'); +INSERT INTO mediatypes (id, name) VALUES (8,'DVD-RW'); +INSERT INTO mediatypes (id, name) VALUES (9,'DVD+R'); +INSERT INTO mediatypes (id, name) VALUES (10,'DVD+RW'); +INSERT INTO mediatypes (id, name) VALUES (11,'DVD-DL'); +INSERT INTO mediatypes (id, name) VALUES (12,'DVD+DL'); +INSERT INTO mediatypes (id, name) VALUES (13,'LaserDisc'); +INSERT INTO mediatypes (id, name) VALUES (14,'HDD'); +INSERT INTO mediatypes (id, name) VALUES (15,'HD-DVD'); +INSERT INTO mediatypes (id, name) VALUES (16,'Blu-ray'); +INSERT INTO mediatypes (id, name) VALUES (17,'AVCHD'); +INSERT INTO mediatypes (id, name) VALUES (18,'CD'); +INSERT INTO mediatypes (id, name) VALUES (50,'wanted'); + +# configuration +CREATE TABLE config ( + opt VARCHAR(50) NOT NULL, + value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY (opt) +) CHARACTER SET UTF8; + +# actors +CREATE TABLE actors ( + name VARCHAR(255) NOT NULL, + actorid VARCHAR(15) NOT NULL DEFAULT '', + imgurl VARCHAR(255) NOT NULL DEFAULT '', + checked TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`name`), + KEY `actorid` (`actorid`) +) CHARACTER SET UTF8; + +# multiusersupport +CREATE TABLE users ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL DEFAULT '', + `passwd` VARCHAR(100) NOT NULL DEFAULT '', + `cookiecode` VARCHAR(100) DEFAULT NULL, + `permissions` INT(10) UNSIGNED DEFAULT NULL, + `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `email` VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) CHARACTER SET UTF8; +INSERT INTO users (id, name, passwd, permissions) VALUES (1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 7); + +# user-specific configuration +CREATE TABLE userconfig ( + `user_id` INT(11) NOT NULL DEFAULT '0', + `opt` VARCHAR(50) NOT NULL DEFAULT '', + `value` VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY (`user_id`,`opt`) +) CHARACTER SET UTF8; + +# user seen table +CREATE TABLE userseen ( + `video_id` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `user_id` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`video_id`,`user_id`) +) CHARACTER SET UTF8; + +# cache table +CREATE TABLE cache ( + `tag` VARCHAR(45) NOT NULL, + `value` VARCHAR(32) DEFAULT NULL, + PRIMARY KEY (`tag`) +) CHARACTER SET UTF8; + +# set DEFAULT options + +# some defaults +REPLACE INTO `config` (opt, value) VALUES ('language', 'en'); +REPLACE INTO `config` (opt, value) VALUES ('mediadefault', '4'); +REPLACE INTO `config` (opt, value) VALUES ('langdefault', 'english'); +REPLACE INTO `config` (opt, value) VALUES ('filterdefault', 'unseen'); +REPLACE INTO `config` (opt, value) VALUES ('IMDBage', '432000'); +REPLACE INTO `config` (opt, value) VALUES ('thumbnail', '1'); +REPLACE INTO `config` (opt, value) VALUES ('template', 'nexgen::nexgen'); +REPLACE INTO `config` (opt, value) VALUES ('languageflags', 'german::spanish::english::french'); +REPLACE INTO `config` (opt, value) VALUES ('adultgenres', '20'); + +REPLACE INTO `config` (opt, value) VALUES ('imdbBrowser', 1); +REPLACE INTO `config` (opt, value) VALUES ('actorpics', 1); +REPLACE INTO `config` (opt, value) VALUES ('listcolumns', 6); +REPLACE INTO `config` (opt, value) VALUES ('castcolumns', 4); + +REPLACE INTO `config` (opt, value) VALUES ('enginedefault', 'imdb'); +REPLACE INTO `config` (opt, value) VALUES ('engineimdb', 1); +REPLACE INTO `config` (opt, value) VALUES ('engineamazon', 1); +REPLACE INTO `config` (opt, value) VALUES ('engineamazonaws', 1); +REPLACE INTO `config` (opt, value) VALUES ('enginegoogle', 1); +REPLACE INTO `config` (opt, value) VALUES ('engineyoutube', 1); + +# user permissions +CREATE TABLE permissions ( + `from_uid` INT(11) NOT NULL, + `to_uid` INT(11) NOT NULL, + `permissions` INT(10) UNSIGNED DEFAULT NULL, + `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`from_uid`,`to_uid`) +) CHARACTER SET UTF8; + +# create guest user +INSERT IGNORE INTO config (opt, value) VALUES ('guestid', '10000'); +INSERT IGNORE INTO config (opt, value) VALUES ('adminid', '1'); +INSERT IGNORE INTO config (opt, value) VALUES ('denyguest', '1'); +INSERT IGNORE INTO users (id, name, passwd, permissions) VALUES (10000 ,'Guest', '---', 2); + +# +# IMPORTANT +# +# Always increase this number in install/install.sql, install/upgrade.sql and +# core/constants.php when changing the database structure! +# + +REPLACE INTO config (opt,value) VALUES ('dbversion', 41); diff --git a/videodb/install/upgrade.php b/videodb/install/upgrade.php new file mode 100644 index 0000000..3f58a20 --- /dev/null +++ b/videodb/install/upgrade.php @@ -0,0 +1,57 @@ + + * @version $Id: upgrade.php,v 1.9 2013/03/13 15:54:24 andig2 Exp $ + */ + +// clear cache table +$res = runSQL("SELECT value FROM config WHERE opt='dbversion'", $dbh, true); +if (count($res) && ($res[0]['value'] >= 30)) +{ + runSQL("DELETE FROM cache", $dbh, true); +} + +$sql = ''; + +// check db encoding +$res = runSQL("SHOW VARIABLES LIKE 'character_set_database'", $dbh, true); +$enc = strtoupper($res[0]['Value']); + +if ($enc !== 'UTF8') +{ + $sql = "ALTER DATABASE CHARACTER SET UTF8;"; +} + +$tables = array('actors', 'cache', 'config', 'genres', 'lent', 'mediatypes', 'permissions', + 'userconfig', 'users', 'userseen', 'videodata', 'videogenre'); + +foreach ($tables as $table) +{ + #!! note: new code- checking table encoding on table level, too + # this is for the case that the DB ist alreaady utf8 but the tables are not + # + # alternative approach: + # SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'videodb' + $res = runSQL("SHOW TABLE STATUS WHERE name = '".$table."'", $dbh, true); + $enc = $res[0]['Collation']; + + if (!preg_match('/UTF8/i', $enc)) $sql .= "\nALTER TABLE ".$table." CONVERT TO CHARACTER SET UTF8;"; +} + +if ($sql) +{ + // add DB prefix + $sql = prefix_query($sql); + + warn('Note:
      + Install detected that your database encoding is not UTF8 (current encoding: '.$enc.'). Please make sure to convert your database encoding to UTF8 before you continue using videoDB. If you do not upgrade your database encoding, you may experience problems using videoDB such as SQL error messages or when exporting data. To perform the conversion please execute the following SQL statements against your database: +

      '.$sql.'

      '); +} + +// signal success +return true; + +?> diff --git a/videodb/install/upgrade.sql b/videodb/install/upgrade.sql new file mode 100644 index 0000000..53b22b2 --- /dev/null +++ b/videodb/install/upgrade.sql @@ -0,0 +1,411 @@ +# +# Database upgrade script +# +# This script should not be run directly, but rather be executed by using install.php. +# Otherwise, important data conversion steps may be missing. +# +# @package Setup +# @author Andreas Goetz +# @version $Id: upgrade.sql,v 1.21 2013/03/16 10:10:07 andig2 Exp $ +# + +# These were introduced by accident but are not needed +DROP TABLE IF EXISTS codec; +DROP TABLE IF EXISTS files; +DROP TABLE IF EXISTS imdb; +DROP TABLE IF EXISTS video; + +# mediatypes +CREATE TABLE IF NOT EXISTS mediatypes( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(15) NULL, + PRIMARY KEY (id) +); + +# add mediatypes +INSERT IGNORE INTO mediatypes (id, name) VALUES (1,'DVD'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (2,'SVCD'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (3,'VCD'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (4,'CD-R'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (5,'CD-RW'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (6,'VHS'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (7,'DVD-R'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (8,'DVD-RW'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (9,'DVD+R'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (10,'DVD+RW'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (11,'DVD-DL'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (12,'DVD+DL'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (13,'LaserDisc'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (50,'wanted'); + +# add genres +INSERT IGNORE INTO genres (id, name) VALUES (20,'Adult'); + +# modify videodata +ALTER TABLE videodata ADD mediatype INT(10) UNSIGNED NOT NULL; +ALTER TABLE videodata ADD custom1 VARCHAR(255) NULL; +ALTER TABLE videodata ADD custom2 VARCHAR(255) NULL; +ALTER TABLE videodata ADD custom3 VARCHAR(255) NULL; +ALTER TABLE videodata ADD custom4 VARCHAR(255) NULL; + +# configuration +CREATE TABLE IF NOT EXISTS config ( + opt VARCHAR(50) NOT NULL, + value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY (opt) +); + +# some DEFAULTs +INSERT IGNORE INTO config (opt, value) VALUES ('language', 'en'); +INSERT IGNORE INTO config (opt, value) VALUES ('mediaDEFAULT', '4'); +INSERT IGNORE INTO config (opt, value) VALUES ('langDEFAULT', 'english'); +INSERT IGNORE INTO config (opt, value) VALUES ('filterDEFAULT', 'unseen'); +INSERT IGNORE INTO config (opt, value) VALUES ('IMDBage', '432000'); +INSERT IGNORE INTO config (opt, value) VALUES ('thumbnail', '1'); +INSERT IGNORE INTO config (opt, value) VALUES ('castcolumns', '1'); +INSERT IGNORE INTO config (opt, value) VALUES ('template', 'modern::compact'); +INSERT IGNORE INTO config (opt, value) VALUES ('languageflags', 'german::spanish::english::french'); + +# actor headshots +CREATE TABLE IF NOT EXISTS actors ( + name VARCHAR(255) NOT NULL, + imgurl VARCHAR(255) NOT NULL DEFAULT '', + checked TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (name) +); + +# some indexes +ALTER TABLE videodata ENGINE=MyISAM; +ALTER TABLE videodata ADD INDEX title_idx (title); +ALTER TABLE videodata ADD INDEX diskid_idx (diskid); +ALTER TABLE videodata ADD FULLTEXT actors_idx (actors); + +# creation date +ALTER TABLE videodata ADD created DATETIME; +UPDATE videodata SET created = lastupdate WHERE created IS NULL; + +# multiusersupport +CREATE TABLE IF NOT EXISTS users ( + id INT(11) NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL DEFAULT '', + passwd VARCHAR(100) NOT NULL DEFAULT '', + cookiecode VARCHAR(100) DEFAULT NULL, + permissions INT(10) UNSIGNED DEFAULT NULL, + email VARCHAR(255) DEFAULT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id), + UNIQUE KEY `name` (`name`) +); +INSERT IGNORE INTO users (name, passwd, permissions) VALUES ('admin', '21232f297a57a5a743894a0e4a801fc3', 15); +ALTER TABLE videodata ADD owner VARCHAR(255) DEFAULT NULL; + +# +# changes in DB version 4 +# + +ALTER TABLE `actors` ADD `actorid` VARCHAR( 15 ) NOT NULL AFTER `name` ; + +# +# changes in DB version 5 +# + +ALTER TABLE `users` ADD `email` VARCHAR( 255 ) ; + +# +# changes in DB version 6 +# + +CREATE TABLE IF NOT EXISTS userconfig ( + user VARCHAR(255) NOT NULL DEFAULT '', + opt VARCHAR(50) NOT NULL DEFAULT '', + value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY (user,opt) +); + +# +# changes in DB version 7 +# + +INSERT IGNORE INTO genres (id, name) VALUES (21,'Music'); + +# +# changes in DB version 8 +# + +CREATE TABLE userseen ( + user VARCHAR(255) NOT NULL, + id INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (user,id) +); + +# +# changes in DB version 9 +# + +ALTER TABLE `videodata` CHANGE `imdbID` `imdbID` VARCHAR( 15 ) DEFAULT NULL; + +# +# changes in DB version 10 +# + +ALTER TABLE `users` ADD `name` VARCHAR( 255 ) NOT NULL AFTER `user`; +UPDATE `users` SET `name` = `user`; +ALTER TABLE `users` DROP PRIMARY KEY; +ALTER TABLE `users` ADD `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; +ALTER TABLE `users` ADD UNIQUE (`name`); +ALTER TABLE `users` DROP `user`; + +ALTER TABLE `userseen` CHANGE `id` `video_id` INT UNSIGNED DEFAULT '0' NOT NULL; +ALTER TABLE `userseen` ADD `user_id` INT NOT NULL ; +ALTER TABLE `userconfig` ADD `user_id` INT NOT NULL FIRST; +ALTER TABLE `videodata` ADD `owner_id` INT NOT NULL ; + +# +# changes in DB version 11 +# +# Note- before this step is applied, the user data conversion must be performed! +# + +ALTER TABLE `userseen` DROP PRIMARY KEY, ADD PRIMARY KEY ( `video_id`, `user_id` ); +ALTER TABLE `userseen` DROP `user`; +ALTER TABLE `userconfig` DROP PRIMARY KEY, ADD PRIMARY KEY ( `user_id`, `opt` ); +ALTER TABLE `userconfig` DROP `user`; +ALTER TABLE `videodata` DROP `owner`; + +# +# changes in DB version 12 +# + +ALTER TABLE `videogenre` CHANGE `id` `video_id` INT( 10 ) UNSIGNED DEFAULT '0' NOT NULL ; +ALTER TABLE `videogenre` CHANGE `gid` `genre_id` INT( 10 ) UNSIGNED DEFAULT '0' NOT NULL ; + +# +# changes in DB version 13 +# + +ALTER TABLE `videodata` MODIFY COLUMN `imgurl` VARCHAR(255) DEFAULT NULL; + +# +# changes in DB version 14 +# + +CREATE TABLE IF NOT EXISTS permissions ( + `from_uid` INT(11) NOT NULL, + `to_uid` INT(11) NOT NULL, + `permissions` INT(10) UNSIGNED DEFAULT NULL, + `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`from_uid`, `to_uid`) +); + +# convert old permissions -> new permissions +CREATE TABLE temp_perm ( + `from_uid` INT(11) NOT NULL, + `to_uid` INT(11) NOT NULL, + `permissions` INT(10) UNSIGNED DEFAULT NULL, + `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`from_uid`, `to_uid`) +); + +INSERT IGNORE INTO temp_perm (from_uid,to_uid,permissions) + SELECT a.id AS from_uid, + b.id AS to_uid, + CASE + WHEN a.permissions & 2 = 2 THEN 6 | COALESCE(c.permissions,0) + WHEN a.permissions & 4 = 4 THEN + CASE + WHEN a.id = b.id THEN 6 | COALESCE(c.permissions,0) + ELSE 2 | COALESCE(c.permissions,0) + END + ELSE 0 | COALESCE(c.permissions,0) + END AS permissions + FROM (users a, users b) + LEFT OUTER JOIN permissions c + ON c.from_uid = a.id + AND c.to_uid = b.id + ORDER BY from_uid DESC; + +DELETE FROM temp_perm WHERE permissions = 0; + +UPDATE users SET permissions = + CASE WHEN (permissions & 2) = 2 + THEN (permissions & 11) | 4 + ELSE (permissions & 9) | 2 + END; + +DELETE FROM permissions; + +INSERT INTO permissions SELECT * FROM temp_perm; + +DROP TABLE temp_perm; + +INSERT IGNORE INTO config (opt, value) VALUES ('guestid', '10000'); +INSERT IGNORE INTO users (id, name, passwd, permissions) VALUES (10000 ,'Guest', '---', 2); + +# +# changes in DB version 15 +# + +INSERT IGNORE INTO genres (id, name) VALUES (22,'Biography'); +INSERT IGNORE INTO genres (id, name) VALUES (23,'History'); +INSERT IGNORE INTO genres (id, name) VALUES (24,'Sport'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (11,'DVD-DL'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (12,'DVD+DL'); + +# +# changes in DB version 16 +# + +ALTER TABLE `videodata` MODIFY COLUMN `imdbID` VARCHAR(30); + +# +# changes in DB version 17 +# + +INSERT IGNORE INTO mediatypes (id, name) VALUES (13,'LaserDisc'); + +# +# changes in DB version 18 +# + +ALTER TABLE `actors` ADD KEY `actorid` (`actorid`); + +# +# changes in DB version 19 +# + +INSERT IGNORE INTO config (opt, value) VALUES ('adminid', '1'); +UPDATE videodata SET owner_id = 1 WHERE owner_id = 0; + +# +# changes in DB version 20 +# + +SELECT 1; + +# +# changes in DB version 21 +# + +ALTER TABLE `videodata` DROP COLUMN `seen`; + +# +# changes in DB version 22 +# + +ALTER TABLE `videodata` MODIFY COLUMN `owner_id` INT NOT NULL DEFAULT '1'; + +# +# changes in DB version 23 +# + +ALTER TABLE `videodata` ADD COLUMN `rating` VARCHAR(15) DEFAULT NULL AFTER `plot`; + +UPDATE lent SET dt=NOW() WHERE dt IS NULL; +ALTER TABLE `lent` CHANGE dt dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; + +# +# changes in DB version 24 +# + +ALTER TABLE `videodata` ADD FULLTEXT INDEX `plot_idx`(`plot`); + +# +# changes in DB version 25 +# + +INSERT IGNORE INTO mediatypes (id, name) VALUES (14,'HDD'); + +# +# changes in DB version 26 +# + +UPDATE config SET value='elegant::modern' WHERE opt='template' AND value LIKE 'modern%'; + +# +# changes in DB version 27 +# + +INSERT IGNORE INTO mediatypes (id, name) VALUES (15,'HD DVD'); +INSERT IGNORE INTO mediatypes (id, name) VALUES (16,'Blu-ray'); + +# +# changes in DB version 28 +# + +ALTER TABLE `videodata` MODIFY COLUMN `year` INT(4) UNSIGNED NOT NULL DEFAULT '0'; + +# +# changes in DB version 29 +# + +CREATE TABLE cache ( + `tag` VARCHAR(32) NOT NULL, + `value` VARCHAR(32) DEFAULT NULL, + PRIMARY KEY (`tag`) +); + +# +# changes in DB version 30 +# + +SELECT 1; + +# +# changes in DB version 31 +# + +ALTER TABLE `cache` MODIFY COLUMN `tag` VARCHAR(45) NOT NULL; + +# +# changes in DB version 32 +# + +UPDATE mediatypes SET name='HD-DVD' WHERE name='HD DVD'; + +# +# changes in DB version 33 +# + +DELETE FROM permissions WHERE NOT EXISTS (SELECT 1 FROM users WHERE id=from_uid); + +# +# changes in DB version 34 +# + +ALTER TABLE `videodata` MODIFY COLUMN `filesize` BIGINT UNSIGNED DEFAULT NULL; + +# +# changes in DB version 35 +# + +INSERT INTO mediatypes (id, name) VALUES (17,'CD'); + +# +# changes in DB version 36 +# + +UPDATE mediatypes SET id=18 WHERE id=17 AND name='CD'; +INSERT IGNORE INTO mediatypes (id, name) VALUES (17,'AVCHD'); + +# +# changes in DB version 40 +# + +REPLACE INTO config (opt,value) VALUES ('template', 'nexgen::nexgen'); +REPLACE INTO config (opt,value) VALUES ('actorpics', '1'); +REPLACE INTO config (opt,value) VALUES ('imdbBrowser', '1'); + +# +# changes in DB version 41 +# + +ALTER TABLE videodata MODIFY actors MEDIUMTEXT; + +# +# IMPORTANT +# +# Always increase this number in install/install.sql, install/upgrade.sql and +# core/constants.php when changing the database structure! +# + +REPLACE INTO config (opt,value) VALUES ('dbversion', 41); diff --git a/videodb/install/upgrade_v10.php b/videodb/install/upgrade_v10.php new file mode 100644 index 0000000..53280db --- /dev/null +++ b/videodb/install/upgrade_v10.php @@ -0,0 +1,84 @@ + + * @version $Id: upgrade_v10.php,v 1.1 2007/01/04 16:14:16 andig2 Exp $ + */ + + +/* + * userseen table upgrade + */ + +$sql = 'SELECT video_id, user, users.id AS user_id + FROM userseen + LEFT JOIN users ON userseen.user = users.name'; +$set = runSQL($sql, $dbh, true); +if ($set === false) return(false); + +foreach ($set as $row) +{ + // don't convert fishy data + if (!empty($row['user_id'])) + { + $sql = "UPDATE userseen SET user_id = ".$row['user_id']." ". + "WHERE video_id = ".$row['video_id']." AND user = '".$row['user']."'"; + + if (runSQL($sql, $dbh) === false) return(false); + } +} + + +/* + * userconfig table upgrade + */ + +$sql = 'SELECT user, opt, users.id AS user_id + FROM userconfig + LEFT JOIN users ON userconfig.user = users.name'; +$set = runSQL($sql, $dbh); +if ($set === false) return(false); + +foreach ($set as $row) +{ + // don't convert fishy data + if (!empty($row['user_id'])) + { + $sql = "UPDATE userconfig SET user_id = ".$row['user_id']." ". + "WHERE opt = '".$row['opt']."' AND user = '".$row['user']."'"; + + if (runSQL($sql, $dbh) === false) return(false); + } +} + + +/* + * videodata table upgrade + */ + +$sql = 'SELECT videodata.id AS id, owner, users.id AS owner_id + FROM videodata + LEFT JOIN users ON videodata.owner = users.name'; +$set = runSQL($sql, $dbh); +if ($set === false) return(false); + +foreach ($set as $row) +{ + // don't convert fishy data + if (!empty($row['owner_id'])) + { + $sql = "UPDATE videodata SET owner_id = ".$row['owner_id']." ". + "WHERE id = ".$row['id']; + + if (runSQL($sql, $dbh) === false) return(false); + } +} + +// signal success +return true; + +?> diff --git a/videodb/install/upgrade_v20.php b/videodb/install/upgrade_v20.php new file mode 100644 index 0000000..6e92740 --- /dev/null +++ b/videodb/install/upgrade_v20.php @@ -0,0 +1,35 @@ + + * @version $Id: upgrade_v20.php,v 1.1 2007/01/04 16:14:16 andig2 Exp $ + */ + + +/* + * Userseen data migration + */ + +$sql = 'SELECT owner_id, seen + FROM videodata + WHERE seen > 0'; +$set = runSQL($sql, $dbh, true); +if ($set === false) return(false); + +foreach ($set as $row) +{ + // don't convert fishy data + if (!empty($row['user_id'])) + { + $sql = "REPLACE INTO ".TBL_USERSEEN." SET user_id=".$row['owner_id'].", video_id=".$row['id']; + + if (runSQL($sql, $dbh) === false) return(false); + } +} + +// signal success +return true; + +?> diff --git a/videodb/install/upgrade_v26.php b/videodb/install/upgrade_v26.php new file mode 100644 index 0000000..3f3edd0 --- /dev/null +++ b/videodb/install/upgrade_v26.php @@ -0,0 +1,47 @@ + + * @version $Id: upgrade_v26.php,v 1.2 2007/12/30 11:09:24 andig2 Exp $ + */ + +/** + * Rating data migration + */ +function migrate_rating($field) +{ + global $dbh; + + $set = runSQL('UPDATE videodata SET rating='.$field.' WHERE '.$field.'>0', $dbh, true); + return $set; +} + +$sql = "SELECT * FROM config WHERE opt LIKE 'custom%type'"; +$set = runSQL($sql, $dbh, true); +if ($set === false) return(false); + +foreach ($set as $row) +{ + if ($row['value'] == 'rating') + { + if (preg_match('/(custom\d)/', $row['opt'], $m)) + { + $field = $m[1]; + $set = migrate_rating($field); + if ($set === false) return(false); + + $sql = "UPDATE config SET value='' WHERE opt LIKE '".$field."%'"; + $set = runSQL($sql, $dbh, true); + if ($set === false) return(false); + } + } +} + +// signal success +return true; + +?> diff --git a/videodb/javascript/.htaccess b/videodb/javascript/.htaccess new file mode 100644 index 0000000..c4ebc32 --- /dev/null +++ b/videodb/javascript/.htaccess @@ -0,0 +1,21 @@ +# +# Apache access control +# +# @author Andreas Goetz +# $Id: .htaccess,v 1.3 2013/03/14 17:17:27 andig2 Exp $ +# + +# avoid image expiry + + ExpiresActive On + ExpiresDefault "access plus 1 month" + + +# enable compression + + # Deflate zum zippen + AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml ap plication/xhtml+xml text/javascript text/css + BrowserMatch ^Mozilla/4 gzip-only-text/html + BrowserMatch ^Mozilla/4.0[678] no-gzip + BrowserMatch \bMSIE !no-gzip !gzip-only-text/html + diff --git a/videodb/javascript/edit.js b/videodb/javascript/edit.js new file mode 100644 index 0000000..ab42b3b --- /dev/null +++ b/videodb/javascript/edit.js @@ -0,0 +1,33 @@ +/** + * Lookup logic + * + * @package JavaScript + * @author Andreas Goetz + * @version $Id: edit.js,v 1.6 2009/04/06 06:50:54 andig2 Exp $ + */ + +function lookupData(title) +{ + var win = open('lookup.php?find=' + encodeURIComponent(title), 'lookup', + 'width=550,height=500,menubar=no,resizable=yes,scrollbars=yes,status=yes,toolbar=no'); + win.focus(); +} + +function lookupImage(title) +{ + var win = open('lookup.php?find=' + encodeURIComponent(title) + '&searchtype=image&engine=google', 'lookup', + 'width=450,height=500,menubar=no,resizable=yes,scrollbars=yes,status=yes,toolbar=no'); + win.focus(); +} + +function changedId() +{ + if (document.edi.imdbID.value) + { + if (document.edi.lookup0.checked) document.edi.lookup1.checked = true; + } + else + { + document.edi.lookup0.checked = true; + } +} diff --git a/videodb/javascript/jquery.lazyload.min.js b/videodb/javascript/jquery.lazyload.min.js new file mode 100644 index 0000000..79663d7 --- /dev/null +++ b/videodb/javascript/jquery.lazyload.min.js @@ -0,0 +1,15 @@ +/* + * Lazy Load - jQuery plugin for lazy loading images + * + * Copyright (c) 2007-2013 Mika Tuupola + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * Project home: + * http://www.appelsiini.net/projects/lazyload + * + * Version: 1.8.4 + * + */ +(function(a,b,c,d){var e=a(b);a.fn.lazyload=function(c){function i(){var b=0;f.each(function(){var c=a(this);if(h.skip_invisible&&!c.is(":visible"))return;if(!a.abovethetop(this,h)&&!a.leftofbegin(this,h))if(!a.belowthefold(this,h)&&!a.rightoffold(this,h))c.trigger("appear"),b=0;else if(++b>h.failure_limit)return!1})}var f=this,g,h={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null};return c&&(d!==c.failurelimit&&(c.failure_limit=c.failurelimit,delete c.failurelimit),d!==c.effectspeed&&(c.effect_speed=c.effectspeed,delete c.effectspeed),a.extend(h,c)),g=h.container===d||h.container===b?e:a(h.container),0===h.event.indexOf("scroll")&&g.bind(h.event,function(a){return i()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,c.one("appear",function(){if(!this.loaded){if(h.appear){var d=f.length;h.appear.call(b,d,h)}a("").bind("load",function(){c.hide().attr("src",c.data(h.data_attribute))[h.effect](h.effect_speed),b.loaded=!0;var d=a.grep(f,function(a){return!a.loaded});f=a(d);if(h.load){var e=f.length;h.load.call(b,e,h)}}).attr("src",c.data(h.data_attribute))}}),0!==h.event.indexOf("scroll")&&c.bind(h.event,function(a){b.loaded||c.trigger("appear")})}),e.bind("resize",function(a){i()}),/iphone|ipod|ipad.*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent.persisted&&f.each(function(){a(this).trigger("appear")})}),a(b).load(function(){i()}),this},a.belowthefold=function(c,f){var g;return f.container===d||f.container===b?g=e.height()+e.scrollTop():g=a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return f.container===d||f.container===b?g=e.width()+e.scrollLeft():g=a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return f.container===d||f.container===b?g=e.scrollTop():g=a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return f.container===d||f.container===b?g=e.scrollLeft():g=a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!a.rightoffold(b,c)&&!a.leftofbegin(b,c)&&!a.belowthefold(b,c)&&!a.abovethetop(b,c)},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})})(jQuery,window,document) diff --git a/videodb/javascript/lookup.js b/videodb/javascript/lookup.js new file mode 100644 index 0000000..afcbc49 --- /dev/null +++ b/videodb/javascript/lookup.js @@ -0,0 +1,28 @@ +/** + * Lookup data submission logic + * + * @package JavaScript + * @author Andreas Goetz + * @version $Id: lookup.js,v 1.6 2007/12/22 13:36:43 andig2 Exp $ + */ + +window.focus(); + +function returnData(id, title, subtitle, engine) +{ + opener.document.edi.imdbID.value = id; + opener.document.edi.engine.value = engine; +// if (!opener.document.edi.title.value) opener.document.edi.title.value = title; + opener.document.edi.title.value = title; + if (!opener.document.edi.subtitle.value) opener.document.edi.subtitle.value = subtitle; + if (opener.document.edi.lookup0.checked) opener.document.edi.lookup1.checked = true; + opener.focus(); + window.close(); +} + +function returnImage(imgurl) +{ + opener.document.edi.imgurl.value = imgurl; + opener.focus(); + window.close(); +} diff --git a/videodb/javascript/prototype/fancyzoom.js b/videodb/javascript/prototype/fancyzoom.js new file mode 100644 index 0000000..82fcec7 --- /dev/null +++ b/videodb/javascript/prototype/fancyzoom.js @@ -0,0 +1,555 @@ +// FancyZoom.js - v1.1 - http://www.fancyzoom.com +// +// Copyright (c) 2008 Cabel Sasser / Panic Inc +// All rights reserved. +// +// Requires: FancyZoomHTML.js +// Instructions: Include JS files in page, call setupZoom() in onLoad. That's it! +// Any
      links to images will be updated to zoom inline. +// Add rel="nozoom" to your to disable zooming for an image. +// +// Redistribution and use of this effect in source form, with or without modification, +// are permitted provided that the following conditions are met: +// +// * USE OF SOURCE ON COMMERCIAL (FOR-PROFIT) WEBSITE REQUIRES ONE-TIME LICENSE FEE PER DOMAIN. +// Reasonably priced! Visit www.fancyzoom.com for licensing instructions. Thanks! +// +// * Non-commercial (personal) website use is permitted without license/payment! +// +// * Redistribution of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution of source code and derived works cannot be sold without specific +// written prior permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var includeCaption = true; // Turn on the "caption" feature, and write out the caption HTML +var zoomTime = 5; // Milliseconds between frames of zoom animation +var zoomSteps = 15; // Number of zoom animation frames +var includeFade = 1; // Set to 1 to fade the image in / out as it zooms +var minBorder = 90; // Amount of padding between large, scaled down images, and the window edges +var shadowSettings = '0px 5px 25px rgba(0, 0, 0, '; // Blur, radius, color of shadow for compatible browsers + +// Init. Do not add anything below this line, unless it's something awesome. +var myWidth = 0, myHeight = 0, myScroll = 0; myScrollWidth = 0; myScrollHeight = 0; +var zoomOpen = false, preloadFrame = 1, preloadActive = false, preloadTime = 0, imgPreload = new Image(); + +var zoomActive = new Array(); var zoomTimer = new Array(); +var zoomOrigW = new Array(); var zoomOrigH = new Array(); +var zoomOrigX = new Array(); var zoomOrigY = new Array(); + +var zoomID = "ZoomBox"; +var theID = "ZoomImage"; +var preloadFrom = false; + +var browserIsIE = (navigator.userAgent.indexOf("MSIE") != -1); + +// Zoom: Load an image into an image object. When done loading, function sets preloadActive to false, +// so other bits know that they can proceed with the zoom. +// Preloaded image is stored in imgPreload and swapped out in the zoom function. +function zoomPreload(from) { + var theimage = from.getAttribute("href"); + + // Only preload if we have to, i.e. the image isn't this image already + if (imgPreload.src.indexOf(theimage.substr(theimage.lastIndexOf("/"))) == -1) { + preloadActive = true; + imgPreload = new Image(); + + // Set a function to fire when the preload is complete, setting flags along the way. + imgPreload.onload = function() { + if (preloadFrom) { +// $("ZoomSpin").setStyle({visibility: "hidden"}); + $('indicator1').setStyle({visibility: "hidden"}); + zoomIn(preloadFrom); + preloadFrom = false; + } + preloadActive = false; + } + + // Load it! + imgPreload.src = theimage; + } +} + +// ZOOM CLICK: We got a click! Should we do the zoom? Or wait for the preload to complete? +// todo?: Double check that imgPreload src = clicked src +function zoomClick(from, evt) { + var shift = getShift(evt); + + // Check for Command / Alt key. If pressed, pass them through -- don't zoom! + if (! evt && window.event && (window.event.metaKey || window.event.altKey)) { + return true; + } else if (evt && (evt.metaKey|| evt.altKey)) { + return true; + } + + // Get browser dimensions + getSize(); + + // make sure to preload in case mouseover event hasn't been fired + if (!imgPreload.src) zoomPreload(from); + + // If preloading still, wait, and display the spinner. + if (preloadActive) { + preloadFrom = from; + $("indicator1").setStyle({left: (myWidth / 2)+'px', top: ((myHeight / 2) + myScroll)+'px', visibility: "visible"}); + } else { + // Otherwise, we're loaded: do the zoom! + zoomIn(from, shift); + } + + return false; +} + +// Zoom: Move an element in to endH endW, using zoomHost as a starting point. +// "from" is an object reference to the href that spawned the zoom. +function zoomIn(from, shift) { + zoomimg.src = from.getAttribute("href"); + + srcimg = (from.getAttribute("srcimg")) ? $(from.getAttribute("srcimg")) : from.down('img'); + if (!srcimg) srcimg = from; + + // Determine the zoom settings from where we came from, the element in the . + // If there's no element in the , or we can't get the width, make stuff up + if (srcimg && srcimg.width) { + startW = srcimg.width; + startH = srcimg.height; + startPos = srcimg.positionedOffset(); + } else { + startW = 50; + startH = 12; + startPos = from.positionedOffset(); + } + + hostX = startPos.left; + hostY = startPos.top; + + // Determine the target zoom settings from the preloaded image object + endW = imgPreload.width; + endH = imgPreload.height; + + // Start! But only if we're not zooming already! + if (zoomActive[theID] != true) { + // Clear everything out just in case something is already open + if ($("ShadowBox")) { + $("ShadowBox").setStyle({visibility: "hidden"}); + } + else if (! browserIsIE) { + // Wipe timer if shadow is fading in still + if (fadeActive["ZoomImage"]) { + clearInterval(fadeTimer["ZoomImage"]); + fadeActive["ZoomImage"] = false; + fadeTimer["ZoomImage"] = false; + } + + $("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)'; + } + + $("ZoomClose").setStyle({visibility: "hidden"}); + + // Setup the CAPTION, if existing. Hide it first, set the text. + if (includeCaption) { + $("ZoomCapDiv").setStyle({visibility: "hidden"}); + $("ZoomCaption").update((from.getAttribute('title')) ? from.getAttribute('title') : ''); + } + + // Store original position in an array for future zoomOut. + zoomOrigW[theID] = startW; + zoomOrigH[theID] = startH; + zoomOrigX[theID] = hostX; + zoomOrigY[theID] = hostY; + + // Show the zooming image container, make it invisible + if (includeFade == 1) setOpacity(0, zoomID); + zoomimg.setStyle({width: startW+'px', height: startH+'px'}); + zoomdiv.setStyle({top: hostY+'px', left: hostX+'px', visibility: "visible"}); + + // If it's too big to fit in the window, shrink the width and height to fit (with ratio). + sizeRatio = endW / endH; + if (endW > myWidth - minBorder) { + endW = myWidth - minBorder; + endH = endW / sizeRatio; + } + if (endH > myHeight - minBorder) { + endH = myHeight - minBorder; + endW = endH * sizeRatio; + } + + zoomChangeX = ((myWidth / 2) - (endW / 2) - hostX); + zoomChangeY = (((myHeight / 2) - (endH / 2) - hostY) + myScroll); + zoomChangeW = (endW - startW); + zoomChangeH = (endH - startH); + + // Shift key? + tempSteps = (shift) ? zoomSteps * 7 : zoomSteps; + + // Setup Zoom + zoomCurrent = 0; + + // Setup Fade with Zoom, If Requested + if (includeFade == 1) { + fadeCurrent = 0; + fadeAmount = (0 - 100) / tempSteps; + } else { + fadeAmount = 0; + } + + // Do It! + zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime); + zoomActive[theID] = true; + } +} + +// Zoom it back out. +function zoomOut(from, evt) { + // Get shift key status. + // IE events don't seem to get passed through the function, so grab it from the window. + tempSteps = (getShift(evt)) ? zoomSteps * 7 : zoomSteps; + + // Check to see if something is happening/open + if (zoomActive[theID] != true) { + + // First, get rid of the shadow if necessary. + if ($("ShadowBox")) { + $("ShadowBox").setStyle({visibility: "hidden"}); + } + else if (! browserIsIE) { + // Wipe timer if shadow is fading in still + if (fadeActive["ZoomImage"]) { + clearInterval(fadeTimer["ZoomImage"]); + fadeActive["ZoomImage"] = false; + fadeTimer["ZoomImage"] = false; + } + $("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)'; + } + + // ..and the close box... + // ...and the caption if necessary! + $("ZoomClose").setStyle({visibility: "hidden"}); + $("ZoomCapDiv").setStyle({visibility: "hidden"}); + + // Now, figure out where we came from, to get back there + xy = zoomdiv.positionedOffset(); + startX = xy.left; + startY = xy.top; + startW = zoomimg.width; + startH = zoomimg.height; + zoomChangeX = zoomOrigX[theID] - startX; + zoomChangeY = zoomOrigY[theID] - startY; + zoomChangeW = zoomOrigW[theID] - startW; + zoomChangeH = zoomOrigH[theID] - startH; + + // Setup Zoom + zoomCurrent = 0; + + // Setup Fade with Zoom, If Requested + if (includeFade == 1) { + fadeCurrent = 0; + fadeAmount = (100 - 0) / tempSteps; + } else { + fadeAmount = 0; + } + + // Do It! + zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime); + zoomActive[theID] = true; + } +} + +// Finished Zooming In +function zoomDoneIn(zoomID, theID) { + // Note that it's open + zoomOpen = true; + zoomdiv = $(zoomID); + zd_xy = zoomdiv.positionedOffset(); + + // Position the table shadow behind the zoomed in image, and display it + if ($("ShadowBox")) { + setOpacity(0, "ShadowBox"); + $("ShadowBox").setStyle({top: (zd_xy.top - 8)+'px', left: (zd_xy.left - 13)+'px', + width: (zoomdiv.offsetWidth + 26)+'px', height: (zoomdiv.offsetHeight + 26)+'px', visibility: "visible"}); + fadeElementSetup("ShadowBox", 0, 100, 5); + } else if (! browserIsIE) { + // Or, do a fade of the modern shadow + fadeElementSetup("ZoomImage", 0, .8, 5, 0, "shadow"); + } + + // Position and display the CAPTION, if existing + if (includeCaption && $("ZoomCaption").innerHTML) { + zoomcapd = $("ZoomCapDiv"); + zoomcapd.setStyle({top: (zd_xy.top + (zoomdiv.offsetHeight + 15))+'px', + left: ((myWidth / 2) - (zoomcapd.offsetWidth / 2))+'px', visibility: "visible"}); + // fadeElementSetup("ZoomCapDiv", 0, 100, 5); + } + + // Display Close Box (fade it if it's not IE) + if (!browserIsIE) setOpacity(0, "ZoomClose"); + $("ZoomClose").setStyle({visibility: "visible"}); + if (!browserIsIE) fadeElementSetup("ZoomClose", 0, 100, 5); + + // Get keypresses + document.onkeypress = getKey; +} + +// Finished Zooming Out +function zoomDone(zoomdiv, theID) { + // No longer open + zoomOpen = false; + + // Clear stuff out, clean up + zoomOrigH[theID] = ""; + zoomOrigW[theID] = ""; + $(zoomdiv).setStyle({visibility: "hidden"}); + zoomActive[theID] == false; + + // Stop getting keypresses + document.onkeypress = null; +} + +// Actually zoom the element +function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, includeFade, fadeAmount, execWhenDone) { + // Test if we're done, or if we continue + if (zoomCurrent == (zoomSteps + 1)) { + zoomActive[theID] = false; + clearInterval(zoomTimer[theID]); + + if (execWhenDone) { + eval(execWhenDone); + } + } else { + // Do the Fade! + if (includeFade == 1) { + setOpacity((fadeAmount < 0) ? Math.abs(zoomCurrent * fadeAmount) : 100 - (zoomCurrent * fadeAmount), zoomdiv); + } + + // Calculate this step's difference, and move it! + moveW = cubicInOut(zoomCurrent, zoomStartW, zoomChangeW, zoomSteps); + moveH = cubicInOut(zoomCurrent, zoomStartH, zoomChangeH, zoomSteps); + moveX = cubicInOut(zoomCurrent, zoomStartX, zoomChangeX, zoomSteps); + moveY = cubicInOut(zoomCurrent, zoomStartY, zoomChangeY, zoomSteps); + + $(zoomdiv).setStyle({top: moveY+'px', left: moveX+'px'}); + zoomimg.setStyle({width: moveW+'px', height: moveH+'px'}); + + zoomCurrent++; + + clearInterval(zoomTimer[theID]); + zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+includeFade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime); + } +} + +// Zoom Utility: Get Key Press when image is open, and act accordingly +function getKey(evt) { + theKey = (evt) ? evt.keyCode : event.keyCode; + + if (theKey == 27) { // ESC + zoomOut(this, evt); + } +} + +//////////////////////////// +// +// FADE Functions +// + +function fadeOut(elem) { + if (elem.id) { + fadeElementSetup(elem.id, 100, 0, 10); + } +} + +function fadeIn(elem) { + if (elem.id) { + fadeElementSetup(elem.id, 0, 100, 10); + } +} + +// Fade: Initialize the fade function +var fadeActive = new Array(); +var fadeQueue = new Array(); +var fadeTimer = new Array(); +var fadeClose = new Array(); +var fadeMode = new Array(); + +function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose, fdMode) { + if (fadeActive[theID] == true) { + // Already animating, queue up this command + fadeQueue[theID] = new Array(theID, fdStart, fdEnd, fdSteps); + } else { + fadeSteps = fdSteps; + fadeCurrent = 0; + fadeAmount = (fdStart - fdEnd) / fadeSteps; + fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15); + fadeActive[theID] = true; + fadeMode[theID] = fdMode; + + fadeClose[theID] = (fdClose == 1); + } +} + +// Fade: Do the fade. This function will call itself, modifying the parameters, so +// many instances can run concurrently. Can fade using opacity, or fade using a box-shadow. +function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) { + if (fadeCurrent == fadeSteps) { + // We're done, so clear. + clearInterval(fadeTimer[theID]); + fadeActive[theID] = false; + fadeTimer[theID] = false; + + // Should we close it once the fade is complete? + if (fadeClose[theID] == true) { + $(theID).setStyle({visibility: "hidden"}); + } + + // Hang on.. did a command queue while we were working? If so, make it happen now + if (fadeQueue[theID] && fadeQueue[theID] != false) { + fadeElementSetup(fadeQueue[theID][0], fadeQueue[theID][1], fadeQueue[theID][2], fadeQueue[theID][3]); + fadeQueue[theID] = false; + } + } else { + + fadeCurrent++; + + // Now actually do the fade adjustment. + if (fadeMode[theID] == "shadow") { + + // Do a special fade on the webkit-box-shadow of the object + if (fadeAmount < 0) { + $(theID).style.webkitBoxShadow = shadowSettings + (Math.abs(fadeCurrent * fadeAmount)) + ')'; + } else { + $(theID).style.webkitBoxShadow = shadowSettings + (100 - (fadeCurrent * fadeAmount)) + ')'; + } + + } else { + + // Set the opacity depending on if we're adding or subtracting (pos or neg) + if (fadeAmount < 0) { + setOpacity(Math.abs(fadeCurrent * fadeAmount), theID); + } else { + setOpacity(100 - (fadeCurrent * fadeAmount), theID); + } + } + + // Keep going, and send myself the updated variables + clearInterval(fadeTimer[theID]); + fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15); + } +} + +//////////////////////////// +// +// UTILITY functions +// + +// Utility: Set the opacity, compatible with a number of browsers. Value from 0 to 100. +function setOpacity(opacity, theID) { + var object = $(theID).style; + + // If it's 100, set it to 99 for Firefox. + if (navigator.userAgent.indexOf("Firefox") != -1) { + if (opacity == 100) { opacity = 99.9999; } // This is majorly awkward + } + + // Multi-browser opacity setting + object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win + object.opacity = (opacity / 100); // Safari 1.2, Firefox+Mozilla +} + +// Utility: Math functions for animation calucations - From http://www.robertpenner.com/easing/ +// +// t = time, b = begin, c = change, d = duration +// time = current frame, begin is fixed, change is basically finish - begin, duration is fixed (frames), + +function linear(t, b, c, d) +{ + return c*t/d + b; +} + +function sineInOut(t, b, c, d) +{ + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; +} + +function cubicIn(t, b, c, d) { + return c*(t/=d)*t*t + b; +} + +function cubicOut(t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; +} + +function cubicInOut(t, b, c, d) +{ + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; +} + +function bounceOut(t, b, c, d) +{ + if ((t/=d) < (1/2.75)){ + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)){ + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)){ + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } +} + + +// Utility: Get the size of the window, and set myWidth and myHeight +// Credit to quirksmode.org +function getSize() { + + // Window Size + if (self.innerHeight) { // Everyone but IE + myWidth = window.innerWidth; + myHeight = window.innerHeight; + myScroll = window.pageYOffset; + } else if (document.documentElement && document.documentElement.clientHeight) { // IE6 Strict + myWidth = document.documentElement.clientWidth; + myHeight = document.documentElement.clientHeight; + myScroll = document.documentElement.scrollTop; + } else if (document.body) { // Other IE, such as IE7 + myWidth = document.body.clientWidth; + myHeight = document.body.clientHeight; + myScroll = document.body.scrollTop; + } + + // Page size w/offscreen areas + if (window.innerHeight && window.scrollMaxY) { + myScrollWidth = document.body.scrollWidth; + myScrollHeight = window.innerHeight + window.scrollMaxY; + } else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac + myScrollWidth = document.body.scrollWidth; + myScrollHeight = document.body.scrollHeight; + } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari + myScrollWidth = document.body.offsetWidth; + myScrollHeight = document.body.offsetHeight; + } +} + +// Utility: Get Shift Key Status +// IE events don't seem to get passed through the function, so grab it from the window. +function getShift(evt) { + var shift = false; + if (! evt && window.event) { + shift = window.event.shiftKey; + } else if (evt) { + shift = evt.shiftKey; + if (shift) evt.stopPropagation(); // Prevents Firefox from doing shifty things + } + return shift; +} diff --git a/videodb/javascript/prototype/prototype-old.js b/videodb/javascript/prototype/prototype-old.js new file mode 100644 index 0000000..37dd39a --- /dev/null +++ b/videodb/javascript/prototype/prototype-old.js @@ -0,0 +1,7036 @@ +/* Prototype JavaScript framework, version 1.7.1 + * (c) 2005-2010 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + + Version: '1.7.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + + SelectorsAPI: !!document.querySelector, + + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = (function(method) { + return function() { return method.valueOf.call(method); }; + })(method); + + value.toString = (function(method) { + return function() { return method.toString.call(method); }; + })(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString, + _hasOwnProperty = Object.prototype.hasOwnProperty, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]', + NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && + typeof JSON.stringify === 'function' && + JSON.stringify(0) === '0' && + typeof JSON.stringify(Prototype.K) === 'undefined'; + + + + var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', + 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key]; + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + var type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { + throw new TypeError("Cyclic reference to '" + value + "' in object"); + } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } + var results = []; + for (var property in object) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + + if (IS_DONTENUM_BUGGY) { + for (var i = 0; property = DONT_ENUMS[i]; i++) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + } + + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) + return this; + + if (!Object.isFunction(this)) + throw new TypeError("The object is not callable."); + + var nop = function() {}; + var __method = this, args = slice.call(arguments, 1); + + var bound = function() { + var a = merge(args, arguments), c = context; + var c = this instanceof bound ? this : context; + return __method.apply(c, a); + }; + + nop.prototype = this.prototype; + bound.prototype = new nop(); + + return bound; + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + var extensions = { + argumentNames: argumentNames, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + }; + + if (!Function.prototype.bind) + extensions.bind = bind; + + return extensions; +})()); + + + +(function(proto) { + + + function toISOString() { + return this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z'; + } + + + function toJSON() { + return this.toISOString(); + } + + if (!proto.toISOString) proto.toISOString = toISOString; + if (!proto.toJSON) proto.toJSON = toJSON; + +})(Date.prototype); + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, (function() { + var NATIVE_JSON_PARSE_SUPPORT = window.JSON && + typeof JSON.parse === 'function' && + JSON.parse('{"test": true}').test; + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + replacement; + } + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), + matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script); }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); + str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); + str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); + return (/^[\],:{}\s]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(), + cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + if (cx.test(json)) { + json = json.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function parseJSON() { + var json = this.unfilterJSON(); + return JSON.parse(json); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.lastIndexOf(pattern, 0) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.indexOf(pattern, d) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim || strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + try { + this._each(iterator, context); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index, this); + if (!result) throw $break; + }, this); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index, this)) + throw $break; + }, this); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) { + result = value; + throw $break; + } + }, this); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index, this); + }, this); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value >= result) + result = value; + }, this); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value < result) + result = value; + }, this); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index, this) ? + trues : falses).push(value); + }, this); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index, this) + }; + }, this).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); + +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator, context) { + for (var i = 0, length = this.length >>> 0; i < length; i++) { + if (i in this) iterator.call(context, this[i], i, this); + } + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline === false ? this.toArray() : this)._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.indexOf(item) !== -1; + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function indexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + + if (i > length) return -1; + + var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); + for (; k < length; k++) + if (k in array && array[k] === item) return k; + return -1; + } + + + function lastIndexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + if (!Object.isUndefined(i)) { + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + } else { + i = length; + } + + var k = i >= 0 ? Math.min(i, length - 1) : + length - Math.abs(i); + + for (; k >= 0; k--) + if (k in array && array[k] === item) return k; + return -1; + } + + function concat(_) { + var array = [], items = slice.call(arguments, 0), item, n = 0; + items.unshift(this); + for (var i = 0, length = items.length; i < length; i++) { + item = items[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { + if (j in item) array[n] = item[j]; + n++; + } + } else { + array[n++] = item; + } + } + array.length = n; + return array; + } + + + function wrapNative(method) { + return function() { + if (arguments.length === 0) { + return method.call(this, Prototype.K); + } else if (arguments[0] === undefined) { + var args = slice.call(arguments, 1); + args.unshift(Prototype.K); + return method.apply(this, args); + } else { + return method.apply(this, arguments); + } + }; + } + + + function map(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + + var object = Object(this); + var results = [], context = arguments[1], n = 0; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + results[n] = iterator.call(context, object[i], i, object); + } + n++; + } + results.length = n; + return results; + } + + if (arrayProto.map) { + map = wrapNative(Array.prototype.map); + } + + function filter(iterator) { + if (this == null || !Object.isFunction(iterator)) + throw new TypeError(); + + var object = Object(this); + var results = [], context = arguments[1], value; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + value = object[i]; + if (iterator.call(context, value, i, object)) { + results.push(value); + } + } + } + return results; + } + + if (arrayProto.filter) { + filter = Array.prototype.filter; + } + + function some(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && iterator.call(context, object[i], i, object)) { + return true; + } + } + + return false; + } + + if (arrayProto.some) { + var some = wrapNative(Array.prototype.some); + } + + + function every(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && !iterator.call(context, object[i], i, object)) { + return false; + } + } + + return true; + } + + if (arrayProto.every) { + var every = wrapNative(Array.prototype.every); + } + + var _reduce = arrayProto.reduce; + function inject(memo, iterator) { + iterator = iterator || Prototype.K; + var context = arguments[2]; + return _reduce.call(this, iterator.bind(context), memo); + } + + if (!arrayProto.reduce) { + var inject = Enumerable.inject; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + + map: map, + collect: map, + select: filter, + filter: filter, + findAll: filter, + some: some, + any: some, + every: every, + all: every, + inject: inject, + + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2); + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + + function _each(iterator, context) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator.call(context, pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + + var value = String.interpret(value); + + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return key + '=' + value; + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) { + var queryValues = []; + for (var i = 0, len = values.length, value; i < len; i++) { + value = values[i]; + queryValues.push(toQueryPair(key, value)); + } + return results.concat(queryValues); + } + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toObject, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator, context) { + var value = this.start; + while (this.include(value)) { + iterator.call(context, value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +var Abstract = { }; + + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator, context) { + this.responders._each(iterator, context); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + params += (params ? '&' : '') + "_method=" + this.method; + this.method = 'post'; + } + + if (params && this.method === 'get') { + this.url += (this.url.include('?') ? '&' : '?') + params; + } + + this.parameters = params.toQueryParams(); + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300) || status == 304; + }, + + getStatus: function() { + try { + if (this.transport.status === 1223) return 204; + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null; } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if (readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + + try { + json = decodeURIComponent(escape(json)); + } catch(e) { + } + + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +(function(GLOBAL) { + + var UNDEFINED; + var SLICE = Array.prototype.slice; + + var DIV = document.createElement('div'); + + + function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); + } + + GLOBAL.$ = $; + + + if (!GLOBAL.Node) GLOBAL.Node = {}; + + if (!GLOBAL.Node.ELEMENT_NODE) { + Object.extend(GLOBAL.Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); + } + + var ELEMENT_CACHE = {}; + + function shouldUseCreationCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } + + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement(''); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } + })(); + + + var oldElement = GLOBAL.Element; + function Element(tagName, attributes) { + attributes = attributes || {}; + tagName = tagName.toLowerCase(); + + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + + if (!ELEMENT_CACHE[tagName]) + ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); + + var node = shouldUseCreationCache(tagName, attributes) ? + ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); + } + + GLOBAL.Element = Element; + + Object.extend(GLOBAL.Element, oldElement || {}); + if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; + + Element.Methods = { ByTag: {}, Simulated: {} }; + + var methods = {}; + + var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; + function inspect(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + + var attribute, value; + for (var property in INSPECT_ATTRIBUTES) { + attribute = INSPECT_ATTRIBUTES[property]; + value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + } + + return result + '>'; + } + + methods.inspect = inspect; + + + function visible(element) { + return $(element).style.display !== 'none'; + } + + function toggle(element, bool) { + element = $(element); + if (Object.isUndefined(bool)) + bool = !Element.visible(element); + Element[bool ? 'show' : 'hide'](element); + + return element; + } + + function hide(element) { + element = $(element); + element.style.display = 'none'; + return element; + } + + function show(element) { + element = $(element); + element.style.display = ''; + return element; + } + + + Object.extend(methods, { + visible: visible, + toggle: toggle, + hide: hide, + show: show + }); + + + function remove(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + } + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = ""; + var isBuggy = (el.childNodes.length === 0); + el = null; + return isBuggy; + } catch(e) { + return true; + } + })(); + + var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || + TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (ANY_INNERHTML_BUGGY) { + if (tagName in INSERTION_TRANSLATIONS.tags) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, + content.stripScripts(), true); + + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + } else { + element.innerHTML = content.stripScripts(); + } + } else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + function replace(element, content) { + element = $(element); + + if (content && content.toElement) { + content = content.toElement(); + } else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + + element.parentNode.replaceChild(content, element); + return element; + } + + var INSERTION_TRANSLATIONS = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + + tags: { + TABLE: ['', '
      ', 1], + TBODY: ['', '
      ', 2], + TR: ['', '
      ', 3], + TD: ['
      ', '
      ', 4], + SELECT: ['', 1] + } + }; + + var tags = INSERTION_TRANSLATIONS.tags; + + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); + + function replace_IE(element, content) { + element = $(element); + if (content && content.toElement) + content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (tagName in INSERTION_TRANSLATIONS.tags) { + var nextSibling = Element.next(element); + var fragments = getContentFromAnonymousElement( + tagName, content.stripScripts()); + + parent.removeChild(element); + + var iterator; + if (nextSibling) + iterator = function(node) { parent.insertBefore(node, nextSibling) }; + else + iterator = function(node) { parent.appendChild(node); } + + fragments.each(iterator); + } else { + element.outerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + if ('outerHTML' in document.documentElement) + replace = replace_IE; + + function isContent(content) { + if (Object.isUndefined(content) || content === null) return false; + + if (Object.isString(content) || Object.isNumber(content)) return true; + if (Object.isElement(content)) return true; + if (content.toElement || content.toHTML) return true; + + return false; + } + + function insertContentAt(element, content, position) { + position = position.toLowerCase(); + var method = INSERTION_TRANSLATIONS[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + method(element, content); + return element; + } + + content = Object.toHTML(content); + var tagName = ((position === 'before' || position === 'after') ? + element.parentNode : element).tagName.toUpperCase(); + + var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position === 'top' || position === 'after') childNodes.reverse(); + + for (var i = 0, node; node = childNodes[i]; i++) + method(element, node); + + content.evalScripts.bind(content).defer(); + } + + function insert(element, insertions) { + element = $(element); + + if (isContent(insertions)) + insertions = { bottom: insertions }; + + for (var position in insertions) + insertContentAt(element, insertions[position], position); + + return element; + } + + function wrap(element, wrapper, attributes) { + element = $(element); + + if (Object.isElement(wrapper)) { + $(wrapper).writeAttribute(attributes || {}); + } else if (Object.isString(wrapper)) { + wrapper = new Element(wrapper, attributes); + } else { + wrapper = new Element('div', wrapper); + } + + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + + wrapper.appendChild(element); + + return wrapper; + } + + function cleanWhitespace(element) { + element = $(element); + var node = element.firstChild; + + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + } + + function empty(element) { + return $(element).innerHTML.blank(); + } + + function getContentFromAnonymousElement(tagName, html, force) { + var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; + + var workaround = !!t; + if (!workaround && force) { + workaround = true; + t = ['', '', 0]; + } + + if (workaround) { + div.innerHTML = ' ' + t[0] + html + t[1]; + div.removeChild(div.firstChild); + for (var i = t[2]; i--; ) + div = div.firstChild; + } else { + div.innerHTML = html; + } + + return $A(div.childNodes); + } + + function clone(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + if (!HAS_UNIQUE_ID_PROPERTY) { + clone._prototypeUID = UNDEFINED; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) + descendants[i]._prototypeUID = UNDEFINED; + } + } + return Element.extend(clone); + } + + function purgeElement(element) { + var uid = getUniqueElementID(element); + if (uid) { + Element.stopObserving(element); + if (!HAS_UNIQUE_ID_PROPERTY) + element._prototypeUID = UNDEFINED; + delete Element.Storage[uid]; + } + } + + function purgeCollection(elements) { + var i = elements.length; + while (i--) + purgeElement(elements[i]); + } + + function purgeCollection_IE(elements) { + var i = elements.length, element, uid; + while (i--) { + element = elements[i]; + uid = getUniqueElementID(element); + delete Element.Storage[uid]; + delete Event.cache[uid]; + } + } + + if (HAS_UNIQUE_ID_PROPERTY) { + purgeCollection = purgeCollection_IE; + } + + + function purge(element) { + if (!(element = $(element))) return; + purgeElement(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + + while (i--) purgeElement(descendants[i]); + + return null; + } + + Object.extend(methods, { + remove: remove, + update: update, + replace: replace, + insert: insert, + wrap: wrap, + cleanWhitespace: cleanWhitespace, + empty: empty, + clone: clone, + purge: purge + }); + + + + function recursivelyCollect(element, property, maximumLength) { + element = $(element); + maximumLength = maximumLength || -1; + var elements = []; + + while (element = element[property]) { + if (element.nodeType === Node.ELEMENT_NODE) + elements.push(Element.extend(element)); + + if (elements.length === maximumLength) break; + } + + return elements; + } + + + function ancestors(element) { + return recursivelyCollect(element, 'parentNode'); + } + + function descendants(element) { + return Element.select(element, '*'); + } + + function firstDescendant(element) { + element = $(element).firstChild; + while (element && element.nodeType !== Node.ELEMENT_NODE) + element = element.nextSibling; + + return $(element); + } + + function immediateDescendants(element) { + var results = [], child = $(element).firstChild; + + while (child) { + if (child.nodeType === Node.ELEMENT_NODE) + results.push(Element.extend(child)); + + child = child.nextSibling; + } + + return results; + } + + function previousSiblings(element) { + return recursivelyCollect(element, 'previousSibling'); + } + + function nextSiblings(element) { + return recursivelyCollect(element, 'nextSibling'); + } + + function siblings(element) { + element = $(element); + var previous = previousSiblings(element), + next = nextSiblings(element); + return previous.reverse().concat(next); + } + + function match(element, selector) { + element = $(element); + + if (Object.isString(selector)) + return Prototype.Selector.match(element, selector); + + return selector.match(element); + } + + + function _recursivelyFind(element, property, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + if (Object.isNumber(expression)) { + index = expression, expression = null; + } + + while (element = element[property]) { + if (element.nodeType !== 1) continue; + if (expression && !Prototype.Selector.match(element, expression)) + continue; + if (--index >= 0) continue; + + return Element.extend(element); + } + } + + + function up(element, expression, index) { + element = $(element); + + if (arguments.length === 1) return $(element.parentNode); + return _recursivelyFind(element, 'parentNode', expression, index); + } + + function down(element, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + + if (Object.isNumber(expression)) + index = expression, expression = '*'; + + var node = Prototype.Selector.select(expression, element)[index]; + return Element.extend(node); + } + + function previous(element, expression, index) { + return _recursivelyFind(element, 'previousSibling', expression, index); + } + + function next(element, expression, index) { + return _recursivelyFind(element, 'nextSibling', expression, index); + } + + function select(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); + } + + function adjacent(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + var siblings = Element.siblings(element), results = []; + for (var i = 0, sibling; sibling = siblings[i]; i++) { + if (Prototype.Selector.match(sibling, expressions)) + results.push(sibling); + } + + return results; + } + + function descendantOf_DOM(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element === ancestor) return true; + return false; + } + + function descendantOf_contains(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!ancestor.contains) return descendantOf_DOM(element, ancestor); + return ancestor.contains(element) && ancestor !== element; + } + + function descendantOf_compareDocumentPosition(element, ancestor) { + element = $(element), ancestor = $(ancestor); + return (element.compareDocumentPosition(ancestor) & 8) === 8; + } + + var descendantOf; + if (DIV.compareDocumentPosition) { + descendantOf = descendantOf_compareDocumentPosition; + } else if (DIV.contains) { + descendantOf = descendantOf_contains; + } else { + descendantOf = descendantOf_DOM; + } + + + Object.extend(methods, { + recursivelyCollect: recursivelyCollect, + ancestors: ancestors, + descendants: descendants, + firstDescendant: firstDescendant, + immediateDescendants: immediateDescendants, + previousSiblings: previousSiblings, + nextSiblings: nextSiblings, + siblings: siblings, + match: match, + up: up, + down: down, + previous: previous, + next: next, + select: select, + adjacent: adjacent, + descendantOf: descendantOf, + + getElementsBySelector: select, + + childElements: immediateDescendants + }); + + + var idCounter = 1; + function identify(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + + do { id = 'anonymous_element_' + idCounter++ } while ($(id)); + + Element.writeAttribute(element, 'id', id); + return id; + } + + + function readAttribute(element, name) { + return $(element).getAttribute(name); + } + + function readAttribute_IE(element, name) { + element = $(element); + + var table = ATTRIBUTE_TRANSLATIONS.read; + if (table.values[name]) + return table.values[name](element, name); + + if (table.names[name]) name = table.names[name]; + + if (name.include(':')) { + if (!element.attributes || !element.attributes[name]) return null; + return element.attributes[name].value; + } + + return element.getAttribute(name); + } + + function readAttribute_Opera(element, name) { + if (name === 'title') return element.title; + return element.getAttribute(name); + } + + var PROBLEMATIC_ATTRIBUTE_READING = (function() { + DIV.setAttribute('onclick', Prototype.emptyFunction); + var value = DIV.getAttribute('onclick'); + var isFunction = (typeof value === 'function'); + DIV.removeAttribute('onclick'); + return isFunction; + })(); + + if (PROBLEMATIC_ATTRIBUTE_READING) { + readAttribute = readAttribute_IE; + } else if (Prototype.Browser.Opera) { + readAttribute = readAttribute_Opera; + } + + + function writeAttribute(element, name, value) { + element = $(element); + var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; + + if (typeof name === 'object') { + attributes = name; + } else { + attributes[name] = Object.isUndefined(value) ? true : value; + } + + for (var attr in attributes) { + name = table.names[attr] || attr; + value = attributes[attr]; + if (table.values[attr]) + name = table.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + + return element; + } + + function hasAttribute(element, attribute) { + attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } + + GLOBAL.Element.Methods.Simulated.hasAttribute = hasAttribute; + + function classNames(element) { + return new Element.ClassNames(element); + } + + var regExpCache = {}; + function getRegExpForClassName(className) { + if (regExpCache[className]) return regExpCache[className]; + + var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); + regExpCache[className] = re; + return re; + } + + function hasClassName(element, className) { + if (!(element = $(element))) return; + + var elementClassName = element.className; + + if (elementClassName.length === 0) return false; + if (elementClassName === className) return true; + + return getRegExpForClassName(className).test(elementClassName); + } + + function addClassName(element, className) { + if (!(element = $(element))) return; + + if (!hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + + return element; + } + + function removeClassName(element, className) { + if (!(element = $(element))) return; + + element.className = element.className.replace( + getRegExpForClassName(className), ' ').strip(); + + return element; + } + + function toggleClassName(element, className, bool) { + if (!(element = $(element))) return; + + if (Object.isUndefined(bool)) + bool = !hasClassName(element, className); + + var method = Element[bool ? 'addClassName' : 'removeClassName']; + return method(element, className); + } + + var ATTRIBUTE_TRANSLATIONS = {}; + + var classProp = 'className', forProp = 'for'; + + DIV.setAttribute(classProp, 'x'); + if (DIV.className !== 'x') { + DIV.setAttribute('class', 'x'); + if (DIV.className === 'x') + classProp = 'class'; + } + + var LABEL = document.createElement('label'); + LABEL.setAttribute(forProp, 'x'); + if (LABEL.htmlFor !== 'x') { + LABEL.setAttribute('htmlFor', 'x'); + if (LABEL.htmlFor === 'x') + forProp = 'htmlFor'; + } + LABEL = null; + + function _getAttr(element, attribute) { + return element.getAttribute(attribute); + } + + function _getAttr2(element, attribute) { + return element.getAttribute(attribute, 2); + } + + function _getAttrNode(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ''; + } + + function _getFlag(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + } + + DIV.onclick = Prototype.emptyFunction; + var onclickValue = DIV.getAttribute('onclick'); + + var _getEv; + + if (String(onclickValue).indexOf('{') > -1) { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + value = value.toString(); + value = value.split('{')[1]; + value = value.split('}')[0]; + return value.strip(); + }; + } + else if (onclickValue === '') { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + return value.strip(); + }; + } + + ATTRIBUTE_TRANSLATIONS.read = { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + + values: { + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.write = { + names: { + className: 'class', + htmlFor: 'for', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, + + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.has = { names: {} }; + + Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, + ATTRIBUTE_TRANSLATIONS.read.names); + + var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + + 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); + + for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { + ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; + ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; + } + + Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { + href: _getAttr2, + src: _getAttr2, + type: _getAttr, + action: _getAttrNode, + disabled: _getFlag, + checked: _getFlag, + readonly: _getFlag, + multiple: _getFlag, + onload: _getEv, + onunload: _getEv, + onclick: _getEv, + ondblclick: _getEv, + onmousedown: _getEv, + onmouseup: _getEv, + onmouseover: _getEv, + onmousemove: _getEv, + onmouseout: _getEv, + onfocus: _getEv, + onblur: _getEv, + onkeypress: _getEv, + onkeydown: _getEv, + onkeyup: _getEv, + onsubmit: _getEv, + onreset: _getEv, + onselect: _getEv, + onchange: _getEv + }); + + + Object.extend(methods, { + identify: identify, + readAttribute: readAttribute, + writeAttribute: writeAttribute, + classNames: classNames, + hasClassName: hasClassName, + addClassName: addClassName, + removeClassName: removeClassName, + toggleClassName: toggleClassName + }); + + + function normalizeStyleName(style) { + if (style === 'float' || style === 'styleFloat') + return 'cssFloat'; + return style.camelize(); + } + + function normalizeStyleName_IE(style) { + if (style === 'float' || style === 'cssFloat') + return 'styleFloat'; + return style.camelize(); + } + + function setStyle(element, styles) { + element = $(element); + var elementStyle = element.style, match; + + if (Object.isString(styles)) { + elementStyle.cssText += ';' + styles; + if (styles.include('opacity')) { + var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; + Element.setOpacity(element, opacity); + } + return element; + } + + for (var property in styles) { + if (property === 'opacity') { + Element.setOpacity(element, styles[property]); + } else { + var value = styles[property]; + if (property === 'float' || property === 'cssFloat') { + property = Object.isUndefined(elementStyle.styleFloat) ? + 'cssFloat' : 'styleFloat'; + } + elementStyle[property] = value; + } + } + + return element; + } + + + function getStyle(element, style) { + element = $(element); + style = normalizeStyleName(style); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getStyle_Opera(element, style) { + switch (style) { + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(getStyle(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + return Element.measure(element, style); + + default: return getStyle(element, style); + } + } + + function getStyle_IE(element, style) { + element = $(element); + style = normalizeStyleName_IE(style); + + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + + if (style === 'opacity' && !STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity_IE(element); + + if (value === 'auto') { + if ((style === 'width' || style === 'height') && Element.visible(element)) + return Element.measure(element, style) + 'px'; + return null; + } + + return value; + } + + function stripAlphaFromFilter_IE(filter) { + return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); + } + + function hasLayout_IE(element) { + if (!element.currentStyle.hasLayout) + element.style.zoom = 1; + return element; + } + + var STANDARD_CSS_OPACITY_SUPPORTED = (function() { + DIV.style.cssText = "opacity:.55"; + return /^0.55/.test(DIV.style.opacity); + })(); + + function setOpacity(element, value) { + element = $(element); + if (value == 1 || value === '') value = ''; + else if (value < 0.00001) value = 0; + element.style.opacity = value; + return element; + } + + function setOpacity_IE(element, value) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return setOpacity(element, value); + + element = hasLayout_IE($(element)); + var filter = Element.getStyle(element, 'filter'), + style = element.style; + + if (value == 1 || value === '') { + filter = stripAlphaFromFilter_IE(filter); + if (filter) style.filter = filter; + else style.removeAttribute('filter'); + return element; + } + + if (value < 0.00001) value = 0; + + style.filter = stripAlphaFromFilter_IE(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + + return element; + } + + + function getOpacity(element) { + return Element.getStyle(element, 'opacity'); + } + + function getOpacity_IE(element) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity(element); + + var filter = Element.getStyle(element, 'filter'); + if (filter.length === 0) return 1.0; + var match = (filter || '').match(/alpha\(opacity=(.*)\)/); + if (match[1]) return parseFloat(match[1]) / 100; + return 1.0; + } + + + Object.extend(methods, { + setStyle: setStyle, + getStyle: getStyle, + setOpacity: setOpacity, + getOpacity: getOpacity + }); + + if ('styleFloat' in DIV.style) { + methods.getStyle = getStyle_IE; + methods.setOpacity = setOpacity_IE; + methods.getOpacity = getOpacity_IE; + } + + var UID = 0; + + GLOBAL.Element.Storage = { UID: 1 }; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); + if (HAS_UNIQUE_ID_PROPERTY) + getUniqueElementID = getUniqueElementID_IE; + + function getStorage(element) { + if (!(element = $(element))) return; + + var uid = getUniqueElementID(element); + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + } + + function store(element, key, value) { + if (!(element = $(element))) return; + var storage = getStorage(element); + if (arguments.length === 2) { + storage.update(key); + } else { + storage.set(key, value); + } + return element; + } + + function retrieve(element, key, defaultValue) { + if (!(element = $(element))) return; + var storage = getStorage(element), value = storage.get(key); + + if (Object.isUndefined(value)) { + storage.set(key, defaultValue); + value = defaultValue; + } + + return value; + } + + + Object.extend(methods, { + getStorage: getStorage, + store: store, + retrieve: retrieve + }); + + + var Methods = {}, ByTag = Element.Methods.ByTag, + F = Prototype.BrowserFeatures; + + if (!F.ElementExtensions && ('__proto__' in DIV)) { + GLOBAL.HTMLElement = {}; + GLOBAL.HTMLElement.prototype = DIV['__proto__']; + F.ElementExtensions = true; + } + + function checkElementPrototypeDeficiency(tagName) { + if (typeof window.Element === 'undefined') return false; + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random() + '').slice(2), + el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + + return false; + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = + checkElementPrototypeDeficiency('object'); + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var EXTENDED = {}; + function elementIsExtended(element) { + var uid = getUniqueElementID(element); + return (uid in EXTENDED); + } + + function extend(element) { + if (!element || elementIsExtended(element)) return element; + if (element.nodeType !== Node.ELEMENT_NODE || element == window) + return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + EXTENDED[getUniqueElementID(element)] = true; + return element; + } + + function extend_IE8(element) { + if (!element || elementIsExtended(element)) return element; + + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + + return element; + } + + if (F.SpecificElementExtensions) { + extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; + } + + function addMethodsToTagName(tagName, methods) { + tagName = tagName.toUpperCase(); + if (!ByTag[tagName]) ByTag[tagName] = {}; + Object.extend(ByTag[tagName], methods); + } + + function mergeMethods(destination, methods, onlyIfAbsent) { + if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + + element = null; + return proto; + } + + function addMethods(methods) { + if (arguments.length === 0) addFormMethods(); + + if (arguments.length === 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) { + Object.extend(Element.Methods, methods || {}); + } else { + if (Object.isArray(tagName)) { + for (var i = 0, tag; tag = tagName[i]; i++) + addMethodsToTagName(tag, methods); + } else { + addMethodsToTagName(tagName, methods); + } + } + + var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + mergeMethods(klass.prototype, ByTag[tag]); + } + } + + Object.extend(Element, Element.Methods); + Object.extend(Element, Element.Methods.Simulated); + delete Element.ByTag; + delete Element.Simulated; + + Element.extend.refresh(); + + ELEMENT_CACHE = {}; + } + + Object.extend(GLOBAL.Element, { + extend: extend, + addMethods: addMethods + }); + + if (extend === Prototype.K) { + GLOBAL.Element.extend.refresh = Prototype.emptyFunction; + } else { + GLOBAL.Element.extend.refresh = function() { + if (Prototype.BrowserFeatures.ElementExtensions) return; + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + + EXTENDED = {}; + }; + } + + function addFormMethods() { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) + }); + } + + Element.addMethods(methods); + +})(this); +(function() { + + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } + + function getRawStyle(element, style) { + element = $(element); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getRawStyle_IE(element, style) { + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + return value; + } + + function getContentWidth(element, context) { + var boxWidth = element.offsetWidth; + + var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; + var br = getPixelValue(element, 'borderRightWidth', context) || 0; + var pl = getPixelValue(element, 'paddingLeft', context) || 0; + var pr = getPixelValue(element, 'paddingRight', context) || 0; + + return boxWidth - bl - br - pl - pr; + } + + if ('currentStyle' in document.documentElement) { + getRawStyle = getRawStyle_IE; + } + + + function getPixelValue(value, property, context) { + var element = null; + if (Object.isElement(value)) { + element = value; + value = getRawStyle(element, property); + } + + if (value === null || Object.isUndefined(value)) { + return null; + } + + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } + + var isPercentage = value.include('%'), isViewport = (context === document.viewport); + + if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { + var style = element.style.left, rStyle = element.runtimeStyle.left; + element.runtimeStyle.left = element.currentStyle.left; + element.style.left = value || 0; + value = element.style.pixelLeft; + element.style.left = style; + element.runtimeStyle.left = rStyle; + + return value; + } + + if (element && isPercentage) { + context = context || element.parentNode; + var decimal = toDecimal(value), whole = null; + + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); + + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); + + if (context === document.viewport) { + if (isHorizontal) { + whole = document.viewport.getWidth(); + } else if (isVertical) { + whole = document.viewport.getHeight(); + } + } else { + if (isHorizontal) { + whole = $(context).measure('width'); + } else if (isVertical) { + whole = $(context).measure('height'); + } + } + + return (whole === null) ? 0 : whole * decimal; + } + + return 0; + } + + function toCSSPixels(number) { + if (Object.isString(number) && number.endsWith('px')) + return number; + return number + 'px'; + } + + function isDisplayed(element) { + while (element && element.parentNode) { + var display = element.getStyle('display'); + if (display === 'none') { + return false; + } + element = $(element.parentNode); + } + return true; + } + + var hasLayout = Prototype.K; + if ('currentStyle' in document.documentElement) { + hasLayout = function(element) { + if (!element.currentStyle.hasLayout) { + element.style.zoom = 1; + } + return element; + }; + } + + function cssNameFor(key) { + if (key.include('border')) key = key + '-width'; + return key.camelize(); + } + + Element.Layout = Class.create(Hash, { + initialize: function($super, element, preCompute) { + $super(); + this.element = $(element); + + Element.Layout.PROPERTIES.each( function(property) { + this._set(property, null); + }, this); + + if (preCompute) { + this._preComputing = true; + this._begin(); + Element.Layout.PROPERTIES.each( this._compute, this ); + this._end(); + this._preComputing = false; + } + }, + + _set: function(property, value) { + return Hash.prototype.set.call(this, property, value); + }, + + set: function(property, value) { + throw "Properties of Element.Layout are read-only."; + }, + + get: function($super, property) { + var value = $super(property); + return value === null ? this._compute(property) : value; + }, + + _begin: function() { + if (this._isPrepared()) return; + + var element = this.element; + if (isDisplayed(element)) { + this._setPrepared(true); + return; + } + + + var originalStyles = { + position: element.style.position || '', + width: element.style.width || '', + visibility: element.style.visibility || '', + display: element.style.display || '' + }; + + element.store('prototype_original_styles', originalStyles); + + var position = getRawStyle(element, 'position'), width = element.offsetWidth; + + if (width === 0 || width === null) { + element.style.display = 'block'; + width = element.offsetWidth; + } + + var context = (position === 'fixed') ? document.viewport : + element.parentNode; + + var tempStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (position !== 'fixed') tempStyles.position = 'absolute'; + + element.setStyle(tempStyles); + + var positionedWidth = element.offsetWidth, newWidth; + if (width && (positionedWidth === width)) { + newWidth = getContentWidth(element, context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getContentWidth(element, context); + } else { + var parent = element.parentNode, pLayout = $(parent).getLayout(); + + newWidth = pLayout.get('width') - + this.get('margin-left') - + this.get('border-left') - + this.get('padding-left') - + this.get('padding-right') - + this.get('border-right') - + this.get('margin-right'); + } + + element.setStyle({ width: newWidth + 'px' }); + + this._setPrepared(true); + }, + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._setPrepared(false); + }, + + _compute: function(property) { + var COMPUTATIONS = Element.Layout.COMPUTATIONS; + if (!(property in COMPUTATIONS)) { + throw "Property not found."; + } + + return this._set(property, COMPUTATIONS[property].call(this, this.element)); + }, + + _isPrepared: function() { + return this.element.retrieve('prototype_element_layout_prepared', false); + }, + + _setPrepared: function(bool) { + return this.element.store('prototype_element_layout_prepared', bool); + }, + + toObject: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var obj = {}; + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + var value = this.get(key); + if (value != null) obj[key] = value; + }, this); + return obj; + }, + + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); + }, + + toCSS: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var css = {}; + + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; + + var value = this.get(key); + if (value != null) css[cssNameFor(key)] = value + 'px'; + }, this); + return css; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Element.Layout, { + PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), + + COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), + + COMPUTATIONS: { + 'height': function(element) { + if (!this._preComputing) this._begin(); + + var bHeight = this.get('border-box-height'); + if (bHeight <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bTop = this.get('border-top'), + bBottom = this.get('border-bottom'); + + var pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + if (!this._preComputing) this._end(); + + return bHeight - bTop - bBottom - pTop - pBottom; + }, + + 'width': function(element) { + if (!this._preComputing) this._begin(); + + var bWidth = this.get('border-box-width'); + if (bWidth <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bLeft = this.get('border-left'), + bRight = this.get('border-right'); + + var pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + if (!this._preComputing) this._end(); + return bWidth - bLeft - bRight - pLeft - pRight; + }, + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; + }, + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; + }, + + 'border-box-height': function(element) { + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; + }, + + 'border-box-width': function(element) { + if (!this._preComputing) this._begin(); + var width = element.offsetWidth; + if (!this._preComputing) this._end(); + return width; + }, + + 'margin-box-height': function(element) { + var bHeight = this.get('border-box-height'), + mTop = this.get('margin-top'), + mBottom = this.get('margin-bottom'); + + if (bHeight <= 0) return 0; + + return bHeight + mTop + mBottom; + }, + + 'margin-box-width': function(element) { + var bWidth = this.get('border-box-width'), + mLeft = this.get('margin-left'), + mRight = this.get('margin-right'); + + if (bWidth <= 0) return 0; + + return bWidth + mLeft + mRight; + }, + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; + }, + + 'bottom': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pHeight = parent.measure('height'); + + var mHeight = this.get('border-box-height'); + + return pHeight - mHeight - offset.top; + }, + + 'left': function(element) { + var offset = element.positionedOffset(); + return offset.left; + }, + + 'right': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pWidth = parent.measure('width'); + + var mWidth = this.get('border-box-width'); + + return pWidth - mWidth - offset.left; + }, + + 'padding-top': function(element) { + return getPixelValue(element, 'paddingTop'); + }, + + 'padding-bottom': function(element) { + return getPixelValue(element, 'paddingBottom'); + }, + + 'padding-left': function(element) { + return getPixelValue(element, 'paddingLeft'); + }, + + 'padding-right': function(element) { + return getPixelValue(element, 'paddingRight'); + }, + + 'border-top': function(element) { + return getPixelValue(element, 'borderTopWidth'); + }, + + 'border-bottom': function(element) { + return getPixelValue(element, 'borderBottomWidth'); + }, + + 'border-left': function(element) { + return getPixelValue(element, 'borderLeftWidth'); + }, + + 'border-right': function(element) { + return getPixelValue(element, 'borderRightWidth'); + }, + + 'margin-top': function(element) { + return getPixelValue(element, 'marginTop'); + }, + + 'margin-bottom': function(element) { + return getPixelValue(element, 'marginBottom'); + }, + + 'margin-left': function(element) { + return getPixelValue(element, 'marginLeft'); + }, + + 'margin-right': function(element) { + return getPixelValue(element, 'marginRight'); + } + } + }); + + if ('getBoundingClientRect' in document.documentElement) { + Object.extend(Element.Layout.COMPUTATIONS, { + 'right': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.right - rect.right).round(); + }, + + 'bottom': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.bottom - rect.bottom).round(); + } + }); + } + + Element.Offset = Class.create({ + initialize: function(left, top) { + this.left = left.round(); + this.top = top.round(); + + this[0] = this.left; + this[1] = this.top; + }, + + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); + }, + + inspect: function() { + return "#".interpolate(this); + }, + + toString: function() { + return "[#{left}, #{top}]".interpolate(this); + }, + + toArray: function() { + return [this.left, this.top]; + } + }); + + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } + + function measure(element, property) { + return $(element).getLayout().get(property); + } + + function getHeight(element) { + return Element.getDimensions(element).height; + } + + function getWidth(element) { + return Element.getDimensions(element).width; + } + + function getDimensions(element) { + element = $(element); + var display = Element.getStyle(element, 'display'); + + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } + + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; + + var newStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; + + Element.setStyle(element, newStyles); + + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; + + Element.setStyle(element, originalStyles); + + return dimensions; + } + + function getOffsetParent(element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var isInline = (Element.getStyle(element, 'display') === 'inline'); + if (!isInline && element.offsetParent) return $(element.offsetParent); + + while ((element = element.parentNode) && element !== document.body) { + if (Element.getStyle(element, 'position') !== 'static') { + return isHtml(element) ? $(document.body) : $(element); + } + } + + return $(document.body); + } + + + function cumulativeOffset(element) { + element = $(element); + var valueT = 0, valueL = 0; + if (element.parentNode) { + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + } + return new Element.Offset(valueL, valueT); + } + + function positionedOffset(element) { + element = $(element); + + var layout = element.getLayout(); + + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (isBody(element)) break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + + valueL -= layout.get('margin-top'); + valueT -= layout.get('margin-left'); + + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return new Element.Offset(valueL, valueT); + } + + function viewportOffset(forElement) { + var valueT = 0, valueL = 0, docBody = document.body; + + var element = $(forElement); + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == docBody && + Element.getStyle(element, 'position') == 'absolute') break; + } while (element = element.offsetParent); + + element = forElement; + do { + if (element != docBody) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } + + function absolutize(element) { + element = $(element); + + if (Element.getStyle(element, 'position') === 'absolute') { + return element; + } + + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); + + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); + + element.store('prototype_absolutize_original_styles', { + left: element.getStyle('left'), + top: element.getStyle('top'), + width: element.getStyle('width'), + height: element.getStyle('height') + }); + + element.setStyle({ + position: 'absolute', + top: offset.top + 'px', + left: offset.left + 'px', + width: layout.get('width') + 'px', + height: layout.get('height') + 'px' + }); + + return element; + } + + function relativize(element) { + element = $(element); + if (Element.getStyle(element, 'position') === 'relative') { + return element; + } + + var originalStyles = + element.retrieve('prototype_absolutize_original_styles'); + + if (originalStyles) element.setStyle(originalStyles); + return element; + } + + + function scrollTo(element) { + element = $(element); + var pos = Element.cumulativeOffset(element); + window.scrollTo(pos.left, pos.top); + return element; + } + + + function makePositioned(element) { + element = $(element); + var position = Element.getStyle(element, 'position'), styles = {}; + if (position === 'static' || !position) { + styles.position = 'relative'; + if (Prototype.Browser.Opera) { + styles.top = 0; + styles.left = 0; + } + Element.setStyle(element, styles); + Element.store(element, 'prototype_made_positioned', true); + } + return element; + } + + function undoPositioned(element) { + element = $(element); + var storage = Element.getStorage(element), + madePositioned = storage.get('prototype_made_positioned'); + + if (madePositioned) { + storage.unset('prototype_made_positioned'); + Element.setStyle(element, { + position: '', + top: '', + bottom: '', + left: '', + right: '' + }); + } + return element; + } + + function makeClipping(element) { + element = $(element); + + var storage = Element.getStorage(element), + madeClipping = storage.get('prototype_made_clipping'); + + if (Object.isUndefined(madeClipping)) { + var overflow = Element.getStyle(element, 'overflow'); + storage.set('prototype_made_clipping', overflow); + if (overflow !== 'hidden') + element.style.overflow = 'hidden'; + } + + return element; + } + + function undoClipping(element) { + element = $(element); + var storage = Element.getStorage(element), + overflow = storage.get('prototype_made_clipping'); + + if (!Object.isUndefined(overflow)) { + storage.unset('prototype_made_clipping'); + element.style.overflow = overflow || ''; + } + + return element; + } + + function clonePosition(element, source, options) { + options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, options || {}); + + source = $(source); + element = $(element); + var p, delta, layout, styles = {}; + + if (options.setLeft || options.setTop) { + p = Element.viewportOffset(source); + delta = [0, 0]; + if (Element.getStyle(element, 'position') === 'absolute') { + var parent = Element.getOffsetParent(element); + if (parent !== document.body) delta = Element.viewportOffset(parent); + } + } + + if (options.setWidth || options.setHeight) { + layout = Element.getLayout(source); + } + + if (options.setLeft) + styles.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) + styles.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + + if (options.setWidth) + styles.width = layout.get('border-box-width') + 'px'; + if (options.setHeight) + styles.height = layout.get('border-box-height') + 'px'; + + return Element.setStyle(element, styles); + } + + + if (Prototype.Browser.IE) { + getOffsetParent = getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + positionedOffset = positionedOffset.wrap(function(proceed, element) { + element = $(element); + if (!element.parentNode) return new Element.Offset(0, 0); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + }); + } else if (Prototype.Browser.Webkit) { + cumulativeOffset = function(element) { + element = $(element); + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) { + if (Element.getStyle(element, 'position') == 'absolute') break; + } + + element = element.offsetParent; + } while (element); + + return new Element.Offset(valueL, valueT); + }; + } + + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getWidth: getWidth, + getHeight: getHeight, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize, + scrollTo: scrollTo, + makePositioned: makePositioned, + undoPositioned: undoPositioned, + makeClipping: makeClipping, + undoClipping: undoClipping, + clonePosition: clonePosition + }); + + function isBody(element) { + return element.nodeName.toUpperCase() === 'BODY'; + } + + function isHtml(element) { + return element.nodeName.toUpperCase() === 'HTML'; + } + + function isDocument(element) { + return element.nodeType === Node.DOCUMENT_NODE; + } + + function isDetached(element) { + return element !== document.body && + !Element.descendantOf(element, document.body); + } + + if ('getBoundingClientRect' in document.documentElement) { + Element.addMethods({ + viewportOffset: function(element) { + element = $(element); + if (isDetached(element)) return new Element.Offset(0, 0); + + var rect = element.getBoundingClientRect(), + docEl = document.documentElement; + return new Element.Offset(rect.left - docEl.clientLeft, + rect.top - docEl.clientTop); + } + }); + } + + +})(); + +(function() { + + var IS_OLD_OPERA = Prototype.Browser.Opera && + (window.parseFloat(window.opera.version()) < 9.5); + var ROOT = null; + function getRootElement() { + if (ROOT) return ROOT; + ROOT = IS_OLD_OPERA ? document.body : document.documentElement; + return ROOT; + } + + function getDimensions() { + return { width: this.getWidth(), height: this.getHeight() }; + } + + function getWidth() { + return getRootElement().clientWidth; + } + + function getHeight() { + return getRootElement().clientHeight; + } + + function getScrollOffsets() { + var x = window.pageXOffset || document.documentElement.scrollLeft || + document.body.scrollLeft; + var y = window.pageYOffset || document.documentElement.scrollTop || + document.body.scrollTop; + + return new Element.Offset(x, y); + } + + document.viewport = { + getDimensions: getDimensions, + getWidth: getWidth, + getHeight: getHeight, + getScrollOffsets: getScrollOffsets + }; + +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; + +Prototype.Selector = (function() { + + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } + + function match() { + throw new Error('Method "Prototype.Selector.match" must be defined.'); + } + + function find(elements, expression, index) { + index = index || 0; + var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; + + for (i = 0; i < length; i++) { + if (match(elements[i], expression) && index == matchIndex++) { + return Element.extend(elements[i]); + } + } + } + + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); + } + return elements; + } + + + var K = Prototype.K; + + return { + select: select, + match: match, + find: find, + extendElements: (Element.extend === K) ? K : extendElements, + extendElement: Element.extend + }; +})(); +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + if ( aup === bup ) { + return siblingCheck( a, b ); + + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +(function(){ + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = "
      "; + + root.insertBefore( form, root.firstChild ); + + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + root = form = null; +})(); + +(function(){ + + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

      "; + + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + if ( !seed && !Sizzle.isXML(context) ) { + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + if ( elem && elem.parentNode ) { + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + if ( ret || !disconnectedMatch || + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
      "; + + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + + +window.Sizzle = Sizzle; + +})(); + +Prototype._original_property = window.Sizzle; + +;(function(engine) { + var extendElements = Prototype.Selector.extendElements; + + function select(selector, scope) { + return extendElements(engine(selector, scope || document)); + } + + function match(element, selector) { + return engine.matches(selector, [element]).length == 1; + } + + Prototype.Selector.engine = engine; + Prototype.Selector.select = select; + Prototype.Selector.match = match; +})(Sizzle); + +window.Sizzle = Prototype._original_property; +delete Prototype._original_property; + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit, accumulator, initial; + + if (options.hash) { + initial = {}; + accumulator = function(result, key, value) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, value) { + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; + } + } + + return elements.inject(initial, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + result = accumulator(result, key, value); + } + } + return result; + }); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'); + var element, results = [], serializers = Form.Element.Serializers; + + for (var i = 0; element = elements[i]; i++) { + if (serializers[element.tagName.toLowerCase()]) + results.push(Element.extend(element)); + } + return results; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + var element = form.findFirstElement(); + if (element) element.activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !(/^(?:button|reset|submit)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; + +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = (function() { + function input(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return inputSelector(element, value); + default: + return valueSelector(element, value); + } + } + + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; + else element.checked = !!value; + } + + function valueSelector(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + } + + function select(element, value) { + if (Object.isUndefined(value)) + return (element.type === 'select-one' ? selectOne : selectMany)(element); + + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + + function selectOne(element) { + var index = element.selectedIndex; + return index >= 0 ? optionValue(element.options[index]) : null; + } + + function selectMany(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(optionValue(opt)); + } + return values; + } + + function optionValue(opt) { + return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; + } + + return { + input: input, + inputSelector: inputSelector, + textarea: valueSelector, + select: select, + selectOne: selectOne, + selectMany: selectMany, + optionValue: optionValue, + button: valueSelector + }; +})(); + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function(GLOBAL) { + var DIV = document.createElement('div'); + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var Event = { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45 + }; + + + var isIELegacyEvent = function(event) { return false; }; + + if (window.attachEvent) { + if (window.addEventListener) { + isIELegacyEvent = function(event) { + return !(event instanceof window.Event); + }; + } else { + isIELegacyEvent = function(event) { return true; }; + } + } + + var _isButton; + + function _isButtonForDOMEvents(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + } + + var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; + function _isButtonForLegacyEvents(event, code) { + return event.button === legacyButtonMap[code]; + } + + function _isButtonForWebKit(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 2 || (event.which == 1 && event.metaKey); + case 2: return event.which == 3; + default: return false; + } + } + + if (window.attachEvent) { + if (!window.addEventListener) { + _isButton = _isButtonForLegacyEvents; + } else { + _isButton = function(event, code) { + return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : + _isButtonForDOMEvents(event, code); + } + } + } else if (Prototype.Browser.WebKit) { + _isButton = _isButtonForWebKit; + } else { + _isButton = _isButtonForDOMEvents; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + return Element.extend(_element(event)); + } + + function _element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + if (node.nodeType == Node.TEXT_NODE) + node = node.parentNode; + + return Element.extend(node); + } + + function findElement(event, expression) { + var element = _element(event), match = Prototype.Selector.match; + if (!expression) return Element.extend(element); + while (element) { + if (Object.isElement(element) && match(element, expression)) + return Element.extend(element); + element = element.parentNode; + } + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (window.attachEvent) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; + } + return Element.extend(element); + } + + var additionalMethods = { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }; + + Event.extend = function(event, element) { + if (!event) return false; + + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; + event._extendedByPrototype = Prototype.emptyFunction; + + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + Object.extend(event, methods); + Object.extend(event, additionalMethods); + + return event; + }; + } else { + Event.extend = Prototype.K; + } + + if (window.addEventListener) { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + } + + var EVENT_TRANSLATIONS = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + + function getDOMEventName(eventName) { + return EVENT_TRANSLATIONS[eventName] || eventName; + } + + if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) + getDOMEventName = Prototype.K; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + if ('uniqueID' in DIV) + getUniqueElementID = getUniqueElementID_IE; + + function isCustomEvent(eventName) { + return eventName.include(':'); + } + + Event._isCustomEvent = isCustomEvent; + + function getRegistryForElement(element, uid) { + var CACHE = GLOBAL.Event.cache; + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + if (!CACHE[uid]) CACHE[uid] = { element: element }; + return CACHE[uid]; + } + + function destroyRegistryForElement(element, uid) { + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + delete GLOBAL.Event.cache[uid]; + } + + + function register(element, eventName, handler) { + var registry = getRegistryForElement(element); + if (!registry[eventName]) registry[eventName] = []; + var entries = registry[eventName]; + + var i = entries.length; + while (i--) + if (entries[i].handler === handler) return null; + + var uid = getUniqueElementID(element); + var responder = GLOBAL.Event._createResponder(uid, eventName, handler); + var entry = { + responder: responder, + handler: handler + }; + + entries.push(entry); + return entry; + } + + function unregister(element, eventName, handler) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + + var i = entries.length, entry; + while (i--) { + if (entries[i].handler === handler) { + entry = entries[i]; + break; + } + } + + if (!entry) return; + + var index = entries.indexOf(entry); + entries.splice(index, 1); + + return entry; + } + + + function observe(element, eventName, handler) { + element = $(element); + var entry = register(element, eventName, handler); + + if (entry === null) return element; + + var responder = entry.responder; + if (isCustomEvent(eventName)) + observeCustomEvent(element, eventName, responder); + else + observeStandardEvent(element, eventName, responder); + + return element; + } + + function observeStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.addEventListener) { + element.addEventListener(actualEventName, responder, false); + } else { + element.attachEvent('on' + actualEventName, responder); + } + } + + function observeCustomEvent(element, eventName, responder) { + if (element.addEventListener) { + element.addEventListener('dataavailable', responder, false); + } else { + element.attachEvent('ondataavailable', responder); + element.attachEvent('onlosecapture', responder); + } + } + + function stopObserving(element, eventName, handler) { + element = $(element); + var handlerGiven = !Object.isUndefined(handler), + eventNameGiven = !Object.isUndefined(eventName); + + if (!eventNameGiven && !handlerGiven) { + stopObservingElement(element); + return element; + } + + if (!handlerGiven) { + stopObservingEventName(element, eventName); + return element; + } + + var entry = unregister(element, eventName, handler); + + if (!entry) return element; + removeEvent(element, eventName, entry.responder); + return element; + } + + function stopObservingStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.removeEventListener) { + element.removeEventListener(actualEventName, responder, false); + } else { + element.detachEvent('on' + actualEventName, responder); + } + } + + function stopObservingCustomEvent(element, eventName, responder) { + if (element.removeEventListener) { + element.removeEventListener('dataavailable', responder, false); + } else { + element.detachEvent('ondataavailable', responder); + element.detachEvent('onlosecapture', responder); + } + } + + + + function stopObservingElement(element) { + var uid = getUniqueElementID(element), + registry = getRegistryForElement(element, uid); + + destroyRegistryForElement(element, uid); + + var entries, i; + for (var eventName in registry) { + if (eventName === 'element') continue; + + entries = registry[eventName]; + i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + } + + function stopObservingEventName(element, eventName) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + delete registry[eventName]; + + var i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + + + function removeEvent(element, eventName, handler) { + if (isCustomEvent(eventName)) + stopObservingCustomEvent(element, eventName, handler); + else + stopObservingStandardEvent(element, eventName, handler); + } + + + + function getFireTarget(element) { + if (element !== document) return element; + if (document.createEvent && !element.dispatchEvent) + return document.documentElement; + return element; + } + + function fire(element, eventName, memo, bubble) { + element = getFireTarget($(element)); + if (Object.isUndefined(bubble)) bubble = true; + memo = memo || {}; + + var event = fireEvent(element, eventName, memo, bubble); + return Event.extend(event); + } + + function fireEvent_DOM(element, eventName, memo, bubble) { + var event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', bubble, true); + + event.eventName = eventName; + event.memo = memo; + + element.dispatchEvent(event); + return event; + } + + function fireEvent_IE(element, eventName, memo, bubble) { + var event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; + + event.eventName = eventName; + event.memo = memo; + + element.fireEvent(event.eventType, event); + return event; + } + + var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; + + + + Event.Handler = Class.create({ + initialize: function(element, eventName, selector, callback) { + this.element = $(element); + this.eventName = eventName; + this.selector = selector; + this.callback = callback; + this.handler = this.handleEvent.bind(this); + }, + + + start: function() { + Event.observe(this.element, this.eventName, this.handler); + return this; + }, + + stop: function() { + Event.stopObserving(this.element, this.eventName, this.handler); + return this; + }, + + handleEvent: function(event) { + var element = Event.findElement(event, this.selector); + if (element) this.callback.call(this.element, event, element); + } + }); + + function on(element, eventName, selector, callback) { + element = $(element); + if (Object.isFunction(selector) && Object.isUndefined(callback)) { + callback = selector, selector = null; + } + + return new Event.Handler(element, eventName, selector, callback).start(); + } + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving, + on: on + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving, + + on: on + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + on: on.methodize(), + + loaded: false + }); + + if (GLOBAL.Event) Object.extend(window.Event, Event); + else GLOBAL.Event = Event; + + GLOBAL.Event.cache = {}; + + function destroyCache_IE() { + GLOBAL.Event.cache = null; + } + + if (window.attachEvent) + window.attachEvent('onunload', destroyCache_IE); + + DIV = null; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Code for creating leak-free event responders is based on work by + John-David Dalton. */ + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + function isSimulatedMouseEnterLeaveEvent(eventName) { + return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === 'mouseenter' || eventName === 'mouseleave'); + } + + function createResponder(uid, eventName, handler) { + if (Event._isCustomEvent(eventName)) + return createResponderForCustomEvent(uid, eventName, handler); + if (isSimulatedMouseEnterLeaveEvent(eventName)) + return createMouseEnterLeaveResponder(uid, eventName, handler); + + return function(event) { + var cacheEntry = Event.cache[uid]; + var element = cacheEntry.element; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createResponderForCustomEvent(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createMouseEnterLeaveResponder(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + Event.extend(event, element); + var parent = event.relatedTarget; + + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + handler.call(element, event); + } + } + + GLOBAL.Event._createResponder = createResponder; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var TIMER; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (TIMER) window.clearTimeout(TIMER); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { + document.documentElement.doScroll('left'); + } catch (e) { + TIMER = pollDoScroll.defer(); + return; + } + + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.attachEvent('onreadystatechange', checkReadyState); + if (window == top) TIMER = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(this); + + +Element.addMethods(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator, context) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator, context); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); + +/*--------------------------------------------------------------------------*/ + +(function() { + window.Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + }, + + findElements: function(rootElement) { + return Prototype.Selector.select(this.expression, rootElement); + }, + + match: function(element) { + return Prototype.Selector.match(element, this.expression); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Selector, { + matchElements: function(elements, expression) { + var match = Prototype.Selector.match, + results = []; + + for (var i = 0, length = elements.length; i < length; i++) { + var element = elements[i]; + if (match(element, expression)) { + results.push(Element.extend(element)); + } + } + return results; + }, + + findElement: function(elements, expression, index) { + index = index || 0; + var matchIndex = 0, element; + for (var i = 0, length = elements.length; i < length; i++) { + element = elements[i]; + if (Prototype.Selector.match(element, expression) && index === matchIndex++) { + return Element.extend(element); + } + } + }, + + findChildElements: function(element, expressions) { + var selector = expressions.toArray().join(', '); + return Prototype.Selector.select(selector, element || document); + } + }); +})(); diff --git a/videodb/javascript/prototype/prototype.js b/videodb/javascript/prototype/prototype.js new file mode 100644 index 0000000..7879223 --- /dev/null +++ b/videodb/javascript/prototype/prototype.js @@ -0,0 +1,7588 @@ +/* Prototype JavaScript framework, version 1.7.3 + * (c) 2005-2010 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + + Version: '1.7.3', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + + SelectorsAPI: !!document.querySelector, + + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = (function(method) { + return function() { return method.valueOf.call(method); }; + })(method); + + value.toString = (function(method) { + return function() { return method.toString.call(method); }; + })(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString, + _hasOwnProperty = Object.prototype.hasOwnProperty, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]', + NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && + typeof JSON.stringify === 'function' && + JSON.stringify(0) === '0' && + typeof JSON.stringify(Prototype.K) === 'undefined'; + + + + var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', + 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key]; + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + var type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { + throw new TypeError("Cyclic reference to '" + value + "' in object"); + } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } + var results = []; + for (var property in object) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + + if (IS_DONTENUM_BUGGY) { + for (var i = 0; property = DONT_ENUMS[i]; i++) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + } + + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) + return this; + + if (!Object.isFunction(this)) + throw new TypeError("The object is not callable."); + + var nop = function() {}; + var __method = this, args = slice.call(arguments, 1); + + var bound = function() { + var a = merge(args, arguments); + var c = this instanceof bound ? this : context; + return __method.apply(c, a); + }; + + nop.prototype = this.prototype; + bound.prototype = new nop(); + + return bound; + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + var extensions = { + argumentNames: argumentNames, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + }; + + if (!Function.prototype.bind) + extensions.bind = bind; + + return extensions; +})()); + + + +(function(proto) { + + + function toISOString() { + return this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z'; + } + + + function toJSON() { + return this.toISOString(); + } + + if (!proto.toISOString) proto.toISOString = toISOString; + if (!proto.toJSON) proto.toJSON = toJSON; + +})(Date.prototype); + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, (function() { + var NATIVE_JSON_PARSE_SUPPORT = window.JSON && + typeof JSON.parse === 'function' && + JSON.parse('{"test": true}').test; + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function isNonEmptyRegExp(regexp) { + return regexp.source && regexp.source !== '(?:)'; + } + + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || isNonEmptyRegExp(pattern))) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + replacement; + } + + while (source.length > 0) { + match = source.match(pattern) + if (match && match[0].length > 0) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), + matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script); }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + + if (value != undefined) { + value = value.gsub('+', ' '); + value = decodeURIComponent(value); + } + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); + str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); + str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); + return (/^[\],:{}\s]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(), + cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g; + if (cx.test(json)) { + json = json.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function parseJSON() { + var json = this.unfilterJSON(); + return JSON.parse(json); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern, position) { + position = Object.isNumber(position) ? position : 0; + return this.lastIndexOf(pattern, position) === position; + } + + function endsWith(pattern, position) { + pattern = String(pattern); + position = Object.isNumber(position) ? position : this.length; + if (position < 0) position = 0; + if (position > this.length) position = this.length; + var d = position - pattern.length; + return d >= 0 && this.indexOf(pattern, d) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim || strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, + include: include, + startsWith: String.prototype.startsWith || startsWith, + endsWith: String.prototype.endsWith || endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + try { + this._each(iterator, context); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index, this); + if (!result) throw $break; + }, this); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index, this)) + throw $break; + }, this); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) { + result = value; + throw $break; + } + }, this); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1) + return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index, this); + }, this); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value >= result) + result = value; + }, this); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value < result) + result = value; + }, this); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index, this) ? + trues : falses).push(value); + }, this); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index, this) + }; + }, this).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); + +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator, context) { + for (var i = 0, length = this.length >>> 0; i < length; i++) { + if (i in this) iterator.call(context, this[i], i, this); + } + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline === false ? this.toArray() : this)._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.indexOf(item) !== -1; + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function indexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + + if (i > length) return -1; + + var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); + for (; k < length; k++) + if (k in array && array[k] === item) return k; + return -1; + } + + + function lastIndexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + if (!Object.isUndefined(i)) { + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + } else { + i = length; + } + + var k = i >= 0 ? Math.min(i, length - 1) : + length - Math.abs(i); + + for (; k >= 0; k--) + if (k in array && array[k] === item) return k; + return -1; + } + + function concat(_) { + var array = [], items = slice.call(arguments, 0), item, n = 0; + items.unshift(this); + for (var i = 0, length = items.length; i < length; i++) { + item = items[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { + if (j in item) array[n] = item[j]; + n++; + } + } else { + array[n++] = item; + } + } + array.length = n; + return array; + } + + + function wrapNative(method) { + return function() { + if (arguments.length === 0) { + return method.call(this, Prototype.K); + } else if (arguments[0] === undefined) { + var args = slice.call(arguments, 1); + args.unshift(Prototype.K); + return method.apply(this, args); + } else { + return method.apply(this, arguments); + } + }; + } + + + function map(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + + var object = Object(this); + var results = [], context = arguments[1], n = 0; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + results[n] = iterator.call(context, object[i], i, object); + } + n++; + } + results.length = n; + return results; + } + + if (arrayProto.map) { + map = wrapNative(Array.prototype.map); + } + + function filter(iterator) { + if (this == null || !Object.isFunction(iterator)) + throw new TypeError(); + + var object = Object(this); + var results = [], context = arguments[1], value; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + value = object[i]; + if (iterator.call(context, value, i, object)) { + results.push(value); + } + } + } + return results; + } + + if (arrayProto.filter) { + filter = Array.prototype.filter; + } + + function some(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && iterator.call(context, object[i], i, object)) { + return true; + } + } + + return false; + } + + if (arrayProto.some) { + some = wrapNative(Array.prototype.some); + } + + function every(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && !iterator.call(context, object[i], i, object)) { + return false; + } + } + + return true; + } + + if (arrayProto.every) { + every = wrapNative(Array.prototype.every); + } + + + Object.extend(arrayProto, Enumerable); + + if (arrayProto.entries === Enumerable.entries) { + delete arrayProto.entries; + } + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + + map: map, + collect: map, + select: filter, + filter: filter, + findAll: filter, + some: some, + any: some, + every: every, + all: every, + + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2); + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + + function _each(iterator, context) { + var i = 0; + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator.call(context, pair, i); + i++; + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + + value = String.interpret(value); + + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return key + '=' + value; + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) { + var queryValues = []; + for (var i = 0, len = values.length, value; i < len; i++) { + value = values[i]; + queryValues.push(toQueryPair(key, value)); + } + return results.concat(queryValues); + } + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toObject, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator, context) { + var value = this.start, i; + for (i = 0; this.include(value); i++) { + iterator.call(context, value, i); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +var Abstract = { }; + + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator, context) { + this.responders._each(iterator, context); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + params += (params ? '&' : '') + "_method=" + this.method; + this.method = 'post'; + } + + if (params && this.method === 'get') { + this.url += (this.url.include('?') ? '&' : '?') + params; + } + + this.parameters = params.toQueryParams(); + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + if (headers[name] != null) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300) || status == 304; + }, + + getStatus: function() { + try { + if (this.transport.status === 1223) return 204; + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null; } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if (readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + + try { + json = decodeURIComponent(escape(json)); + } catch(e) { + } + + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +(function(GLOBAL) { + + var UNDEFINED; + var SLICE = Array.prototype.slice; + + var DIV = document.createElement('div'); + + + function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); + } + + GLOBAL.$ = $; + + + if (!GLOBAL.Node) GLOBAL.Node = {}; + + if (!GLOBAL.Node.ELEMENT_NODE) { + Object.extend(GLOBAL.Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); + } + + var ELEMENT_CACHE = {}; + + function shouldUseCreationCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } + + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement(''); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } + })(); + + + var oldElement = GLOBAL.Element; + function Element(tagName, attributes) { + attributes = attributes || {}; + tagName = tagName.toLowerCase(); + + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + + if (!ELEMENT_CACHE[tagName]) + ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); + + var node = shouldUseCreationCache(tagName, attributes) ? + ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); + } + + GLOBAL.Element = Element; + + Object.extend(GLOBAL.Element, oldElement || {}); + if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; + + Element.Methods = { ByTag: {}, Simulated: {} }; + + var methods = {}; + + var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; + function inspect(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + + var attribute, value; + for (var property in INSPECT_ATTRIBUTES) { + attribute = INSPECT_ATTRIBUTES[property]; + value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + } + + return result + '>'; + } + + methods.inspect = inspect; + + + function visible(element) { + return $(element).getStyle('display') !== 'none'; + } + + function toggle(element, bool) { + element = $(element); + if (typeof bool !== 'boolean') + bool = !Element.visible(element); + Element[bool ? 'show' : 'hide'](element); + + return element; + } + + function hide(element) { + element = $(element); + element.style.display = 'none'; + return element; + } + + function show(element) { + element = $(element); + element.style.display = ''; + return element; + } + + + Object.extend(methods, { + visible: visible, + toggle: toggle, + hide: hide, + show: show + }); + + + function remove(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + } + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = ""; + var isBuggy = (el.childNodes.length === 0); + el = null; + return isBuggy; + } catch(e) { + return true; + } + })(); + + var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || + TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (ANY_INNERHTML_BUGGY) { + if (tagName in INSERTION_TRANSLATIONS.tags) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, + content.stripScripts(), true); + + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + } else { + element.innerHTML = content.stripScripts(); + } + } else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + function replace(element, content) { + element = $(element); + + if (content && content.toElement) { + content = content.toElement(); + } else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + + element.parentNode.replaceChild(content, element); + return element; + } + + var INSERTION_TRANSLATIONS = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + + tags: { + TABLE: ['', '
      ', 1], + TBODY: ['', '
      ', 2], + TR: ['', '
      ', 3], + TD: ['
      ', '
      ', 4], + SELECT: ['', 1] + } + }; + + var tags = INSERTION_TRANSLATIONS.tags; + + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); + + function replace_IE(element, content) { + element = $(element); + if (content && content.toElement) + content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (tagName in INSERTION_TRANSLATIONS.tags) { + var nextSibling = Element.next(element); + var fragments = getContentFromAnonymousElement( + tagName, content.stripScripts()); + + parent.removeChild(element); + + var iterator; + if (nextSibling) + iterator = function(node) { parent.insertBefore(node, nextSibling) }; + else + iterator = function(node) { parent.appendChild(node); } + + fragments.each(iterator); + } else { + element.outerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + if ('outerHTML' in document.documentElement) + replace = replace_IE; + + function isContent(content) { + if (Object.isUndefined(content) || content === null) return false; + + if (Object.isString(content) || Object.isNumber(content)) return true; + if (Object.isElement(content)) return true; + if (content.toElement || content.toHTML) return true; + + return false; + } + + function insertContentAt(element, content, position) { + position = position.toLowerCase(); + var method = INSERTION_TRANSLATIONS[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + method(element, content); + return element; + } + + content = Object.toHTML(content); + var tagName = ((position === 'before' || position === 'after') ? + element.parentNode : element).tagName.toUpperCase(); + + var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position === 'top' || position === 'after') childNodes.reverse(); + + for (var i = 0, node; node = childNodes[i]; i++) + method(element, node); + + content.evalScripts.bind(content).defer(); + } + + function insert(element, insertions) { + element = $(element); + + if (isContent(insertions)) + insertions = { bottom: insertions }; + + for (var position in insertions) + insertContentAt(element, insertions[position], position); + + return element; + } + + function wrap(element, wrapper, attributes) { + element = $(element); + + if (Object.isElement(wrapper)) { + $(wrapper).writeAttribute(attributes || {}); + } else if (Object.isString(wrapper)) { + wrapper = new Element(wrapper, attributes); + } else { + wrapper = new Element('div', wrapper); + } + + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + + wrapper.appendChild(element); + + return wrapper; + } + + function cleanWhitespace(element) { + element = $(element); + var node = element.firstChild; + + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + } + + function empty(element) { + return $(element).innerHTML.blank(); + } + + function getContentFromAnonymousElement(tagName, html, force) { + var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; + + var workaround = !!t; + if (!workaround && force) { + workaround = true; + t = ['', '', 0]; + } + + if (workaround) { + div.innerHTML = ' ' + t[0] + html + t[1]; + div.removeChild(div.firstChild); + for (var i = t[2]; i--; ) + div = div.firstChild; + } else { + div.innerHTML = html; + } + + return $A(div.childNodes); + } + + function clone(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + if (!HAS_UNIQUE_ID_PROPERTY) { + clone._prototypeUID = UNDEFINED; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) + descendants[i]._prototypeUID = UNDEFINED; + } + } + return Element.extend(clone); + } + + function purgeElement(element) { + var uid = getUniqueElementID(element); + if (uid) { + Element.stopObserving(element); + if (!HAS_UNIQUE_ID_PROPERTY) + element._prototypeUID = UNDEFINED; + delete Element.Storage[uid]; + } + } + + function purgeCollection(elements) { + var i = elements.length; + while (i--) + purgeElement(elements[i]); + } + + function purgeCollection_IE(elements) { + var i = elements.length, element, uid; + while (i--) { + element = elements[i]; + uid = getUniqueElementID(element); + delete Element.Storage[uid]; + delete Event.cache[uid]; + } + } + + if (HAS_UNIQUE_ID_PROPERTY) { + purgeCollection = purgeCollection_IE; + } + + + function purge(element) { + if (!(element = $(element))) return; + purgeElement(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + + while (i--) purgeElement(descendants[i]); + + return null; + } + + Object.extend(methods, { + remove: remove, + update: update, + replace: replace, + insert: insert, + wrap: wrap, + cleanWhitespace: cleanWhitespace, + empty: empty, + clone: clone, + purge: purge + }); + + + + function recursivelyCollect(element, property, maximumLength) { + element = $(element); + maximumLength = maximumLength || -1; + var elements = []; + + while (element = element[property]) { + if (element.nodeType === Node.ELEMENT_NODE) + elements.push(Element.extend(element)); + + if (elements.length === maximumLength) break; + } + + return elements; + } + + + function ancestors(element) { + return recursivelyCollect(element, 'parentNode'); + } + + function descendants(element) { + return Element.select(element, '*'); + } + + function firstDescendant(element) { + element = $(element).firstChild; + while (element && element.nodeType !== Node.ELEMENT_NODE) + element = element.nextSibling; + + return $(element); + } + + function immediateDescendants(element) { + var results = [], child = $(element).firstChild; + + while (child) { + if (child.nodeType === Node.ELEMENT_NODE) + results.push(Element.extend(child)); + + child = child.nextSibling; + } + + return results; + } + + function previousSiblings(element) { + return recursivelyCollect(element, 'previousSibling'); + } + + function nextSiblings(element) { + return recursivelyCollect(element, 'nextSibling'); + } + + function siblings(element) { + element = $(element); + var previous = previousSiblings(element), + next = nextSiblings(element); + return previous.reverse().concat(next); + } + + function match(element, selector) { + element = $(element); + + if (Object.isString(selector)) + return Prototype.Selector.match(element, selector); + + return selector.match(element); + } + + + function _recursivelyFind(element, property, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + if (Object.isNumber(expression)) { + index = expression, expression = null; + } + + while (element = element[property]) { + if (element.nodeType !== 1) continue; + if (expression && !Prototype.Selector.match(element, expression)) + continue; + if (--index >= 0) continue; + + return Element.extend(element); + } + } + + + function up(element, expression, index) { + element = $(element); + + if (arguments.length === 1) return $(element.parentNode); + return _recursivelyFind(element, 'parentNode', expression, index); + } + + function down(element, expression, index) { + if (arguments.length === 1) return firstDescendant(element); + element = $(element), expression = expression || 0, index = index || 0; + + if (Object.isNumber(expression)) + index = expression, expression = '*'; + + var node = Prototype.Selector.select(expression, element)[index]; + return Element.extend(node); + } + + function previous(element, expression, index) { + return _recursivelyFind(element, 'previousSibling', expression, index); + } + + function next(element, expression, index) { + return _recursivelyFind(element, 'nextSibling', expression, index); + } + + function select(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); + } + + function adjacent(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + var siblings = Element.siblings(element), results = []; + for (var i = 0, sibling; sibling = siblings[i]; i++) { + if (Prototype.Selector.match(sibling, expressions)) + results.push(sibling); + } + + return results; + } + + function descendantOf_DOM(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!element || !ancestor) return false; + while (element = element.parentNode) + if (element === ancestor) return true; + return false; + } + + function descendantOf_contains(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!element || !ancestor) return false; + if (!ancestor.contains) return descendantOf_DOM(element, ancestor); + return ancestor.contains(element) && ancestor !== element; + } + + function descendantOf_compareDocumentPosition(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!element || !ancestor) return false; + return (element.compareDocumentPosition(ancestor) & 8) === 8; + } + + var descendantOf; + if (DIV.compareDocumentPosition) { + descendantOf = descendantOf_compareDocumentPosition; + } else if (DIV.contains) { + descendantOf = descendantOf_contains; + } else { + descendantOf = descendantOf_DOM; + } + + + Object.extend(methods, { + recursivelyCollect: recursivelyCollect, + ancestors: ancestors, + descendants: descendants, + firstDescendant: firstDescendant, + immediateDescendants: immediateDescendants, + previousSiblings: previousSiblings, + nextSiblings: nextSiblings, + siblings: siblings, + match: match, + up: up, + down: down, + previous: previous, + next: next, + select: select, + adjacent: adjacent, + descendantOf: descendantOf, + + getElementsBySelector: select, + + childElements: immediateDescendants + }); + + + var idCounter = 1; + function identify(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + + do { id = 'anonymous_element_' + idCounter++ } while ($(id)); + + Element.writeAttribute(element, 'id', id); + return id; + } + + + function readAttribute(element, name) { + return $(element).getAttribute(name); + } + + function readAttribute_IE(element, name) { + element = $(element); + + var table = ATTRIBUTE_TRANSLATIONS.read; + if (table.values[name]) + return table.values[name](element, name); + + if (table.names[name]) name = table.names[name]; + + if (name.include(':')) { + if (!element.attributes || !element.attributes[name]) return null; + return element.attributes[name].value; + } + + return element.getAttribute(name); + } + + function readAttribute_Opera(element, name) { + if (name === 'title') return element.title; + return element.getAttribute(name); + } + + var PROBLEMATIC_ATTRIBUTE_READING = (function() { + DIV.setAttribute('onclick', []); + var value = DIV.getAttribute('onclick'); + var isFunction = Object.isArray(value); + DIV.removeAttribute('onclick'); + return isFunction; + })(); + + if (PROBLEMATIC_ATTRIBUTE_READING) { + readAttribute = readAttribute_IE; + } else if (Prototype.Browser.Opera) { + readAttribute = readAttribute_Opera; + } + + + function writeAttribute(element, name, value) { + element = $(element); + var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; + + if (typeof name === 'object') { + attributes = name; + } else { + attributes[name] = Object.isUndefined(value) ? true : value; + } + + for (var attr in attributes) { + name = table.names[attr] || attr; + value = attributes[attr]; + if (table.values[attr]) { + value = table.values[attr](element, value); + if (Object.isUndefined(value)) continue; + } + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + + return element; + } + + var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () { + if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) { + return false; + } + var checkbox = document.createElement(''); + checkbox.checked = true; + var node = checkbox.getAttributeNode('checked'); + return !node || !node.specified; + })(); + + function hasAttribute(element, attribute) { + attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } + + function hasAttribute_IE(element, attribute) { + if (attribute === 'checked') { + return element.checked; + } + return hasAttribute(element, attribute); + } + + GLOBAL.Element.Methods.Simulated.hasAttribute = + PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ? + hasAttribute_IE : hasAttribute; + + function classNames(element) { + return new Element.ClassNames(element); + } + + var regExpCache = {}; + function getRegExpForClassName(className) { + if (regExpCache[className]) return regExpCache[className]; + + var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); + regExpCache[className] = re; + return re; + } + + function hasClassName(element, className) { + if (!(element = $(element))) return; + + var elementClassName = element.className; + + if (elementClassName.length === 0) return false; + if (elementClassName === className) return true; + + return getRegExpForClassName(className).test(elementClassName); + } + + function addClassName(element, className) { + if (!(element = $(element))) return; + + if (!hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + + return element; + } + + function removeClassName(element, className) { + if (!(element = $(element))) return; + + element.className = element.className.replace( + getRegExpForClassName(className), ' ').strip(); + + return element; + } + + function toggleClassName(element, className, bool) { + if (!(element = $(element))) return; + + if (Object.isUndefined(bool)) + bool = !hasClassName(element, className); + + var method = Element[bool ? 'addClassName' : 'removeClassName']; + return method(element, className); + } + + var ATTRIBUTE_TRANSLATIONS = {}; + + var classProp = 'className', forProp = 'for'; + + DIV.setAttribute(classProp, 'x'); + if (DIV.className !== 'x') { + DIV.setAttribute('class', 'x'); + if (DIV.className === 'x') + classProp = 'class'; + } + + var LABEL = document.createElement('label'); + LABEL.setAttribute(forProp, 'x'); + if (LABEL.htmlFor !== 'x') { + LABEL.setAttribute('htmlFor', 'x'); + if (LABEL.htmlFor === 'x') + forProp = 'htmlFor'; + } + LABEL = null; + + function _getAttr(element, attribute) { + return element.getAttribute(attribute); + } + + function _getAttr2(element, attribute) { + return element.getAttribute(attribute, 2); + } + + function _getAttrNode(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ''; + } + + function _getFlag(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + } + + DIV.onclick = Prototype.emptyFunction; + var onclickValue = DIV.getAttribute('onclick'); + + var _getEv; + + if (String(onclickValue).indexOf('{') > -1) { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + value = value.toString(); + value = value.split('{')[1]; + value = value.split('}')[0]; + return value.strip(); + }; + } + else if (onclickValue === '') { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + return value.strip(); + }; + } + + ATTRIBUTE_TRANSLATIONS.read = { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + + values: { + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.write = { + names: { + className: 'class', + htmlFor: 'for', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, + + values: { + checked: function(element, value) { + value = !!value; + element.checked = value; + return value ? 'checked' : null; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.has = { names: {} }; + + Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, + ATTRIBUTE_TRANSLATIONS.read.names); + + var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + + 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); + + for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { + ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; + ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; + } + + Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { + href: _getAttr2, + src: _getAttr2, + type: _getAttr, + action: _getAttrNode, + disabled: _getFlag, + checked: _getFlag, + readonly: _getFlag, + multiple: _getFlag, + onload: _getEv, + onunload: _getEv, + onclick: _getEv, + ondblclick: _getEv, + onmousedown: _getEv, + onmouseup: _getEv, + onmouseover: _getEv, + onmousemove: _getEv, + onmouseout: _getEv, + onfocus: _getEv, + onblur: _getEv, + onkeypress: _getEv, + onkeydown: _getEv, + onkeyup: _getEv, + onsubmit: _getEv, + onreset: _getEv, + onselect: _getEv, + onchange: _getEv + }); + + + Object.extend(methods, { + identify: identify, + readAttribute: readAttribute, + writeAttribute: writeAttribute, + classNames: classNames, + hasClassName: hasClassName, + addClassName: addClassName, + removeClassName: removeClassName, + toggleClassName: toggleClassName + }); + + + function normalizeStyleName(style) { + if (style === 'float' || style === 'styleFloat') + return 'cssFloat'; + return style.camelize(); + } + + function normalizeStyleName_IE(style) { + if (style === 'float' || style === 'cssFloat') + return 'styleFloat'; + return style.camelize(); + } + + function setStyle(element, styles) { + element = $(element); + var elementStyle = element.style, match; + + if (Object.isString(styles)) { + elementStyle.cssText += ';' + styles; + if (styles.include('opacity')) { + var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; + Element.setOpacity(element, opacity); + } + return element; + } + + for (var property in styles) { + if (property === 'opacity') { + Element.setOpacity(element, styles[property]); + } else { + var value = styles[property]; + if (property === 'float' || property === 'cssFloat') { + property = Object.isUndefined(elementStyle.styleFloat) ? + 'cssFloat' : 'styleFloat'; + } + elementStyle[property] = value; + } + } + + return element; + } + + + function getStyle(element, style) { + element = $(element); + style = normalizeStyleName(style); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getStyle_Opera(element, style) { + switch (style) { + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(getStyle(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + return Element.measure(element, style); + + default: return getStyle(element, style); + } + } + + function getStyle_IE(element, style) { + element = $(element); + style = normalizeStyleName_IE(style); + + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + + if (style === 'opacity') { + if (!STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity_IE(element); + else return value ? parseFloat(value) : 1.0; + } + + if (value === 'auto') { + if ((style === 'width' || style === 'height') && Element.visible(element)) + return Element.measure(element, style) + 'px'; + return null; + } + + return value; + } + + function stripAlphaFromFilter_IE(filter) { + return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); + } + + function hasLayout_IE(element) { + if (!element.currentStyle || !element.currentStyle.hasLayout) + element.style.zoom = 1; + return element; + } + + var STANDARD_CSS_OPACITY_SUPPORTED = (function() { + DIV.style.cssText = "opacity:.55"; + return /^0.55/.test(DIV.style.opacity); + })(); + + function setOpacity(element, value) { + element = $(element); + if (value == 1 || value === '') value = ''; + else if (value < 0.00001) value = 0; + element.style.opacity = value; + return element; + } + + function setOpacity_IE(element, value) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return setOpacity(element, value); + + element = hasLayout_IE($(element)); + var filter = Element.getStyle(element, 'filter'), + style = element.style; + + if (value == 1 || value === '') { + filter = stripAlphaFromFilter_IE(filter); + if (filter) style.filter = filter; + else style.removeAttribute('filter'); + return element; + } + + if (value < 0.00001) value = 0; + + style.filter = stripAlphaFromFilter_IE(filter) + + ' alpha(opacity=' + (value * 100) + ')'; + + return element; + } + + + function getOpacity(element) { + return Element.getStyle(element, 'opacity'); + } + + function getOpacity_IE(element) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity(element); + + var filter = Element.getStyle(element, 'filter'); + if (filter.length === 0) return 1.0; + var match = (filter || '').match(/alpha\(opacity=(.*)\)/i); + if (match && match[1]) return parseFloat(match[1]) / 100; + return 1.0; + } + + + Object.extend(methods, { + setStyle: setStyle, + getStyle: getStyle, + setOpacity: setOpacity, + getOpacity: getOpacity + }); + + if ('styleFloat' in DIV.style) { + methods.getStyle = getStyle_IE; + methods.setOpacity = setOpacity_IE; + methods.getOpacity = getOpacity_IE; + } + + var UID = 0; + + GLOBAL.Element.Storage = { UID: 1 }; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); + if (HAS_UNIQUE_ID_PROPERTY) + getUniqueElementID = getUniqueElementID_IE; + + function getStorage(element) { + if (!(element = $(element))) return; + + var uid = getUniqueElementID(element); + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + } + + function store(element, key, value) { + if (!(element = $(element))) return; + var storage = getStorage(element); + if (arguments.length === 2) { + storage.update(key); + } else { + storage.set(key, value); + } + return element; + } + + function retrieve(element, key, defaultValue) { + if (!(element = $(element))) return; + var storage = getStorage(element), value = storage.get(key); + + if (Object.isUndefined(value)) { + storage.set(key, defaultValue); + value = defaultValue; + } + + return value; + } + + + Object.extend(methods, { + getStorage: getStorage, + store: store, + retrieve: retrieve + }); + + + var Methods = {}, ByTag = Element.Methods.ByTag, + F = Prototype.BrowserFeatures; + + if (!F.ElementExtensions && ('__proto__' in DIV)) { + GLOBAL.HTMLElement = {}; + GLOBAL.HTMLElement.prototype = DIV['__proto__']; + F.ElementExtensions = true; + } + + function checkElementPrototypeDeficiency(tagName) { + if (typeof window.Element === 'undefined') return false; + if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false; + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random() + '').slice(2), + el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + + return false; + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = + checkElementPrototypeDeficiency('object'); + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var EXTENDED = {}; + function elementIsExtended(element) { + var uid = getUniqueElementID(element); + return (uid in EXTENDED); + } + + function extend(element) { + if (!element || elementIsExtended(element)) return element; + if (element.nodeType !== Node.ELEMENT_NODE || element == window) + return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + EXTENDED[getUniqueElementID(element)] = true; + return element; + } + + function extend_IE8(element) { + if (!element || elementIsExtended(element)) return element; + + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + + return element; + } + + if (F.SpecificElementExtensions) { + extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; + } + + function addMethodsToTagName(tagName, methods) { + tagName = tagName.toUpperCase(); + if (!ByTag[tagName]) ByTag[tagName] = {}; + Object.extend(ByTag[tagName], methods); + } + + function mergeMethods(destination, methods, onlyIfAbsent) { + if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + + element = null; + return proto; + } + + function addMethods(methods) { + if (arguments.length === 0) addFormMethods(); + + if (arguments.length === 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) { + Object.extend(Element.Methods, methods || {}); + } else { + if (Object.isArray(tagName)) { + for (var i = 0, tag; tag = tagName[i]; i++) + addMethodsToTagName(tag, methods); + } else { + addMethodsToTagName(tagName, methods); + } + } + + var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + mergeMethods(klass.prototype, ByTag[tag]); + } + } + + Object.extend(Element, Element.Methods); + Object.extend(Element, Element.Methods.Simulated); + delete Element.ByTag; + delete Element.Simulated; + + Element.extend.refresh(); + + ELEMENT_CACHE = {}; + } + + Object.extend(GLOBAL.Element, { + extend: extend, + addMethods: addMethods + }); + + if (extend === Prototype.K) { + GLOBAL.Element.extend.refresh = Prototype.emptyFunction; + } else { + GLOBAL.Element.extend.refresh = function() { + if (Prototype.BrowserFeatures.ElementExtensions) return; + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + + EXTENDED = {}; + }; + } + + function addFormMethods() { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) + }); + } + + Element.addMethods(methods); + + function destroyCache_IE() { + DIV = null; + ELEMENT_CACHE = null; + } + + if (window.attachEvent) + window.attachEvent('onunload', destroyCache_IE); + +})(this); +(function() { + + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } + + function getRawStyle(element, style) { + element = $(element); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getRawStyle_IE(element, style) { + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + return value; + } + + function getContentWidth(element, context) { + var boxWidth = element.offsetWidth; + + var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; + var br = getPixelValue(element, 'borderRightWidth', context) || 0; + var pl = getPixelValue(element, 'paddingLeft', context) || 0; + var pr = getPixelValue(element, 'paddingRight', context) || 0; + + return boxWidth - bl - br - pl - pr; + } + + if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) { + getRawStyle = getRawStyle_IE; + } + + + function getPixelValue(value, property, context) { + var element = null; + if (Object.isElement(value)) { + element = value; + value = getRawStyle(element, property); + } + + if (value === null || Object.isUndefined(value)) { + return null; + } + + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } + + var isPercentage = value.include('%'), isViewport = (context === document.viewport); + + if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { + var style = element.style.left, rStyle = element.runtimeStyle.left; + element.runtimeStyle.left = element.currentStyle.left; + element.style.left = value || 0; + value = element.style.pixelLeft; + element.style.left = style; + element.runtimeStyle.left = rStyle; + + return value; + } + + if (element && isPercentage) { + context = context || element.parentNode; + var decimal = toDecimal(value), whole = null; + + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); + + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); + + if (context === document.viewport) { + if (isHorizontal) { + whole = document.viewport.getWidth(); + } else if (isVertical) { + whole = document.viewport.getHeight(); + } + } else { + if (isHorizontal) { + whole = $(context).measure('width'); + } else if (isVertical) { + whole = $(context).measure('height'); + } + } + + return (whole === null) ? 0 : whole * decimal; + } + + return 0; + } + + function toCSSPixels(number) { + if (Object.isString(number) && number.endsWith('px')) + return number; + return number + 'px'; + } + + function isDisplayed(element) { + while (element && element.parentNode) { + var display = element.getStyle('display'); + if (display === 'none') { + return false; + } + element = $(element.parentNode); + } + return true; + } + + var hasLayout = Prototype.K; + if ('currentStyle' in document.documentElement) { + hasLayout = function(element) { + if (!element.currentStyle.hasLayout) { + element.style.zoom = 1; + } + return element; + }; + } + + function cssNameFor(key) { + if (key.include('border')) key = key + '-width'; + return key.camelize(); + } + + Element.Layout = Class.create(Hash, { + initialize: function($super, element, preCompute) { + $super(); + this.element = $(element); + + Element.Layout.PROPERTIES.each( function(property) { + this._set(property, null); + }, this); + + if (preCompute) { + this._preComputing = true; + this._begin(); + Element.Layout.PROPERTIES.each( this._compute, this ); + this._end(); + this._preComputing = false; + } + }, + + _set: function(property, value) { + return Hash.prototype.set.call(this, property, value); + }, + + set: function(property, value) { + throw "Properties of Element.Layout are read-only."; + }, + + get: function($super, property) { + var value = $super(property); + return value === null ? this._compute(property) : value; + }, + + _begin: function() { + if (this._isPrepared()) return; + + var element = this.element; + if (isDisplayed(element)) { + this._setPrepared(true); + return; + } + + + var originalStyles = { + position: element.style.position || '', + width: element.style.width || '', + visibility: element.style.visibility || '', + display: element.style.display || '' + }; + + element.store('prototype_original_styles', originalStyles); + + var position = getRawStyle(element, 'position'), width = element.offsetWidth; + + if (width === 0 || width === null) { + element.style.display = 'block'; + width = element.offsetWidth; + } + + var context = (position === 'fixed') ? document.viewport : + element.parentNode; + + var tempStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (position !== 'fixed') tempStyles.position = 'absolute'; + + element.setStyle(tempStyles); + + var positionedWidth = element.offsetWidth, newWidth; + if (width && (positionedWidth === width)) { + newWidth = getContentWidth(element, context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getContentWidth(element, context); + } else { + var parent = element.parentNode, pLayout = $(parent).getLayout(); + + newWidth = pLayout.get('width') - + this.get('margin-left') - + this.get('border-left') - + this.get('padding-left') - + this.get('padding-right') - + this.get('border-right') - + this.get('margin-right'); + } + + element.setStyle({ width: newWidth + 'px' }); + + this._setPrepared(true); + }, + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._setPrepared(false); + }, + + _compute: function(property) { + var COMPUTATIONS = Element.Layout.COMPUTATIONS; + if (!(property in COMPUTATIONS)) { + throw "Property not found."; + } + + return this._set(property, COMPUTATIONS[property].call(this, this.element)); + }, + + _isPrepared: function() { + return this.element.retrieve('prototype_element_layout_prepared', false); + }, + + _setPrepared: function(bool) { + return this.element.store('prototype_element_layout_prepared', bool); + }, + + toObject: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var obj = {}; + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + var value = this.get(key); + if (value != null) obj[key] = value; + }, this); + return obj; + }, + + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); + }, + + toCSS: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var css = {}; + + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; + + var value = this.get(key); + if (value != null) css[cssNameFor(key)] = value + 'px'; + }, this); + return css; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Element.Layout, { + PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), + + COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), + + COMPUTATIONS: { + 'height': function(element) { + if (!this._preComputing) this._begin(); + + var bHeight = this.get('border-box-height'); + if (bHeight <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bTop = this.get('border-top'), + bBottom = this.get('border-bottom'); + + var pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + if (!this._preComputing) this._end(); + + return bHeight - bTop - bBottom - pTop - pBottom; + }, + + 'width': function(element) { + if (!this._preComputing) this._begin(); + + var bWidth = this.get('border-box-width'); + if (bWidth <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bLeft = this.get('border-left'), + bRight = this.get('border-right'); + + var pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + if (!this._preComputing) this._end(); + return bWidth - bLeft - bRight - pLeft - pRight; + }, + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; + }, + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; + }, + + 'border-box-height': function(element) { + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; + }, + + 'border-box-width': function(element) { + if (!this._preComputing) this._begin(); + var width = element.offsetWidth; + if (!this._preComputing) this._end(); + return width; + }, + + 'margin-box-height': function(element) { + var bHeight = this.get('border-box-height'), + mTop = this.get('margin-top'), + mBottom = this.get('margin-bottom'); + + if (bHeight <= 0) return 0; + + return bHeight + mTop + mBottom; + }, + + 'margin-box-width': function(element) { + var bWidth = this.get('border-box-width'), + mLeft = this.get('margin-left'), + mRight = this.get('margin-right'); + + if (bWidth <= 0) return 0; + + return bWidth + mLeft + mRight; + }, + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; + }, + + 'bottom': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pHeight = parent.measure('height'); + + var mHeight = this.get('border-box-height'); + + return pHeight - mHeight - offset.top; + }, + + 'left': function(element) { + var offset = element.positionedOffset(); + return offset.left; + }, + + 'right': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pWidth = parent.measure('width'); + + var mWidth = this.get('border-box-width'); + + return pWidth - mWidth - offset.left; + }, + + 'padding-top': function(element) { + return getPixelValue(element, 'paddingTop'); + }, + + 'padding-bottom': function(element) { + return getPixelValue(element, 'paddingBottom'); + }, + + 'padding-left': function(element) { + return getPixelValue(element, 'paddingLeft'); + }, + + 'padding-right': function(element) { + return getPixelValue(element, 'paddingRight'); + }, + + 'border-top': function(element) { + return getPixelValue(element, 'borderTopWidth'); + }, + + 'border-bottom': function(element) { + return getPixelValue(element, 'borderBottomWidth'); + }, + + 'border-left': function(element) { + return getPixelValue(element, 'borderLeftWidth'); + }, + + 'border-right': function(element) { + return getPixelValue(element, 'borderRightWidth'); + }, + + 'margin-top': function(element) { + return getPixelValue(element, 'marginTop'); + }, + + 'margin-bottom': function(element) { + return getPixelValue(element, 'marginBottom'); + }, + + 'margin-left': function(element) { + return getPixelValue(element, 'marginLeft'); + }, + + 'margin-right': function(element) { + return getPixelValue(element, 'marginRight'); + } + } + }); + + if ('getBoundingClientRect' in document.documentElement) { + Object.extend(Element.Layout.COMPUTATIONS, { + 'right': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.right - rect.right).round(); + }, + + 'bottom': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.bottom - rect.bottom).round(); + } + }); + } + + Element.Offset = Class.create({ + initialize: function(left, top) { + this.left = left.round(); + this.top = top.round(); + + this[0] = this.left; + this[1] = this.top; + }, + + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); + }, + + inspect: function() { + return "#".interpolate(this); + }, + + toString: function() { + return "[#{left}, #{top}]".interpolate(this); + }, + + toArray: function() { + return [this.left, this.top]; + } + }); + + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } + + function measure(element, property) { + return $(element).getLayout().get(property); + } + + function getHeight(element) { + return Element.getDimensions(element).height; + } + + function getWidth(element) { + return Element.getDimensions(element).width; + } + + function getDimensions(element) { + element = $(element); + var display = Element.getStyle(element, 'display'); + + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } + + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; + + var newStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; + + Element.setStyle(element, newStyles); + + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; + + Element.setStyle(element, originalStyles); + + return dimensions; + } + + function getOffsetParent(element) { + element = $(element); + + function selfOrBody(element) { + return isHtml(element) ? $(document.body) : $(element); + } + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var isInline = (Element.getStyle(element, 'display') === 'inline'); + if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent); + + while ((element = element.parentNode) && element !== document.body) { + if (Element.getStyle(element, 'position') !== 'static') { + return selfOrBody(element); + } + } + + return $(document.body); + } + + + function cumulativeOffset(element) { + element = $(element); + var valueT = 0, valueL = 0; + if (element.parentNode) { + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + } + return new Element.Offset(valueL, valueT); + } + + function positionedOffset(element) { + element = $(element); + + var layout = element.getLayout(); + + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (isBody(element)) break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + + valueL -= layout.get('margin-left'); + valueT -= layout.get('margin-top'); + + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + if (element === document.body) { + var bodyScrollNode = document.documentElement || document.body.parentNode || document.body; + valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0; + valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0; + break; + } else { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } + } while (element); + return new Element.Offset(valueL, valueT); + } + + function viewportOffset(forElement) { + var valueT = 0, valueL = 0, docBody = document.body; + + forElement = $(forElement); + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == docBody && + Element.getStyle(element, 'position') == 'absolute') break; + } while (element = element.offsetParent); + + element = forElement; + do { + if (element != docBody) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } + + function absolutize(element) { + element = $(element); + + if (Element.getStyle(element, 'position') === 'absolute') { + return element; + } + + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); + + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); + + element.store('prototype_absolutize_original_styles', { + position: element.getStyle('position'), + left: element.getStyle('left'), + top: element.getStyle('top'), + width: element.getStyle('width'), + height: element.getStyle('height') + }); + + element.setStyle({ + position: 'absolute', + top: offset.top + 'px', + left: offset.left + 'px', + width: layout.get('width') + 'px', + height: layout.get('height') + 'px' + }); + + return element; + } + + function relativize(element) { + element = $(element); + if (Element.getStyle(element, 'position') === 'relative') { + return element; + } + + var originalStyles = + element.retrieve('prototype_absolutize_original_styles'); + + if (originalStyles) element.setStyle(originalStyles); + return element; + } + + + function scrollTo(element) { + element = $(element); + var pos = Element.cumulativeOffset(element); + window.scrollTo(pos.left, pos.top); + return element; + } + + + function makePositioned(element) { + element = $(element); + var position = Element.getStyle(element, 'position'), styles = {}; + if (position === 'static' || !position) { + styles.position = 'relative'; + if (Prototype.Browser.Opera) { + styles.top = 0; + styles.left = 0; + } + Element.setStyle(element, styles); + Element.store(element, 'prototype_made_positioned', true); + } + return element; + } + + function undoPositioned(element) { + element = $(element); + var storage = Element.getStorage(element), + madePositioned = storage.get('prototype_made_positioned'); + + if (madePositioned) { + storage.unset('prototype_made_positioned'); + Element.setStyle(element, { + position: '', + top: '', + bottom: '', + left: '', + right: '' + }); + } + return element; + } + + function makeClipping(element) { + element = $(element); + + var storage = Element.getStorage(element), + madeClipping = storage.get('prototype_made_clipping'); + + if (Object.isUndefined(madeClipping)) { + var overflow = Element.getStyle(element, 'overflow'); + storage.set('prototype_made_clipping', overflow); + if (overflow !== 'hidden') + element.style.overflow = 'hidden'; + } + + return element; + } + + function undoClipping(element) { + element = $(element); + var storage = Element.getStorage(element), + overflow = storage.get('prototype_made_clipping'); + + if (!Object.isUndefined(overflow)) { + storage.unset('prototype_made_clipping'); + element.style.overflow = overflow || ''; + } + + return element; + } + + function clonePosition(element, source, options) { + options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, options || {}); + + var docEl = document.documentElement; + + source = $(source); + element = $(element); + var p, delta, layout, styles = {}; + + if (options.setLeft || options.setTop) { + p = Element.viewportOffset(source); + delta = [0, 0]; + if (Element.getStyle(element, 'position') === 'absolute') { + var parent = Element.getOffsetParent(element); + if (parent !== document.body) delta = Element.viewportOffset(parent); + } + } + + function pageScrollXY() { + var x = 0, y = 0; + if (Object.isNumber(window.pageXOffset)) { + x = window.pageXOffset; + y = window.pageYOffset; + } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { + x = document.body.scrollLeft; + y = document.body.scrollTop; + } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) { + x = docEl.scrollLeft; + y = docEl.scrollTop; + } + return { x: x, y: y }; + } + + var pageXY = pageScrollXY(); + + + if (options.setWidth || options.setHeight) { + layout = Element.getLayout(source); + } + + if (options.setLeft) + styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) + styles.top = (p[1] + pageXY.y - delta[1] + options.offsetTop) + 'px'; + + var currentLayout = element.getLayout(); + + if (options.setWidth) { + styles.width = layout.get('width') + 'px'; + } + if (options.setHeight) { + styles.height = layout.get('height') + 'px'; + } + + return Element.setStyle(element, styles); + } + + + if (Prototype.Browser.IE) { + getOffsetParent = getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + positionedOffset = positionedOffset.wrap(function(proceed, element) { + element = $(element); + if (!element.parentNode) return new Element.Offset(0, 0); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + }); + } else if (Prototype.Browser.Webkit) { + cumulativeOffset = function(element) { + element = $(element); + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) { + if (Element.getStyle(element, 'position') == 'absolute') break; + } + + element = element.offsetParent; + } while (element); + + return new Element.Offset(valueL, valueT); + }; + } + + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getWidth: getWidth, + getHeight: getHeight, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize, + scrollTo: scrollTo, + makePositioned: makePositioned, + undoPositioned: undoPositioned, + makeClipping: makeClipping, + undoClipping: undoClipping, + clonePosition: clonePosition + }); + + function isBody(element) { + return element.nodeName.toUpperCase() === 'BODY'; + } + + function isHtml(element) { + return element.nodeName.toUpperCase() === 'HTML'; + } + + function isDocument(element) { + return element.nodeType === Node.DOCUMENT_NODE; + } + + function isDetached(element) { + return element !== document.body && + !Element.descendantOf(element, document.body); + } + + if ('getBoundingClientRect' in document.documentElement) { + Element.addMethods({ + viewportOffset: function(element) { + element = $(element); + if (isDetached(element)) return new Element.Offset(0, 0); + + var rect = element.getBoundingClientRect(), + docEl = document.documentElement; + return new Element.Offset(rect.left - docEl.clientLeft, + rect.top - docEl.clientTop); + } + }); + } + + +})(); + +(function() { + + var IS_OLD_OPERA = Prototype.Browser.Opera && + (window.parseFloat(window.opera.version()) < 9.5); + var ROOT = null; + function getRootElement() { + if (ROOT) return ROOT; + ROOT = IS_OLD_OPERA ? document.body : document.documentElement; + return ROOT; + } + + function getDimensions() { + return { width: this.getWidth(), height: this.getHeight() }; + } + + function getWidth() { + return getRootElement().clientWidth; + } + + function getHeight() { + return getRootElement().clientHeight; + } + + function getScrollOffsets() { + var x = window.pageXOffset || document.documentElement.scrollLeft || + document.body.scrollLeft; + var y = window.pageYOffset || document.documentElement.scrollTop || + document.body.scrollTop; + + return new Element.Offset(x, y); + } + + document.viewport = { + getDimensions: getDimensions, + getWidth: getWidth, + getHeight: getHeight, + getScrollOffsets: getScrollOffsets + }; + +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; + +Prototype.Selector = (function() { + + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } + + function match() { + throw new Error('Method "Prototype.Selector.match" must be defined.'); + } + + function find(elements, expression, index) { + index = index || 0; + var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; + + for (i = 0; i < length; i++) { + if (match(elements[i], expression) && index == matchIndex++) { + return Element.extend(elements[i]); + } + } + } + + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); + } + return elements; + } + + + var K = Prototype.K; + + return { + select: select, + match: match, + find: find, + extendElements: (Element.extend === K) ? K : extendElements, + extendElement: Element.extend + }; +})(); +Prototype._original_property = window.Sizzle; + +;(function () { + function fakeDefine(fn) { + Prototype._actual_sizzle = fn(); + } + fakeDefine.amd = true; + + if (typeof define !== 'undefined' && define.amd) { + Prototype._original_define = define; + Prototype._actual_sizzle = null; + window.define = fakeDefine; + } +})(); + +/*! + * Sizzle CSS Selector Engine v1.10.18 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-02-05 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + + whitespace = "[\\x20\\t\\r\\n\\f]", + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + identifier = characterEncoding.replace( "w", "w#" ), + + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + function( target, els ) { + var j = target.length, + i = 0; + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + if ( (match = rquickExpr.exec( selector )) ) { + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + if ( elem && elem.parentNode ) { + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + if ( keys.push( key + " " ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + if ( diff ) { + return diff; + } + + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + document = doc; + docElem = doc.documentElement; + + documentIsHTML = !isXML( doc ); + + if ( parent && parent !== parent.top ) { + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
      "; + + div.firstChild.className = "i"; + return div.getElementsByClassName("i").length === 2; + }); + + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + + rbuggyMatches = []; + + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + assert(function( div ) { + div.innerHTML = ""; + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + support.disconnectedMatch = matches.call( div, "div" ); + + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + sortOrder = hasCompare ? + function( a, b ) { + + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + 1; + + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + siblingCheck( ap[i], bp[i] ) : + + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + if ( ret || support.disconnectedMatch || + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + while ( (node = elem[i++]) ) { + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + return ret; +}; + +Expr = Sizzle.selectors = { + + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + } else if ( unquoted && rpseudo.test( unquoted ) && + (excess = tokenize( unquoted, true )) && + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + if ( forward && useCache ) { + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + } else { + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + if ( fn[ expando ] ) { + return fn( argument ); + } + + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "lang": markFunction( function( lang ) { + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "empty": function( elem ) { + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + outerCache[ dir ] = newCache; + + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + [] : + + results : + matcherIn; + + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + if ( matcher[ expando ] ) { + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + if ( bySet ) { + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + if ( seed ) { + unmatched.push( elem ); + } + } + } + + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + setMatched = condense( setMatched ); + } + + push.apply( results, setMatched ); + + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + if ( match.length === 1 ) { + + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + + +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +support.detectDuplicates = !!hasDuplicate; + +setDocument(); + +support.sortDetached = assert(function( div1 ) { + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +if ( typeof define === "function" && define.amd ) { + define(function() { return Sizzle; }); +} else if ( typeof module !== "undefined" && module.exports ) { + module.exports = Sizzle; +} else { + window.Sizzle = Sizzle; +} + +})( window ); + +;(function() { + if (typeof Sizzle !== 'undefined') { + return; + } + + if (typeof define !== 'undefined' && define.amd) { + window.Sizzle = Prototype._actual_sizzle; + window.define = Prototype._original_define; + delete Prototype._actual_sizzle; + delete Prototype._original_define; + } else if (typeof module !== 'undefined' && module.exports) { + window.Sizzle = module.exports; + module.exports = {}; + } +})(); + +;(function(engine) { + var extendElements = Prototype.Selector.extendElements; + + function select(selector, scope) { + return extendElements(engine(selector, scope || document)); + } + + function match(element, selector) { + return engine.matches(selector, [element]).length == 1; + } + + Prototype.Selector.engine = engine; + Prototype.Selector.select = select; + Prototype.Selector.match = match; +})(Sizzle); + +window.Sizzle = Prototype._original_property; +delete Prototype._original_property; + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit, accumulator, initial; + + if (options.hash) { + initial = {}; + accumulator = function(result, key, value) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key] = result[key].concat(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, values) { + if (!Object.isArray(values)) {values = [values];} + if (!values.length) {return result;} + var encodedKey = encodeURIComponent(key).gsub(/%20/, '+'); + return result + (result ? "&" : "") + values.map(function (value) { + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return encodedKey + "=" + value; + }).join("&"); + }; + } + + return elements.inject(initial, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + result = accumulator(result, key, value); + } + } + return result; + }); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'); + var element, results = [], serializers = Form.Element.Serializers; + + for (var i = 0; element = elements[i]; i++) { + if (serializers[element.tagName.toLowerCase()]) + results.push(Element.extend(element)); + } + return results; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + var element = form.findFirstElement(); + if (element) element.activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !(/^(?:button|reset|submit)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; + +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = (function() { + function input(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return inputSelector(element, value); + default: + return valueSelector(element, value); + } + } + + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; + else element.checked = !!value; + } + + function valueSelector(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + } + + function select(element, value) { + if (Object.isUndefined(value)) + return (element.type === 'select-one' ? selectOne : selectMany)(element); + + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + + function selectOne(element) { + var index = element.selectedIndex; + return index >= 0 ? optionValue(element.options[index]) : null; + } + + function selectMany(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(optionValue(opt)); + } + return values; + } + + function optionValue(opt) { + return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; + } + + return { + input: input, + inputSelector: inputSelector, + textarea: valueSelector, + select: select, + selectOne: selectOne, + selectMany: selectMany, + optionValue: optionValue, + button: valueSelector + }; +})(); + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function(GLOBAL) { + var DIV = document.createElement('div'); + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var Event = { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45 + }; + + + var isIELegacyEvent = function(event) { return false; }; + + if (window.attachEvent) { + if (window.addEventListener) { + isIELegacyEvent = function(event) { + return !(event instanceof window.Event); + }; + } else { + isIELegacyEvent = function(event) { return true; }; + } + } + + var _isButton; + + function _isButtonForDOMEvents(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + } + + var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; + function _isButtonForLegacyEvents(event, code) { + return event.button === legacyButtonMap[code]; + } + + function _isButtonForWebKit(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 2 || (event.which == 1 && event.metaKey); + case 2: return event.which == 3; + default: return false; + } + } + + if (window.attachEvent) { + if (!window.addEventListener) { + _isButton = _isButtonForLegacyEvents; + } else { + _isButton = function(event, code) { + return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : + _isButtonForDOMEvents(event, code); + } + } + } else if (Prototype.Browser.WebKit) { + _isButton = _isButtonForWebKit; + } else { + _isButton = _isButtonForDOMEvents; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + return Element.extend(_element(event)); + } + + function _element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + return node.nodeType == Node.TEXT_NODE ? node.parentNode : node; + } + + function findElement(event, expression) { + var element = _element(event), selector = Prototype.Selector; + if (!expression) return Element.extend(element); + while (element) { + if (Object.isElement(element) && selector.match(element, expression)) + return Element.extend(element); + element = element.parentNode; + } + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (window.attachEvent) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; + } + return Element.extend(element); + } + + var additionalMethods = { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }; + + Event.extend = function(event, element) { + if (!event) return false; + + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; + event._extendedByPrototype = Prototype.emptyFunction; + + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + Object.extend(event, methods); + Object.extend(event, additionalMethods); + + return event; + }; + } else { + Event.extend = Prototype.K; + } + + if (window.addEventListener) { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + } + + var EVENT_TRANSLATIONS = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + + function getDOMEventName(eventName) { + return EVENT_TRANSLATIONS[eventName] || eventName; + } + + if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) + getDOMEventName = Prototype.K; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + if ('uniqueID' in DIV) + getUniqueElementID = getUniqueElementID_IE; + + function isCustomEvent(eventName) { + return eventName.include(':'); + } + + Event._isCustomEvent = isCustomEvent; + + function getOrCreateRegistryFor(element, uid) { + var CACHE = GLOBAL.Event.cache; + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + if (!CACHE[uid]) CACHE[uid] = { element: element }; + return CACHE[uid]; + } + + function destroyRegistryForElement(element, uid) { + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + delete GLOBAL.Event.cache[uid]; + } + + + function register(element, eventName, handler) { + var registry = getOrCreateRegistryFor(element); + if (!registry[eventName]) registry[eventName] = []; + var entries = registry[eventName]; + + var i = entries.length; + while (i--) + if (entries[i].handler === handler) return null; + + var uid = getUniqueElementID(element); + var responder = GLOBAL.Event._createResponder(uid, eventName, handler); + var entry = { + responder: responder, + handler: handler + }; + + entries.push(entry); + return entry; + } + + function unregister(element, eventName, handler) { + var registry = getOrCreateRegistryFor(element); + var entries = registry[eventName] || []; + + var i = entries.length, entry; + while (i--) { + if (entries[i].handler === handler) { + entry = entries[i]; + break; + } + } + + if (entry) { + var index = entries.indexOf(entry); + entries.splice(index, 1); + } + + if (entries.length === 0) { + delete registry[eventName]; + if (Object.keys(registry).length === 1 && ('element' in registry)) + destroyRegistryForElement(element); + } + + return entry; + } + + + function observe(element, eventName, handler) { + element = $(element); + var entry = register(element, eventName, handler); + + if (entry === null) return element; + + var responder = entry.responder; + if (isCustomEvent(eventName)) + observeCustomEvent(element, eventName, responder); + else + observeStandardEvent(element, eventName, responder); + + return element; + } + + function observeStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.addEventListener) { + element.addEventListener(actualEventName, responder, false); + } else { + element.attachEvent('on' + actualEventName, responder); + } + } + + function observeCustomEvent(element, eventName, responder) { + if (element.addEventListener) { + element.addEventListener('dataavailable', responder, false); + } else { + element.attachEvent('ondataavailable', responder); + element.attachEvent('onlosecapture', responder); + } + } + + function stopObserving(element, eventName, handler) { + element = $(element); + var handlerGiven = !Object.isUndefined(handler), + eventNameGiven = !Object.isUndefined(eventName); + + if (!eventNameGiven && !handlerGiven) { + stopObservingElement(element); + return element; + } + + if (!handlerGiven) { + stopObservingEventName(element, eventName); + return element; + } + + var entry = unregister(element, eventName, handler); + + if (!entry) return element; + removeEvent(element, eventName, entry.responder); + return element; + } + + function stopObservingStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.removeEventListener) { + element.removeEventListener(actualEventName, responder, false); + } else { + element.detachEvent('on' + actualEventName, responder); + } + } + + function stopObservingCustomEvent(element, eventName, responder) { + if (element.removeEventListener) { + element.removeEventListener('dataavailable', responder, false); + } else { + element.detachEvent('ondataavailable', responder); + element.detachEvent('onlosecapture', responder); + } + } + + + + function stopObservingElement(element) { + var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid]; + if (!registry) return; + + destroyRegistryForElement(element, uid); + + var entries, i; + for (var eventName in registry) { + if (eventName === 'element') continue; + + entries = registry[eventName]; + i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + } + + function stopObservingEventName(element, eventName) { + var registry = getOrCreateRegistryFor(element); + var entries = registry[eventName]; + if (entries) { + delete registry[eventName]; + } + + entries = entries || []; + + var i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + + for (var name in registry) { + if (name === 'element') continue; + return; // There is another registered event + } + + destroyRegistryForElement(element); + } + + + function removeEvent(element, eventName, handler) { + if (isCustomEvent(eventName)) + stopObservingCustomEvent(element, eventName, handler); + else + stopObservingStandardEvent(element, eventName, handler); + } + + + + function getFireTarget(element) { + if (element !== document) return element; + if (document.createEvent && !element.dispatchEvent) + return document.documentElement; + return element; + } + + function fire(element, eventName, memo, bubble) { + element = getFireTarget($(element)); + if (Object.isUndefined(bubble)) bubble = true; + memo = memo || {}; + + var event = fireEvent(element, eventName, memo, bubble); + return Event.extend(event); + } + + function fireEvent_DOM(element, eventName, memo, bubble) { + var event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', bubble, true); + + event.eventName = eventName; + event.memo = memo; + + element.dispatchEvent(event); + return event; + } + + function fireEvent_IE(element, eventName, memo, bubble) { + var event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; + + event.eventName = eventName; + event.memo = memo; + + element.fireEvent(event.eventType, event); + return event; + } + + var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; + + + + Event.Handler = Class.create({ + initialize: function(element, eventName, selector, callback) { + this.element = $(element); + this.eventName = eventName; + this.selector = selector; + this.callback = callback; + this.handler = this.handleEvent.bind(this); + }, + + + start: function() { + Event.observe(this.element, this.eventName, this.handler); + return this; + }, + + stop: function() { + Event.stopObserving(this.element, this.eventName, this.handler); + return this; + }, + + handleEvent: function(event) { + var element = Event.findElement(event, this.selector); + if (element) this.callback.call(this.element, event, element); + } + }); + + function on(element, eventName, selector, callback) { + element = $(element); + if (Object.isFunction(selector) && Object.isUndefined(callback)) { + callback = selector, selector = null; + } + + return new Event.Handler(element, eventName, selector, callback).start(); + } + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving, + on: on + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving, + + on: on + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + on: on.methodize(), + + loaded: false + }); + + if (GLOBAL.Event) Object.extend(window.Event, Event); + else GLOBAL.Event = Event; + + GLOBAL.Event.cache = {}; + + function destroyCache_IE() { + GLOBAL.Event.cache = null; + } + + if (window.attachEvent) + window.attachEvent('onunload', destroyCache_IE); + + DIV = null; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Code for creating leak-free event responders is based on work by + John-David Dalton. */ + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + function isSimulatedMouseEnterLeaveEvent(eventName) { + return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === 'mouseenter' || eventName === 'mouseleave'); + } + + function createResponder(uid, eventName, handler) { + if (Event._isCustomEvent(eventName)) + return createResponderForCustomEvent(uid, eventName, handler); + if (isSimulatedMouseEnterLeaveEvent(eventName)) + return createMouseEnterLeaveResponder(uid, eventName, handler); + + return function(event) { + if (!Event.cache) return; + + var element = Event.cache[uid].element; + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createResponderForCustomEvent(uid, eventName, handler) { + return function(event) { + var cache = Event.cache[uid]; + var element = cache && cache.element; + + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createMouseEnterLeaveResponder(uid, eventName, handler) { + return function(event) { + var element = Event.cache[uid].element; + + Event.extend(event, element); + var parent = event.relatedTarget; + + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + handler.call(element, event); + } + } + + GLOBAL.Event._createResponder = createResponder; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var TIMER; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (TIMER) window.clearTimeout(TIMER); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { + document.documentElement.doScroll('left'); + } catch (e) { + TIMER = pollDoScroll.defer(); + return; + } + + fireContentLoadedEvent(); + } + + + if (document.readyState === 'complete') { + fireContentLoadedEvent(); + return; + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.attachEvent('onreadystatechange', checkReadyState); + if (window == top) TIMER = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(this); + + +Element.addMethods(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.addMethods({ + childOf: Element.Methods.descendantOf +}); + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator, context) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator, context); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); + +/*--------------------------------------------------------------------------*/ + +(function() { + window.Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + }, + + findElements: function(rootElement) { + return Prototype.Selector.select(this.expression, rootElement); + }, + + match: function(element) { + return Prototype.Selector.match(element, this.expression); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Selector, { + matchElements: function(elements, expression) { + var match = Prototype.Selector.match, + results = []; + + for (var i = 0, length = elements.length; i < length; i++) { + var element = elements[i]; + if (match(element, expression)) { + results.push(Element.extend(element)); + } + } + return results; + }, + + findElement: function(elements, expression, index) { + index = index || 0; + var matchIndex = 0, element; + for (var i = 0, length = elements.length; i < length; i++) { + element = elements[i]; + if (Prototype.Selector.match(element, expression) && index === matchIndex++) { + return Element.extend(element); + } + } + }, + + findChildElements: function(element, expressions) { + var selector = expressions.toArray().join(', '); + return Prototype.Selector.select(selector, element || document); + } + }); +})(); \ No newline at end of file diff --git a/videodb/javascript/prototype/rating.js b/videodb/javascript/prototype/rating.js new file mode 100644 index 0000000..84febd7 --- /dev/null +++ b/videodb/javascript/prototype/rating.js @@ -0,0 +1,182 @@ +/** + * @author Ryan Johnson + * + * @author Andreas Gtz + * Added rating request enhancements + * + * @copyright 2007 LivePipe LLC + * @package Control.Rating + * @license MIT + * @url http://livepipe.net/projects/control_rating/ + * @version 1.0.1 + * + * $Id: rating.js,v 1.3 2007/12/30 11:27:12 andig2 Exp $ + */ + +if(typeof(Control) == 'undefined') + Control = {}; +Control.Rating = Class.create(); +Object.extend(Control.Rating,{ + instances: [], + findByElementId: function(id){ + return Control.Rating.instances.find(function(instance){ + return (instance.container.id && instance.container.id == id); + }); + } +}); +Object.extend(Control.Rating.prototype,{ + container: false, + value: false, + options: {}, + initialize: function(container,options){ + Control.Rating.instances.push(this); + this.value = false; + this.links = []; + this.container = $(container); + this.container.update(''); + this.options = { + min: 1, + max: 5, + rated: false, + input: false, + reverse: false, + capture: true, + multiple: false, + classNames: { + off: 'rating_off', + half: 'rating_half', + on: 'rating_on', + selected: 'rating_selected' + }, + updateUrl: false, + updateParameterName: 'value', + parameters: {}, //!! + onChange: Prototype.emptyFunction, //!! + afterChange: Prototype.emptyFunction + }; + Object.extend(this.options,options || {}); + if(this.options.value){ + this.value = this.options.value; + delete this.options.value; + } + if(this.options.input){ + this.options.input = $(this.options.input); + this.options.input.observe('change',function(input){ + this.setValueFromInput(input); + }.bind(this,this.options.input)); + this.setValueFromInput(this.options.input,true); + } + var range = $R(this.options.min,this.options.max); + (this.options.reverse ? $A(range).reverse() : range).each(function(i){ + var link = this.buildLink(i); + this.container.appendChild(link); + this.links.push(link); + }.bind(this)); + this.setValue(this.value || this.options.min - 1,false,true); + }, + buildLink: function(rating){ + var link = $(document.createElement('a')); + link.value = rating; + if(this.options.multiple || (!this.options.rated && !this.options.multiple)){ + link.href = ''; + link.onmouseover = this.mouseOver.bind(this,link); + link.onmouseout = this.mouseOut.bind(this,link); + link.onclick = this.click.bindAsEventListener(this,link); + }else{ + link.style.cursor = 'default'; + link.observe('click',function(event){ + Event.stop(event); + return false; + }.bindAsEventListener(this)); + } + link.addClassName(this.options.classNames.off); + return link; + }, + disable: function(){ + this.links.each(function(link){ + link.onmouseover = Prototype.emptyFunction; + link.onmouseout = Prototype.emptyFunction; + link.onclick = Prototype.emptyFunction; + link.observe('click',function(event){ + Event.stop(event); + return false; + }.bindAsEventListener(this)); + link.style.cursor = 'default'; + }.bind(this)); + }, + setValueFromInput: function(input,prevent_callbacks){ + this.setValue((input.options ? input.options[input.options.selectedIndex].value : input.value),true,prevent_callbacks); + }, + setValue: function(value,force_selected,prevent_callbacks){ + this.value = value; + if(this.options.input){ + if(this.options.input.options){ + $A(this.options.input.options).each(function(option,i){ + if(option.value == this.value){ + this.options.input.options.selectedIndex = i; + throw $break; + } + }.bind(this)); + }else + this.options.input.value = this.value; + } + this.render(this.value,force_selected); + if(!prevent_callbacks){ + this.notify('onChange',this.value); //!! + if(this.options.updateUrl){ + var params = this.options.parameters || {}; //!! + params[this.options.updateParameterName] = this.value; + var req = new Ajax.Request(this.options.updateUrl,{ + parameters: params, + + //!! + onSuccess: function(transport) { + req.creator.notify('afterChange',req.creator.value); + } + }); + req.creator = this; //!! + } + //!! added else + else this.notify('afterChange',this.value); + } + }, + render: function(rating,force_selected){ + (this.options.reverse ? this.links.reverse() : this.links).each(function(link,i){ + if(link.value <= Math.ceil(rating)){ + link.className = this.options.classNames[link.value <= rating ? 'on' : 'half']; + if(this.options.rated || force_selected) + link.addClassName(this.options.classNames.selected); + }else + link.className = this.options.classNames.off; + }.bind(this)); + }, + mouseOver: function(link){ + this.render(link.value,true); + }, + mouseOut: function(link){ + this.render(this.value); + }, + click: function(event,link){ + this.options.rated = true; + this.setValue((link.value ? link.value : link),true); + if(!this.options.multiple) + this.disable(); + if(this.options.capture){ + Event.stop(event); + return false; + } + }, + notify: function(event_name){ + try{ + if(this.options[event_name]) + return [this.options[event_name].apply(this.options[event_name],$A(arguments).slice(1))]; + }catch(e){ + if(e != $break) + throw e; + else + return false; + } + } +}); +if(typeof(Object.Event) != 'undefined') + Object.Event.extend(Control.Rating); \ No newline at end of file diff --git a/videodb/javascript/scriptaculous/builder.js b/videodb/javascript/scriptaculous/builder.js new file mode 100644 index 0000000..7325038 --- /dev/null +++ b/videodb/javascript/scriptaculous/builder.js @@ -0,0 +1,136 @@ +// script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" + elementName + ">"; + } catch(e) {} + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName.toUpperCase() != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array) || + arguments[1].tagName) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + } catch(e) {} + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName.toUpperCase() != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return $(element); + }, + _text: function(text) { + return document.createTextNode(text); + }, + + ATTR_MAP: { + 'className': 'class', + 'htmlFor': 'for' + }, + + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + + '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(children.tagName) { + element.appendChild(children); + return; + } + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e); + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + }, + build: function(html) { + var element = this.node('div'); + $(element).update(html.strip()); + return element.down(); + }, + dump: function(scope) { + if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope + + var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + + "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + + "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ + "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ + "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ + "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); + + tags.each( function(tag){ + scope[tag] = function() { + return Builder.node.apply(Builder, [tag].concat($A(arguments))); + }; + }); + } +}; \ No newline at end of file diff --git a/videodb/javascript/scriptaculous/controls.js b/videodb/javascript/scriptaculous/controls.js new file mode 100644 index 0000000..5137ab5 --- /dev/null +++ b/videodb/javascript/scriptaculous/controls.js @@ -0,0 +1,965 @@ +// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2010 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// Autocompleter.Base handles all the autocompletion functionality +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least, +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it +// allows smart autocompletion after linebreaks. + +if(typeof Effect == 'undefined') + throw("controls.js requires including script.aculo.us' effects.js library"); + +var Autocompleter = { }; +Autocompleter.Base = Class.create({ + baseInitialize: function(element, update, options) { + element = $(element); + this.element = element; + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entryCount = 0; + this.oldElementValue = this.element.value; + + if(this.setOptions) + this.setOptions(options); + else + this.options = options || { }; + + this.options.paramName = this.options.paramName || this.element.name; + this.options.tokens = this.options.tokens || []; + this.options.frequency = this.options.frequency || 0.4; + this.options.minChars = this.options.minChars || 1; + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + Position.clone(element, update, { + setHeight: false, + offsetTop: element.offsetHeight + }); + } + Effect.Appear(update,{duration:0.15}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + + if(typeof(this.options.tokens) == 'string') + this.options.tokens = new Array(this.options.tokens); + // Force carriage returns as token delimiters anyway + if (!this.options.tokens.include('\n')) + this.options.tokens.push('\n'); + + this.observer = null; + + this.element.setAttribute('autocomplete','off'); + + Element.hide(this.update); + + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); + }, + + show: function() { + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && + (Prototype.Browser.IE) && + (Element.getStyle(this.update, 'position')=='absolute')) { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); + }, + + fixIEOverlapping: function() { + Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + }, + + hide: function() { + this.stopIndicator(); + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.options.indicator) Element.show(this.options.indicator); + }, + + stopIndicator: function() { + if(this.options.indicator) Element.hide(this.options.indicator); + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.selectEntry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + Event.stop(event); + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.markPrevious(); + this.render(); + Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || + (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + activate: function() { + this.changed = false; + this.hasFocus = true; + this.getUpdatedChoices(); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.selectEntry(); + this.hide(); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.hasFocus = false; + this.active = false; + }, + + render: function() { + if(this.entryCount > 0) { + for (var i = 0; i < this.entryCount; i++) + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : + Element.removeClassName(this.getEntry(i),"selected"); + if(this.hasFocus) { + this.show(); + this.active = true; + } + } else { + this.active = false; + this.hide(); + } + }, + + markPrevious: function() { + if(this.index > 0) this.index--; + else this.index = this.entryCount-1; + this.getEntry(this.index).scrollIntoView(true); + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++; + else this.index = 0; + this.getEntry(this.index).scrollIntoView(false); + }, + + getEntry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + getCurrentEntry: function() { + return this.getEntry(this.index); + }, + + selectEntry: function() { + this.active = false; + this.updateElement(this.getCurrentEntry()); + }, + + updateElement: function(selectedElement) { + if (this.options.updateElement) { + this.options.updateElement(selectedElement); + return; + } + var value = ''; + if (this.options.select) { + var nodes = $(selectedElement).select('.' + this.options.select) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var bounds = this.getTokenBounds(); + if (bounds[0] != -1) { + var newValue = this.element.value.substr(0, bounds[0]); + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value + this.element.value.substr(bounds[1]); + } else { + this.element.value = value; + } + this.oldElementValue = this.element.value; + this.element.focus(); + + if (this.options.afterUpdateElement) + this.options.afterUpdateElement(this.element, selectedElement); + }, + + updateChoices: function(choices) { + if(!this.changed && this.hasFocus) { + this.update.innerHTML = choices; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.down()); + + if(this.update.firstChild && this.update.down().childNodes) { + this.entryCount = + this.update.down().childNodes.length; + for (var i = 0; i < this.entryCount; i++) { + var entry = this.getEntry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entryCount = 0; + } + + this.stopIndicator(); + this.index = 0; + + if(this.entryCount==1 && this.options.autoSelect) { + this.selectEntry(); + this.hide(); + } else { + this.render(); + } + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onObserverEvent: function() { + this.changed = false; + this.tokenBounds = null; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + this.oldElementValue = this.element.value; + }, + + getToken: function() { + var bounds = this.getTokenBounds(); + return this.element.value.substring(bounds[0], bounds[1]).strip(); + }, + + getTokenBounds: function() { + if (null != this.tokenBounds) return this.tokenBounds; + var value = this.element.value; + if (value.strip().empty()) return [-1, 0]; + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); + var offset = (diff == this.oldElementValue.length ? 1 : 0); + var prevTokenPos = -1, nextTokenPos = value.length; + var tp; + for (var index = 0, l = this.options.tokens.length; index < l; ++index) { + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); + if (tp > prevTokenPos) prevTokenPos = tp; + tp = value.indexOf(this.options.tokens[index], diff + offset); + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; + } + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); + } +}); + +Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { + var boundary = Math.min(newS.length, oldS.length); + for (var index = 0; index < boundary; ++index) + if (newS[index] != oldS[index]) + return index; + return boundary; +}; + +Ajax.Autocompleter = Class.create(Autocompleter.Base, { + initialize: function(element, update, url, options) { + this.baseInitialize(element, update, options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this); + this.options.defaultParams = this.options.parameters || null; + this.url = url; + }, + + getUpdatedChoices: function() { + this.startIndicator(); + + var entry = encodeURIComponent(this.options.paramName) + '=' + + encodeURIComponent(this.getToken()); + + this.options.parameters = this.options.callback ? + this.options.callback(this.element, entry) : entry; + + if(this.options.defaultParams) + this.options.parameters += '&' + this.options.defaultParams; + + new Ajax.Request(this.url, this.options); + }, + + onComplete: function(request) { + this.updateChoices(request.responseText); + } +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +// text only at the beginning of strings in the +// autocomplete array. Defaults to true, which will +// match text at the beginning of any *word* in the +// strings in the autocomplete array. If you want to +// search anywhere in the string, additionally set +// the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +// a partial match (unlike minChars, which defines +// how many characters are required to do any match +// at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +// Defaults to true. +// +// It's possible to pass in a custom function as the 'selector' +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(Autocompleter.Base, { + initialize: function(element, update, array, options) { + this.baseInitialize(element, update, options); + this.options.array = array; + }, + + getUpdatedChoices: function() { + this.updateChoices(this.options.selector(this)); + }, + + setOptions: function(options) { + this.options = Object.extend({ + choices: 10, + partialSearch: true, + partialChars: 2, + ignoreCase: true, + fullSearch: false, + selector: function(instance) { + var ret = []; // Beginning matches + var partial = []; // Inside matches + var entry = instance.getToken(); + var count = 0; + + for (var i = 0; i < instance.options.array.length && + ret.length < instance.options.choices ; i++) { + + var elem = instance.options.array[i]; + var foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase()) : + elem.indexOf(entry); + + while (foundPos != -1) { + if (foundPos == 0 && elem.length != entry.length) { + ret.push("
    • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
    • "); + break; + } else if (entry.length >= instance.options.partialChars && + instance.options.partialSearch && foundPos != -1) { + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { + partial.push("
    • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
    • "); + break; + } + } + + foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + elem.indexOf(entry, foundPos + 1); + + } + } + if (partial.length) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); + return "
        " + ret.join('') + "
      "; + } + }, options || { }); + } +}); + +// AJAX in-place editor and collection editor +// Full rewrite by Christophe Porteneuve (April 2007). + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +}; + +Ajax.InPlaceEditor = Class.create({ + initialize: function(element, url, options) { + this.url = url; + this.element = element = $(element); + this.prepareOptions(); + this._controls = { }; + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! + Object.extend(this.options, options || { }); + if (!this.options.formId && this.element.id) { + this.options.formId = this.element.id + '-inplaceeditor'; + if ($(this.options.formId)) + this.options.formId = ''; + } + if (this.options.externalControl) + this.options.externalControl = $(this.options.externalControl); + if (!this.options.externalControl) + this.options.externalControlOnly = false; + this._originalBackground = this.element.getStyle('background-color') || 'transparent'; + this.element.title = this.options.clickToEditText; + this._boundCancelHandler = this.handleFormCancellation.bind(this); + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); + this._boundFailureHandler = this.handleAJAXFailure.bind(this); + this._boundSubmitHandler = this.handleFormSubmission.bind(this); + this._boundWrapperHandler = this.wrapUp.bind(this); + this.registerListeners(); + }, + checkForEscapeOrReturn: function(e) { + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; + if (Event.KEY_ESC == e.keyCode) + this.handleFormCancellation(e); + else if (Event.KEY_RETURN == e.keyCode) + this.handleFormSubmission(e); + }, + createControl: function(mode, handler, extraClasses) { + var control = this.options[mode + 'Control']; + var text = this.options[mode + 'Text']; + if ('button' == control) { + var btn = document.createElement('input'); + btn.type = 'submit'; + btn.value = text; + btn.className = 'editor_' + mode + '_button'; + if ('cancel' == mode) + btn.onclick = this._boundCancelHandler; + this._form.appendChild(btn); + this._controls[mode] = btn; + } else if ('link' == control) { + var link = document.createElement('a'); + link.href = '#'; + link.appendChild(document.createTextNode(text)); + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; + link.className = 'editor_' + mode + '_link'; + if (extraClasses) + link.className += ' ' + extraClasses; + this._form.appendChild(link); + this._controls[mode] = link; + } + }, + createEditField: function() { + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); + var fld; + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { + fld = document.createElement('input'); + fld.type = 'text'; + var size = this.options.size || this.options.cols || 0; + if (0 < size) fld.size = size; + } else { + fld = document.createElement('textarea'); + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); + fld.cols = this.options.cols || 40; + } + fld.name = this.options.paramName; + fld.value = text; // No HTML breaks conversion anymore + fld.className = 'editor_field'; + if (this.options.submitOnBlur) + fld.onblur = this._boundSubmitHandler; + this._controls.editor = fld; + if (this.options.loadTextURL) + this.loadExternalText(); + this._form.appendChild(this._controls.editor); + }, + createForm: function() { + var ipe = this; + function addText(mode, condition) { + var text = ipe.options['text' + mode + 'Controls']; + if (!text || condition === false) return; + ipe._form.appendChild(document.createTextNode(text)); + }; + this._form = $(document.createElement('form')); + this._form.id = this.options.formId; + this._form.addClassName(this.options.formClassName); + this._form.onsubmit = this._boundSubmitHandler; + this.createEditField(); + if ('textarea' == this._controls.editor.tagName.toLowerCase()) + this._form.appendChild(document.createElement('br')); + if (this.options.onFormCustomization) + this.options.onFormCustomization(this, this._form); + addText('Before', this.options.okControl || this.options.cancelControl); + this.createControl('ok', this._boundSubmitHandler); + addText('Between', this.options.okControl && this.options.cancelControl); + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); + addText('After', this.options.okControl || this.options.cancelControl); + }, + destroy: function() { + if (this._oldInnerHTML) + this.element.innerHTML = this._oldInnerHTML; + this.leaveEditMode(); + this.unregisterListeners(); + }, + enterEditMode: function(e) { + if (this._saving || this._editing) return; + this._editing = true; + this.triggerCallback('onEnterEditMode'); + if (this.options.externalControl) + this.options.externalControl.hide(); + this.element.hide(); + this.createForm(); + this.element.parentNode.insertBefore(this._form, this.element); + if (!this.options.loadTextURL) + this.postProcessEditField(); + if (e) Event.stop(e); + }, + enterHover: function(e) { + if (this.options.hoverClassName) + this.element.addClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onEnterHover'); + }, + getText: function() { + return this.element.innerHTML.unescapeHTML(); + }, + handleAJAXFailure: function(transport) { + this.triggerCallback('onFailure', transport); + if (this._oldInnerHTML) { + this.element.innerHTML = this._oldInnerHTML; + this._oldInnerHTML = null; + } + }, + handleFormCancellation: function(e) { + this.wrapUp(); + if (e) Event.stop(e); + }, + handleFormSubmission: function(e) { + var form = this._form; + var value = $F(this._controls.editor); + this.prepareSubmission(); + var params = this.options.callback(form, value) || ''; + if (Object.isString(params)) + params = params.toQueryParams(); + params.editorId = this.element.id; + if (this.options.htmlResponse) { + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Updater({ success: this.element }, this.url, options); + } else { + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.url, options); + } + if (e) Event.stop(e); + }, + leaveEditMode: function() { + this.element.removeClassName(this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + if (this.options.externalControl) + this.options.externalControl.show(); + this._saving = false; + this._editing = false; + this._oldInnerHTML = null; + this.triggerCallback('onLeaveEditMode'); + }, + leaveHover: function(e) { + if (this.options.hoverClassName) + this.element.removeClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onLeaveHover'); + }, + loadExternalText: function() { + this._form.addClassName(this.options.loadingClassName); + this._controls.editor.disabled = true; + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._form.removeClassName(this.options.loadingClassName); + var text = transport.responseText; + if (this.options.stripLoadedTextTags) + text = text.stripTags(); + this._controls.editor.value = text; + this._controls.editor.disabled = false; + this.postProcessEditField(); + }.bind(this), + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + postProcessEditField: function() { + var fpc = this.options.fieldPostCreation; + if (fpc) + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); + }, + prepareOptions: function() { + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); + [this._extraDefaultOptions].flatten().compact().each(function(defs) { + Object.extend(this.options, defs); + }.bind(this)); + }, + prepareSubmission: function() { + this._saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + registerListeners: function() { + this._listeners = { }; + var listener; + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { + listener = this[pair.value].bind(this); + this._listeners[pair.key] = listener; + if (!this.options.externalControlOnly) + this.element.observe(pair.key, listener); + if (this.options.externalControl) + this.options.externalControl.observe(pair.key, listener); + }.bind(this)); + }, + removeForm: function() { + if (!this._form) return; + this._form.remove(); + this._form = null; + this._controls = { }; + }, + showSaving: function() { + this._oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + this.element.addClassName(this.options.savingClassName); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + }, + triggerCallback: function(cbName, arg) { + if ('function' == typeof this.options[cbName]) { + this.options[cbName](this, arg); + } + }, + unregisterListeners: function() { + $H(this._listeners).each(function(pair) { + if (!this.options.externalControlOnly) + this.element.stopObserving(pair.key, pair.value); + if (this.options.externalControl) + this.options.externalControl.stopObserving(pair.key, pair.value); + }.bind(this)); + }, + wrapUp: function(transport) { + this.leaveEditMode(); + // Can't use triggerCallback due to backward compatibility: requires + // binding + direct element + this._boundComplete(transport, this.element); + } +}); + +Object.extend(Ajax.InPlaceEditor.prototype, { + dispose: Ajax.InPlaceEditor.prototype.destroy +}); + +Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { + initialize: function($super, element, url, options) { + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; + $super(element, url, options); + }, + + createEditField: function() { + var list = document.createElement('select'); + list.name = this.options.paramName; + list.size = 1; + this._controls.editor = list; + this._collection = this.options.collection || []; + if (this.options.loadCollectionURL) + this.loadCollection(); + else + this.checkForExternalText(); + this._form.appendChild(this._controls.editor); + }, + + loadCollection: function() { + this._form.addClassName(this.options.loadingClassName); + this.showLoadingText(this.options.loadingCollectionText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check + throw('Server returned an invalid collection representation.'); + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadCollectionURL, options); + }, + + showLoadingText: function(text) { + this._controls.editor.disabled = true; + var tempOption = this._controls.editor.firstChild; + if (!tempOption) { + tempOption = document.createElement('option'); + tempOption.value = ''; + this._controls.editor.appendChild(tempOption); + tempOption.selected = true; + } + tempOption.update((text || '').stripScripts().stripTags()); + }, + + checkForExternalText: function() { + this._text = this.getText(); + if (this.options.loadTextURL) + this.loadExternalText(); + else + this.buildOptionList(); + }, + + loadExternalText: function() { + this.showLoadingText(this.options.loadingText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._text = transport.responseText.strip(); + this.buildOptionList(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + + buildOptionList: function() { + this._form.removeClassName(this.options.loadingClassName); + this._collection = this._collection.map(function(entry) { + return 2 === entry.length ? entry : [entry, entry].flatten(); + }); + var marker = ('value' in this.options) ? this.options.value : this._text; + var textFound = this._collection.any(function(entry) { + return entry[0] == marker; + }.bind(this)); + this._controls.editor.update(''); + var option; + this._collection.each(function(entry, index) { + option = document.createElement('option'); + option.value = entry[0]; + option.selected = textFound ? entry[0] == marker : 0 == index; + option.appendChild(document.createTextNode(entry[1])); + this._controls.editor.appendChild(option); + }.bind(this)); + this._controls.editor.disabled = false; + Field.scrollFreeActivate(this._controls.editor); + } +}); + +//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** +//**** This only exists for a while, in order to let **** +//**** users adapt to the new API. Read up on the new **** +//**** API and convert your code to it ASAP! **** + +Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { + if (!options) return; + function fallback(name, expr) { + if (name in options || expr === undefined) return; + options[name] = expr; + }; + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : + options.cancelLink == options.cancelButton == false ? false : undefined))); + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : + options.okLink == options.okButton == false ? false : undefined))); + fallback('highlightColor', options.highlightcolor); + fallback('highlightEndColor', options.highlightendcolor); +}; + +Object.extend(Ajax.InPlaceEditor, { + DefaultOptions: { + ajaxOptions: { }, + autoRows: 3, // Use when multi-line w/ rows == 1 + cancelControl: 'link', // 'link'|'button'|false + cancelText: 'cancel', + clickToEditText: 'Click to edit', + externalControl: null, // id|elt + externalControlOnly: false, + fieldPostCreation: 'activate', // 'activate'|'focus'|false + formClassName: 'inplaceeditor-form', + formId: null, // id|elt + highlightColor: '#ffff99', + highlightEndColor: '#ffffff', + hoverClassName: '', + htmlResponse: true, + loadingClassName: 'inplaceeditor-loading', + loadingText: 'Loading...', + okControl: 'button', // 'link'|'button'|false + okText: 'ok', + paramName: 'value', + rows: 1, // If 1 and multi-line, uses autoRows + savingClassName: 'inplaceeditor-saving', + savingText: 'Saving...', + size: 0, + stripLoadedTextTags: false, + submitOnBlur: false, + textAfterControls: '', + textBeforeControls: '', + textBetweenControls: '' + }, + DefaultCallbacks: { + callback: function(form) { + return Form.serialize(form); + }, + onComplete: function(transport, element) { + // For backward compatibility, this one is bound to the IPE, and passes + // the element directly. It was too often customized, so we don't break it. + new Effect.Highlight(element, { + startcolor: this.options.highlightColor, keepBackgroundImage: true }); + }, + onEnterEditMode: null, + onEnterHover: function(ipe) { + ipe.element.style.backgroundColor = ipe.options.highlightColor; + if (ipe._effect) + ipe._effect.cancel(); + }, + onFailure: function(transport, ipe) { + alert('Error communication with the server: ' + transport.responseText.stripTags()); + }, + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. + onLeaveEditMode: null, + onLeaveHover: function(ipe) { + ipe._effect = new Effect.Highlight(ipe.element, { + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, + restorecolor: ipe._originalBackground, keepBackgroundImage: true + }); + } + }, + Listeners: { + click: 'enterEditMode', + keydown: 'checkForEscapeOrReturn', + mouseover: 'enterHover', + mouseout: 'leaveHover' + } +}); + +Ajax.InPlaceCollectionEditor.DefaultOptions = { + loadingCollectionText: 'Loading options...' +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create({ + initialize: function(element, delay, callback) { + this.delay = delay || 0.5; + this.element = $(element); + this.callback = callback; + this.timer = null; + this.lastValue = $F(this.element); + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); + }, + delayedListener: function(event) { + if(this.lastValue == $F(this.element)) return; + if(this.timer) clearTimeout(this.timer); + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); + this.lastValue = $F(this.element); + }, + onTimerEvent: function() { + this.timer = null; + this.callback(this.element, $F(this.element)); + } +}); \ No newline at end of file diff --git a/videodb/javascript/scriptaculous/dragdrop.js b/videodb/javascript/scriptaculous/dragdrop.js new file mode 100644 index 0000000..9ebfe24 --- /dev/null +++ b/videodb/javascript/scriptaculous/dragdrop.js @@ -0,0 +1,974 @@ +// script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +if(Object.isUndefined(Effect)) + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null, + tree: false + }, arguments[1] || { }); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if(Object.isArray(containment)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + findDeepestChild: function(drops) { + deepest = drops[0]; + + for (i = 1; i < drops.length; ++i) + if (Element.isParent(drops[i].element, deepest.element)) + deepest = drops[i]; + + return deepest; + }, + + isContained: function(element, drop) { + var containmentNode; + if(drop.tree) { + containmentNode = element.treeNode; + } else { + containmentNode = element.parentNode; + } + return drop._containers.detect(function(c) { return containmentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + var drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) Droppables.activate(drop); + } + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) { + this.last_active.onDrop(element, this.last_active.element, event); + return true; + } + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +}; + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + if(draggable.options.delay) { + this._timeout = setTimeout(function() { + Draggables._timeout = null; + window.focus(); + Draggables.activeDraggable = draggable; + }.bind(this), draggable.options.delay); + } else { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + } + }, + + deactivate: function() { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; + } + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + this.activeDraggable = null; + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + if(draggable.options[eventName]) draggable.options[eventName](draggable, event); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +}; + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create({ + initialize: function(element) { + var defaults = { + handle: false, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); + }, + endeffect: function(element) { + var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ + Draggable._dragging[element] = false + } + }); + }, + zindex: 1000, + revert: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } + delay: 0 + }; + + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) + Object.extend(defaults, { + starteffect: function(element) { + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + } + }); + + var options = Object.extend(defaults, arguments[1] || { }); + + this.element = $(element); + + if(options.handle && Object.isString(options.handle)) + this.handle = this.element.down('.'+options.handle, 0); + + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { + options.scroll = $(options.scroll); + this._isScrollChild = Element.childOf(this.element, options.scroll); + } + + Element.makePositioned(this.element); // fix IE + + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(Element.getStyle(this.element,'left') || '0'), + parseInt(Element.getStyle(this.element,'top') || '0')]); + }, + + initDrag: function(event) { + if(!Object.isUndefined(Draggable._dragging[this.element]) && + Draggable._dragging[this.element]) return; + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if((tag_name = src.tagName.toUpperCase()) && ( + tag_name=='INPUT' || + tag_name=='SELECT' || + tag_name=='OPTION' || + tag_name=='BUTTON' || + tag_name=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = this.element.cumulativeOffset(); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + if(!this.delta) + this.delta = this.currentDelta(); + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + + if(!this.options.quiet){ + Position.prepare(); + Droppables.show(pointer, this.element); + } + + Draggables.notify('onDrag', this, event); + + this.draw(pointer); + if(this.options.change) this.options.change(this); + + if(this.options.scroll) { + this.stopScrolling(); + + var p; + if (this.options.scroll == window) { + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } + } else { + p = Position.page(this.options.scroll).toArray(); + p[0] += this.options.scroll.scrollLeft + Position.deltaX; + p[1] += this.options.scroll.scrollTop + Position.deltaY; + p.push(p[0]+this.options.scroll.offsetWidth); + p.push(p[1]+this.options.scroll.offsetHeight); + } + var speed = [0,0]; + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); + this.startScrolling(speed); + } + + // fix AppleWebKit rendering + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.quiet){ + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + Droppables.show(pointer, this.element); + } + + if(this.options.ghosting) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + Element.remove(this._clone); + this._clone = null; + } + + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; + } + if(dropped && this.options.onDropped) this.options.onDropped(this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && Object.isFunction(revert)) revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + if (dropped == 0 || revert != 'failure') + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(event.keyCode!=Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.stopScrolling(); + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = this.element.cumulativeOffset(); + if(this.options.ghosting) { + var r = Position.realOffset(this.element); + pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; + } + + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*this.options.snap }.bind(this)); + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + + stopScrolling: function() { + if(this.scrollInterval) { + clearInterval(this.scrollInterval); + this.scrollInterval = null; + Draggables._lastScrollPointer = null; + } + }, + + startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; + this.lastScrolled = new Date(); + this.scrollInterval = setInterval(this.scroll.bind(this), 10); + }, + + scroll: function() { + var current = new Date(); + var delta = current - this.lastScrolled; + this.lastScrolled = current; + if(this.options.scroll == window) { + with (this._getWindowScroll(this.options.scroll)) { + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { + var d = delta / 1000; + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); + } + } + } else { + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; + } + + Position.prepare(); + Droppables.show(Draggables._lastPointer, this.element); + Draggables.notify('onDrag', this); + if (this._isScrollChild) { + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; + if (Draggables._lastScrollPointer[0] < 0) + Draggables._lastScrollPointer[0] = 0; + if (Draggables._lastScrollPointer[1] < 0) + Draggables._lastScrollPointer[1] = 0; + this.draw(Draggables._lastScrollPointer); + } + + if(this.options.change) this.options.change(this); + }, + + _getWindowScroll: function(w) { + var T, L, W, H; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight; + } + } + return { top: T, left: L, width: W, height: H }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +}); + +var Sortable = { + SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, + + sortables: { }, + + _findRootElement: function(element) { + while (element.tagName.toUpperCase() != "BODY") { + if(element.id && Sortable.sortables[element.id]) return element; + element = element.parentNode; + } + }, + + options: function(element) { + element = Sortable._findRootElement($(element)); + if(!element) return; + return Sortable.sortables[element.id]; + }, + + destroy: function(element){ + element = $(element); + var s = Sortable.sortables[element.id]; + + if(s) { + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + + delete Sortable.sortables[s.element.id]; + } + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, + treeTag: 'ul', + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + delay: 0, + hoverclass: null, + ghosting: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + format: this.SERIALIZE_RULE, + + // these take arrays of elements or ids and can be + // used for better initialization performance + elements: false, + handles: false, + + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || { }); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + quiet: options.quiet, + scroll: options.scroll, + scrollSpeed: options.scrollSpeed, + scrollSensitivity: options.scrollSensitivity, + delay: options.delay, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + tree: options.tree, + hoverclass: options.hoverclass, + onHover: Sortable.onHover + }; + + var options_for_tree = { + onHover: Sortable.onEmptyHover, + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass + }; + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // drop on empty handling + if(options.dropOnEmpty || options.tree) { + Droppables.add(element, options_for_tree); + options.droppables.push(element); + } + + (options.elements || this.findElements(element, options) || []).each( function(e,i) { + var handle = options.handles ? $(options.handles[i]) : + (options.handle ? $(e).select('.' + options.handle)[0] : e); + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + if(options.tree) e.treeNode = element; + options.droppables.push(e); + }); + + if(options.tree) { + (Sortable.findTreeElements(element, options) || []).each( function(e) { + Droppables.add(e, options_for_tree); + e.treeNode = element; + options.droppables.push(e); + }); + } + + // keep reference + this.sortables[element.identify()] = options; + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.tag); + }, + + findTreeElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.treeTag); + }, + + onHover: function(element, dropon, overlap) { + if(Element.isParent(dropon, element)) return; + + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { + return; + } else if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon, overlap) { + var oldParentNode = element.parentNode; + var droponOptions = Sortable.options(dropon); + + if(!Element.isParent(dropon, element)) { + var index; + + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); + var child = null; + + if(children) { + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); + + for (index = 0; index < children.length; index += 1) { + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { + offset -= Element.offsetSize (children[index], droponOptions.overlap); + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { + child = index + 1 < children.length ? children[index + 1] : null; + break; + } else { + child = children[index]; + break; + } + } + } + + dropon.insertBefore(element, child); + + Sortable.options(oldParentNode).onChange(element); + droponOptions.onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Sortable._marker.hide(); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = + ($('dropmarker') || Element.extend(document.createElement('DIV'))). + hide().addClassName('dropmarker').setStyle({position:'absolute'}); + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = dropon.cumulativeOffset(); + Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); + else + Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); + + Sortable._marker.show(); + }, + + _tree: function(element, options, parent) { + var children = Sortable.findElements(element, options) || []; + + for (var i = 0; i < children.length; ++i) { + var match = children[i].id.match(options.format); + + if (!match) continue; + + var child = { + id: encodeURIComponent(match ? match[1] : null), + element: element, + parent: parent, + children: [], + position: parent.children.length, + container: $(children[i]).down(options.treeTag) + }; + + /* Get the element containing the children and recurse over it */ + if (child.container) + this._tree(child.container, options, child); + + parent.children.push (child); + } + + return parent; + }, + + tree: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + treeTag: sortableOptions.treeTag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format + }, arguments[1] || { }); + + var root = { + id: null, + parent: null, + children: [], + container: element, + position: 0 + }; + + return Sortable._tree(element, options, root); + }, + + /* Construct a [i] index for a particular node */ + _constructIndex: function(node) { + var index = ''; + do { + if (node.id) index = '[' + node.position + ']' + index; + } while ((node = node.parent) != null); + return index; + }, + + sequence: function(element) { + element = $(element); + var options = Object.extend(this.options(element), arguments[1] || { }); + + return $(this.findElements(element, options) || []).map( function(item) { + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; + }); + }, + + setSequence: function(element, new_sequence) { + element = $(element); + var options = Object.extend(this.options(element), arguments[2] || { }); + + var nodeMap = { }; + this.findElements(element, options).each( function(n) { + if (n.id.match(options.format)) + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; + n.parentNode.removeChild(n); + }); + + new_sequence.each(function(ident) { + var n = nodeMap[ident]; + if (n) { + n[1].appendChild(n[0]); + delete nodeMap[ident]; + } + }); + }, + + serialize: function(element) { + element = $(element); + var options = Object.extend(Sortable.options(element), arguments[1] || { }); + var name = encodeURIComponent( + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); + + if (options.tree) { + return Sortable.tree(element, arguments[1]).children.map( function (item) { + return [name + Sortable._constructIndex(item) + "[id]=" + + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); + }).flatten().join('&'); + } else { + return Sortable.sequence(element, arguments[1]).map( function(item) { + return name + "[]=" + encodeURIComponent(item); + }).join('&'); + } + } +}; + +// Returns true if child is contained within element +Element.isParent = function(child, element) { + if (!child.parentNode || child == element) return false; + if (child.parentNode == element) return true; + return Element.isParent(child.parentNode, element); +}; + +Element.findChildren = function(element, only, recursive, tagName) { + if(!element.hasChildNodes()) return null; + tagName = tagName.toUpperCase(); + if(only) only = [only].flatten(); + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName.toUpperCase()==tagName && + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) + elements.push(e); + if(recursive) { + var grandchildren = Element.findChildren(e, only, recursive, tagName); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : []); +}; + +Element.offsetSize = function (element, type) { + return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; +}; \ No newline at end of file diff --git a/videodb/javascript/scriptaculous/effects.js b/videodb/javascript/scriptaculous/effects.js new file mode 100644 index 0000000..860ddc0 --- /dev/null +++ b/videodb/javascript/scriptaculous/effects.js @@ -0,0 +1,1123 @@ +// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if (this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if (this.slice(0,1) == '#') { + if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if (this.length==7) color = this.toLowerCase(); + } + } + return (color.length==7 ? color : (arguments[0] || this)); +}; + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +}; + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +}; + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if (Prototype.Browser.WebKit) window.scrollBy(0,0); + return element; +}; + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +}; + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + .5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; + }, + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + var tagifyStyle = 'position:relative'; + if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + + element = $(element); + $A(element.childNodes).each( function(child) { + if (child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || { }); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect, options) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + + return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, options || {})); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(Enumerable, { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = Object.isString(effect.options.queue) ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if (!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if (this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i= this.startOn) { + if (timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if (this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if (this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if (effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || { }); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); +}; + +Effect.Scale = Class.create(Effect.Base, { + initialize: function(element, percent) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or { } with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || { }); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = { }; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%','pt'].each( function(fontSizeType) { + if (fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if (this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if (/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if (!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if (this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = { }; + if (this.options.scaleX) d.width = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + 'px'; + if (this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if (this.elementPositioning == 'absolute') { + if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if (this.options.scaleY) d.top = -topd + 'px'; + if (this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if (this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = { }; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if (!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if (!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()); } + ); +}; + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if (effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + } + }, arguments[1] || { }); + return new Effect.Opacity(element,options); +}; + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || { }); + return new Effect.Opacity(element,options); +}; + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || { }) + ); +}; + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || { }) + ); +}; + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || { })); +}; + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }); + } + }, arguments[1] || { })); +}; + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || { })); +}; + +Effect.Shake = function(element) { + element = $(element); + var options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}); }}); }}); }}); }}); }}); +}; + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || { }) + ); +}; + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, arguments[1] || { }) + ); +}; + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +}; + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ); + } + }); +}; + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +}; + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +}; + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || { })); +}; + +Effect.Morph = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.style] = ''; + }); + }; + } + } + this.start(options); + }, + + setup: function(){ + function parseColor(color){ + if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ); + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if (value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if (property == 'opacity') { + value = parseFloat(value); + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if (Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ); + }); + }, + update: function(position) { + var style = { }, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + (transform.originalValue + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: track.keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
      '; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +} + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element); + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + }; + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); \ No newline at end of file diff --git a/videodb/javascript/scriptaculous/scriptaculous.js b/videodb/javascript/scriptaculous/scriptaculous.js new file mode 100644 index 0000000..0ea5c44 --- /dev/null +++ b/videodb/javascript/scriptaculous/scriptaculous.js @@ -0,0 +1,68 @@ +// script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.9.0', + require: function(libraryName) { + try{ + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(' + + + + +
      +
      +

      Welcome to Foundation

      +

      This is version 4.3.1.

      +
      +
      +
      + +
      +
      +

      The Grid

      + + +
      +
      +
      +

      This is a twelve column section in a row. Each of these includes a div.panel element so you can see where the columns are - it's not required at all for the grid.

      +
      +
      +
      +
      +
      +
      +

      Six columns

      +
      +
      +
      +
      +

      Six columns

      +
      +
      +
      +
      +
      +
      +

      Four columns

      +
      +
      +
      +
      +

      Four columns

      +
      +
      +
      +
      +

      Four columns

      +
      +
      +
      + +

      Buttons

      + + +
      + +
      +

      Getting Started

      +

      We're stoked you want to try Foundation! To get going, this file (index.html) includes some basic styles you can modify, play around with, or totally destroy to get going.

      + +

      Other Resources

      +

      Once you've exhausted the fun in this document, you should check out:

      +
        +
      • Foundation Documentation
        Everything you need to know about using the framework.
      • +
      • Foundation on Github
        Latest code, issue reports, feature requests and more.
      • +
      • @foundationzurb
        Ping us on Twitter if you have questions. If you build something with this we'd love to see it (and send you a totally boss sticker).
      • +
      +
      +
      + + + + + + + + + diff --git a/videodb/lib/foundation4/js/foundation.min.js b/videodb/lib/foundation4/js/foundation.min.js new file mode 100644 index 0000000..25ea922 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation.min.js @@ -0,0 +1,15 @@ +/* + * Foundation Responsive Library + * http://foundation.zurb.com + * Copyright 2013, ZURB + * Free to use under the MIT license. + * http://www.opensource.org/licenses/mit-license.php +*/ +/*jslint unparam: true, browser: true, indent: 2 */ +// Accommodate running jQuery or Zepto in noConflict() mode by +// using an anonymous function to redefine the $ shorthand name. +// See http://docs.jquery.com/Using_jQuery_with_Other_Libraries +// and http://zeptojs.com/ +var libFuncName=null;if(typeof jQuery=="undefined"&&typeof Zepto=="undefined"&&typeof $=="function")libFuncName=$;else if(typeof jQuery=="function")libFuncName=jQuery;else{if(typeof Zepto!="function")throw new TypeError;libFuncName=Zepto}(function(e,t,n,r){"use strict";t.matchMedia=t.matchMedia||function(e,t){var n,r=e.documentElement,i=r.firstElementChild||r.firstChild,s=e.createElement("body"),o=e.createElement("div");return o.id="mq-test-1",o.style.cssText="position:absolute;top:-100em",s.style.background="none",s.appendChild(o),function(e){return o.innerHTML='­',r.insertBefore(s,i),n=o.offsetWidth===42,r.removeChild(s),{matches:n,media:e}}}(n),Array.prototype.filter||(Array.prototype.filter=function(e){if(this==null)throw new TypeError;var t=Object(this),n=t.length>>>0;if(typeof e!="function")return;var r=[],i=arguments[1];for(var s=0;s>>0;if(n===0)return-1;var r=0;arguments.length>1&&(r=Number(arguments[1]),r!=r?r=0:r!=0&&r!=Infinity&&r!=-Infinity&&(r=(r>0||-1)*Math.floor(Math.abs(r))));if(r>=n)return-1;var i=r>=0?r:Math.max(n-Math.abs(r),0);for(;i0)for(var l=u.length-1;l>=0;l--)f.push(this.init_lib(u[l],a))}else{/reflow/i.test(n)&&(a[1]="reflow");for(var c in this.libs)f.push(this.init_lib(c,a))}return typeof n=="function"&&a.unshift(n),this.response_obj(f,a)},response_obj:function(e,t){for(var n=0,r=t.length;n=0;r--)this.lib_methods.hasOwnProperty(n[r])&&(this.libs[e.name][n[r]]=this.lib_methods[n[r]])},random_str:function(e){var t="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");e||(e=Math.floor(Math.random()*t.length));var n="";for(var r=0;r=0;r--)i=s[r].split(":"),/true/i.test(i[1])&&(i[1]=!0),/false/i.test(i[1])&&(i[1]=!1),u(i[1])&&(i[1]=parseInt(i[1],10)),i.length===2&&i[0].length>0&&(n[a(i[0])]=a(i[1]));return n},delay:function(e,t){return setTimeout(e,t)},scrollTo:function(n,r,i){if(i<0)return;var s=r-e(t).scrollTop(),o=s/i*10;this.scrollToTimerCache=setTimeout(function(){isNaN(parseInt(o,10))||(t.scrollTo(0,e(t).scrollTop()+o),this.scrollTo(n,r,i-10))}.bind(this),10)},scrollLeft:function(e){if(!e.length)return;return"scrollLeft"in e[0]?e[0].scrollLeft:e[0].pageXOffset},empty:function(e){if(e.length&&e.length>0)return!1;if(e.length&&e.length===0)return!0;for(var t in e)if(hasOwnProperty.call(e,t))return!1;return!0}},fix_outer:function(e){e.outerHeight=function(e,t){return typeof Zepto=="function"?e.height():typeof t!="undefined"?e.outerHeight(t):e.outerHeight()},e.outerWidth=function(e,t){return typeof Zepto=="function"?e.width():typeof t!="undefined"?e.outerWidth(t):e.outerWidth()}},error:function(e){return e.name+" "+e.message+"; "+e.more},off:function(){return e(this.scope).off(".fndtn"),e(t).off(".fndtn"),!0},zj:e},e.fn.foundation=function(){var e=Array.prototype.slice.call(arguments,0);return this.each(function(){return Foundation.init.apply(Foundation,[this].concat(e)),this})}})(libFuncName,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.alerts={name:"alerts",version:"4.2.2",settings:{speed:300,callback:function(){}},init:function(t,n,r){return this.scope=t||this.scope,typeof n=="object"&&e.extend(!0,this.settings,n),typeof n!="string"?(this.settings.init||this.events(),this.settings.init):this[n].call(this,r)},events:function(){var t=this;e(this.scope).on("click.fndtn.alerts","[data-alert] a.close",function(n){n.preventDefault(),e(this).closest("[data-alert]").fadeOut(t.speed,function(){e(this).remove(),t.settings.callback()})}),this.settings.init=!0},off:function(){e(this.scope).off(".fndtn.alerts")},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.clearing={name:"clearing",version:"4.3.1",settings:{templates:{viewing:'×'},close_selectors:".clearing-close",init:!1,locked:!1},init:function(t,n,r){var i=this;return Foundation.inherit(this,"set_data get_data remove_data throttle data_options"),typeof n=="object"&&(r=e.extend(!0,this.settings,n)),typeof n!="string"?(e(this.scope).find("ul[data-clearing]").each(function(){var t=e(this),n=n||{},r=t.find("li"),s=i.get_data(t);!s&&r.length>0&&(n.$parent=t.parent(),i.set_data(t,e.extend({},i.settings,n,i.data_options(t))),i.assemble(t.find("li")),i.settings.init||i.events().swipe_events())}),this.settings.init):this[n].call(this,r)},events:function(){var n=this;return e(this.scope).on("click.fndtn.clearing","ul[data-clearing] li",function(t,r,i){var r=r||e(this),i=i||r,s=r.next("li"),o=n.get_data(r.parent()),u=e(t.target);t.preventDefault(),o||n.init(),i.hasClass("visible")&&r[0]===i[0]&&s.length>0&&n.is_open(r)&&(i=s,u=i.find("img")),n.open(u,r,i),n.update_paddles(i)}).on("click.fndtn.clearing",".clearing-main-next",function(e){this.nav(e,"next")}.bind(this)).on("click.fndtn.clearing",".clearing-main-prev",function(e){this.nav(e,"prev")}.bind(this)).on("click.fndtn.clearing",this.settings.close_selectors,function(e){Foundation.libs.clearing.close(e,this)}).on("keydown.fndtn.clearing",function(e){this.keydown(e)}.bind(this)),e(t).on("resize.fndtn.clearing",function(){this.resize()}.bind(this)),this.settings.init=!0,this},swipe_events:function(){var t=this;e(this.scope).on("touchstart.fndtn.clearing",".visible-img",function(t){t.touches||(t=t.originalEvent);var n={start_page_x:t.touches[0].pageX,start_page_y:t.touches[0].pageY,start_time:(new Date).getTime(),delta_x:0,is_scrolling:r};e(this).data("swipe-transition",n),t.stopPropagation()}).on("touchmove.fndtn.clearing",".visible-img",function(n){n.touches||(n=n.originalEvent);if(n.touches.length>1||n.scale&&n.scale!==1)return;var r=e(this).data("swipe-transition");typeof r=="undefined"&&(r={}),r.delta_x=n.touches[0].pageX-r.start_page_x,typeof r.is_scrolling=="undefined"&&(r.is_scrolling=!!(r.is_scrolling||Math.abs(r.delta_x)
      ');var r=e("#foundationClearingHolder"),i=this.get_data(n),s=n.detach(),o={grid:'",viewing:i.templates.viewing},u='
      '+o.viewing+o.grid+"
      ";return r.after(u).remove()},open:function(e,t,n){var r=n.closest(".clearing-assembled"),i=r.find("div").first(),s=i.find(".visible-img"),o=s.find("img").not(e);this.locked()||(o.attr("src",this.load(e)).css("visibility","hidden"),this.loaded(o,function(){o.css("visibility","visible"),r.addClass("clearing-blackout"),i.addClass("clearing-container"),s.show(),this.fix_height(n).caption(s.find(".clearing-caption"),e).center(o).shift(t,n,function(){n.siblings().removeClass("visible"),n.addClass("visible")})}.bind(this)))},close:function(t,n){t.preventDefault();var r=function(e){return/blackout/.test(e.selector)?e:e.closest(".clearing-blackout")}(e(n)),i,s;return n===t.target&&r&&(i=r.find("div").first(),s=i.find(".visible-img"),this.settings.prev_index=0,r.find("ul[data-clearing]").attr("style","").closest(".clearing-blackout").removeClass("clearing-blackout"),i.removeClass("clearing-container"),s.hide()),!1},is_open:function(e){return e.parent().attr("style").length>0},keydown:function(t){var n=e(".clearing-blackout").find("ul[data-clearing]");t.which===39&&this.go(n,"next"),t.which===37&&this.go(n,"prev"),t.which===27&&e("a.clearing-close").trigger("click")},nav:function(t,n){var r=e(".clearing-blackout").find("ul[data-clearing]");t.preventDefault(),this.go(r,n)},resize:function(){var t=e(".clearing-blackout .visible-img").find("img");t.length&&this.center(t)},fix_height:function(t){var n=t.parent().children(),r=this;return n.each(function(){var t=e(this),n=t.find("img");t.height()>r.outerHeight(n)&&t.addClass("fix-height")}).closest("ul").width(n.length*100+"%"),this},update_paddles:function(e){var t=e.closest(".carousel").siblings(".visible-img");e.next().length>0?t.find(".clearing-main-next").removeClass("disabled"):t.find(".clearing-main-next").addClass("disabled"),e.prev().length>0?t.find(".clearing-main-prev").removeClass("disabled"):t.find(".clearing-main-prev").addClass("disabled")},center:function(e){return this.rtl?e.css({marginRight:-(this.outerWidth(e)/2),marginTop:-(this.outerHeight(e)/2)}):e.css({marginLeft:-(this.outerWidth(e)/2),marginTop:-(this.outerHeight(e)/2)}),this},load:function(e){if(e[0].nodeName==="A")var t=e.attr("href");else var t=e.parent().attr("href");return this.preload(e),t?t:e.attr("src")},preload:function(e){this.img(e.closest("li").next()).img(e.closest("li").prev())},loaded:function(e,t){function n(){t()}function r(){this.one("load",n);if(/MSIE (\d+\.\d+);/.test(navigator.userAgent)){var e=this.attr("src"),t=e.match(/\?/)?"&":"?";t+="random="+(new Date).getTime(),this.attr("src",e+t)}}if(!e.attr("src")){n();return}e[0].complete||e[0].readyState===4?n():r.call(e)},img:function(e){if(e.length){var t=new Image,n=e.find("a");n.length?t.src=n.attr("href"):t.src=e.find("img").attr("src")}return this},caption:function(e,t){var n=t.data("caption");return n?e.html(n).show():e.text("").hide(),this},go:function(e,t){var n=e.find(".visible"),r=n[t]();r.length&&r.find("img").trigger("click",[n,r])},shift:function(e,t,n){var r=t.parent(),i=this.settings.prev_index||t.index(),s=this.direction(r,e,t),o=parseInt(r.css("left"),10),u=this.outerWidth(t),a;t.index()!==i&&!/skip/.test(s)?/left/.test(s)?(this.lock(),r.animate({left:o+u},300,this.unlock())):/right/.test(s)&&(this.lock(),r.animate({left:o-u},300,this.unlock())):/skip/.test(s)&&(a=t.index()-this.settings.up_count,this.lock(),a>0?r.animate({left:-(a*u)},300,this.unlock()):r.animate({left:0},300,this.unlock())),n()},direction:function(t,n,r){var i=t.find("li"),s=this.outerWidth(i)+this.outerWidth(i)/4,o=Math.floor(this.outerWidth(e(".clearing-container"))/s)-1,u=i.index(r),a;return this.settings.up_count=o,this.adjacent(this.settings.prev_index,u)?u>o&&u>this.settings.prev_index?a="right":u>o-1&&u<=this.settings.prev_index?a="left":a=!1:a="skip",this.settings.prev_index=u,a},adjacent:function(e,t){for(var n=t+1;n>=t-1;n--)if(n===e)return!0;return!1},lock:function(){this.settings.locked=!0},unlock:function(){this.settings.locked=!1},locked:function(){return this.settings.locked},outerHTML:function(e){return e.outerHTML||(new XMLSerializer).serializeToString(e)},off:function(){e(this.scope).off(".fndtn.clearing"),e(t).off(".fndtn.clearing"),this.remove_data(),this.settings.init=!1},reflow:function(){this.init()}}}(Foundation.zj,this,this.document),function(e,t,n){function i(e){return e}function s(e){return decodeURIComponent(e.replace(r," "))}var r=/\+/g,o=e.cookie=function(r,u,a){if(u!==n){a=e.extend({},o.defaults,a),u===null&&(a.expires=-1);if(typeof a.expires=="number"){var f=a.expires,l=a.expires=new Date;l.setDate(l.getDate()+f)}return u=o.json?JSON.stringify(u):String(u),t.cookie=[encodeURIComponent(r),"=",o.raw?u:encodeURIComponent(u),a.expires?"; expires="+a.expires.toUTCString():"",a.path?"; path="+a.path:"",a.domain?"; domain="+a.domain:"",a.secure?"; secure":""].join("")}var c=o.raw?i:s,h=t.cookie.split("; ");for(var p=0,d=h.length;p0&&(e(t.target).is("[data-dropdown-content]")||e.contains(n.first()[0],t.target))){t.stopPropagation();return}r.close.call(r,e("[data-dropdown-content]"))}),e(t).on("resize.fndtn.dropdown",r.throttle(function(){r.resize.call(r)},50)).trigger("resize"),this.settings.init=!0},close:function(t){var n=this;t.each(function(){e(this).hasClass(n.settings.activeClass)&&(e(this).css(Foundation.rtl?"right":"left","-99999px").removeClass(n.settings.activeClass),e(this).trigger("closed"))})},open:function(e,t){this.css(e.addClass(this.settings.activeClass),t),e.trigger("opened")},toggle:function(t){var n=e("#"+t.data("dropdown"));this.close.call(this,e("[data-dropdown-content]").not(n)),n.hasClass(this.settings.activeClass)?this.close.call(this,n):(this.close.call(this,e("[data-dropdown-content]")),this.open.call(this,n,t))},resize:function(){var t=e("[data-dropdown-content].open"),n=e("[data-dropdown='"+t.attr("id")+"']");t.length&&n.length&&this.css(t,n)},css:function(n,r){var i=n.offsetParent(),s=r.offset();s.top-=i.offset().top,s.left-=i.offset().left;if(this.small())n.css({position:"absolute",width:"95%",left:"2.5%","max-width":"none",top:s.top+this.outerHeight(r)});else{if(!Foundation.rtl&&e(t).width()>this.outerWidth(n)+r.offset().left){var o=s.left;n.hasClass("right")&&n.removeClass("right")}else{n.hasClass("right")||n.addClass("right");var o=s.left-(this.outerWidth(n)-this.outerWidth(r))}n.attr("style","").css({position:"absolute",top:s.top+this.outerHeight(r),left:o})}return n},small:function(){return e(t).width()<768||e("html").hasClass("lt-ie9")},off:function(){e(this.scope).off(".fndtn.dropdown"),e("html, body").off(".fndtn.dropdown"),e(t).off(".fndtn.dropdown"),e("[data-dropdown-content]").off(".fndtn.dropdown"),this.settings.init=!1},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.forms={name:"forms",version:"4.3.1",cache:{},settings:{disable_class:"no-custom",last_combo:null},init:function(t,n,r){return typeof n=="object"&&e.extend(!0,this.settings,n),typeof n!="string"?(this.settings.init||this.events(),this.assemble(),this.settings.init):this[n].call(this,r)},assemble:function(){e('form.custom input[type="radio"]',e(this.scope)).not('[data-customforms="disabled"]').not("."+this.settings.disable_class).each(this.append_custom_markup),e('form.custom input[type="checkbox"]',e(this.scope)).not('[data-customforms="disabled"]').not("."+this.settings.disable_class).each(this.append_custom_markup),e("form.custom select",e(this.scope)).not('[data-customforms="disabled"]').not("."+this.settings.disable_class).not("[multiple=multiple]").each(this.append_custom_select)},events:function(){var r=this;e(this.scope).on("click.fndtn.forms","form.custom span.custom.checkbox",function(t){t.preventDefault(),t.stopPropagation(),r.toggle_checkbox(e(this))}).on("click.fndtn.forms","form.custom span.custom.radio",function(t){t.preventDefault(),t.stopPropagation(),r.toggle_radio(e(this))}).on("change.fndtn.forms","form.custom select",function(t,n){if(e(this).is('[data-customforms="disabled"]'))return;r.refresh_custom_select(e(this),n)}).on("click.fndtn.forms","form.custom label",function(t){if(e(t.target).is("label")){var n=e("#"+r.escape(e(this).attr("for"))).not('[data-customforms="disabled"]'),i,s;n.length!==0&&(n.attr("type")==="checkbox"?(t.preventDefault(),i=e(this).find("span.custom.checkbox"),i.length===0&&(i=n.add(this).siblings("span.custom.checkbox").first()),r.toggle_checkbox(i)):n.attr("type")==="radio"&&(t.preventDefault(),s=e(this).find("span.custom.radio"),s.length===0&&(s=n.add(this).siblings("span.custom.radio").first()),r.toggle_radio(s)))}}).on("mousedown.fndtn.forms","form.custom div.custom.dropdown",function(){return!1}).on("click.fndtn.forms","form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector",function(t){var n=e(this),s=n.closest("div.custom.dropdown"),o=i(s,"select");s.hasClass("open")||e(r.scope).trigger("click"),t.preventDefault();if(!1===o.is(":disabled"))return s.toggleClass("open"),s.hasClass("open")?e(r.scope).on("click.fndtn.forms.customdropdown",function(){s.removeClass("open"),e(r.scope).off(".fndtn.forms.customdropdown")}):e(r.scope).on(".fndtn.forms.customdropdown"),!1}).on("click.fndtn.forms touchend.fndtn.forms","form.custom div.custom.dropdown li",function(t){var r=e(this),s=r.closest("div.custom.dropdown"),o=i(s,"select"),u=0;t.preventDefault(),t.stopPropagation();if(!e(this).hasClass("disabled")){e("div.dropdown").not(s).removeClass("open");var a=r.closest("ul").find("li.selected");a.removeClass("selected"),r.addClass("selected"),s.removeClass("open").find("a.current").text(r.text()),r.closest("ul").find("li").each(function(e){r[0]===this&&(u=e)}),o[0].selectedIndex=u,o.data("prevalue",a.html());if(typeof n.createEvent!="undefined"){var f=n.createEvent("HTMLEvents");f.initEvent("change",!0,!0),o[0].dispatchEvent(f)}else o[0].fireEvent("onchange")}}),e(t).on("keydown",function(t){var r=n.activeElement,i=Foundation.libs.forms,s=e(".custom.dropdown.open");if(s.length>0){t.preventDefault(),t.which===13&&s.find("li.selected").trigger("click"),t.which===27&&s.removeClass("open");if(t.which>=65&&t.which<=90){var o=i.go_to(s,t.which),u=s.find("li.selected");o&&(u.removeClass("selected"),i.scrollTo(o.addClass("selected"),300))}if(t.which===38){var u=s.find("li.selected"),a=u.prev(":not(.disabled)");a.length>0&&(a.parent()[0].scrollTop=a.parent().scrollTop()-i.outerHeight(a),u.removeClass("selected"),a.addClass("selected"))}else if(t.which===40){var u=s.find("li.selected"),o=u.next(":not(.disabled)");o.length>0&&(o.parent()[0].scrollTop=o.parent().scrollTop()+i.outerHeight(o),u.removeClass("selected"),o.addClass("selected"))}}}),this.settings.init=!0},go_to:function(e,t){var n=e.find("li"),r=n.length;if(r>0)for(var i=0;i').insertAfter(r)),s.toggleClass("checked",r.is(":checked")),s.toggleClass("disabled",r.is(":disabled"))},append_custom_select:function(t,n){var r=Foundation.libs.forms,i=e(n),s=i.next("div.custom.dropdown"),o=s.find("ul"),u=s.find(".current"),a=s.find(".selector"),f=i.find("option"),l=f.filter(":selected"),c=i.attr("class")?i.attr("class").split(" "):[],h=0,p="",d,v=!1;if(s.length===0){var m=i.hasClass("small")?"small":i.hasClass("medium")?"medium":i.hasClass("large")?"large":i.hasClass("expand")?"expand":"";s=e('
        '),a=s.find(".selector"),o=s.find("ul"),p=f.map(function(){var t=e(this).attr("class")?e(this).attr("class"):"";return"
      • "+e(this).html()+"
      • "}).get().join(""),o.append(p),v=s.prepend(''+l.html()+"").find(".current"),i.after(s).addClass("hidden-field")}else p=f.map(function(){return"
      • "+e(this).html()+"
      • "}).get().join(""),o.html("").append(p);r.assign_id(i,s),s.toggleClass("disabled",i.is(":disabled")),d=o.find("li"),r.cache[s.data("id")]=d.length,f.each(function(t){this.selected&&(d.eq(t).addClass("selected"),v&&v.html(e(this).html())),e(this).is(":disabled")&&d.eq(t).addClass("disabled")});if(!s.is(".small, .medium, .large, .expand")){s.addClass("open");var r=Foundation.libs.forms;r.hidden_fix.adjust(o),h=r.outerWidth(d)>h?r.outerWidth(d):h,Foundation.libs.forms.hidden_fix.reset(),s.removeClass("open")}},assign_id:function(e,t){var n=[+(new Date),Foundation.random_str(5)].join("-");e.attr("data-id",n),t.attr("data-id",n)},refresh_custom_select:function(t,n){var r=this,i=0,s=t.next(),o=t.find("option"),u=s.find("li");if(u.length!==this.cache[s.data("id")]||n)s.find("ul").html(""),o.each(function(){var t=e("
      • "+e(this).html()+"
      • ");s.find("ul").append(t)}),o.each(function(t){this.selected&&(s.find("li").eq(t).addClass("selected"),s.find(".current").html(e(this).html())),e(this).is(":disabled")&&s.find("li").eq(t).addClass("disabled")}),s.removeAttr("style").find("ul").removeAttr("style"),s.find("li").each(function(){s.addClass("open"),r.outerWidth(e(this))>i&&(i=r.outerWidth(e(this))),s.removeClass("open")}),u=s.find("li"),this.cache[s.data("id")]=u.length},toggle_checkbox:function(e){var t=e.prev(),n=t[0];!1===t.is(":disabled")&&(n.checked=n.checked?!1:!0,e.toggleClass("checked"),t.trigger("change"))},toggle_radio:function(e){var t=e.prev(),n=t.closest("form.custom"),r=t[0];!1===t.is(":disabled")&&(n.find('input[type="radio"][name="'+this.escape(t.attr("name"))+'"]').next().not(e).removeClass("checked"),e.hasClass("checked")||e.toggleClass("checked"),r.checked=e.hasClass("checked"),t.trigger("change"))},escape:function(e){return e?e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"):""},hidden_fix:{tmp:[],hidden:null,adjust:function(t){var n=this;n.hidden=t.parents(),n.hidden=n.hidden.add(t).filter(":hidden"),n.hidden.each(function(){var t=e(this);n.tmp.push(t.attr("style")),t.css({visibility:"hidden",display:"block"})})},reset:function(){var t=this;t.hidden.each(function(n){var i=e(this),s=t.tmp[n];s===r?i.removeAttr("style"):i.attr("style",s)}),t.tmp=[],t.hidden=null}},off:function(){e(this.scope).off(".fndtn.forms")},reflow:function(){}};var i=function(t,n){var t=t.prev();while(t.length){if(t.is(n))return t;t=t.prev()}return e()}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.joyride={name:"joyride",version:"4.2.2",defaults:{expose:!1,modal:!1,tipLocation:"bottom",nubPosition:"auto",scrollSpeed:300,timer:0,startTimerOnClick:!0,startOffset:0,nextButton:!0,tipAnimation:"fade",pauseAfter:[],exposed:[],tipAnimationFadeSpeed:300,cookieMonster:!1,cookieName:"joyride",cookieDomain:!1,cookieExpires:365,tipContainer:"body",postRideCallback:function(){},postStepCallback:function(){},preStepCallback:function(){},preRideCallback:function(){},postExposeCallback:function(){},template:{link:'×',timer:'
        ',tip:'
        ',wrapper:'
        ',button:'',modal:'
        ',expose:'
        ',exposeCover:'
        '},exposeAddClass:""},settings:{},init:function(t,n,r){return this.scope=t||this.scope,Foundation.inherit(this,"throttle data_options scrollTo scrollLeft delay"),typeof n=="object"?e.extend(!0,this.settings,this.defaults,n):e.extend(!0,this.settings,this.defaults,r),typeof n!="string"?(this.settings.init||this.events(),this.settings.init):this[n].call(this,r)},events:function(){var n=this;e(this.scope).on("click.joyride",".joyride-next-tip, .joyride-modal-bg",function(e){e.preventDefault(),this.settings.$li.next().length<1?this.end():this.settings.timer>0?(clearTimeout(this.settings.automate),this.hide(),this.show(),this.startTimer()):(this.hide(),this.show())}.bind(this)).on("click.joyride",".joyride-close-tip",function(e){e.preventDefault(),this.end()}.bind(this)),e(t).on("resize.fndtn.joyride",n.throttle(function(){if(e("[data-joyride]").length>0&&n.settings.$next_tip){if(n.settings.exposed.length>0){var t=e(n.settings.exposed);t.each(function(){var t=e(this);n.un_expose(t),n.expose(t)})}n.is_phone()?n.pos_phone():n.pos_default(!1,!0)}},100)),this.settings.init=!0},start:function(){var t=this,n=e(this.scope).find("[data-joyride]"),r=["timer","scrollSpeed","startOffset","tipAnimationFadeSpeed","cookieExpires"],i=r.length;this.settings.init||this.init(),this.settings.$content_el=n,this.settings.$body=e(this.settings.tipContainer),this.settings.body_offset=e(this.settings.tipContainer).position(),this.settings.$tip_content=this.settings.$content_el.find("> li"),this.settings.paused=!1,this.settings.attempts=0,this.settings.tipLocationPatterns={top:["bottom"],bottom:[],left:["right","top","bottom"],right:["left","top","bottom"]},typeof e.cookie!="function"&&(this.settings.cookieMonster=!1);if(!this.settings.cookieMonster||this.settings.cookieMonster&&e.cookie(this.settings.cookieName)===null)this.settings.$tip_content.each(function(n){var s=e(this);e.extend(!0,t.settings,t.data_options(s));for(var o=i-1;o>=0;o--)t.settings[r[o]]=parseInt(t.settings[r[o]],10);t.create({$li:s,index:n})}),!this.settings.startTimerOnClick&&this.settings.timer>0?(this.show("init"),this.startTimer()):this.show("init")},resume:function(){this.set_li(),this.show()},tip_template:function(t){var n,r;return t.tip_class=t.tip_class||"",n=e(this.settings.template.tip).addClass(t.tip_class),r=e.trim(e(t.li).html())+this.button_text(t.button_text)+this.settings.template.link+this.timer_instance(t.index),n.append(e(this.settings.template.wrapper)),n.first().attr("data-index",t.index),e(".joyride-content-wrapper",n).append(r),n[0]},timer_instance:function(t){var n;return t===0&&this.settings.startTimerOnClick&&this.settings.timer>0||this.settings.timer===0?n="":n=this.outerHTML(e(this.settings.template.timer)[0]),n},button_text:function(t){return this.settings.nextButton?(t=e.trim(t)||"Next",t=this.outerHTML(e(this.settings.template.button).append(t)[0])):t="",t},create:function(t){var n=t.$li.attr("data-button")||t.$li.attr("data-text"),r=t.$li.attr("class"),i=e(this.tip_template({tip_class:r,index:t.index,button_text:n,li:t.$li}));e(this.settings.tipContainer).append(i)},show:function(t){var n=null;this.settings.$li===r||e.inArray(this.settings.$li.index(),this.settings.pauseAfter)===-1?(this.settings.paused?this.settings.paused=!1:this.set_li(t),this.settings.attempts=0,this.settings.$li.length&&this.settings.$target.length>0?(t&&(this.settings.preRideCallback(this.settings.$li.index(),this.settings.$next_tip),this.settings.modal&&this.show_modal()),this.settings.preStepCallback(this.settings.$li.index(),this.settings.$next_tip),this.settings.modal&&this.settings.expose&&this.expose(),this.settings.tipSettings=e.extend(this.settings,this.data_options(this.settings.$li)),this.settings.timer=parseInt(this.settings.timer,10),this.settings.tipSettings.tipLocationPattern=this.settings.tipLocationPatterns[this.settings.tipSettings.tipLocation],/body/i.test(this.settings.$target.selector)||this.scroll_to(),this.is_phone()?this.pos_phone(!0):this.pos_default(!0),n=this.settings.$next_tip.find(".joyride-timer-indicator"),/pop/i.test(this.settings.tipAnimation)?(n.width(0),this.settings.timer>0?(this.settings.$next_tip.show(),this.delay(function(){n.animate({width:n.parent().width()},this.settings.timer,"linear")}.bind(this),this.settings.tipAnimationFadeSpeed)):this.settings.$next_tip.show()):/fade/i.test(this.settings.tipAnimation)&&(n.width(0),this.settings.timer>0?(this.settings.$next_tip.fadeIn(this.settings.tipAnimationFadeSpeed).show(),this.delay(function(){n.animate({width:n.parent().width()},this.settings.timer,"linear")}.bind(this),this.settings.tipAnimationFadeSpeed)):this.settings.$next_tip.fadeIn(this.settings.tipAnimationFadeSpeed)),this.settings.$current_tip=this.settings.$next_tip):this.settings.$li&&this.settings.$target.length<1?this.show():this.end()):this.settings.paused=!0},is_phone:function(){return Modernizr?Modernizr.mq("only screen and (max-width: 767px)")||e(".lt-ie9").length>0:this.settings.$window.width()<767},hide:function(){this.settings.modal&&this.settings.expose&&this.un_expose(),this.settings.modal||e(".joyride-modal-bg").hide(),this.settings.$current_tip.css("visibility","hidden"),setTimeout(e.proxy(function(){this.hide(),this.css("visibility","visible")},this.settings.$current_tip),0),this.settings.postStepCallback(this.settings.$li.index(),this.settings.$current_tip)},set_li:function(e){e?(this.settings.$li=this.settings.$tip_content.eq(this.settings.startOffset),this.set_next_tip(),this.settings.$current_tip=this.settings.$next_tip):(this.settings.$li=this.settings.$li.next(),this.set_next_tip()),this.set_target()},set_next_tip:function(){this.settings.$next_tip=e(".joyride-tip-guide[data-index='"+this.settings.$li.index()+"']"),this.settings.$next_tip.data("closed","")},set_target:function(){var t=this +.settings.$li.attr("data-class"),r=this.settings.$li.attr("data-id"),i=function(){return r?e(n.getElementById(r)):t?e("."+t).first():e("body")};this.settings.$target=i()},scroll_to:function(){var n,r;n=e(t).height()/2,r=Math.ceil(this.settings.$target.offset().top-n+this.outerHeight(this.settings.$next_tip)),r>0&&this.scrollTo(e("html, body"),r,this.settings.scrollSpeed)},paused:function(){return e.inArray(this.settings.$li.index()+1,this.settings.pauseAfter)===-1},restart:function(){this.hide(),this.settings.$li=r,this.show("init")},pos_default:function(n,r){var i=Math.ceil(e(t).height()/2),s=this.settings.$next_tip.offset(),o=this.settings.$next_tip.find(".joyride-nub"),u=Math.ceil(this.outerWidth(o)/2),a=Math.ceil(this.outerHeight(o)/2),f=n||!1;f&&(this.settings.$next_tip.css("visibility","hidden"),this.settings.$next_tip.show()),typeof r=="undefined"&&(r=!1);if(!/body/i.test(this.settings.$target.selector)){if(this.bottom()){var l=this.settings.$target.offset().left;Foundation.rtl&&(l=this.settings.$target.offset().width-this.settings.$next_tip.width()+l),this.settings.$next_tip.css({top:this.settings.$target.offset().top+a+this.outerHeight(this.settings.$target),left:l}),this.nub_position(o,this.settings.tipSettings.nubPosition,"top")}else if(this.top()){var l=this.settings.$target.offset().left;Foundation.rtl&&(l=this.settings.$target.offset().width-this.settings.$next_tip.width()+l),this.settings.$next_tip.css({top:this.settings.$target.offset().top-this.outerHeight(this.settings.$next_tip)-a,left:l}),this.nub_position(o,this.settings.tipSettings.nubPosition,"bottom")}else this.right()?(this.settings.$next_tip.css({top:this.settings.$target.offset().top,left:this.outerWidth(this.settings.$target)+this.settings.$target.offset().left+u}),this.nub_position(o,this.settings.tipSettings.nubPosition,"left")):this.left()&&(this.settings.$next_tip.css({top:this.settings.$target.offset().top,left:this.settings.$target.offset().left-this.outerWidth(this.settings.$next_tip)-u}),this.nub_position(o,this.settings.tipSettings.nubPosition,"right"));!this.visible(this.corners(this.settings.$next_tip))&&this.settings.attempts0&&arguments[0]instanceof e)i=arguments[0];else{if(!this.settings.$target||!!/body/i.test(this.settings.$target.selector))return!1;i=this.settings.$target}if(i.length<1)return t.console&&console.error("element not valid",i),!1;n=e(this.settings.template.expose),this.settings.$body.append(n),n.css({top:i.offset().top,left:i.offset().left,width:this.outerWidth(i,!0),height:this.outerHeight(i,!0)}),r=e(this.settings.template.exposeCover),s={zIndex:i.css("z-index"),position:i.css("position")},o=i.attr("class")==null?"":i.attr("class"),i.css("z-index",parseInt(n.css("z-index"))+1),s.position=="static"&&i.css("position","relative"),i.data("expose-css",s),i.data("orig-class",o),i.attr("class",o+" "+this.settings.exposeAddClass),r.css({top:i.offset().top,left:i.offset().left,width:this.outerWidth(i,!0),height:this.outerHeight(i,!0)}),this.settings.$body.append(r),n.addClass(u),r.addClass(u),i.data("expose",u),this.settings.postExposeCallback(this.settings.$li.index(),this.settings.$next_tip,i),this.add_exposed(i)},un_expose:function(){var n,r,i,s,o,u=!1;if(arguments.length>0&&arguments[0]instanceof e)r=arguments[0];else{if(!this.settings.$target||!!/body/i.test(this.settings.$target.selector))return!1;r=this.settings.$target}if(r.length<1)return t.console&&console.error("element not valid",r),!1;n=r.data("expose"),i=e("."+n),arguments.length>1&&(u=arguments[1]),u===!0?e(".joyride-expose-wrapper,.joyride-expose-cover").remove():i.remove(),s=r.data("expose-css"),s.zIndex=="auto"?r.css("z-index",""):r.css("z-index",s.zIndex),s.position!=r.css("position")&&(s.position=="static"?r.css("position",""):r.css("position",s.position)),o=r.data("orig-class"),r.attr("class",o),r.removeData("orig-classes"),r.removeData("expose"),r.removeData("expose-z-index"),this.remove_exposed(r)},add_exposed:function(t){this.settings.exposed=this.settings.exposed||[],t instanceof e||typeof t=="object"?this.settings.exposed.push(t[0]):typeof t=="string"&&this.settings.exposed.push(t)},remove_exposed:function(t){var n,r;t instanceof e?n=t[0]:typeof t=="string"&&(n=t),this.settings.exposed=this.settings.exposed||[],r=this.settings.exposed.length;for(var i=0;ia&&(a=u),[n.offset().topn.offset().left]},visible:function(e){var t=e.length;while(t--)if(e[t])return!1;return!0},nub_position:function(e,t,n){t==="auto"?e.addClass(n):e.addClass(t)},startTimer:function(){this.settings.$li.length?this.settings.automate=setTimeout(function(){this.hide(),this.show(),this.startTimer()}.bind(this),this.settings.timer):clearTimeout(this.settings.automate)},end:function(){this.settings.cookieMonster&&e.cookie(this.settings.cookieName,"ridden",{expires:this.settings.cookieExpires,domain:this.settings.cookieDomain}),this.settings.timer>0&&clearTimeout(this.settings.automate),this.settings.modal&&this.settings.expose&&this.un_expose(),this.settings.$next_tip.data("closed",!0),e(".joyride-modal-bg").hide(),this.settings.$current_tip.hide(),this.settings.postStepCallback(this.settings.$li.index(),this.settings.$current_tip),this.settings.postRideCallback(this.settings.$li.index(),this.settings.$current_tip),e(".joyride-tip-guide").remove()},outerHTML:function(e){return e.outerHTML||(new XMLSerializer).serializeToString(e)},off:function(){e(this.scope).off(".joyride"),e(t).off(".joyride"),e(".joyride-close-tip, .joyride-next-tip, .joyride-modal-bg").off(".joyride"),e(".joyride-tip-guide, .joyride-modal-bg").remove(),clearTimeout(this.settings.automate),this.settings={}},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.magellan={name:"magellan",version:"4.2.2",settings:{activeClass:"active",threshold:0},init:function(t,n,r){return this.scope=t||this.scope,Foundation.inherit(this,"data_options"),typeof n=="object"&&e.extend(!0,this.settings,n),typeof n!="string"?(this.settings.init||(this.fixed_magellan=e("[data-magellan-expedition]"),this.set_threshold(),this.last_destination=e("[data-magellan-destination]").last(),this.events()),this.settings.init):this[n].call(this,r)},events:function(){var n=this;e(this.scope).on("arrival.fndtn.magellan","[data-magellan-arrival]",function(t){var r=e(this),i=r.closest("[data-magellan-expedition]"),s=i.attr("data-magellan-active-class")||n.settings.activeClass;r.closest("[data-magellan-expedition]").find("[data-magellan-arrival]").not(r).removeClass(s),r.addClass(s)}),this.fixed_magellan.on("update-position.fndtn.magellan",function(){var t=e(this)}).trigger("update-position"),e(t).on("resize.fndtn.magellan",function(){this.fixed_magellan.trigger("update-position")}.bind(this)).on("scroll.fndtn.magellan",function(){var r=e(t).scrollTop();n.fixed_magellan.each(function(){var t=e(this);typeof t.data("magellan-top-offset")=="undefined"&&t.data("magellan-top-offset",t.offset().top),typeof t.data("magellan-fixed-position")=="undefined"&&t.data("magellan-fixed-position",!1);var i=r+n.settings.threshold>t.data("magellan-top-offset"),s=t.attr("data-magellan-top-offset");t.data("magellan-fixed-position")!=i&&(t.data("magellan-fixed-position",i),i?(t.addClass("fixed"),t.css({position:"fixed",top:0})):(t.removeClass("fixed"),t.css({position:"",top:""})),i&&typeof s!="undefined"&&s!=0&&t.css({position:"fixed",top:s+"px"}))})}),this.last_destination.length>0&&e(t).on("scroll.fndtn.magellan",function(r){var i=e(t).scrollTop(),s=i+e(t).height(),o=Math.ceil(n.last_destination.offset().top);e("[data-magellan-destination]").each(function(){var t=e(this),r=t.attr("data-magellan-destination"),u=t.offset().top-i;u<=n.settings.threshold&&e("[data-magellan-arrival='"+r+"']").trigger("arrival"),s>=e(n.scope).height()&&o>i&&o0?this.outerHeight(this.fixed_magellan,!0):0)},off:function(){e(this.scope).off(".fndtn.magellan")},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";var i=function(){},s=function(i,s){if(i.hasClass(s.slides_container_class))return this;var f=this,l,c=i,h,p,d,v=0,m,g,y=!1,b=!1;c.children().first().addClass(s.active_slide_class),f.update_slide_number=function(t){s.slide_number&&(h.find("span:first").text(parseInt(t)+1),h.find("span:last").text(c.children().length)),s.bullets&&(p.children().removeClass(s.bullets_active_class),e(p.children().get(t)).addClass(s.bullets_active_class))},f.build_markup=function(){c.wrap('
        '),l=c.parent(),c.addClass(s.slides_container_class),s.navigation_arrows&&(l.append(e("").addClass(s.prev_class).append("")),l.append(e("").addClass(s.next_class).append(""))),s.timer&&(d=e("
        ").addClass(s.timer_container_class),d.append(""),d.append(e("
        ").addClass(s.timer_progress_class)),d.addClass(s.timer_paused_class),l.append(d)),s.slide_number&&(h=e("
        ").addClass(s.slide_number_class),h.append(" of "),l.append(h)),s.bullets&&(p=e("
          ").addClass(s.bullets_container_class),l.append(p),c.children().each(function(t,n){var r=e("
        1. ").attr("data-orbit-slide",t);p.append(r)})),s.stack_on_small&&l.addClass(s.stack_on_small_class),f.update_slide_number(0)},f._goto=function(t,n){if(t===v)return!1;typeof g=="object"&&g.restart();var r=c.children(),i="next";y=!0,t=r.length?t=0:t<0&&(t=r.length-1);var o=e(r.get(v)),u=e(r.get(t));o.css("zIndex",2),u.css("zIndex",4).addClass("active"),c.trigger("orbit:before-slide-change"),s.before_slide_change();var a=function(){var e=function(){v=t,y=!1,n===!0&&(g=f.create_timer(),g.start()),f.update_slide_number(v),c.trigger("orbit:after-slide-change",[{slide_number:v,total_slides:r.length}]),s.after_slide_change(v,r.length)};c.height()!=u.height()?c.animate({height:u.height()},250,"linear",e):e()};if(r.length===1)return a(),!1;var l=function(){i==="next"&&m.next(o,u,a),i==="prev"&&m.prev(o,u,a)};u.height()>c.height()?c.animate({height:u.height()},250,"linear",l):l()},f.next=function(e){e.stopImmediatePropagation(),e.preventDefault(),f._goto(v+1)},f.prev=function(e){e.stopImmediatePropagation(),e.preventDefault(),f._goto(v-1)},f.link_custom=function(t){t.preventDefault();var n=e(this).attr("data-orbit-link");if(typeof n=="string"&&(n=e.trim(n))!=""){var r=l.find("[data-orbit-slide="+n+"]");r.index()!=-1&&f._goto(r.index())}},f.link_bullet=function(t){var n=e(this).attr("data-orbit-slide");typeof n=="string"&&(n=e.trim(n))!=""&&f._goto(n)},f.timer_callback=function(){f._goto(v+1,!0)},f.compute_dimensions=function(){var t=e(c.children().get(v)),n=t.height();s.variable_height||c.children().each(function(){e(this).height()>n&&(n=e(this).height())}),c.height(n)},f.create_timer=function(){var e=new o(l.find("."+s.timer_container_class),s,f.timer_callback);return e},f.stop_timer=function(){typeof g=="object"&&g.stop()},f.toggle_timer=function(){var e=l.find("."+s.timer_container_class);e.hasClass(s.timer_paused_class)?(typeof g=="undefined"&&(g=f.create_timer()),g.start()):typeof g=="object"&&g.stop()},f.init=function(){f.build_markup(),s.timer&&(g=f.create_timer(),g.start()),m=new a(c),s.animation==="slide"&&(m=new u(c)),l.on("click","."+s.next_class,f.next),l.on("click","."+s.prev_class,f.prev),l.on("click","[data-orbit-slide]",f.link_bullet),l.on("click",f.toggle_timer),l.on("touchstart.fndtn.orbit",function(e){e.touches||(e=e.originalEvent);var t={start_page_x:e.touches[0].pageX,start_page_y:e.touches[0].pageY,start_time:(new Date).getTime(),delta_x:0,is_scrolling:r};l.data("swipe-transition",t),e.stopPropagation()}).on("touchmove.fndtn.orbit",function(e){e.touches||(e=e.originalEvent);if(e.touches.length>1||e.scale&&e.scale!==1)return;var t=l.data("swipe-transition");typeof t=="undefined"&&(t={}),t.delta_x=e.touches[0].pageX-t.start_page_x,typeof t.is_scrolling=="undefined"&&(t.is_scrolling=!!(t.is_scrolling||Math.abs(t.delta_x)0&&(this.locked=!0,t.trigger("close"),this.toggle_bg(t),this.hide(n,this.settings.css.close))},close_targets:function(){var e="."+this.settings.dismissModalClass;return this.settings.closeOnBackgroundClick?e+", ."+this.settings.bgClass:e},toggle_bg:function(t){e(".reveal-modal-bg").length===0&&(this.settings.bg=e("
          ",{"class":this.settings.bgClass}).appendTo("body")),this.settings.bg.filter(":visible").length>0?this.hide(this.settings.bg):this.show(this.settings.bg)},show:function(n,r){if(r){if(/pop/i.test(this.settings.animation)){r.top=e(t).scrollTop()-n.data("offset")+"px";var i={top:e(t).scrollTop()+n.data("css-top")+"px",opacity:1};return this.delay(function(){return n.css(r).animate(i,this.settings.animationSpeed,"linear",function(){this.locked=!1,n.trigger("opened")}.bind(this)).addClass("open")}.bind(this),this.settings.animationSpeed/2)}if(/fade/i.test(this.settings.animation)){var i={opacity:1};return this.delay(function(){return n.css(r).animate(i,this.settings.animationSpeed,"linear",function(){this.locked=!1,n.trigger("opened")}.bind(this)).addClass("open")}.bind(this),this.settings.animationSpeed/2)}return n.css(r).show().css({opacity:1}).addClass("open").trigger("opened")}return/fade/i.test(this.settings.animation)?n.fadeIn(this.settings.animationSpeed/2):n.show()},hide:function(n,r){if(r){if(/pop/i.test(this.settings.animation)){var i={top:-e(t).scrollTop()-n.data("offset")+"px",opacity:0};return this.delay(function(){return n.animate(i,this.settings.animationSpeed,"linear",function(){this.locked=!1,n.css(r).trigger("closed")}.bind(this)).removeClass("open")}.bind(this),this.settings.animationSpeed/2)}if(/fade/i.test(this.settings.animation)){var i={opacity:0};return this.delay(function(){return n.animate(i,this.settings.animationSpeed,"linear",function(){this.locked=!1,n.css(r).trigger("closed")}.bind(this)).removeClass("open")}.bind(this),this.settings.animationSpeed/2)}return n.hide().css(r).removeClass("open").trigger("closed")}return/fade/i.test(this.settings.animation)?n.fadeOut(this.settings.animationSpeed/2):n.hide()},close_video:function(t){var n=e(this).find(".flex-video"),r=n.find("iframe");r.length>0&&(r.attr("data-src",r[0].src),r.attr("src","about:blank"),n.hide())},open_video:function(t){var n=e(this).find(".flex-video"),i=n.find("iframe");if(i.length>0){var s=i.attr("data-src");if(typeof s=="string")i[0].src=i.attr("data-src");else{var o=i[0].src;i[0].src=r,i[0].src=o}n.show()}},cache_offset:function(e){var t=e.show().height()+parseInt(e.css("top"),10);return e.hide(),t},off:function(){e(this.scope).off(".fndtn.reveal")},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n){"use strict";Foundation.libs.section={name:"section",version:"4.3.1",settings:{deep_linking:!1,small_breakpoint:768,one_up:!0,section_selector:"[data-section]",region_selector:"section, .section, [data-section-region]",title_selector:".title, [data-section-title]",resized_data_attr:"data-section-resized",small_style_data_attr:"data-section-small-style",content_selector:".content, [data-section-content]",nav_selector:'[data-section="vertical-nav"], [data-section="horizontal-nav"]',active_class:"active",callback:function(){}},init:function(t,n,r){var i=this;return Foundation.inherit(this,"throttle data_options position_right offset_right"),typeof n=="object"&&e.extend(!0,i.settings,n),typeof n!="string"?(this.events(),!0):this[n].call(this,r)},events:function(){var r=this,i=[],s=r.settings.section_selector,o=r.settings.region_selector.split(","),u=r.settings.title_selector.split(",");for(var a=0,f=o.length;a"+l+">"+u[c];i.push(p+" a"),i.push(p)}}e(r.scope).on("click.fndtn.section",i.join(","),function(t){var n=e(this).closest(r.settings.title_selector);r.close_navs(n),n.siblings(r.settings.content_selector).length>0&&r.toggle_active.call(n[0],t)}),e(t).on("resize.fndtn.section",r.throttle(function(){r.resize()},30)).on("hashchange.fndtn.section",r.set_active_from_hash),e(n).on("click.fndtn.section",function(t){if(t.isPropagationStopped&&t.isPropagationStopped())return;if(t.target===n)return;r.close_navs(e(t.target).closest(r.settings.title_selector))}),e(t).triggerHandler("resize.fndtn.section"),e(t).triggerHandler("hashchange.fndtn.section")},close_navs:function(t){var n=Foundation.libs.section,r=e(n.settings.nav_selector).filter(function(){return!e.extend({},n.settings,n.data_options(e(this))).one_up});if(t.length>0){var i=t.parent().parent();if(n.is_horizontal_nav(i)||n.is_vertical_nav(i))r=r.filter(function(){return this!==i[0]})}r.children(n.settings.region_selector).removeClass(n.settings.active_class)},toggle_active:function(t){var n=e(this),r=Foundation.libs.section,i=n.parent(),s=n.siblings(r.settings.content_selector),o=i.parent(),u=e.extend({},r.settings,r.data_options(o)),a=o.children(r.settings.region_selector).filter("."+r.settings.active_class);!u.deep_linking&&s.length>0&&t.preventDefault(),t.stopPropagation(),i.hasClass(r.settings.active_class)?!u.one_up&&(r.small(o)||r.is_vertical_nav(o)||r.is_horizontal_nav(o)||r.is_accordion(o))&&i.removeClass(r.settings.active_class):(a.removeClass(r.settings.active_class),i.addClass(r.settings.active_class),r.resize(i.find(r.settings.section_selector).not("["+r.settings.resized_data_attr+"]"),!0)),u.callback(o)},check_resize_timer:null,resize:function(t,r){var i=Foundation.libs.section,s=i.small(e(n)),o=function(e,t){return!i.is_accordion(e)&&!e.is("["+i.settings.resized_data_attr+"]")&&(!s||i.is_horizontal_tabs(e))&&t===(e.css("display")==="none"||!e.parent().is(":visible"))};t=t||e(i.settings.section_selector),clearTimeout(i.check_resize_timer),s||t.removeAttr(i.settings.small_style_data_attr),t.filter(function(){return o(e(this),!1)}).each(function(){var t=e(this),n=t.children(i.settings.region_selector),s=n.children(i.settings.title_selector),o=n.children(i.settings.content_selector),u=0;if(r&&t.children(i.settings.region_selector).filter("."+i.settings.active_class).length==0){var a=e.extend({},i.settings,i.data_options(t));!a.deep_linking&&(a.one_up||!i.is_horizontal_nav(t)&&!i.is_vertical_nav(t)&&!i.is_accordion(t))&&n.filter(":visible").first().addClass(i.settings.active_class)}if(i.is_horizontal_tabs(t)||i.is_auto(t)){var f=0;s.each(function(){var t=e(this);if(t.is(":visible")){t.css(i.rtl?"right":"left",f);var n=parseInt(t.css("border-"+(i.rtl?"left":"right")+"-width"),10);n.toString()==="Nan"&&(n=0),f+=i.outerWidth(t)-n,u=Math.max(u,i.outerHeight(t))}}),s.css("height",u),n.each(function(){var t=e(this),n=t.children(i.settings.content_selector),r=parseInt(n.css("border-top-width"),10);r.toString()==="Nan"&&(r=0),t.css("padding-top",u-r)}),t.css("min-height",u)}else if(i.is_horizontal_nav(t)){var l=!0;s.each(function(){u=Math.max(u,i.outerHeight(e(this)))}),n.each(function(){var n=e(this);n.css("margin-left","-"+(l?t:n.children(i.settings.title_selector)).css("border-left-width")),l=!1}),n.css("margin-top","-"+t.css("border-top-width")),s.css("height",u),o.css("top",u),t.css("min-height",u)}else if(i.is_vertical_tabs(t)){var c=0;s.each(function(){var t=e(this);if(t.is(":visible")){t.css("top",c);var n=parseInt(t.css("border-top-width"),10);n.toString()==="Nan"&&(n=0),c+=i.outerHeight(t)-n}}),o.css("min-height",c+1)}else if(i.is_vertical_nav(t)){var h=0,p=!0;s.each(function(){h=Math.max(h,i.outerWidth(e(this)))}),n.each(function(){var n=e(this);n.css("margin-top","-"+(p?t:n.children(i.settings.title_selector)).css("border-top-width")),p=!1}),s.css("width",h),o.css(i.rtl?"right":"left",h),t.css("width",h)}t.attr(i.settings.resized_data_attr,!0)}),e(i.settings.section_selector).filter(function(){return o(e(this),!0)}).length>0&&(i.check_resize_timer=setTimeout(function(){i.resize(t.filter(function(){return o(e(this),!1)}),!0)},700)),s&&t.attr(i.settings.small_style_data_attr,!0)},is_vertical_nav:function(e){return/vertical-nav/i.test(e.data("section"))},is_horizontal_nav:function(e){return/horizontal-nav/i.test(e.data("section"))},is_accordion:function(e){return/accordion/i.test(e.data("section"))},is_horizontal_tabs:function(e){return/^tabs$/i.test(e.data("section"))},is_vertical_tabs:function(e){return/vertical-tabs/i.test(e.data("section"))},is_auto:function(e){var t=e.data("section");return t===""||/auto/i.test(t)},set_active_from_hash:function(){var n=Foundation.libs.section,r=t.location.hash.substring(1),i=e(n.settings.section_selector);i.each(function(){var t=e(this),i=e.extend({},n.settings,n.data_options(t)),s=t.children(n.settings.region_selector),o=i.deep_linking&&r.length>0,u=!1;s.each(function(){var t=e(this);if(u)t.removeClass(n.settings.active_class);else if(o){var i=t.children(n.settings.content_selector).data("slug");i&&(new RegExp(i,"i")).test(r)?(t.hasClass(n.settings.active_class)||t.addClass(n.settings.active_class),u=!0):t.removeClass(n.settings.active_class)}else t.hasClass(n.settings.active_class)&&(u=!0)}),!u&&!i.deep_linking&&(i.one_up||!n.is_horizontal_nav(t)&&!n.is_vertical_nav(t)&&!n.is_accordion(t))&&s.filter(":visible").first().addClass(n.settings.active_class)})},reflow:function(){var t=Foundation.libs.section;e(t.settings.section_selector).removeAttr(t.settings.resized_data_attr),t.throttle(function(){t.resize()},30)()},small:function(t){var n=e.extend({},this.settings,this.data_options(t));return this.is_horizontal_tabs(t)?!1:t&&this.is_accordion(t)?!0:e("html").hasClass("lt-ie9")?!0:e("html").hasClass("ie8compat")?!0:e(this.scope).width()'+t+''}},cache:{},init:function(t,n,r){Foundation.inherit(this,"data_options");var i=this;typeof n=="object"?e.extend(!0,this.settings,n):typeof r!="undefined"&&e.extend(!0,this.settings,r);if(typeof n=="string")return this[n].call(this,r);Modernizr.touch?e(this.scope).on("click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip","[data-tooltip]",function(t){var n=e.extend({},i.settings,i.data_options(e(this)));n["disable-for-touch"]||(t.preventDefault(),e(n.tooltipClass).hide(),i.showOrCreateTip(e(this)))}).on("click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip",this.settings.tooltipClass,function(t){t.preventDefault(),e(this).fadeOut(150)}):e(this.scope).on("mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip","[data-tooltip]",function(t){var n=e(this);/enter|over/i.test(t.type)?i.showOrCreateTip(n):(t.type==="mouseout"||t.type==="mouseleave")&&i.hide(n)})},showOrCreateTip:function(e){var t=this.getTip(e);return t&&t.length>0?this.show(e):this.create(e)},getTip:function(t){var n=this.selector(t),r=null;return n&&(r=e('span[data-selector="'+n+'"]'+this.settings.tooltipClass)),typeof r=="object"?r:!1},selector:function(e){var t=e.attr("id"),n=e.attr("data-tooltip")||e.attr("data-selector");return(t&&t.length<1||!t)&&typeof n!="string"&&(n="tooltip"+Math.random().toString(36).substring(7),e.attr("data-selector",n)),t&&t.length>0?t:n},create:function(t){var n=e(this.settings.tipTemplate(this.selector(t),e("
          ").html(t.attr("title")).html())),r=this.inheritable_classes(t);n.addClass(r).appendTo(this.settings.appendTo),Modernizr.touch&&n.append('tap to close '),t.removeAttr("title").attr("title",""),this.show(t)},reposition:function(n,r,i){var s,o,u,a,f,l;r.css("visibility","hidden").show(),s=n.data("width"),o=r.children(".nub"),u=this.outerHeight(o),a=this.outerHeight(o),l=function(e,t,n,r,i,s){return e.css({top:t?t:"auto",bottom:r?r:"auto",left:i?i:"auto",right:n?n:"auto",width:s?s:"auto"}).end()},l(r,n.offset().top+this.outerHeight(n)+10,"auto","auto",n.offset().left,s);if(e(t).width()<767)l(r,n +.offset().top+this.outerHeight(n)+10,"auto","auto",12.5,e(this.scope).width()),r.addClass("tip-override"),l(o,-u,"auto","auto",n.offset().left);else{var c=n.offset().left;Foundation.rtl&&(c=n.offset().left+n.offset().width-this.outerWidth(r)),l(r,n.offset().top+this.outerHeight(n)+10,"auto","auto",c,s),r.removeClass("tip-override"),i&&i.indexOf("tip-top")>-1?l(r,n.offset().top-this.outerHeight(r),"auto","auto",c,s).removeClass("tip-override"):i&&i.indexOf("tip-left")>-1?l(r,n.offset().top+this.outerHeight(n)/2-u*2.5,"auto","auto",n.offset().left-this.outerWidth(r)-u,s).removeClass("tip-override"):i&&i.indexOf("tip-right")>-1&&l(r,n.offset().top+this.outerHeight(n)/2-u*2.5,"auto","auto",n.offset().left+this.outerWidth(n)+u,s).removeClass("tip-override")}r.css("visibility","visible").hide()},inheritable_classes:function(t){var n=["tip-top","tip-left","tip-bottom","tip-right","noradius"].concat(this.settings.additionalInheritableClasses),r=t.attr("class"),i=r?e.map(r.split(" "),function(t,r){if(e.inArray(t,n)!==-1)return t}).join(" "):"";return e.trim(i)},show:function(e){var t=this.getTip(e);this.reposition(e,t,e.attr("class")),t.fadeIn(150)},hide:function(e){var t=this.getTip(e);t.fadeOut(150)},reload:function(){var t=e(this);return t.data("fndtn-tooltips")?t.foundationTooltips("destroy").foundationTooltips("init"):t.foundationTooltips("init")},off:function(){e(this.scope).off(".fndtn.tooltip"),e(this.settings.tooltipClass).each(function(t){e("[data-tooltip]").get(t).attr("title",e(this).text())}).remove()},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.topbar={name:"topbar",version:"4.3.1",settings:{index:0,stickyClass:"sticky",custom_back_text:!0,back_text:"Back",is_hover:!0,mobile_show_parent_link:!0,scrolltop:!0,init:!1},init:function(n,r,i){Foundation.inherit(this,"data_options");var s=this;return typeof r=="object"?e.extend(!0,this.settings,r):typeof i!="undefined"&&e.extend(!0,this.settings,i),typeof r!="string"?(e(".top-bar, [data-topbar]").each(function(){e.extend(!0,s.settings,s.data_options(e(this))),s.settings.$w=e(t),s.settings.$topbar=e(this),s.settings.$section=s.settings.$topbar.find("section"),s.settings.$titlebar=s.settings.$topbar.children("ul").first(),s.settings.$topbar.data("index",0);var n=e("
          ").insertAfter(s.settings.$topbar);s.settings.breakPoint=n.width(),n.remove(),s.assemble(),s.settings.is_hover&&s.settings.$topbar.find(".has-dropdown").addClass("not-click"),s.settings.$topbar.parent().hasClass("fixed")&&e("body").css("padding-top",s.outerHeight(s.settings.$topbar))}),s.settings.init||this.events(),this.settings.init):this[r].call(this,i)},timer:null,events:function(){var n=this,r=this.outerHeight(e(".top-bar, [data-topbar]"));e(this.scope).off(".fndtn.topbar").on("click.fndtn.topbar",".top-bar .toggle-topbar, [data-topbar] .toggle-topbar",function(i){var s=e(this).closest(".top-bar, [data-topbar]"),o=s.find("section, .section"),u=s.children("ul").first();i.preventDefault(),n.breakpoint()&&(n.rtl?(o.css({right:"0%"}),o.find(">.name").css({right:"100%"})):(o.css({left:"0%"}),o.find(">.name").css({left:"100%"})),o.find("li.moved").removeClass("moved"),s.data("index",0),s.toggleClass("expanded").css("height","")),s.hasClass("expanded")?s.parent().hasClass("fixed")&&(s.parent().removeClass("fixed"),s.addClass("fixed"),e("body").css("padding-top","0"),n.settings.scrolltop&&t.scrollTo(0,0)):s.hasClass("fixed")&&(s.parent().addClass("fixed"),s.removeClass("fixed"),e("body").css("padding-top",r))}).on("click.fndtn.topbar",".top-bar li.has-dropdown",function(t){if(n.breakpoint())return;var r=e(this),i=e(t.target),s=r.closest("[data-topbar], .top-bar"),o=s.data("topbar");if(n.settings.is_hover&&!Modernizr.touch)return;t.stopImmediatePropagation(),i[0].nodeName==="A"&&i.parent().hasClass("has-dropdown")&&t.preventDefault(),r.hasClass("hover")?r.removeClass("hover").find("li").removeClass("hover"):r.addClass("hover")}).on("click.fndtn.topbar",".top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a",function(t){if(n.breakpoint()){t.preventDefault();var r=e(this),i=r.closest(".top-bar, [data-topbar]"),s=i.find("section, .section"),o=i.children("ul").first(),u=r.next(".dropdown").outerHeight(),a=r.closest("li");i.data("index",i.data("index")+1),a.addClass("moved"),n.rtl?(s.css({right:-(100*i.data("index"))+"%"}),s.find(">.name").css({right:100*i.data("index")+"%"})):(s.css({left:-(100*i.data("index"))+"%"}),s.find(">.name").css({left:100*i.data("index")+"%"})),i.css("height",n.outerHeight(r.siblings("ul"),!0)+n.height(o))}}),e(t).on("resize.fndtn.topbar",function(){n.breakpoint()||e(".top-bar, [data-topbar]").css("height","").removeClass("expanded").find("li").removeClass("hover")}.bind(this)),e("body").on("click.fndtn.topbar",function(t){var n=e(t.target).closest("[data-topbar], .top-bar");if(n.length>0)return;e(".top-bar li, [data-topbar] li").removeClass("hover")}),e(this.scope).on("click.fndtn",".top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back",function(t){t.preventDefault();var r=e(this),i=r.closest(".top-bar, [data-topbar]"),s=i.children("ul").first(),o=i.find("section, .section"),u=r.closest("li.moved"),a=u.parent();i.data("index",i.data("index")-1),n.rtl?(o.css({right:-(100*i.data("index"))+"%"}),o.find(">.name").css({right:100*i.data("index")+"%"})):(o.css({left:-(100*i.data("index"))+"%"}),o.find(">.name").css({left:100*i.data("index")+"%"})),i.data("index")===0?i.css("height",""):i.css("height",n.outerHeight(a,!0)+n.height(s)),setTimeout(function(){u.removeClass("moved")},300)})},breakpoint:function(){return e(n).width()<=this.settings.breakPoint||e("html").hasClass("lt-ie9")},assemble:function(){var t=this;this.settings.$section.detach(),this.settings.$section.find(".has-dropdown>a").each(function(){var n=e(this),r=n.siblings(".dropdown"),i=n.attr("href");if(t.settings.mobile_show_parent_link&&i&&i.length>1)var s=e('
        2. '+n.text()+"
        3. ");else var s=e('
        4. ');t.settings.custom_back_text==1?s.find("h5>a").html("« "+t.settings.back_text):s.find("h5>a").html("« "+n.html()),r.prepend(s)}),this.settings.$section.appendTo(this.settings.$topbar),this.sticky()},height:function(t){var n=0,r=this;return t.find("> li").each(function(){n+=r.outerHeight(e(this),!0)}),n},sticky:function(){var n="."+this.settings.stickyClass;if(e(n).length>0){var r=e(n).length?e(n).offset().top:0,i=e(t),s=this.outerHeight(e(".top-bar")),o;e(t).resize(function(){clearTimeout(o),o=setTimeout(function(){r=e(n).offset().top},105)}),i.scroll(function(){i.scrollTop()>r?(e(n).addClass("fixed"),e("body").css("padding-top",s)):i.scrollTop()<=r&&(e(n).removeClass("fixed"),e("body").css("padding-top","0"))})}},off:function(){e(this.scope).off(".fndtn.topbar"),e(t).off(".fndtn.topbar")},reflow:function(){}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.interchange={name:"interchange",version:"4.2.4",cache:{},images_loaded:!1,settings:{load_attr:"interchange",named_queries:{"default":"only screen and (min-width: 1px)",small:"only screen and (min-width: 768px)",medium:"only screen and (min-width: 1280px)",large:"only screen and (min-width: 1440px)",landscape:"only screen and (orientation: landscape)",portrait:"only screen and (orientation: portrait)",retina:"only screen and (-webkit-min-device-pixel-ratio: 2),only screen and (min--moz-device-pixel-ratio: 2),only screen and (-o-min-device-pixel-ratio: 2/1),only screen and (min-device-pixel-ratio: 2),only screen and (min-resolution: 192dpi),only screen and (min-resolution: 2dppx)"},directives:{replace:function(e,t){if(/IMG/.test(e[0].nodeName)){var n=e[0].src;if((new RegExp(t,"i")).test(n))return;return e[0].src=t,e.trigger("replace",[e[0].src,n])}}}},init:function(t,n,r){return Foundation.inherit(this,"throttle"),typeof n=="object"&&e.extend(!0,this.settings,n),this.events(),this.images(),typeof n!="string"?this.settings.init:this[n].call(this,r)},events:function(){var n=this;e(t).on("resize.fndtn.interchange",n.throttle(function(){n.resize.call(n)},50))},resize:function(){var t=this.cache;if(!this.images_loaded){setTimeout(e.proxy(this.resize,this),50);return}for(var n in t)if(t.hasOwnProperty(n)){var r=this.results(n,t[n]);r&&this.settings.directives[r.scenario[1]](r.el,r.scenario[0])}},results:function(t,n){var r=n.length;if(r>0){var i=e('[data-uuid="'+t+'"]');for(var s=r-1;s>=0;s--){var o,u=n[s][2];this.settings.named_queries.hasOwnProperty(u)?o=matchMedia(this.settings.named_queries[u]):o=matchMedia(u);if(o.matches)return{el:i,scenario:n[s]}}}return!1},images:function(e){return typeof this.cached_images=="undefined"||e?this.update_images():this.cached_images},update_images:function(){var t=n.getElementsByTagName("img"),r=t.length,i=0,s="data-"+this.settings.load_attr;this.cached_images=[],this.images_loaded=!1;for(var o=r-1;o>=0;o--)this.loaded(e(t[o]),function(e){i++;if(e){var t=e.getAttribute(s)||"";t.length>0&&this.cached_images.push(e)}i===r&&(this.images_loaded=!0,this.enhance())}.bind(this));return"deferred"},loaded:function(e,t){function n(){t(e[0])}function r(){this.one("load",n);if(/MSIE (\d+\.\d+);/.test(navigator.userAgent)){var e=this.attr("src"),t=e.match(/\?/)?"&":"?";t+="random="+(new Date).getTime(),this.attr("src",e+t)}}if(!e.attr("src")){n();return}e[0].complete||e[0].readyState===4?n():r.call(e)},enhance:function(){var n=this.images().length;for(var r=n-1;r>=0;r--)this._object(e(this.images()[r]));return e(t).trigger("resize")},parse_params:function(e,t,n){return[this.trim(e),this.convert_directive(t),this.trim(n)]},convert_directive:function(e){var t=this.trim(e);return t.length>0?t:"replace"},_object:function(e){var t=this.parse_data_attr(e),n=[],r=t.length;if(r>0)for(var i=r-1;i>=0;i--){var s=t[i].split(/\((.*?)(\))$/);if(s.length>1){var o=s[0].split(","),u=this.parse_params(o[0],o[1],s[1]);n.push(u)}}return this.store(e,n)},uuid:function(e){function n(){return((1+Math.random())*65536|0).toString(16).substring(1)}var t=e||"-";return n()+n()+t+n()+t+n()+t+n()+t+n()+n()+n()},store:function(e,t){var n=this.uuid(),r=e.data("uuid");return r?this.cache[r]:(e.attr("data-uuid",n),this.cache[n]=t)},trim:function(t){return typeof t=="string"?e.trim(t):t},parse_data_attr:function(e){var t=e.data(this.settings.load_attr).split(/\[(.*?)\]/),n=t.length,r=[];for(var i=n-1;i>=0;i--)t[i].replace(/[\W\d]+/,"").length>4&&r.push(t[i]);return r},reflow:function(){this.images(!0)}}}(Foundation.zj,this,this.document),function(e,t,n){function f(e){var t={},r=/^jQuery\d+$/;return n.each(e.attributes,function(e,n){n.specified&&!r.test(n.name)&&(t[n.name]=n.value)}),t}function l(e,r){var i=this,s=n(i);if(i.value==s.attr("placeholder")&&s.hasClass("placeholder"))if(s.data("placeholder-password")){s=s.hide().next().show().attr("id",s.removeAttr("id").data("placeholder-id"));if(e===!0)return s[0].value=r;s.focus()}else i.value="",s.removeClass("placeholder"),i==t.activeElement&&i.select()}function c(){var e,t=this,r=n(t),i=r,s=this.id;if(t.value==""){if(t.type=="password"){if(!r.data("placeholder-textinput")){try{e=r.clone().attr({type:"text"})}catch(o){e=n("").attr(n.extend(f(this),{type:"text"}))}e.removeAttr("name").data({"placeholder-password":!0,"placeholder-id":s}).bind("focus.placeholder",l),r.data({"placeholder-textinput":e,"placeholder-id":s}).before(e)}r=r.removeAttr("id").hide().prev().attr("id",s).show()}r.addClass("placeholder"),r[0].value=r.attr("placeholder")}else r.removeClass("placeholder")}var r="placeholder"in t.createElement("input"),i="placeholder"in t.createElement("textarea"),s=n.fn,o=n.valHooks,u,a;r&&i?(a=s.placeholder=function(){return this},a.input=a.textarea=!0):(a=s.placeholder=function(){var e=this;return e.filter((r?"textarea":":input")+"[placeholder]").not(".placeholder").bind({"focus.placeholder":l,"blur.placeholder":c}).data("placeholder-enabled",!0).trigger("blur.placeholder"),e},a.input=r,a.textarea=i,u={get:function(e){var t=n(e);return t.data("placeholder-enabled")&&t.hasClass("placeholder")?"":e.value},set:function(e,r){var i=n(e);return i.data("placeholder-enabled")?(r==""?(e.value=r,e!=t.activeElement&&c.call(e)):i.hasClass("placeholder")?l.call(e,!0,r)||(e.value=r):e.value=r,i):e.value=r}},r||(o.input=u),i||(o.textarea=u),n(function(){n(t).delegate("form","submit.placeholder",function(){var e=n(".placeholder",this).each(l);setTimeout(function(){e.each(c)},10)})}),n(e).bind("beforeunload.placeholder",function(){n(".placeholder").each(function(){this.value=""})}))}(this,document,Foundation.zj),function(e,t,n,r){"use strict";Foundation.libs.placeholder={name:"placeholder",version:"4.2.2",init:function(n,r,i){this.scope=n||this.scope,typeof r!="string"&&(t.onload=function(){e("input, textarea").placeholder()})}}}(Foundation.zj,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.abide={name:"abide",version:"4.3.0",settings:{live_validate:!0,focus_on_invalid:!0,timeout:1e3,patterns:{alpha:/[a-zA-Z]+/,alpha_numeric:/[a-zA-Z0-9]+/,integer:/-?\d+/,number:/-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?/,password:/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,card:/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,cvv:/^([0-9]){3,4}$/,email:/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,url:/(https?|ftp|file|ssh):\/\/(((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?/,domain:/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/,datetime:/([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])T([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(Z|([\-\+]([0-1][0-9])\:00))/,date:/(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,time:/(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}/,dateISO:/\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/,month_day_year:/(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/,color:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/}},timer:null,init:function(t,n,r){typeof n=="object"&&e.extend(!0,this.settings,n);if(typeof n=="string")return this[n].call(this,r);this.settings.init||this.events()},events:function(){var t=this,n=e("form[data-abide]",this.scope).attr("novalidate","novalidate");n.on("submit validate",function(n){return t.validate(e(this).find("input, textarea, select").get(),n)}),this.settings.init=!0;if(!this.settings.live_validate)return;n.find("input, textarea, select").on("blur change",function(e){t.validate([this],e)}).on("keydown",function(e){clearTimeout(t.timer),t.timer=setTimeout(function(){t.validate([this],e)}.bind(this),t.settings.timeout)})},validate:function(t,n){var r=this.parse_patterns(t),i=r.length,s=e(t[0]).closest("form");while(i--)if(!r[i]&&/submit/.test(n.type))return this.settings.focus_on_invalid&&t[i].focus(),s.trigger("invalid"),e(t[i]).closest("form").attr("data-invalid",""),!1;return/submit/.test(n.type)&&s.trigger("valid"),s.removeAttr("data-invalid"),!0},parse_patterns:function(e){var t=e.length,n=[];for(var r=t-1;r>=0;r--)n.push(this.pattern(e[r]));return this.check_validation_and_apply_styles(n)},pattern:function(e){var t=e.getAttribute("type"),n=typeof e.getAttribute("required")=="string";if(this.settings.patterns.hasOwnProperty(t))return[e,this.settings.patterns[t],n];var r=e.getAttribute("pattern")||"";return this.settings.patterns.hasOwnProperty(r)&&r.length>0?[e,this.settings.patterns[r],n]:r.length>0?[e,new RegExp(r),n]:(r=/.*/,[e,r,n])},check_validation_and_apply_styles:function(t){var n=t.length,r=[];for(var i=n-1;i>=0;i--){var s=t[i][0],o=t[i][2],u=s.value,a=s.type==="radio",f=o?s.value.length>0:!0;a&&o?r.push(this.valid_radio(s,o)):t[i][1].test(u)&&f||!o&&s.value.length<1?(e(s).removeAttr("data-invalid").parent().removeClass("error"),r.push(!0)):(e(s).attr("data-invalid","").parent().addClass("error"),r.push(!1))}return r},valid_radio:function(t,r){var i=t.getAttribute("name"),s=n.getElementsByName(i),o=s.length,u=!1;for(var a=0;a= 0; i--) { + el_patterns.push(this.pattern(els[i])); + } + + return this.check_validation_and_apply_styles(el_patterns); + }, + + pattern : function (el) { + var type = el.getAttribute('type'), + required = typeof el.getAttribute('required') === 'string'; + + if (this.settings.patterns.hasOwnProperty(type)) { + return [el, this.settings.patterns[type], required]; + } + + var pattern = el.getAttribute('pattern') || ''; + + if (this.settings.patterns.hasOwnProperty(pattern) && pattern.length > 0) { + return [el, this.settings.patterns[pattern], required]; + } else if (pattern.length > 0) { + return [el, new RegExp(pattern), required]; + } + + pattern = /.*/; + + return [el, pattern, required]; + }, + + check_validation_and_apply_styles : function (el_patterns) { + var count = el_patterns.length, + validations = []; + + for (var i = count - 1; i >= 0; i--) { + var el = el_patterns[i][0], + required = el_patterns[i][2], + value = el.value, + is_radio = el.type === "radio", + valid_length = (required) ? (el.value.length > 0) : true; + + if (is_radio && required) { + validations.push(this.valid_radio(el, required)); + } else { + if (el_patterns[i][1].test(value) && valid_length || + !required && el.value.length < 1) { + $(el).removeAttr('data-invalid').parent().removeClass('error'); + validations.push(true); + } else { + $(el).attr('data-invalid', '').parent().addClass('error'); + validations.push(false); + } + } + } + + return validations; + }, + + valid_radio : function (el, required) { + var name = el.getAttribute('name'), + group = document.getElementsByName(name), + count = group.length, + valid = false; + + for (var i=0; i < count; i++) { + if (group[i].checked) valid = true; + } + + for (var i=0; i < count; i++) { + if (valid) { + $(group[i]).removeAttr('data-invalid').parent().removeClass('error'); + } else { + $(group[i]).attr('data-invalid', '').parent().addClass('error'); + } + } + + return valid; + } + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.alerts.js b/videodb/lib/foundation4/js/foundation/foundation.alerts.js new file mode 100644 index 0000000..a0a8d6f --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.alerts.js @@ -0,0 +1,52 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.alerts = { + name : 'alerts', + + version : '4.2.2', + + settings : { + speed: 300, // fade out speed + callback: function (){} + }, + + init : function (scope, method, options) { + this.scope = scope || this.scope; + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } + + if (typeof method !== 'string') { + if (!this.settings.init) { this.events(); } + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + + $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) { + e.preventDefault(); + $(this).closest("[data-alert]").fadeOut(self.speed, function () { + $(this).remove(); + self.settings.callback(); + }); + }); + + this.settings.init = true; + }, + + off : function () { + $(this.scope).off('.fndtn.alerts'); + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.clearing.js b/videodb/lib/foundation4/js/foundation/foundation.clearing.js new file mode 100644 index 0000000..32547ea --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.clearing.js @@ -0,0 +1,516 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.clearing = { + name : 'clearing', + + version: '4.3.1', + + settings : { + templates : { + viewing : '×' + + '' + }, + + // comma delimited list of selectors that, on click, will close clearing, + // add 'div.clearing-blackout, div.visible-img' to close on background click + close_selectors : '.clearing-close', + + // event initializers and locks + init : false, + locked : false + }, + + init : function (scope, method, options) { + var self = this; + Foundation.inherit(this, 'set_data get_data remove_data throttle data_options'); + + if (typeof method === 'object') { + options = $.extend(true, this.settings, method); + } + + if (typeof method !== 'string') { + $(this.scope).find('ul[data-clearing]').each(function () { + var $el = $(this), + options = options || {}, + lis = $el.find('li'), + settings = self.get_data($el); + + if (!settings && lis.length > 0) { + options.$parent = $el.parent(); + + self.set_data($el, $.extend({}, self.settings, options, self.data_options($el))); + + self.assemble($el.find('li')); + + if (!self.settings.init) { + self.events().swipe_events(); + } + } + }); + + return this.settings.init; + } else { + // fire method + return this[method].call(this, options); + } + }, + + // event binding and initial setup + + events : function () { + var self = this; + + $(this.scope) + .on('click.fndtn.clearing', 'ul[data-clearing] li', + function (e, current, target) { + var current = current || $(this), + target = target || current, + next = current.next('li'), + settings = self.get_data(current.parent()), + image = $(e.target); + + e.preventDefault(); + if (!settings) self.init(); + + // if clearing is open and the current image is + // clicked, go to the next image in sequence + if (target.hasClass('visible') && + current[0] === target[0] && + next.length > 0 && self.is_open(current)) { + target = next; + image = target.find('img'); + } + + // set current and target to the clicked li if not otherwise defined. + self.open(image, current, target); + self.update_paddles(target); + }) + + .on('click.fndtn.clearing', '.clearing-main-next', + function (e) { this.nav(e, 'next') }.bind(this)) + .on('click.fndtn.clearing', '.clearing-main-prev', + function (e) { this.nav(e, 'prev') }.bind(this)) + .on('click.fndtn.clearing', this.settings.close_selectors, + function (e) { Foundation.libs.clearing.close(e, this) }) + .on('keydown.fndtn.clearing', + function (e) { this.keydown(e) }.bind(this)); + + $(window).on('resize.fndtn.clearing', + function () { this.resize() }.bind(this)); + + this.settings.init = true; + return this; + }, + + swipe_events : function () { + var self = this; + + $(this.scope) + .on('touchstart.fndtn.clearing', '.visible-img', function(e) { + if (!e.touches) { e = e.originalEvent; } + var data = { + start_page_x: e.touches[0].pageX, + start_page_y: e.touches[0].pageY, + start_time: (new Date()).getTime(), + delta_x: 0, + is_scrolling: undefined + }; + + $(this).data('swipe-transition', data); + e.stopPropagation(); + }) + .on('touchmove.fndtn.clearing', '.visible-img', function(e) { + if (!e.touches) { e = e.originalEvent; } + // Ignore pinch/zoom events + if(e.touches.length > 1 || e.scale && e.scale !== 1) return; + + var data = $(this).data('swipe-transition'); + + if (typeof data === 'undefined') { + data = {}; + } + + data.delta_x = e.touches[0].pageX - data.start_page_x; + + if ( typeof data.is_scrolling === 'undefined') { + data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); + } + + if (!data.is_scrolling && !data.active) { + e.preventDefault(); + var direction = (data.delta_x < 0) ? 'next' : 'prev'; + data.active = true; + self.nav(e, direction); + } + }) + .on('touchend.fndtn.clearing', '.visible-img', function(e) { + $(this).data('swipe-transition', {}); + e.stopPropagation(); + }); + }, + + assemble : function ($li) { + var $el = $li.parent(); + $el.after('
          '); + + var holder = $('#foundationClearingHolder'), + settings = this.get_data($el), + grid = $el.detach(), + data = { + grid: '', + viewing: settings.templates.viewing + }, + wrapper = '
          ' + data.viewing + + data.grid + '
          '; + + return holder.after(wrapper).remove(); + }, + + // event callbacks + + open : function ($image, current, target) { + var root = target.closest('.clearing-assembled'), + container = root.find('div').first(), + visible_image = container.find('.visible-img'), + image = visible_image.find('img').not($image); + + if (!this.locked()) { + // set the image to the selected thumbnail + image + .attr('src', this.load($image)) + .css('visibility', 'hidden'); + + this.loaded(image, function () { + image.css('visibility', 'visible'); + // toggle the gallery + root.addClass('clearing-blackout'); + container.addClass('clearing-container'); + visible_image.show(); + this.fix_height(target) + .caption(visible_image.find('.clearing-caption'), $image) + .center(image) + .shift(current, target, function () { + target.siblings().removeClass('visible'); + target.addClass('visible'); + }); + }.bind(this)); + } + }, + + close : function (e, el) { + e.preventDefault(); + + var root = (function (target) { + if (/blackout/.test(target.selector)) { + return target; + } else { + return target.closest('.clearing-blackout'); + } + }($(el))), container, visible_image; + + if (el === e.target && root) { + container = root.find('div').first(); + visible_image = container.find('.visible-img'); + this.settings.prev_index = 0; + root.find('ul[data-clearing]') + .attr('style', '').closest('.clearing-blackout') + .removeClass('clearing-blackout'); + container.removeClass('clearing-container'); + visible_image.hide(); + } + + return false; + }, + + is_open : function (current) { + return current.parent().attr('style').length > 0; + }, + + keydown : function (e) { + var clearing = $('.clearing-blackout').find('ul[data-clearing]'); + + if (e.which === 39) this.go(clearing, 'next'); + if (e.which === 37) this.go(clearing, 'prev'); + if (e.which === 27) $('a.clearing-close').trigger('click'); + }, + + nav : function (e, direction) { + var clearing = $('.clearing-blackout').find('ul[data-clearing]'); + + e.preventDefault(); + this.go(clearing, direction); + }, + + resize : function () { + var image = $('.clearing-blackout .visible-img').find('img'); + + if (image.length) { + this.center(image); + } + }, + + // visual adjustments + fix_height : function (target) { + var lis = target.parent().children(), + self = this; + + lis.each(function () { + var li = $(this), + image = li.find('img'); + + if (li.height() > self.outerHeight(image)) { + li.addClass('fix-height'); + } + }) + .closest('ul') + .width(lis.length * 100 + '%'); + + return this; + }, + + update_paddles : function (target) { + var visible_image = target + .closest('.carousel') + .siblings('.visible-img'); + + if (target.next().length > 0) { + visible_image + .find('.clearing-main-next') + .removeClass('disabled'); + } else { + visible_image + .find('.clearing-main-next') + .addClass('disabled'); + } + + if (target.prev().length > 0) { + visible_image + .find('.clearing-main-prev') + .removeClass('disabled'); + } else { + visible_image + .find('.clearing-main-prev') + .addClass('disabled'); + } + }, + + center : function (target) { + if (!this.rtl) { + target.css({ + marginLeft : -(this.outerWidth(target) / 2), + marginTop : -(this.outerHeight(target) / 2) + }); + } else { + target.css({ + marginRight : -(this.outerWidth(target) / 2), + marginTop : -(this.outerHeight(target) / 2) + }); + } + return this; + }, + + // image loading and preloading + + load : function ($image) { + if ($image[0].nodeName === "A") { + var href = $image.attr('href'); + } else { + var href = $image.parent().attr('href'); + } + + this.preload($image); + + if (href) return href; + return $image.attr('src'); + }, + + preload : function ($image) { + this + .img($image.closest('li').next()) + .img($image.closest('li').prev()); + }, + + loaded : function (image, callback) { + // based on jquery.imageready.js + // @weblinc, @jsantell, (c) 2012 + + function loaded () { + callback(); + } + + function bindLoad () { + this.one('load', loaded); + + if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { + var src = this.attr( 'src' ), + param = src.match( /\?/ ) ? '&' : '?'; + + param += 'random=' + (new Date()).getTime(); + this.attr('src', src + param); + } + } + + if (!image.attr('src')) { + loaded(); + return; + } + + if (image[0].complete || image[0].readyState === 4) { + loaded(); + } else { + bindLoad.call(image); + } + }, + + img : function (img) { + if (img.length) { + var new_img = new Image(), + new_a = img.find('a'); + + if (new_a.length) { + new_img.src = new_a.attr('href'); + } else { + new_img.src = img.find('img').attr('src'); + } + } + return this; + }, + + // image caption + + caption : function (container, $image) { + var caption = $image.data('caption'); + + if (caption) { + container + .html(caption) + .show(); + } else { + container + .text('') + .hide(); + } + return this; + }, + + // directional methods + + go : function ($ul, direction) { + var current = $ul.find('.visible'), + target = current[direction](); + + if (target.length) { + target + .find('img') + .trigger('click', [current, target]); + } + }, + + shift : function (current, target, callback) { + var clearing = target.parent(), + old_index = this.settings.prev_index || target.index(), + direction = this.direction(clearing, current, target), + left = parseInt(clearing.css('left'), 10), + width = this.outerWidth(target), + skip_shift; + + // we use jQuery animate instead of CSS transitions because we + // need a callback to unlock the next animation + if (target.index() !== old_index && !/skip/.test(direction)){ + if (/left/.test(direction)) { + this.lock(); + clearing.animate({left : left + width}, 300, this.unlock()); + } else if (/right/.test(direction)) { + this.lock(); + clearing.animate({left : left - width}, 300, this.unlock()); + } + } else if (/skip/.test(direction)) { + // the target image is not adjacent to the current image, so + // do we scroll right or not + skip_shift = target.index() - this.settings.up_count; + this.lock(); + + if (skip_shift > 0) { + clearing.animate({left : -(skip_shift * width)}, 300, this.unlock()); + } else { + clearing.animate({left : 0}, 300, this.unlock()); + } + } + + callback(); + }, + + direction : function ($el, current, target) { + var lis = $el.find('li'), + li_width = this.outerWidth(lis) + (this.outerWidth(lis) / 4), + up_count = Math.floor(this.outerWidth($('.clearing-container')) / li_width) - 1, + target_index = lis.index(target), + response; + + this.settings.up_count = up_count; + + if (this.adjacent(this.settings.prev_index, target_index)) { + if ((target_index > up_count) + && target_index > this.settings.prev_index) { + response = 'right'; + } else if ((target_index > up_count - 1) + && target_index <= this.settings.prev_index) { + response = 'left'; + } else { + response = false; + } + } else { + response = 'skip'; + } + + this.settings.prev_index = target_index; + + return response; + }, + + adjacent : function (current_index, target_index) { + for (var i = target_index + 1; i >= target_index - 1; i--) { + if (i === current_index) return true; + } + return false; + }, + + // lock management + + lock : function () { + this.settings.locked = true; + }, + + unlock : function () { + this.settings.locked = false; + }, + + locked : function () { + return this.settings.locked; + }, + + // plugin management/browser quirks + + outerHTML : function (el) { + // support FireFox < 11 + return el.outerHTML || new XMLSerializer().serializeToString(el); + }, + + off : function () { + $(this.scope).off('.fndtn.clearing'); + $(window).off('.fndtn.clearing'); + this.remove_data(); // empty settings cache + this.settings.init = false; + }, + + reflow : function () { + this.init(); + } + }; + +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.cookie.js b/videodb/lib/foundation4/js/foundation/foundation.cookie.js new file mode 100644 index 0000000..862027c --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.cookie.js @@ -0,0 +1,74 @@ +/*! + * jQuery Cookie Plugin v1.3 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2011, Klaus Hartl + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://www.opensource.org/licenses/mit-license.php + * http://www.opensource.org/licenses/GPL-2.0 + * + * Modified to work with Zepto.js by ZURB + */ +(function ($, document, undefined) { + + var pluses = /\+/g; + + function raw(s) { + return s; + } + + function decoded(s) { + return decodeURIComponent(s.replace(pluses, ' ')); + } + + var config = $.cookie = function (key, value, options) { + + // write + if (value !== undefined) { + options = $.extend({}, config.defaults, options); + + if (value === null) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = config.json ? JSON.stringify(value) : String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // read + var decode = config.raw ? raw : decoded; + var cookies = document.cookie.split('; '); + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + if (decode(parts.shift()) === key) { + var cookie = decode(parts.join('=')); + return config.json ? JSON.parse(cookie) : cookie; + } + } + + return null; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) !== null) { + $.cookie(key, null, options); + return true; + } + return false; + }; + +})(Foundation.zj, document); diff --git a/videodb/lib/foundation4/js/foundation/foundation.dropdown.js b/videodb/lib/foundation4/js/foundation/foundation.dropdown.js new file mode 100644 index 0000000..22b5524 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.dropdown.js @@ -0,0 +1,177 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.dropdown = { + name : 'dropdown', + + version : '4.3.0', + + settings : { + activeClass: 'open', + is_hover: false, + opened: function(){}, + closed: function(){} + }, + + init : function (scope, method, options) { + this.scope = scope || this.scope; + Foundation.inherit(this, 'throttle scrollLeft data_options'); + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } + + if (typeof method !== 'string') { + + if (!this.settings.init) { + this.events(); + } + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + + $(this.scope) + .on('click.fndtn.dropdown', '[data-dropdown]', function (e) { + var settings = $.extend({}, self.settings, self.data_options($(this))); + e.preventDefault(); + + if (!settings.is_hover) self.toggle($(this)); + }) + .on('mouseenter', '[data-dropdown]', function (e) { + var settings = $.extend({}, self.settings, self.data_options($(this))); + if (settings.is_hover) self.toggle($(this)); + }) + .on('mouseleave', '[data-dropdown-content]', function (e) { + var target = $('[data-dropdown="' + $(this).attr('id') + '"]'), + settings = $.extend({}, self.settings, self.data_options(target)); + if (settings.is_hover) self.close.call(self, $(this)); + }) + .on('opened.fndtn.dropdown', '[data-dropdown-content]', this.settings.opened) + .on('closed.fndtn.dropdown', '[data-dropdown-content]', this.settings.closed); + + $(document).on('click.fndtn.dropdown', function (e) { + var parent = $(e.target).closest('[data-dropdown-content]'); + + if ($(e.target).data('dropdown')) { + return; + } + if (parent.length > 0 && ($(e.target).is('[data-dropdown-content]') || $.contains(parent.first()[0], e.target))) { + e.stopPropagation(); + return; + } + + self.close.call(self, $('[data-dropdown-content]')); + }); + + $(window).on('resize.fndtn.dropdown', self.throttle(function () { + self.resize.call(self); + }, 50)).trigger('resize'); + + this.settings.init = true; + }, + + close: function (dropdown) { + var self = this; + dropdown.each(function () { + if ($(this).hasClass(self.settings.activeClass)) { + $(this) + .css(Foundation.rtl ? 'right':'left', '-99999px') + .removeClass(self.settings.activeClass); + $(this).trigger('closed'); + } + }); + }, + + open: function (dropdown, target) { + this + .css(dropdown + .addClass(this.settings.activeClass), target); + dropdown.trigger('opened'); + }, + + toggle : function (target) { + var dropdown = $('#' + target.data('dropdown')); + + this.close.call(this, $('[data-dropdown-content]').not(dropdown)); + + if (dropdown.hasClass(this.settings.activeClass)) { + this.close.call(this, dropdown); + } else { + this.close.call(this, $('[data-dropdown-content]')) + this.open.call(this, dropdown, target); + } + }, + + resize : function () { + var dropdown = $('[data-dropdown-content].open'), + target = $("[data-dropdown='" + dropdown.attr('id') + "']"); + + if (dropdown.length && target.length) { + this.css(dropdown, target); + } + }, + + css : function (dropdown, target) { + var offset_parent = dropdown.offsetParent(); + // if (offset_parent.length > 0 && /body/i.test(dropdown.offsetParent()[0].nodeName)) { + var position = target.offset(); + position.top -= offset_parent.offset().top; + position.left -= offset_parent.offset().left; + // } else { + // var position = target.position(); + // } + + if (this.small()) { + dropdown.css({ + position : 'absolute', + width: '95%', + left: '2.5%', + 'max-width': 'none', + top: position.top + this.outerHeight(target) + }); + } else { + if (!Foundation.rtl && $(window).width() > this.outerWidth(dropdown) + target.offset().left) { + var left = position.left; + if (dropdown.hasClass('right')) { + dropdown.removeClass('right'); + } + } else { + if (!dropdown.hasClass('right')) { + dropdown.addClass('right'); + } + var left = position.left - (this.outerWidth(dropdown) - this.outerWidth(target)); + } + + dropdown.attr('style', '').css({ + position : 'absolute', + top: position.top + this.outerHeight(target), + left: left + }); + } + + return dropdown; + }, + + small : function () { + return $(window).width() < 768 || $('html').hasClass('lt-ie9'); + }, + + off: function () { + $(this.scope).off('.fndtn.dropdown'); + $('html, body').off('.fndtn.dropdown'); + $(window).off('.fndtn.dropdown'); + $('[data-dropdown-content]').off('.fndtn.dropdown'); + this.settings.init = false; + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.forms.js b/videodb/lib/foundation4/js/foundation/foundation.forms.js new file mode 100644 index 0000000..d1a87b9 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.forms.js @@ -0,0 +1,533 @@ +(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.forms = { + name : 'forms', + + version: '4.3.1', + + cache: {}, + + settings: { + disable_class: 'no-custom', + last_combo : null + }, + + init: function (scope, method, options) { + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } + + if (typeof method !== 'string') { + if (!this.settings.init) { + this.events(); + } + + this.assemble(); + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + assemble: function () { + $('form.custom input[type="radio"]', $(this.scope)) + .not('[data-customforms="disabled"]') + .not('.' + this.settings.disable_class) + .each(this.append_custom_markup); + $('form.custom input[type="checkbox"]', $(this.scope)) + .not('[data-customforms="disabled"]') + .not('.' + this.settings.disable_class) + .each(this.append_custom_markup); + $('form.custom select', $(this.scope)) + .not('[data-customforms="disabled"]') + .not('.' + this.settings.disable_class) + .not('[multiple=multiple]') + .each(this.append_custom_select); + }, + + events: function () { + var self = this; + + $(this.scope) + .on('click.fndtn.forms', 'form.custom span.custom.checkbox', function (e) { + e.preventDefault(); + e.stopPropagation(); + self.toggle_checkbox($(this)); + }) + .on('click.fndtn.forms', 'form.custom span.custom.radio', function (e) { + e.preventDefault(); + e.stopPropagation(); + self.toggle_radio($(this)); + }) + .on('change.fndtn.forms', 'form.custom select', function (e, force_refresh) { + if ($(this).is('[data-customforms="disabled"]')) return; + self.refresh_custom_select($(this), force_refresh); + }) + .on('click.fndtn.forms', 'form.custom label', function (e) { + if ($(e.target).is('label')) { + var $associatedElement = $('#' + self.escape($(this).attr('for'))).not('[data-customforms="disabled"]'), + $customCheckbox, + $customRadio; + + if ($associatedElement.length !== 0) { + if ($associatedElement.attr('type') === 'checkbox') { + e.preventDefault(); + $customCheckbox = $(this).find('span.custom.checkbox'); + //the checkbox might be outside after the label or inside of another element + if ($customCheckbox.length === 0) { + $customCheckbox = $associatedElement.add(this).siblings('span.custom.checkbox').first(); + } + self.toggle_checkbox($customCheckbox); + } else if ($associatedElement.attr('type') === 'radio') { + e.preventDefault(); + $customRadio = $(this).find('span.custom.radio'); + //the radio might be outside after the label or inside of another element + if ($customRadio.length === 0) { + $customRadio = $associatedElement.add(this).siblings('span.custom.radio').first(); + } + self.toggle_radio($customRadio); + } + } + } + }) + .on('mousedown.fndtn.forms', 'form.custom div.custom.dropdown', function () { + return false; + }) + .on('click.fndtn.forms', 'form.custom div.custom.dropdown a.current, form.custom div.custom.dropdown a.selector', function (e) { + var $this = $(this), + $dropdown = $this.closest('div.custom.dropdown'), + $select = getFirstPrevSibling($dropdown, 'select'); + + // make sure other dropdowns close + if (!$dropdown.hasClass('open')) $(self.scope).trigger('click'); + + e.preventDefault(); + if (false === $select.is(':disabled')) { + $dropdown.toggleClass('open'); + + if ($dropdown.hasClass('open')) { + $(self.scope).on('click.fndtn.forms.customdropdown', function () { + $dropdown.removeClass('open'); + $(self.scope).off('.fndtn.forms.customdropdown'); + }); + } else { + $(self.scope).on('.fndtn.forms.customdropdown'); + } + return false; + } + }) + .on('click.fndtn.forms touchend.fndtn.forms', 'form.custom div.custom.dropdown li', function (e) { + var $this = $(this), + $customDropdown = $this.closest('div.custom.dropdown'), + $select = getFirstPrevSibling($customDropdown, 'select'), + selectedIndex = 0; + + e.preventDefault(); + e.stopPropagation(); + + if (!$(this).hasClass('disabled')) { + $('div.dropdown').not($customDropdown).removeClass('open'); + + var $oldThis = $this.closest('ul') + .find('li.selected'); + $oldThis.removeClass('selected'); + + $this.addClass('selected'); + + $customDropdown.removeClass('open') + .find('a.current') + .text($this.text()); + + $this.closest('ul').find('li').each(function (index) { + if ($this[0] === this) { + selectedIndex = index; + } + }); + $select[0].selectedIndex = selectedIndex; + + //store the old value in data + $select.data('prevalue', $oldThis.html()); + + // Kick off full DOM change event + if (typeof (document.createEvent) != 'undefined') { + var event = document.createEvent('HTMLEvents'); + event.initEvent('change', true, true); + $select[0].dispatchEvent(event); + } else { + $select[0].fireEvent('onchange'); // for IE + } + } + }); + + $(window).on('keydown', function (e) { + var focus = document.activeElement, + self = Foundation.libs.forms, + dropdown = $('.custom.dropdown.open'); + + if (dropdown.length > 0) { + e.preventDefault(); + + if (e.which === 13) { + dropdown.find('li.selected').trigger('click'); + } + + if (e.which === 27) { + dropdown.removeClass('open'); + } + + if (e.which >= 65 && e.which <= 90) { + var next = self.go_to(dropdown, e.which), + current = dropdown.find('li.selected'); + + if (next) { + current.removeClass('selected'); + self.scrollTo(next.addClass('selected'), 300); + } + } + + if (e.which === 38) { + var current = dropdown.find('li.selected'), + prev = current.prev(':not(.disabled)'); + + if (prev.length > 0) { + prev.parent()[0].scrollTop = prev.parent().scrollTop() - self.outerHeight(prev); + current.removeClass('selected'); + prev.addClass('selected'); + } + } else if (e.which === 40) { + var current = dropdown.find('li.selected'), + next = current.next(':not(.disabled)'); + + if (next.length > 0) { + next.parent()[0].scrollTop = next.parent().scrollTop() + self.outerHeight(next); + current.removeClass('selected'); + next.addClass('selected'); + } + } + } + }); + + this.settings.init = true; + }, + + go_to: function (dropdown, character) { + var lis = dropdown.find('li'), + count = lis.length; + + if (count > 0) { + for (var i = 0; i < count; i++) { + var first_letter = lis.eq(i).text().charAt(0).toLowerCase(); + if (first_letter === String.fromCharCode(character).toLowerCase()) return lis.eq(i); + } + } + }, + + scrollTo: function (el, duration) { + if (duration < 0) return; + var parent = el.parent(); + var li_height = this.outerHeight(el); + var difference = (li_height * (el.index())) - parent.scrollTop(); + var perTick = difference / duration * 10; + + this.scrollToTimerCache = setTimeout(function () { + if (!isNaN(parseInt(perTick, 10))) { + parent[0].scrollTop = parent.scrollTop() + perTick; + this.scrollTo(el, duration - 10); + } + }.bind(this), 10); + }, + + append_custom_markup: function (idx, sel) { + var $this = $(sel), + type = $this.attr('type'), + $span = $this.next('span.custom.' + type); + + if (!$this.parent().hasClass('switch')) { + $this.addClass('hidden-field'); + } + + if ($span.length === 0) { + $span = $('').insertAfter($this); + } + + $span.toggleClass('checked', $this.is(':checked')); + $span.toggleClass('disabled', $this.is(':disabled')); + }, + + append_custom_select: function (idx, sel) { + var self = Foundation.libs.forms, + $this = $(sel), + $customSelect = $this.next('div.custom.dropdown'), + $customList = $customSelect.find('ul'), + $selectCurrent = $customSelect.find(".current"), + $selector = $customSelect.find(".selector"), + $options = $this.find('option'), + $selectedOption = $options.filter(':selected'), + copyClasses = $this.attr('class') ? $this.attr('class').split(' ') : [], + maxWidth = 0, + liHtml = '', + $listItems, + $currentSelect = false; + + if ($customSelect.length === 0) { + var customSelectSize = $this.hasClass('small') ? 'small' : $this.hasClass('medium') ? 'medium' : $this.hasClass('large') ? 'large' : $this.hasClass('expand') ? 'expand' : ''; + + $customSelect = $('
            '); + + $selector = $customSelect.find(".selector"); + $customList = $customSelect.find("ul"); + + liHtml = $options.map(function () { + var copyClasses = $(this).attr('class') ? $(this).attr('class') : ''; + return "
          • " + $(this).html() + "
          • "; + }).get().join(''); + + $customList.append(liHtml); + + $currentSelect = $customSelect + .prepend('' + $selectedOption.html() + '') + .find(".current"); + + $this.after($customSelect) + .addClass('hidden-field'); + } else { + liHtml = $options.map(function () { + return "
          • " + $(this).html() + "
          • "; + }) + .get().join(''); + + $customList.html('') + .append(liHtml); + + } // endif $customSelect.length === 0 + + self.assign_id($this, $customSelect); + $customSelect.toggleClass('disabled', $this.is(':disabled')); + $listItems = $customList.find('li'); + + // cache list length + self.cache[$customSelect.data('id')] = $listItems.length; + + $options.each(function (index) { + if (this.selected) { + $listItems.eq(index).addClass('selected'); + + if ($currentSelect) { + $currentSelect.html($(this).html()); + } + } + if ($(this).is(':disabled')) { + $listItems.eq(index).addClass('disabled'); + } + }); + + // + // If we're not specifying a predetermined form size. + // + if (!$customSelect.is('.small, .medium, .large, .expand')) { + + // ------------------------------------------------------------------------------------ + // This is a work-around for when elements are contained within hidden parents. + // For example, when custom-form elements are inside of a hidden reveal modal. + // + // We need to display the current custom list element as well as hidden parent elements + // in order to properly calculate the list item element's width property. + // ------------------------------------------------------------------------------------- + + $customSelect.addClass('open'); + // + // Quickly, display all parent elements. + // This should help us calcualate the width of the list item's within the drop down. + // + var self = Foundation.libs.forms; + self.hidden_fix.adjust($customList); + + maxWidth = (self.outerWidth($listItems) > maxWidth) ? self.outerWidth($listItems) : maxWidth; + + Foundation.libs.forms.hidden_fix.reset(); + + $customSelect.removeClass('open'); + + } // endif + + }, + + assign_id: function ($select, $customSelect) { + var id = [+new Date(), Foundation.random_str(5)].join('-'); + $select.attr('data-id', id); + $customSelect.attr('data-id', id); + }, + + refresh_custom_select: function ($select, force_refresh) { + var self = this; + var maxWidth = 0, + $customSelect = $select.next(), + $options = $select.find('option'), + $listItems = $customSelect.find('li'); + + if ($listItems.length !== this.cache[$customSelect.data('id')] || force_refresh) { + $customSelect.find('ul').html(''); + + $options.each(function () { + var $li = $('
          • ' + $(this).html() + '
          • '); + $customSelect.find('ul').append($li); + }); + + // re-populate + $options.each(function (index) { + if (this.selected) { + $customSelect.find('li').eq(index).addClass('selected'); + $customSelect.find('.current').html($(this).html()); + } + if ($(this).is(':disabled')) { + $customSelect.find('li').eq(index).addClass('disabled'); + } + }); + + // fix width + $customSelect.removeAttr('style') + .find('ul').removeAttr('style'); + $customSelect.find('li').each(function () { + $customSelect.addClass('open'); + if (self.outerWidth($(this)) > maxWidth) { + maxWidth = self.outerWidth($(this)); + } + $customSelect.removeClass('open'); + }); + + $listItems = $customSelect.find('li'); + // cache list length + this.cache[$customSelect.data('id')] = $listItems.length; + } + }, + + toggle_checkbox: function ($element) { + var $input = $element.prev(), + input = $input[0]; + + if (false === $input.is(':disabled')) { + input.checked = ((input.checked) ? false : true); + $element.toggleClass('checked'); + + $input.trigger('change'); + } + }, + + toggle_radio: function ($element) { + var $input = $element.prev(), + $form = $input.closest('form.custom'), + input = $input[0]; + + if (false === $input.is(':disabled')) { + $form.find('input[type="radio"][name="' + this.escape($input.attr('name')) + '"]') + .next().not($element).removeClass('checked'); + + if (!$element.hasClass('checked')) { + $element.toggleClass('checked'); + } + + input.checked = $element.hasClass('checked'); + + $input.trigger('change'); + } + }, + + escape: function (text) { + if (!text) return ''; + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + }, + + hidden_fix: { + /** + * Sets all hidden parent elements and self to visibile. + * + * @method adjust + * @param {jQuery Object} $child + */ + + // We'll use this to temporarily store style properties. + tmp: [], + + // We'll use this to set hidden parent elements. + hidden: null, + + adjust: function ($child) { + // Internal reference. + var _self = this; + + // Set all hidden parent elements, including this element. + _self.hidden = $child.parents(); + _self.hidden = _self.hidden.add($child).filter(":hidden"); + + // Loop through all hidden elements. + _self.hidden.each(function () { + + // Cache the element. + var $elem = $(this); + + // Store the style attribute. + // Undefined if element doesn't have a style attribute. + _self.tmp.push($elem.attr('style')); + + // Set the element's display property to block, + // but ensure it's visibility is hidden. + $elem.css({ + 'visibility': 'hidden', + 'display': 'block' + }); + }); + + }, // end adjust + + /** + * Resets the elements previous state. + * + * @method reset + */ + reset: function () { + // Internal reference. + var _self = this; + // Loop through our hidden element collection. + _self.hidden.each(function (i) { + // Cache this element. + var $elem = $(this), + _tmp = _self.tmp[i]; // Get the stored 'style' value for this element. + + // If the stored value is undefined. + if (_tmp === undefined) + // Remove the style attribute. + $elem.removeAttr('style'); + else + // Otherwise, reset the element style attribute. + $elem.attr('style', _tmp); + }); + // Reset the tmp array. + _self.tmp = []; + // Reset the hidden elements variable. + _self.hidden = null; + + } // end reset + }, + + off: function () { + $(this.scope).off('.fndtn.forms'); + }, + + reflow : function () {} + }; + + var getFirstPrevSibling = function($el, selector) { + var $el = $el.prev(); + while ($el.length) { + if ($el.is(selector)) return $el; + $el = $el.prev(); + } + return $(); + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.interchange.js b/videodb/lib/foundation4/js/foundation/foundation.interchange.js new file mode 100644 index 0000000..28c5acb --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.interchange.js @@ -0,0 +1,280 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.interchange = { + name : 'interchange', + + version : '4.2.4', + + cache : {}, + + images_loaded : false, + + settings : { + load_attr : 'interchange', + + named_queries : { + 'default' : 'only screen and (min-width: 1px)', + small : 'only screen and (min-width: 768px)', + medium : 'only screen and (min-width: 1280px)', + large : 'only screen and (min-width: 1440px)', + landscape : 'only screen and (orientation: landscape)', + portrait : 'only screen and (orientation: portrait)', + retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + + 'only screen and (min--moz-device-pixel-ratio: 2),' + + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + + 'only screen and (min-device-pixel-ratio: 2),' + + 'only screen and (min-resolution: 192dpi),' + + 'only screen and (min-resolution: 2dppx)' + }, + + directives : { + replace: function (el, path) { + if (/IMG/.test(el[0].nodeName)) { + var orig_path = el[0].src; + + if (new RegExp(path, 'i').test(orig_path)) return; + + el[0].src = path; + + return el.trigger('replace', [el[0].src, orig_path]); + } + } + } + }, + + init : function (scope, method, options) { + Foundation.inherit(this, 'throttle'); + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } + + this.events(); + this.images(); + + if (typeof method !== 'string') { + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + + $(window).on('resize.fndtn.interchange', self.throttle(function () { + self.resize.call(self); + }, 50)); + }, + + resize : function () { + var cache = this.cache; + + if(!this.images_loaded) { + setTimeout($.proxy(this.resize, this), 50); + return; + } + + for (var uuid in cache) { + if (cache.hasOwnProperty(uuid)) { + var passed = this.results(uuid, cache[uuid]); + + if (passed) { + this.settings.directives[passed + .scenario[1]](passed.el, passed.scenario[0]); + } + } + } + + }, + + results : function (uuid, scenarios) { + var count = scenarios.length; + + if (count > 0) { + var el = $('[data-uuid="' + uuid + '"]'); + + for (var i = count - 1; i >= 0; i--) { + var mq, rule = scenarios[i][2]; + if (this.settings.named_queries.hasOwnProperty(rule)) { + mq = matchMedia(this.settings.named_queries[rule]); + } else { + mq = matchMedia(rule); + } + if (mq.matches) { + return {el: el, scenario: scenarios[i]}; + } + } + } + + return false; + }, + + images : function (force_update) { + if (typeof this.cached_images === 'undefined' || force_update) { + return this.update_images(); + } + + return this.cached_images; + }, + + update_images : function () { + var images = document.getElementsByTagName('img'), + count = images.length, + loaded_count = 0, + data_attr = 'data-' + this.settings.load_attr; + + this.cached_images = []; + this.images_loaded = false; + + for (var i = count - 1; i >= 0; i--) { + this.loaded($(images[i]), function (image) { + loaded_count++; + if (image) { + var str = image.getAttribute(data_attr) || ''; + + if (str.length > 0) { + this.cached_images.push(image); + } + } + + if(loaded_count === count) { + this.images_loaded = true; + this.enhance(); + } + }.bind(this)); + } + + return 'deferred'; + }, + + // based on jquery.imageready.js + // @weblinc, @jsantell, (c) 2012 + + loaded : function (image, callback) { + function loaded () { + callback(image[0]); + } + + function bindLoad () { + this.one('load', loaded); + + if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { + var src = this.attr( 'src' ), + param = src.match( /\?/ ) ? '&' : '?'; + + param += 'random=' + (new Date()).getTime(); + this.attr('src', src + param); + } + } + + if (!image.attr('src')) { + loaded(); + return; + } + + if (image[0].complete || image[0].readyState === 4) { + loaded(); + } else { + bindLoad.call(image); + } + }, + + enhance : function () { + var count = this.images().length; + + for (var i = count - 1; i >= 0; i--) { + this._object($(this.images()[i])); + } + + return $(window).trigger('resize'); + }, + + parse_params : function (path, directive, mq) { + return [this.trim(path), this.convert_directive(directive), this.trim(mq)]; + }, + + convert_directive : function (directive) { + var trimmed = this.trim(directive); + + if (trimmed.length > 0) { + return trimmed; + } + + return 'replace'; + }, + + _object : function(el) { + var raw_arr = this.parse_data_attr(el), + scenarios = [], count = raw_arr.length; + + if (count > 0) { + for (var i = count - 1; i >= 0; i--) { + var split = raw_arr[i].split(/\((.*?)(\))$/); + + if (split.length > 1) { + var cached_split = split[0].split(','), + params = this.parse_params(cached_split[0], + cached_split[1], split[1]); + + scenarios.push(params); + } + } + } + + return this.store(el, scenarios); + }, + + uuid : function (separator) { + var delim = separator || "-"; + + function S4() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + } + + return (S4() + S4() + delim + S4() + delim + S4() + + delim + S4() + delim + S4() + S4() + S4()); + }, + + store : function (el, scenarios) { + var uuid = this.uuid(), + current_uuid = el.data('uuid'); + + if (current_uuid) return this.cache[current_uuid]; + + el.attr('data-uuid', uuid); + + return this.cache[uuid] = scenarios; + }, + + trim : function(str) { + if (typeof str === 'string') { + return $.trim(str); + } + + return str; + }, + + parse_data_attr : function (el) { + var raw = el.data(this.settings.load_attr).split(/\[(.*?)\]/), + count = raw.length, output = []; + + for (var i = count - 1; i >= 0; i--) { + if (raw[i].replace(/[\W\d]+/, '').length > 4) { + output.push(raw[i]); + } + } + + return output; + }, + + reflow : function () { + this.images(true); + } + + }; + +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.joyride.js b/videodb/lib/foundation4/js/foundation/foundation.joyride.js new file mode 100644 index 0000000..7b99105 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.joyride.js @@ -0,0 +1,850 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.joyride = { + name : 'joyride', + + version : '4.2.2', + + defaults : { + expose : false, // turn on or off the expose feature + modal : false, // Whether to cover page with modal during the tour + tipLocation : 'bottom', // 'top' or 'bottom' in relation to parent + nubPosition : 'auto', // override on a per tooltip bases + scrollSpeed : 300, // Page scrolling speed in milliseconds, 0 = no scroll animation + timer : 0, // 0 = no timer , all other numbers = timer in milliseconds + startTimerOnClick : true, // true or false - true requires clicking the first button start the timer + startOffset : 0, // the index of the tooltip you want to start on (index of the li) + nextButton : true, // true or false to control whether a next button is used + tipAnimation : 'fade', // 'pop' or 'fade' in each tip + pauseAfter : [], // array of indexes where to pause the tour after + exposed : [], // array of expose elements + tipAnimationFadeSpeed: 300, // when tipAnimation = 'fade' this is speed in milliseconds for the transition + cookieMonster : false, // true or false to control whether cookies are used + cookieName : 'joyride', // Name the cookie you'll use + cookieDomain : false, // Will this cookie be attached to a domain, ie. '.notableapp.com' + cookieExpires : 365, // set when you would like the cookie to expire. + tipContainer : 'body', // Where will the tip be attached + postRideCallback : function (){}, // A method to call once the tour closes (canceled or complete) + postStepCallback : function (){}, // A method to call after each step + preStepCallback : function (){}, // A method to call before each step + preRideCallback : function (){}, // A method to call before the tour starts (passed index, tip, and cloned exposed element) + postExposeCallback : function (){}, // A method to call after an element has been exposed + template : { // HTML segments for tip layout + link : '×', + timer : '
            ', + tip : '
            ', + wrapper : '
            ', + button : '', + modal : '
            ', + expose : '
            ', + exposeCover: '
            ' + }, + exposeAddClass : '' // One or more space-separated class names to be added to exposed element + }, + + settings : {}, + + init : function (scope, method, options) { + this.scope = scope || this.scope; + Foundation.inherit(this, 'throttle data_options scrollTo scrollLeft delay'); + + if (typeof method === 'object') { + $.extend(true, this.settings, this.defaults, method); + } else { + $.extend(true, this.settings, this.defaults, options); + } + + if (typeof method !== 'string') { + if (!this.settings.init) this.events(); + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + + $(this.scope) + .on('click.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) { + e.preventDefault(); + + if (this.settings.$li.next().length < 1) { + this.end(); + } else if (this.settings.timer > 0) { + clearTimeout(this.settings.automate); + this.hide(); + this.show(); + this.startTimer(); + } else { + this.hide(); + this.show(); + } + + }.bind(this)) + + .on('click.joyride', '.joyride-close-tip', function (e) { + e.preventDefault(); + this.end(); + }.bind(this)); + + $(window).on('resize.fndtn.joyride', self.throttle(function () { + if ($('[data-joyride]').length > 0 && self.settings.$next_tip) { + if (self.settings.exposed.length > 0) { + var $els = $(self.settings.exposed); + + $els.each(function () { + var $this = $(this); + self.un_expose($this); + self.expose($this); + }); + } + + if (self.is_phone()) { + self.pos_phone(); + } else { + self.pos_default(false, true); + } + } + }, 100)); + + this.settings.init = true; + }, + + start : function () { + var self = this, + $this = $(this.scope).find('[data-joyride]'), + integer_settings = ['timer', 'scrollSpeed', 'startOffset', 'tipAnimationFadeSpeed', 'cookieExpires'], + int_settings_count = integer_settings.length; + + if (!this.settings.init) this.init(); + + // non configureable settings + this.settings.$content_el = $this; + this.settings.$body = $(this.settings.tipContainer); + this.settings.body_offset = $(this.settings.tipContainer).position(); + this.settings.$tip_content = this.settings.$content_el.find('> li'); + this.settings.paused = false; + this.settings.attempts = 0; + + this.settings.tipLocationPatterns = { + top: ['bottom'], + bottom: [], // bottom should not need to be repositioned + left: ['right', 'top', 'bottom'], + right: ['left', 'top', 'bottom'] + }; + + // can we create cookies? + if (typeof $.cookie !== 'function') { + this.settings.cookieMonster = false; + } + + // generate the tips and insert into dom. + if (!this.settings.cookieMonster || this.settings.cookieMonster && $.cookie(this.settings.cookieName) === null) { + this.settings.$tip_content.each(function (index) { + var $this = $(this); + $.extend(true, self.settings, self.data_options($this)); + // Make sure that settings parsed from data_options are integers where necessary + for (var i = int_settings_count - 1; i >= 0; i--) { + self.settings[integer_settings[i]] = parseInt(self.settings[integer_settings[i]], 10); + } + self.create({$li : $this, index : index}); + }); + + // show first tip + if (!this.settings.startTimerOnClick && this.settings.timer > 0) { + this.show('init'); + this.startTimer(); + } else { + this.show('init'); + } + + } + }, + + resume : function () { + this.set_li(); + this.show(); + }, + + tip_template : function (opts) { + var $blank, content; + + opts.tip_class = opts.tip_class || ''; + + $blank = $(this.settings.template.tip).addClass(opts.tip_class); + content = $.trim($(opts.li).html()) + + this.button_text(opts.button_text) + + this.settings.template.link + + this.timer_instance(opts.index); + + $blank.append($(this.settings.template.wrapper)); + $blank.first().attr('data-index', opts.index); + $('.joyride-content-wrapper', $blank).append(content); + + return $blank[0]; + }, + + timer_instance : function (index) { + var txt; + + if ((index === 0 && this.settings.startTimerOnClick && this.settings.timer > 0) || this.settings.timer === 0) { + txt = ''; + } else { + txt = this.outerHTML($(this.settings.template.timer)[0]); + } + return txt; + }, + + button_text : function (txt) { + if (this.settings.nextButton) { + txt = $.trim(txt) || 'Next'; + txt = this.outerHTML($(this.settings.template.button).append(txt)[0]); + } else { + txt = ''; + } + return txt; + }, + + create : function (opts) { + var buttonText = opts.$li.attr('data-button') || opts.$li.attr('data-text'), + tipClass = opts.$li.attr('class'), + $tip_content = $(this.tip_template({ + tip_class : tipClass, + index : opts.index, + button_text : buttonText, + li : opts.$li + })); + + $(this.settings.tipContainer).append($tip_content); + }, + + show : function (init) { + var $timer = null; + + // are we paused? + if (this.settings.$li === undefined + || ($.inArray(this.settings.$li.index(), this.settings.pauseAfter) === -1)) { + + // don't go to the next li if the tour was paused + if (this.settings.paused) { + this.settings.paused = false; + } else { + this.set_li(init); + } + + this.settings.attempts = 0; + + if (this.settings.$li.length && this.settings.$target.length > 0) { + if (init) { //run when we first start + this.settings.preRideCallback(this.settings.$li.index(), this.settings.$next_tip); + if (this.settings.modal) { + this.show_modal(); + } + } + + this.settings.preStepCallback(this.settings.$li.index(), this.settings.$next_tip); + + if (this.settings.modal && this.settings.expose) { + this.expose(); + } + + this.settings.tipSettings = $.extend(this.settings, this.data_options(this.settings.$li)); + + this.settings.timer = parseInt(this.settings.timer, 10); + + this.settings.tipSettings.tipLocationPattern = this.settings.tipLocationPatterns[this.settings.tipSettings.tipLocation]; + + // scroll if not modal + if (!/body/i.test(this.settings.$target.selector)) { + this.scroll_to(); + } + + if (this.is_phone()) { + this.pos_phone(true); + } else { + this.pos_default(true); + } + + $timer = this.settings.$next_tip.find('.joyride-timer-indicator'); + + if (/pop/i.test(this.settings.tipAnimation)) { + + $timer.width(0); + + if (this.settings.timer > 0) { + + this.settings.$next_tip.show(); + + this.delay(function () { + $timer.animate({ + width: $timer.parent().width() + }, this.settings.timer, 'linear'); + }.bind(this), this.settings.tipAnimationFadeSpeed); + + } else { + this.settings.$next_tip.show(); + + } + + + } else if (/fade/i.test(this.settings.tipAnimation)) { + + $timer.width(0); + + if (this.settings.timer > 0) { + + this.settings.$next_tip + .fadeIn(this.settings.tipAnimationFadeSpeed) + .show(); + + this.delay(function () { + $timer.animate({ + width: $timer.parent().width() + }, this.settings.timer, 'linear'); + }.bind(this), this.settings.tipAnimationFadeSpeed); + + } else { + this.settings.$next_tip.fadeIn(this.settings.tipAnimationFadeSpeed); + + } + } + + this.settings.$current_tip = this.settings.$next_tip; + + // skip non-existant targets + } else if (this.settings.$li && this.settings.$target.length < 1) { + + this.show(); + + } else { + + this.end(); + + } + } else { + + this.settings.paused = true; + + } + + }, + + is_phone : function () { + if (Modernizr) { + return Modernizr.mq('only screen and (max-width: 767px)') || $('.lt-ie9').length > 0; + } + + return (this.settings.$window.width() < 767); + }, + + hide : function () { + if (this.settings.modal && this.settings.expose) { + this.un_expose(); + } + + if (!this.settings.modal) { + $('.joyride-modal-bg').hide(); + } + + // Prevent scroll bouncing...wait to remove from layout + this.settings.$current_tip.css('visibility', 'hidden'); + setTimeout($.proxy(function() { + this.hide(); + this.css('visibility', 'visible'); + }, this.settings.$current_tip), 0); + this.settings.postStepCallback(this.settings.$li.index(), + this.settings.$current_tip); + }, + + set_li : function (init) { + if (init) { + this.settings.$li = this.settings.$tip_content.eq(this.settings.startOffset); + this.set_next_tip(); + this.settings.$current_tip = this.settings.$next_tip; + } else { + this.settings.$li = this.settings.$li.next(); + this.set_next_tip(); + } + + this.set_target(); + }, + + set_next_tip : function () { + this.settings.$next_tip = $(".joyride-tip-guide[data-index='" + this.settings.$li.index() + "']"); + this.settings.$next_tip.data('closed', ''); + }, + + set_target : function () { + var cl = this.settings.$li.attr('data-class'), + id = this.settings.$li.attr('data-id'), + $sel = function () { + if (id) { + return $(document.getElementById(id)); + } else if (cl) { + return $('.' + cl).first(); + } else { + return $('body'); + } + }; + + this.settings.$target = $sel(); + }, + + scroll_to : function () { + var window_half, tipOffset; + + window_half = $(window).height() / 2; + tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.outerHeight(this.settings.$next_tip)); + if (tipOffset > 0) { + this.scrollTo($('html, body'), tipOffset, this.settings.scrollSpeed); + } + }, + + paused : function () { + return ($.inArray((this.settings.$li.index() + 1), this.settings.pauseAfter) === -1); + }, + + restart : function () { + this.hide(); + this.settings.$li = undefined; + this.show('init'); + }, + + pos_default : function (init, resizing) { + var half_fold = Math.ceil($(window).height() / 2), + tip_position = this.settings.$next_tip.offset(), + $nub = this.settings.$next_tip.find('.joyride-nub'), + nub_width = Math.ceil(this.outerWidth($nub) / 2), + nub_height = Math.ceil(this.outerHeight($nub) / 2), + toggle = init || false; + + // tip must not be "display: none" to calculate position + if (toggle) { + this.settings.$next_tip.css('visibility', 'hidden'); + this.settings.$next_tip.show(); + } + + if (typeof resizing === 'undefined') { + resizing = false; + } + + if (!/body/i.test(this.settings.$target.selector)) { + + if (this.bottom()) { + var leftOffset = this.settings.$target.offset().left; + if (Foundation.rtl) { + leftOffset = this.settings.$target.offset().width - this.settings.$next_tip.width() + leftOffset; + } + this.settings.$next_tip.css({ + top: (this.settings.$target.offset().top + nub_height + this.outerHeight(this.settings.$target)), + left: leftOffset}); + + this.nub_position($nub, this.settings.tipSettings.nubPosition, 'top'); + + } else if (this.top()) { + var leftOffset = this.settings.$target.offset().left; + if (Foundation.rtl) { + leftOffset = this.settings.$target.offset().width - this.settings.$next_tip.width() + leftOffset; + } + this.settings.$next_tip.css({ + top: (this.settings.$target.offset().top - this.outerHeight(this.settings.$next_tip) - nub_height), + left: leftOffset}); + + this.nub_position($nub, this.settings.tipSettings.nubPosition, 'bottom'); + + } else if (this.right()) { + + this.settings.$next_tip.css({ + top: this.settings.$target.offset().top, + left: (this.outerWidth(this.settings.$target) + this.settings.$target.offset().left + nub_width)}); + + this.nub_position($nub, this.settings.tipSettings.nubPosition, 'left'); + + } else if (this.left()) { + + this.settings.$next_tip.css({ + top: this.settings.$target.offset().top, + left: (this.settings.$target.offset().left - this.outerWidth(this.settings.$next_tip) - nub_width)}); + + this.nub_position($nub, this.settings.tipSettings.nubPosition, 'right'); + + } + + if (!this.visible(this.corners(this.settings.$next_tip)) && this.settings.attempts < this.settings.tipSettings.tipLocationPattern.length) { + + $nub.removeClass('bottom') + .removeClass('top') + .removeClass('right') + .removeClass('left'); + + this.settings.tipSettings.tipLocation = this.settings.tipSettings.tipLocationPattern[this.settings.attempts]; + + this.settings.attempts++; + + this.pos_default(); + + } + + } else if (this.settings.$li.length) { + + this.pos_modal($nub); + + } + + if (toggle) { + this.settings.$next_tip.hide(); + this.settings.$next_tip.css('visibility', 'visible'); + } + + }, + + pos_phone : function (init) { + var tip_height = this.outerHeight(this.settings.$next_tip), + tip_offset = this.settings.$next_tip.offset(), + target_height = this.outerHeight(this.settings.$target), + $nub = $('.joyride-nub', this.settings.$next_tip), + nub_height = Math.ceil(this.outerHeight($nub) / 2), + toggle = init || false; + + $nub.removeClass('bottom') + .removeClass('top') + .removeClass('right') + .removeClass('left'); + + if (toggle) { + this.settings.$next_tip.css('visibility', 'hidden'); + this.settings.$next_tip.show(); + } + + if (!/body/i.test(this.settings.$target.selector)) { + + if (this.top()) { + + this.settings.$next_tip.offset({top: this.settings.$target.offset().top - tip_height - nub_height}); + $nub.addClass('bottom'); + + } else { + + this.settings.$next_tip.offset({top: this.settings.$target.offset().top + target_height + nub_height}); + $nub.addClass('top'); + + } + + } else if (this.settings.$li.length) { + this.pos_modal($nub); + } + + if (toggle) { + this.settings.$next_tip.hide(); + this.settings.$next_tip.css('visibility', 'visible'); + } + }, + + pos_modal : function ($nub) { + this.center(); + $nub.hide(); + + this.show_modal(); + }, + + show_modal : function () { + if (!this.settings.$next_tip.data('closed')) { + var joyridemodalbg = $('.joyride-modal-bg'); + if (joyridemodalbg.length < 1) { + $('body').append(this.settings.template.modal).show(); + } + + if (/pop/i.test(this.settings.tipAnimation)) { + joyridemodalbg.show(); + } else { + joyridemodalbg.fadeIn(this.settings.tipAnimationFadeSpeed); + } + } + }, + + expose : function () { + var expose, + exposeCover, + el, + origCSS, + origClasses, + randId = 'expose-'+Math.floor(Math.random()*10000); + + if (arguments.length > 0 && arguments[0] instanceof $) { + el = arguments[0]; + } else if(this.settings.$target && !/body/i.test(this.settings.$target.selector)){ + el = this.settings.$target; + } else { + return false; + } + + if(el.length < 1){ + if(window.console){ + console.error('element not valid', el); + } + return false; + } + + expose = $(this.settings.template.expose); + this.settings.$body.append(expose); + expose.css({ + top: el.offset().top, + left: el.offset().left, + width: this.outerWidth(el, true), + height: this.outerHeight(el, true) + }); + + exposeCover = $(this.settings.template.exposeCover); + + origCSS = { + zIndex: el.css('z-index'), + position: el.css('position') + }; + + origClasses = el.attr('class') == null ? '' : el.attr('class'); + + el.css('z-index',parseInt(expose.css('z-index'))+1); + + if (origCSS.position == 'static') { + el.css('position','relative'); + } + + el.data('expose-css',origCSS); + el.data('orig-class', origClasses); + el.attr('class', origClasses + ' ' + this.settings.exposeAddClass); + + exposeCover.css({ + top: el.offset().top, + left: el.offset().left, + width: this.outerWidth(el, true), + height: this.outerHeight(el, true) + }); + + this.settings.$body.append(exposeCover); + expose.addClass(randId); + exposeCover.addClass(randId); + el.data('expose', randId); + this.settings.postExposeCallback(this.settings.$li.index(), this.settings.$next_tip, el); + this.add_exposed(el); + }, + + un_expose : function () { + var exposeId, + el, + expose , + origCSS, + origClasses, + clearAll = false; + + if (arguments.length > 0 && arguments[0] instanceof $) { + el = arguments[0]; + } else if(this.settings.$target && !/body/i.test(this.settings.$target.selector)){ + el = this.settings.$target; + } else { + return false; + } + + if(el.length < 1){ + if (window.console) { + console.error('element not valid', el); + } + return false; + } + + exposeId = el.data('expose'); + expose = $('.' + exposeId); + + if (arguments.length > 1) { + clearAll = arguments[1]; + } + + if (clearAll === true) { + $('.joyride-expose-wrapper,.joyride-expose-cover').remove(); + } else { + expose.remove(); + } + + origCSS = el.data('expose-css'); + + if (origCSS.zIndex == 'auto') { + el.css('z-index', ''); + } else { + el.css('z-index', origCSS.zIndex); + } + + if (origCSS.position != el.css('position')) { + if(origCSS.position == 'static') {// this is default, no need to set it. + el.css('position', ''); + } else { + el.css('position', origCSS.position); + } + } + + origClasses = el.data('orig-class'); + el.attr('class', origClasses); + el.removeData('orig-classes'); + + el.removeData('expose'); + el.removeData('expose-z-index'); + this.remove_exposed(el); + }, + + add_exposed: function(el){ + this.settings.exposed = this.settings.exposed || []; + if (el instanceof $ || typeof el === 'object') { + this.settings.exposed.push(el[0]); + } else if (typeof el == 'string') { + this.settings.exposed.push(el); + } + }, + + remove_exposed: function(el){ + var search, count; + if (el instanceof $) { + search = el[0] + } else if (typeof el == 'string'){ + search = el; + } + + this.settings.exposed = this.settings.exposed || []; + count = this.settings.exposed.length; + + for (var i=0; i < count; i++) { + if (this.settings.exposed[i] == search) { + this.settings.exposed.splice(i, 1); + return; + } + } + }, + + center : function () { + var $w = $(window); + + this.settings.$next_tip.css({ + top : ((($w.height() - this.outerHeight(this.settings.$next_tip)) / 2) + $w.scrollTop()), + left : ((($w.width() - this.outerWidth(this.settings.$next_tip)) / 2) + this.scrollLeft($w)) + }); + + return true; + }, + + bottom : function () { + return /bottom/i.test(this.settings.tipSettings.tipLocation); + }, + + top : function () { + return /top/i.test(this.settings.tipSettings.tipLocation); + }, + + right : function () { + return /right/i.test(this.settings.tipSettings.tipLocation); + }, + + left : function () { + return /left/i.test(this.settings.tipSettings.tipLocation); + }, + + corners : function (el) { + var w = $(window), + window_half = w.height() / 2, + //using this to calculate since scroll may not have finished yet. + tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight()), + right = w.width() + this.scrollLeft(w), + offsetBottom = w.height() + tipOffset, + bottom = w.height() + w.scrollTop(), + top = w.scrollTop(); + + if (tipOffset < top) { + if (tipOffset < 0) { + top = 0; + } else { + top = tipOffset; + } + } + + if (offsetBottom > bottom) { + bottom = offsetBottom; + } + + return [ + el.offset().top < top, + right < el.offset().left + el.outerWidth(), + bottom < el.offset().top + el.outerHeight(), + this.scrollLeft(w) > el.offset().left + ]; + }, + + visible : function (hidden_corners) { + var i = hidden_corners.length; + + while (i--) { + if (hidden_corners[i]) return false; + } + + return true; + }, + + nub_position : function (nub, pos, def) { + if (pos === 'auto') { + nub.addClass(def); + } else { + nub.addClass(pos); + } + }, + + startTimer : function () { + if (this.settings.$li.length) { + this.settings.automate = setTimeout(function () { + this.hide(); + this.show(); + this.startTimer(); + }.bind(this), this.settings.timer); + } else { + clearTimeout(this.settings.automate); + } + }, + + end : function () { + if (this.settings.cookieMonster) { + $.cookie(this.settings.cookieName, 'ridden', { expires: this.settings.cookieExpires, domain: this.settings.cookieDomain }); + } + + if (this.settings.timer > 0) { + clearTimeout(this.settings.automate); + } + + if (this.settings.modal && this.settings.expose) { + this.un_expose(); + } + + this.settings.$next_tip.data('closed', true); + + $('.joyride-modal-bg').hide(); + this.settings.$current_tip.hide(); + this.settings.postStepCallback(this.settings.$li.index(), this.settings.$current_tip); + this.settings.postRideCallback(this.settings.$li.index(), this.settings.$current_tip); + $('.joyride-tip-guide').remove(); + }, + + outerHTML : function (el) { + // support FireFox < 11 + return el.outerHTML || new XMLSerializer().serializeToString(el); + }, + + off : function () { + $(this.scope).off('.joyride'); + $(window).off('.joyride'); + $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride'); + $('.joyride-tip-guide, .joyride-modal-bg').remove(); + clearTimeout(this.settings.automate); + this.settings = {}; + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.js b/videodb/lib/foundation4/js/foundation/foundation.js new file mode 100644 index 0000000..f1ac142 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.js @@ -0,0 +1,440 @@ +/* + * Foundation Responsive Library + * http://foundation.zurb.com + * Copyright 2013, ZURB + * Free to use under the MIT license. + * http://www.opensource.org/licenses/mit-license.php +*/ + +/*jslint unparam: true, browser: true, indent: 2 */ + +// Accommodate running jQuery or Zepto in noConflict() mode by +// using an anonymous function to redefine the $ shorthand name. +// See http://docs.jquery.com/Using_jQuery_with_Other_Libraries +// and http://zeptojs.com/ +var libFuncName = null; + +if (typeof jQuery === "undefined" && + typeof Zepto === "undefined" && + typeof $ === "function") { + libFuncName = $; +} else if (typeof jQuery === "function") { + libFuncName = jQuery; +} else if (typeof Zepto === "function") { + libFuncName = Zepto; +} else { + throw new TypeError(); +} + +(function ($, window, document, undefined) { + 'use strict'; + + /* + matchMedia() polyfill - Test a CSS media + type/query in JS. Authors & copyright (c) 2012: + Scott Jehl, Paul Irish, Nicholas Zakas. + Dual MIT/BSD license + + https://github.com/paulirish/matchMedia.js + */ + + window.matchMedia = window.matchMedia || (function( doc, undefined ) { + + "use strict"; + + var bool, + docElem = doc.documentElement, + refNode = docElem.firstElementChild || docElem.firstChild, + // fakeBody required for + fakeBody = doc.createElement( "body" ), + div = doc.createElement( "div" ); + + div.id = "mq-test-1"; + div.style.cssText = "position:absolute;top:-100em"; + fakeBody.style.background = "none"; + fakeBody.appendChild(div); + + return function(q){ + + div.innerHTML = "­"; + + docElem.insertBefore( fakeBody, refNode ); + bool = div.offsetWidth === 42; + docElem.removeChild( fakeBody ); + + return { + matches: bool, + media: q + }; + + }; + + }( document )); + + // add dusty browser stuff + if (!Array.prototype.filter) { + Array.prototype.filter = function(fun /*, thisp */) { + "use strict"; + + if (this == null) { + throw new TypeError(); + } + + var t = Object(this), + len = t.length >>> 0; + if (typeof fun !== "function") { + return; + } + + var res = [], + thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; // in case fun mutates this + if (fun && fun.call(thisp, val, i, t)) { + res.push(val); + } + } + } + + return res; + } + } + + if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; + }; + } + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + "use strict"; + if (this == null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n != 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + } + } + + // fake stop() for zepto. + $.fn.stop = $.fn.stop || function() { + return this; + }; + + window.Foundation = { + name : 'Foundation', + + version : '4.3.1', + + cache : {}, + + init : function (scope, libraries, method, options, response, /* internal */ nc) { + var library_arr, + args = [scope, method, options, response], + responses = [], + nc = nc || false; + + // disable library error catching, + // used for development only + if (nc) this.nc = nc; + + // check RTL + this.rtl = /rtl/i.test($('html').attr('dir')); + + // set foundation global scope + this.scope = scope || this.scope; + + if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) { + if (/off/i.test(libraries)) return this.off(); + + library_arr = libraries.split(' '); + + if (library_arr.length > 0) { + for (var i = library_arr.length - 1; i >= 0; i--) { + responses.push(this.init_lib(library_arr[i], args)); + } + } + } else { + if (/reflow/i.test(libraries)) args[1] = 'reflow'; + + for (var lib in this.libs) { + responses.push(this.init_lib(lib, args)); + } + } + + // if first argument is callback, add to args + if (typeof libraries === 'function') { + args.unshift(libraries); + } + + return this.response_obj(responses, args); + }, + + response_obj : function (response_arr, args) { + for (var i = 0, len = args.length; i < len; i++) { + if (typeof args[i] === 'function') { + return args[i]({ + errors: response_arr.filter(function (s) { + if (typeof s === 'string') return s; + }) + }); + } + } + + return response_arr; + }, + + init_lib : function (lib, args) { + return this.trap(function () { + if (this.libs.hasOwnProperty(lib)) { + this.patch(this.libs[lib]); + return this.libs[lib].init.apply(this.libs[lib], args); + } else { + return function () {}; + } + }.bind(this), lib); + }, + + trap : function (fun, lib) { + if (!this.nc) { + try { + return fun(); + } catch (e) { + return this.error({name: lib, message: 'could not be initialized', more: e.name + ' ' + e.message}); + } + } + + return fun(); + }, + + patch : function (lib) { + this.fix_outer(lib); + lib.scope = this.scope; + lib.rtl = this.rtl; + }, + + inherit : function (scope, methods) { + var methods_arr = methods.split(' '); + + for (var i = methods_arr.length - 1; i >= 0; i--) { + if (this.lib_methods.hasOwnProperty(methods_arr[i])) { + this.libs[scope.name][methods_arr[i]] = this.lib_methods[methods_arr[i]]; + } + } + }, + + random_str : function (length) { + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + + if (!length) { + length = Math.floor(Math.random() * chars.length); + } + + var str = ''; + for (var i = 0; i < length; i++) { + str += chars[Math.floor(Math.random() * chars.length)]; + } + return str; + }, + + libs : {}, + + // methods that can be inherited in libraries + lib_methods : { + set_data : function (node, data) { + // this.name references the name of the library calling this method + var id = [this.name,+new Date(),Foundation.random_str(5)].join('-'); + + Foundation.cache[id] = data; + node.attr('data-' + this.name + '-id', id); + return data; + }, + + get_data : function (node) { + return Foundation.cache[node.attr('data-' + this.name + '-id')]; + }, + + remove_data : function (node) { + if (node) { + delete Foundation.cache[node.attr('data-' + this.name + '-id')]; + node.attr('data-' + this.name + '-id', ''); + } else { + $('[data-' + this.name + '-id]').each(function () { + delete Foundation.cache[$(this).attr('data-' + this.name + '-id')]; + $(this).attr('data-' + this.name + '-id', ''); + }); + } + }, + + throttle : function(fun, delay) { + var timer = null; + return function () { + var context = this, args = arguments; + clearTimeout(timer); + timer = setTimeout(function () { + fun.apply(context, args); + }, delay); + }; + }, + + // parses data-options attribute on nodes and turns + // them into an object + data_options : function (el) { + var opts = {}, ii, p, + opts_arr = (el.attr('data-options') || ':').split(';'), + opts_len = opts_arr.length; + + function isNumber (o) { + return ! isNaN (o-0) && o !== null && o !== "" && o !== false && o !== true; + } + + function trim(str) { + if (typeof str === 'string') return $.trim(str); + return str; + } + + // parse options + for (ii = opts_len - 1; ii >= 0; ii--) { + p = opts_arr[ii].split(':'); + + if (/true/i.test(p[1])) p[1] = true; + if (/false/i.test(p[1])) p[1] = false; + if (isNumber(p[1])) p[1] = parseInt(p[1], 10); + + if (p.length === 2 && p[0].length > 0) { + opts[trim(p[0])] = trim(p[1]); + } + } + + return opts; + }, + + delay : function (fun, delay) { + return setTimeout(fun, delay); + }, + + // animated scrolling + scrollTo : function (el, to, duration) { + if (duration < 0) return; + var difference = to - $(window).scrollTop(); + var perTick = difference / duration * 10; + + this.scrollToTimerCache = setTimeout(function() { + if (!isNaN(parseInt(perTick, 10))) { + window.scrollTo(0, $(window).scrollTop() + perTick); + this.scrollTo(el, to, duration - 10); + } + }.bind(this), 10); + }, + + // not supported in core Zepto + scrollLeft : function (el) { + if (!el.length) return; + return ('scrollLeft' in el[0]) ? el[0].scrollLeft : el[0].pageXOffset; + }, + + // test for empty object or array + empty : function (obj) { + if (obj.length && obj.length > 0) return false; + if (obj.length && obj.length === 0) return true; + + for (var key in obj) { + if (hasOwnProperty.call(obj, key)) return false; + } + + return true; + } + }, + + fix_outer : function (lib) { + lib.outerHeight = function (el, bool) { + if (typeof Zepto === 'function') { + return el.height(); + } + + if (typeof bool !== 'undefined') { + return el.outerHeight(bool); + } + + return el.outerHeight(); + }; + + lib.outerWidth = function (el, bool) { + if (typeof Zepto === 'function') { + return el.width(); + } + + if (typeof bool !== 'undefined') { + return el.outerWidth(bool); + } + + return el.outerWidth(); + }; + }, + + error : function (error) { + return error.name + ' ' + error.message + '; ' + error.more; + }, + + // remove all foundation events. + off: function () { + $(this.scope).off('.fndtn'); + $(window).off('.fndtn'); + return true; + }, + + zj : $ + }; + + $.fn.foundation = function () { + var args = Array.prototype.slice.call(arguments, 0); + + return this.each(function () { + Foundation.init.apply(Foundation, [this].concat(args)); + return this; + }); + }; + +}(libFuncName, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.magellan.js b/videodb/lib/foundation4/js/foundation/foundation.magellan.js new file mode 100644 index 0000000..26eb614 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.magellan.js @@ -0,0 +1,135 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.magellan = { + name : 'magellan', + + version : '4.2.2', + + settings : { + activeClass: 'active', + threshold: 0 + }, + + init : function (scope, method, options) { + this.scope = scope || this.scope; + Foundation.inherit(this, 'data_options'); + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } + + if (typeof method !== 'string') { + if (!this.settings.init) { + this.fixed_magellan = $("[data-magellan-expedition]"); + this.set_threshold(); + this.last_destination = $('[data-magellan-destination]').last(); + this.events(); + } + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + $(this.scope).on('arrival.fndtn.magellan', '[data-magellan-arrival]', function (e) { + var $destination = $(this), + $expedition = $destination.closest('[data-magellan-expedition]'), + activeClass = $expedition.attr('data-magellan-active-class') + || self.settings.activeClass; + + $destination + .closest('[data-magellan-expedition]') + .find('[data-magellan-arrival]') + .not($destination) + .removeClass(activeClass); + $destination.addClass(activeClass); + }); + + this.fixed_magellan + .on('update-position.fndtn.magellan', function(){ + var $el = $(this); + // $el.data("magellan-fixed-position",""); + // $el.data("magellan-top-offset", ""); + }) + .trigger('update-position'); + + $(window) + .on('resize.fndtn.magellan', function() { + this.fixed_magellan.trigger('update-position'); + }.bind(this)) + + .on('scroll.fndtn.magellan', function() { + var windowScrollTop = $(window).scrollTop(); + self.fixed_magellan.each(function() { + var $expedition = $(this); + if (typeof $expedition.data('magellan-top-offset') === 'undefined') { + $expedition.data('magellan-top-offset', $expedition.offset().top); + } + if (typeof $expedition.data('magellan-fixed-position') === 'undefined') { + $expedition.data('magellan-fixed-position', false) + } + var fixed_position = (windowScrollTop + self.settings.threshold) > $expedition.data("magellan-top-offset"); + var attr = $expedition.attr('data-magellan-top-offset'); + + if ($expedition.data("magellan-fixed-position") != fixed_position) { + $expedition.data("magellan-fixed-position", fixed_position); + if (fixed_position) { + $expedition.addClass('fixed'); + $expedition.css({position:"fixed", top:0}); + } else { + $expedition.removeClass('fixed'); + $expedition.css({position:"", top:""}); + } + if (fixed_position && typeof attr != 'undefined' && attr != false) { + $expedition.css({position:"fixed", top:attr + "px"}); + } + } + }); + }); + + + if (this.last_destination.length > 0) { + $(window).on('scroll.fndtn.magellan', function (e) { + var windowScrollTop = $(window).scrollTop(), + scrolltopPlusHeight = windowScrollTop + $(window).height(), + lastDestinationTop = Math.ceil(self.last_destination.offset().top); + + $('[data-magellan-destination]').each(function () { + var $destination = $(this), + destination_name = $destination.attr('data-magellan-destination'), + topOffset = $destination.offset().top - windowScrollTop; + + if (topOffset <= self.settings.threshold) { + $("[data-magellan-arrival='" + destination_name + "']").trigger('arrival'); + } + // In large screens we may hit the bottom of the page and dont reach the top of the last magellan-destination, so lets force it + if (scrolltopPlusHeight >= $(self.scope).height() && lastDestinationTop > windowScrollTop && lastDestinationTop < scrolltopPlusHeight) { + $('[data-magellan-arrival]').last().trigger('arrival'); + } + }); + }); + } + + this.settings.init = true; + }, + + set_threshold : function () { + if (!this.settings.threshold) { + this.settings.threshold = (this.fixed_magellan.length > 0) ? + this.outerHeight(this.fixed_magellan, true) : 0; + } + }, + + off : function () { + $(this.scope).off('.fndtn.magellan'); + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.orbit.js b/videodb/lib/foundation4/js/foundation/foundation.orbit.js new file mode 100644 index 0000000..ce5183b --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.orbit.js @@ -0,0 +1,412 @@ +;(function ($, window, document, undefined) { + 'use strict'; + + var noop = function() {}; + + var Orbit = function(el, settings) { + // Don't reinitialize plugin + if (el.hasClass(settings.slides_container_class)) { + return this; + } + + var self = this, + container, + slides_container = el, + number_container, + bullets_container, + timer_container, + idx = 0, + animate, + timer, + locked = false, + adjust_height_after = false; + + slides_container.children().first().addClass(settings.active_slide_class); + + self.update_slide_number = function(index) { + if (settings.slide_number) { + number_container.find('span:first').text(parseInt(index)+1); + number_container.find('span:last').text(slides_container.children().length); + } + if (settings.bullets) { + bullets_container.children().removeClass(settings.bullets_active_class); + $(bullets_container.children().get(index)).addClass(settings.bullets_active_class); + } + }; + + self.build_markup = function() { + slides_container.wrap('
            '); + container = slides_container.parent(); + slides_container.addClass(settings.slides_container_class); + + if (settings.navigation_arrows) { + container.append($('').addClass(settings.prev_class).append('')); + container.append($('').addClass(settings.next_class).append('')); + } + + if (settings.timer) { + timer_container = $('
            ').addClass(settings.timer_container_class); + timer_container.append(''); + timer_container.append($('
            ').addClass(settings.timer_progress_class)); + timer_container.addClass(settings.timer_paused_class); + container.append(timer_container); + } + + if (settings.slide_number) { + number_container = $('
            ').addClass(settings.slide_number_class); + number_container.append(' of '); + container.append(number_container); + } + + if (settings.bullets) { + bullets_container = $('
              ').addClass(settings.bullets_container_class); + container.append(bullets_container); + slides_container.children().each(function(idx, el) { + var bullet = $('
            1. ').attr('data-orbit-slide', idx); + bullets_container.append(bullet); + }); + } + + if (settings.stack_on_small) { + container.addClass(settings.stack_on_small_class); + } + + self.update_slide_number(0); + }; + + self._goto = function(next_idx, start_timer) { + // if (locked) {return false;} + if (next_idx === idx) {return false;} + if (typeof timer === 'object') {timer.restart();} + var slides = slides_container.children(); + + var dir = 'next'; + locked = true; + if (next_idx < idx) {dir = 'prev';} + if (next_idx >= slides.length) {next_idx = 0;} + else if (next_idx < 0) {next_idx = slides.length - 1;} + + var current = $(slides.get(idx)); + var next = $(slides.get(next_idx)); + + current.css('zIndex', 2); + next.css('zIndex', 4).addClass('active'); + + slides_container.trigger('orbit:before-slide-change'); + settings.before_slide_change(); + + var callback = function() { + var unlock = function() { + idx = next_idx; + locked = false; + if (start_timer === true) {timer = self.create_timer(); timer.start();} + self.update_slide_number(idx); + slides_container.trigger('orbit:after-slide-change',[{slide_number: idx, total_slides: slides.length}]); + settings.after_slide_change(idx, slides.length); + }; + if (slides_container.height() != next.height()) { + slides_container.animate({'height': next.height()}, 250, 'linear', unlock); + } else { + unlock(); + } + }; + + if (slides.length === 1) {callback(); return false;} + + var start_animation = function() { + if (dir === 'next') {animate.next(current, next, callback);} + if (dir === 'prev') {animate.prev(current, next, callback);} + }; + + if (next.height() > slides_container.height()) { + slides_container.animate({'height': next.height()}, 250, 'linear', start_animation); + } else { + start_animation(); + } + }; + + self.next = function(e) { + e.stopImmediatePropagation(); + e.preventDefault(); + self._goto(idx + 1); + }; + + self.prev = function(e) { + e.stopImmediatePropagation(); + e.preventDefault(); + self._goto(idx - 1); + }; + + self.link_custom = function(e) { + e.preventDefault(); + var link = $(this).attr('data-orbit-link'); + if ((typeof link === 'string') && (link = $.trim(link)) != "") { + var slide = container.find('[data-orbit-slide='+link+']'); + if (slide.index() != -1) {self._goto(slide.index());} + } + }; + + self.link_bullet = function(e) { + var index = $(this).attr('data-orbit-slide'); + if ((typeof index === 'string') && (index = $.trim(index)) != "") { + self._goto(index); + } + } + + self.timer_callback = function() { + self._goto(idx + 1, true); + } + + self.compute_dimensions = function() { + var current = $(slides_container.children().get(idx)); + var h = current.height(); + if (!settings.variable_height) { + slides_container.children().each(function(){ + if ($(this).height() > h) { h = $(this).height(); } + }); + } + slides_container.height(h); + }; + + self.create_timer = function() { + var t = new Timer( + container.find('.'+settings.timer_container_class), + settings, + self.timer_callback + ); + return t; + }; + + self.stop_timer = function() { + if (typeof timer === 'object') timer.stop(); + }; + + self.toggle_timer = function() { + var t = container.find('.'+settings.timer_container_class); + if (t.hasClass(settings.timer_paused_class)) { + if (typeof timer === 'undefined') {timer = self.create_timer();} + timer.start(); + } + else { + if (typeof timer === 'object') {timer.stop();} + } + }; + + self.init = function() { + self.build_markup(); + if (settings.timer) {timer = self.create_timer(); timer.start();} + animate = new FadeAnimation(slides_container); + if (settings.animation === 'slide') + animate = new SlideAnimation(slides_container); + container.on('click', '.'+settings.next_class, self.next); + container.on('click', '.'+settings.prev_class, self.prev); + container.on('click', '[data-orbit-slide]', self.link_bullet); + container.on('click', self.toggle_timer); + container.on('touchstart.fndtn.orbit', function(e) { + if (!e.touches) {e = e.originalEvent;} + var data = { + start_page_x: e.touches[0].pageX, + start_page_y: e.touches[0].pageY, + start_time: (new Date()).getTime(), + delta_x: 0, + is_scrolling: undefined + }; + container.data('swipe-transition', data); + e.stopPropagation(); + }) + .on('touchmove.fndtn.orbit', function(e) { + if (!e.touches) { e = e.originalEvent; } + // Ignore pinch/zoom events + if(e.touches.length > 1 || e.scale && e.scale !== 1) return; + + var data = container.data('swipe-transition'); + if (typeof data === 'undefined') {data = {};} + + data.delta_x = e.touches[0].pageX - data.start_page_x; + + if ( typeof data.is_scrolling === 'undefined') { + data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) ); + } + + if (!data.is_scrolling && !data.active) { + e.preventDefault(); + var direction = (data.delta_x < 0) ? (idx+1) : (idx-1); + data.active = true; + self._goto(direction); + } + }) + .on('touchend.fndtn.orbit', function(e) { + container.data('swipe-transition', {}); + e.stopPropagation(); + }) + .on('mouseenter.fndtn.orbit', function(e) { + if (settings.timer && settings.pause_on_hover) { + self.stop_timer(); + } + }) + .on('mouseleave.fndtn.orbit', function(e) { + if (settings.timer && settings.resume_on_mouseout) { + timer.start(); + } + }); + + $(document).on('click', '[data-orbit-link]', self.link_custom); + $(window).on('resize', self.compute_dimensions); + $(window).on('load', self.compute_dimensions); + slides_container.trigger('orbit:ready'); + }; + + self.init(); + }; + + var Timer = function(el, settings, callback) { + var self = this, + duration = settings.timer_speed, + progress = el.find('.'+settings.timer_progress_class), + start, + timeout, + left = -1; + + this.update_progress = function(w) { + var new_progress = progress.clone(); + new_progress.attr('style', ''); + new_progress.css('width', w+'%'); + progress.replaceWith(new_progress); + progress = new_progress; + }; + + this.restart = function() { + clearTimeout(timeout); + el.addClass(settings.timer_paused_class); + left = -1; + self.update_progress(0); + }; + + this.start = function() { + if (!el.hasClass(settings.timer_paused_class)) {return true;} + left = (left === -1) ? duration : left; + el.removeClass(settings.timer_paused_class); + start = new Date().getTime(); + progress.animate({'width': '100%'}, left, 'linear'); + timeout = setTimeout(function() { + self.restart(); + callback(); + }, left); + el.trigger('orbit:timer-started') + }; + + this.stop = function() { + if (el.hasClass(settings.timer_paused_class)) {return true;} + clearTimeout(timeout); + el.addClass(settings.timer_paused_class); + var end = new Date().getTime(); + left = left - (end - start); + var w = 100 - ((left / duration) * 100); + self.update_progress(w); + el.trigger('orbit:timer-stopped'); + }; + }; + + var SlideAnimation = function(container) { + var duration = 400; + var is_rtl = ($('html[dir=rtl]').length === 1); + var margin = is_rtl ? 'marginRight' : 'marginLeft'; + + this.next = function(current, next, callback) { + next.animate({margin: '0%'}, duration, 'linear', function() { + current.css(margin, '100%'); + callback(); + }); + }; + + this.prev = function(current, prev, callback) { + prev.css(margin, '-100%'); + prev.animate({margin:'0%'}, duration, 'linear', function() { + current.css(margin, '100%'); + callback(); + }); + }; + }; + + var FadeAnimation = function(container) { + var duration = 250; + + this.next = function(current, next, callback) { + next.css({'marginLeft':'0%', 'opacity':'0.01'}); + next.animate({'opacity':'1'}, duration, 'linear', function() { + current.css('marginLeft', '100%'); + callback(); + }); + }; + + this.prev = function(current, prev, callback) { + prev.css({'marginLeft':'0%', 'opacity':'0.01'}); + prev.animate({'opacity':'1'}, duration, 'linear', function() { + current.css('marginLeft', '100%'); + callback(); + }); + }; + }; + + + Foundation.libs = Foundation.libs || {}; + + Foundation.libs.orbit = { + name: 'orbit', + + version: '4.3.1', + + settings: { + animation: 'slide', + timer_speed: 10000, + pause_on_hover: true, + resume_on_mouseout: false, + animation_speed: 500, + stack_on_small: false, + navigation_arrows: true, + slide_number: true, + container_class: 'orbit-container', + stack_on_small_class: 'orbit-stack-on-small', + next_class: 'orbit-next', + prev_class: 'orbit-prev', + timer_container_class: 'orbit-timer', + timer_paused_class: 'paused', + timer_progress_class: 'orbit-progress', + slides_container_class: 'orbit-slides-container', + bullets_container_class: 'orbit-bullets', + bullets_active_class: 'active', + slide_number_class: 'orbit-slide-number', + caption_class: 'orbit-caption', + active_slide_class: 'active', + orbit_transition_class: 'orbit-transitioning', + bullets: true, + timer: true, + variable_height: false, + before_slide_change: noop, + after_slide_change: noop + }, + + init: function (scope, method, options) { + var self = this; + Foundation.inherit(self, 'data_options'); + + if (typeof method === 'object') { + $.extend(true, self.settings, method); + } + + if ($(scope).is('[data-orbit]')) { + var $el = $(scope); + var opts = self.data_options($el); + new Orbit($el, $.extend({},self.settings, opts)); + } + + $('[data-orbit]', scope).each(function(idx, el) { + var $el = $(el); + var opts = self.data_options($el); + new Orbit($el, $.extend({},self.settings, opts)); + }); + } + }; + + +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.placeholder.js b/videodb/lib/foundation4/js/foundation/foundation.placeholder.js new file mode 100644 index 0000000..b979ed1 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.placeholder.js @@ -0,0 +1,179 @@ +/*! http://mths.be/placeholder v2.0.7 by @mathias + Modified to work with Zepto.js by ZURB +*/ +;(function(window, document, $) { + + var isInputSupported = 'placeholder' in document.createElement('input'), + isTextareaSupported = 'placeholder' in document.createElement('textarea'), + prototype = $.fn, + valHooks = $.valHooks, + hooks, + placeholder; + + if (isInputSupported && isTextareaSupported) { + + placeholder = prototype.placeholder = function() { + return this; + }; + + placeholder.input = placeholder.textarea = true; + + } else { + + placeholder = prototype.placeholder = function() { + var $this = this; + $this + .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') + .not('.placeholder') + .bind({ + 'focus.placeholder': clearPlaceholder, + 'blur.placeholder': setPlaceholder + }) + .data('placeholder-enabled', true) + .trigger('blur.placeholder'); + return $this; + }; + + placeholder.input = isInputSupported; + placeholder.textarea = isTextareaSupported; + + hooks = { + 'get': function(element) { + var $element = $(element); + return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value; + }, + 'set': function(element, value) { + var $element = $(element); + if (!$element.data('placeholder-enabled')) { + return element.value = value; + } + if (value == '') { + element.value = value; + // Issue #56: Setting the placeholder causes problems if the element continues to have focus. + if (element != document.activeElement) { + // We can't use `triggerHandler` here because of dummy text/password inputs :( + setPlaceholder.call(element); + } + } else if ($element.hasClass('placeholder')) { + clearPlaceholder.call(element, true, value) || (element.value = value); + } else { + element.value = value; + } + // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363 + return $element; + } + }; + + isInputSupported || (valHooks.input = hooks); + isTextareaSupported || (valHooks.textarea = hooks); + + $(function() { + // Look for forms + $(document).delegate('form', 'submit.placeholder', function() { + // Clear the placeholder values so they don't get submitted + var $inputs = $('.placeholder', this).each(clearPlaceholder); + setTimeout(function() { + $inputs.each(setPlaceholder); + }, 10); + }); + }); + + // Clear placeholder values upon page reload + $(window).bind('beforeunload.placeholder', function() { + $('.placeholder').each(function() { + this.value = ''; + }); + }); + + } + + function args(elem) { + // Return an object of element attributes + var newAttrs = {}, + rinlinejQuery = /^jQuery\d+$/; + $.each(elem.attributes, function(i, attr) { + if (attr.specified && !rinlinejQuery.test(attr.name)) { + newAttrs[attr.name] = attr.value; + } + }); + return newAttrs; + } + + function clearPlaceholder(event, value) { + var input = this, + $input = $(input); + if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) { + if ($input.data('placeholder-password')) { + $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id')); + // If `clearPlaceholder` was called from `$.valHooks.input.set` + if (event === true) { + return $input[0].value = value; + } + $input.focus(); + } else { + input.value = ''; + $input.removeClass('placeholder'); + input == document.activeElement && input.select(); + } + } + } + + function setPlaceholder() { + var $replacement, + input = this, + $input = $(input), + $origInput = $input, + id = this.id; + if (input.value == '') { + if (input.type == 'password') { + if (!$input.data('placeholder-textinput')) { + try { + $replacement = $input.clone().attr({ 'type': 'text' }); + } catch(e) { + $replacement = $('').attr($.extend(args(this), { 'type': 'text' })); + } + $replacement + .removeAttr('name') + .data({ + 'placeholder-password': true, + 'placeholder-id': id + }) + .bind('focus.placeholder', clearPlaceholder); + $input + .data({ + 'placeholder-textinput': $replacement, + 'placeholder-id': id + }) + .before($replacement); + } + $input = $input.removeAttr('id').hide().prev().attr('id', id).show(); + // Note: `$input[0] != input` now! + } + $input.addClass('placeholder'); + $input[0].value = $input.attr('placeholder'); + } else { + $input.removeClass('placeholder'); + } + } + +}(this, document, Foundation.zj)); + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.placeholder = { + name : 'placeholder', + + version : '4.2.2', + + init : function (scope, method, options) { + this.scope = scope || this.scope; + + if (typeof method !== 'string') { + window.onload = function () { + $('input, textarea').placeholder(); + } + } + } + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.reveal.js b/videodb/lib/foundation4/js/foundation/foundation.reveal.js new file mode 100644 index 0000000..138b24f --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.reveal.js @@ -0,0 +1,330 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.reveal = { + name : 'reveal', + + version : '4.2.2', + + locked : false, + + settings : { + animation: 'fadeAndPop', + animationSpeed: 250, + closeOnBackgroundClick: true, + closeOnEsc: true, + dismissModalClass: 'close-reveal-modal', + bgClass: 'reveal-modal-bg', + open: function(){}, + opened: function(){}, + close: function(){}, + closed: function(){}, + bg : $('.reveal-modal-bg'), + css : { + open : { + 'opacity': 0, + 'visibility': 'visible', + 'display' : 'block' + }, + close : { + 'opacity': 1, + 'visibility': 'hidden', + 'display': 'none' + } + } + }, + + init : function (scope, method, options) { + Foundation.inherit(this, 'data_options delay'); + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } else if (typeof options !== 'undefined') { + $.extend(true, this.settings, options); + } + + if (typeof method !== 'string') { + this.events(); + + return this.settings.init; + } else { + return this[method].call(this, options); + } + }, + + events : function () { + var self = this; + + $(this.scope) + .off('.fndtn.reveal') + .on('click.fndtn.reveal', '[data-reveal-id]', function (e) { + e.preventDefault(); + + if (!self.locked) { + var element = $(this), + ajax = element.data('reveal-ajax'); + + self.locked = true; + + if (typeof ajax === 'undefined') { + self.open.call(self, element); + } else { + var url = ajax === true ? element.attr('href') : ajax; + + self.open.call(self, element, {url: url}); + } + } + }) + .on('click.fndtn.reveal', this.close_targets(), function (e) { + e.preventDefault(); + if (!self.locked) { + var settings = $.extend({}, self.settings, self.data_options($('.reveal-modal.open'))); + if ($(e.target)[0] === $('.' + settings.bgClass)[0] && !settings.closeOnBackgroundClick) { + return; + } + + self.locked = true; + self.close.call(self, $(this).closest('.reveal-modal')); + } + }) + .on('open.fndtn.reveal', '.reveal-modal', this.settings.open) + .on('opened.fndtn.reveal', '.reveal-modal', this.settings.opened) + .on('opened.fndtn.reveal', '.reveal-modal', this.open_video) + .on('close.fndtn.reveal', '.reveal-modal', this.settings.close) + .on('closed.fndtn.reveal', '.reveal-modal', this.settings.closed) + .on('closed.fndtn.reveal', '.reveal-modal', this.close_video); + + $( 'body' ).bind( 'keyup.reveal', function ( event ) { + var open_modal = $('.reveal-modal.open'), + settings = $.extend({}, self.settings, self.data_options(open_modal)); + if ( event.which === 27 && settings.closeOnEsc) { // 27 is the keycode for the Escape key + open_modal.foundation('reveal', 'close'); + } + }); + + return true; + }, + + open : function (target, ajax_settings) { + if (target) { + if (typeof target.selector !== 'undefined') { + var modal = $('#' + target.data('reveal-id')); + } else { + var modal = $(this.scope); + + ajax_settings = target; + } + } else { + var modal = $(this.scope); + } + + if (!modal.hasClass('open')) { + var open_modal = $('.reveal-modal.open'); + + if (typeof modal.data('css-top') === 'undefined') { + modal.data('css-top', parseInt(modal.css('top'), 10)) + .data('offset', this.cache_offset(modal)); + } + + modal.trigger('open'); + + if (open_modal.length < 1) { + this.toggle_bg(modal); + } + + if (typeof ajax_settings === 'undefined' || !ajax_settings.url) { + this.hide(open_modal, this.settings.css.close); + this.show(modal, this.settings.css.open); + } else { + var self = this, + old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null; + + $.extend(ajax_settings, { + success: function (data, textStatus, jqXHR) { + if ( $.isFunction(old_success) ) { + old_success(data, textStatus, jqXHR); + } + + modal.html(data); + $(modal).foundation('section', 'reflow'); + + self.hide(open_modal, self.settings.css.close); + self.show(modal, self.settings.css.open); + } + }); + + $.ajax(ajax_settings); + } + } + }, + + close : function (modal) { + + var modal = modal && modal.length ? modal : $(this.scope), + open_modals = $('.reveal-modal.open'); + + if (open_modals.length > 0) { + this.locked = true; + modal.trigger('close'); + this.toggle_bg(modal); + this.hide(open_modals, this.settings.css.close); + } + }, + + close_targets : function () { + var base = '.' + this.settings.dismissModalClass; + + if (this.settings.closeOnBackgroundClick) { + return base + ', .' + this.settings.bgClass; + } + + return base; + }, + + toggle_bg : function (modal) { + if ($('.reveal-modal-bg').length === 0) { + this.settings.bg = $('
              ', {'class': this.settings.bgClass}) + .appendTo('body'); + } + + if (this.settings.bg.filter(':visible').length > 0) { + this.hide(this.settings.bg); + } else { + this.show(this.settings.bg); + } + }, + + show : function (el, css) { + // is modal + if (css) { + if (/pop/i.test(this.settings.animation)) { + css.top = $(window).scrollTop() - el.data('offset') + 'px'; + var end_css = { + top: $(window).scrollTop() + el.data('css-top') + 'px', + opacity: 1 + }; + + return this.delay(function () { + return el + .css(css) + .animate(end_css, this.settings.animationSpeed, 'linear', function () { + this.locked = false; + el.trigger('opened'); + }.bind(this)) + .addClass('open'); + }.bind(this), this.settings.animationSpeed / 2); + } + + if (/fade/i.test(this.settings.animation)) { + var end_css = {opacity: 1}; + + return this.delay(function () { + return el + .css(css) + .animate(end_css, this.settings.animationSpeed, 'linear', function () { + this.locked = false; + el.trigger('opened'); + }.bind(this)) + .addClass('open'); + }.bind(this), this.settings.animationSpeed / 2); + } + + return el.css(css).show().css({opacity: 1}).addClass('open').trigger('opened'); + } + + // should we animate the background? + if (/fade/i.test(this.settings.animation)) { + return el.fadeIn(this.settings.animationSpeed / 2); + } + + return el.show(); + }, + + hide : function (el, css) { + // is modal + if (css) { + if (/pop/i.test(this.settings.animation)) { + var end_css = { + top: - $(window).scrollTop() - el.data('offset') + 'px', + opacity: 0 + }; + + return this.delay(function () { + return el + .animate(end_css, this.settings.animationSpeed, 'linear', function () { + this.locked = false; + el.css(css).trigger('closed'); + }.bind(this)) + .removeClass('open'); + }.bind(this), this.settings.animationSpeed / 2); + } + + if (/fade/i.test(this.settings.animation)) { + var end_css = {opacity: 0}; + + return this.delay(function () { + return el + .animate(end_css, this.settings.animationSpeed, 'linear', function () { + this.locked = false; + el.css(css).trigger('closed'); + }.bind(this)) + .removeClass('open'); + }.bind(this), this.settings.animationSpeed / 2); + } + + return el.hide().css(css).removeClass('open').trigger('closed'); + } + + // should we animate the background? + if (/fade/i.test(this.settings.animation)) { + return el.fadeOut(this.settings.animationSpeed / 2); + } + + return el.hide(); + }, + + close_video : function (e) { + var video = $(this).find('.flex-video'), + iframe = video.find('iframe'); + + if (iframe.length > 0) { + iframe.attr('data-src', iframe[0].src); + iframe.attr('src', 'about:blank'); + video.hide(); + } + }, + + open_video : function (e) { + var video = $(this).find('.flex-video'), + iframe = video.find('iframe'); + + if (iframe.length > 0) { + var data_src = iframe.attr('data-src'); + if (typeof data_src === 'string') { + iframe[0].src = iframe.attr('data-src'); + } else { + var src = iframe[0].src; + iframe[0].src = undefined; + iframe[0].src = src; + } + video.show(); + } + }, + + cache_offset : function (modal) { + var offset = modal.show().height() + parseInt(modal.css('top'), 10); + + modal.hide(); + + return offset; + }, + + off : function () { + $(this.scope).off('.fndtn.reveal'); + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.section.js b/videodb/lib/foundation4/js/foundation/foundation.section.js new file mode 100644 index 0000000..95fb647 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.section.js @@ -0,0 +1,400 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +; +(function($, window, document) { + 'use strict'; + + Foundation.libs.section = { + name : 'section', + + version : '4.3.1', + + settings: { + deep_linking: false, + small_breakpoint: 768, + one_up: true, + section_selector: '[data-section]', + region_selector: 'section, .section, [data-section-region]', + title_selector: '.title, [data-section-title]', + //marker: container is resized + resized_data_attr: 'data-section-resized', + //marker: container should apply accordion style + small_style_data_attr: 'data-section-small-style', + content_selector: '.content, [data-section-content]', + nav_selector: '[data-section="vertical-nav"], [data-section="horizontal-nav"]', + active_class: 'active', + callback: function() {} + }, + + init: function(scope, method, options) { + var self = this; + Foundation.inherit(this, 'throttle data_options position_right offset_right'); + + if (typeof method === 'object') { + $.extend(true, self.settings, method); + } + + if (typeof method !== 'string') { + this.events(); + return true; + } else { + return this[method].call(this, options); + } + }, + + events: function() { + var self = this; + + //combine titles selector from settings for click event binding + var click_title_selectors = [], + section_selector = self.settings.section_selector, + region_selectors = self.settings.region_selector.split(","), + title_selectors = self.settings.title_selector.split(","); + + for (var i = 0, len = region_selectors.length; i < len; i++) { + var region_selector = region_selectors[i]; + + for (var j = 0, len1 = title_selectors.length; j < len1; j++) { + var title_selector = section_selector + ">" + region_selector + ">" + title_selectors[j]; + + click_title_selectors.push(title_selector + " a"); //or we can not do preventDefault for click event of + click_title_selectors.push(title_selector); + } + } + + $(self.scope) + .on('click.fndtn.section', click_title_selectors.join(","), function(e) { + var title = $(this).closest(self.settings.title_selector); + + self.close_navs(title); + if (title.siblings(self.settings.content_selector).length > 0) { + self.toggle_active.call(title[0], e); + } + }); + + $(window) + .on('resize.fndtn.section', self.throttle(function() { self.resize(); }, 30)) + .on('hashchange.fndtn.section', self.set_active_from_hash); + + $(document).on('click.fndtn.section', function (e) { + if (e.isPropagationStopped && e.isPropagationStopped()) return; + if (e.target === document) return; + self.close_navs($(e.target).closest(self.settings.title_selector)); + }); + + $(window).triggerHandler('resize.fndtn.section'); + $(window).triggerHandler('hashchange.fndtn.section'); + }, + + //close nav !one_up on click elsewhere + close_navs: function(except_nav_with_title) { + var self = Foundation.libs.section, + navsToClose = $(self.settings.nav_selector) + .filter(function() { return !$.extend({}, + self.settings, self.data_options($(this))).one_up; }); + + if (except_nav_with_title.length > 0) { + var section = except_nav_with_title.parent().parent(); + + if (self.is_horizontal_nav(section) || self.is_vertical_nav(section)) { + //exclude current nav from list + navsToClose = navsToClose.filter(function() { return this !== section[0]; }); + } + } + //close navs on click on title + navsToClose.children(self.settings.region_selector).removeClass(self.settings.active_class); + }, + + toggle_active: function(e) { + var $this = $(this), + self = Foundation.libs.section, + region = $this.parent(), + content = $this.siblings(self.settings.content_selector), + section = region.parent(), + settings = $.extend({}, self.settings, self.data_options(section)), + prev_active_region = section.children(self.settings.region_selector).filter("." + self.settings.active_class); + + //for anchors inside [data-section-title] + if (!settings.deep_linking && content.length > 0) { + e.preventDefault(); + } + + e.stopPropagation(); //do not catch same click again on parent + + if (!region.hasClass(self.settings.active_class)) { + prev_active_region.removeClass(self.settings.active_class); + region.addClass(self.settings.active_class); + //force resize for better performance (do not wait timer) + self.resize(region.find(self.settings.section_selector).not("[" + self.settings.resized_data_attr + "]"), true); + } else if (!settings.one_up && (self.small(section) || self.is_vertical_nav(section) || self.is_horizontal_nav(section) || self.is_accordion(section))) { + region.removeClass(self.settings.active_class); + } + settings.callback(section); + }, + + check_resize_timer: null, + + //main function that sets title and content positions; runs for :not(.resized) and :visible once when window width is medium up + //sections: + // selected sections to resize, are defined on resize forced by visibility changes + //ensure_has_active_region: + // is true when we force resize for no resized sections that were hidden and became visible, + // these sections can have no selected region, because all regions were hidden along with section on executing set_active_from_hash + resize: function(sections, ensure_has_active_region) { + + var self = Foundation.libs.section, + is_small_window = self.small($(document)), + //filter for section resize + should_be_resized = function (section, now_is_hidden) { + return !self.is_accordion(section) && + !section.is("[" + self.settings.resized_data_attr + "]") && + (!is_small_window || self.is_horizontal_tabs(section)) && + now_is_hidden === (section.css('display') === 'none' || + !section.parent().is(':visible')); + }; + + sections = sections || $(self.settings.section_selector); + + clearTimeout(self.check_resize_timer); + + if (!is_small_window) { + sections.removeAttr(self.settings.small_style_data_attr); + } + + //resize + sections.filter(function() { return should_be_resized($(this), false); }) + .each(function() { + var section = $(this), + regions = section.children(self.settings.region_selector), + titles = regions.children(self.settings.title_selector), + content = regions.children(self.settings.content_selector), + titles_max_height = 0; + + if (ensure_has_active_region && + section.children(self.settings.region_selector).filter("." + self.settings.active_class).length == 0) { + var settings = $.extend({}, self.settings, self.data_options(section)); + + if (!settings.deep_linking && (settings.one_up || !self.is_horizontal_nav(section) && + !self.is_vertical_nav(section) && !self.is_accordion(section))) { + regions.filter(":visible").first().addClass(self.settings.active_class); + } + } + + if (self.is_horizontal_tabs(section) || self.is_auto(section)) { + // region: position relative + // title: position absolute + // content: position static + var titles_sum_width = 0; + + titles.each(function() { + var title = $(this); + + if (title.is(":visible")) { + title.css(!self.rtl ? 'left' : 'right', titles_sum_width); + var title_h_border_width = parseInt(title.css("border-" + (self.rtl ? 'left' : 'right') + "-width"), 10); + + if (title_h_border_width.toString() === 'Nan') { + title_h_border_width = 0; + } + + titles_sum_width += self.outerWidth(title) - title_h_border_width; + titles_max_height = Math.max(titles_max_height, self.outerHeight(title)); + } + }); + titles.css('height', titles_max_height); + regions.each(function() { + var region = $(this), + region_content = region.children(self.settings.content_selector), + content_top_border_width = parseInt(region_content.css("border-top-width"), 10); + + if (content_top_border_width.toString() === 'Nan') { + content_top_border_width = 0; + } + + region.css('padding-top', titles_max_height - content_top_border_width); + }); + + section.css("min-height", titles_max_height); + } else if (self.is_horizontal_nav(section)) { + var first = true; + // region: positon relative, float left + // title: position static + // content: position absolute + titles.each(function() { + titles_max_height = Math.max(titles_max_height, self.outerHeight($(this))); + }); + + regions.each(function() { + var region = $(this); + + region.css("margin-left", "-" + (first ? section : region.children(self.settings.title_selector)).css("border-left-width")); + first = false; + }); + + regions.css("margin-top", "-" + section.css("border-top-width")); + titles.css('height', titles_max_height); + content.css('top', titles_max_height); + section.css("min-height", titles_max_height); + } else if (self.is_vertical_tabs(section)) { + var titles_sum_height = 0; + // region: position relative, for .active: fixed padding==title.width + // title: fixed width, position absolute + // content: position static + titles.each(function() { + var title = $(this); + + if (title.is(":visible")) { + title.css('top', titles_sum_height); + var title_top_border_width = parseInt(title.css("border-top-width"), 10); + + if (title_top_border_width.toString() === 'Nan') { + title_top_border_width = 0; + } + + titles_sum_height += self.outerHeight(title) - title_top_border_width; + } + }); + + content.css('min-height', titles_sum_height + 1); + } else if (self.is_vertical_nav(section)) { + var titles_max_width = 0, + first1 = true; + // region: positon relative + // title: position static + // content: position absolute + titles.each(function() { + titles_max_width = Math.max(titles_max_width, self.outerWidth($(this))); + }); + + regions.each(function () { + var region = $(this); + + region.css("margin-top", "-" + (first1 ? section : region.children(self.settings.title_selector)).css("border-top-width")); + first1 = false; + }); + + titles.css('width', titles_max_width); + content.css(!self.rtl ? 'left' : 'right', titles_max_width); + section.css('width', titles_max_width); + } + + section.attr(self.settings.resized_data_attr, true); + }); + + //wait elements to become visible then resize + if ($(self.settings.section_selector).filter(function() { return should_be_resized($(this), true); }).length > 0) + self.check_resize_timer = setTimeout(function() { + self.resize(sections.filter(function() { return should_be_resized($(this), false); }), true); + }, 700); + + if (is_small_window) { + sections.attr(self.settings.small_style_data_attr, true); + } + }, + + is_vertical_nav: function(el) { + return /vertical-nav/i.test(el.data('section')); + }, + + is_horizontal_nav: function(el) { + return /horizontal-nav/i.test(el.data('section')); + }, + + is_accordion: function(el) { + return /accordion/i.test(el.data('section')); + }, + + is_horizontal_tabs: function(el) { + return /^tabs$/i.test(el.data('section')); + }, + + is_vertical_tabs: function(el) { + return /vertical-tabs/i.test(el.data('section')); + }, + + is_auto: function (el) { + var data_section = el.data('section'); + return data_section === '' || /auto/i.test(data_section); + }, + + set_active_from_hash: function() { + var self = Foundation.libs.section, + hash = window.location.hash.substring(1), + sections = $(self.settings.section_selector); + + sections.each(function() { + var section = $(this), + settings = $.extend({}, self.settings, self.data_options(section)), + regions = section.children(self.settings.region_selector), + set_active_from_hash = settings.deep_linking && hash.length > 0, + selected = false; + + regions.each(function() { + var region = $(this); + + if (selected) { + region.removeClass(self.settings.active_class); + } else if (set_active_from_hash) { + var data_slug = region.children(self.settings.content_selector).data('slug'); + + if (data_slug && new RegExp(data_slug, 'i').test(hash)) { + if (!region.hasClass(self.settings.active_class)) + region.addClass(self.settings.active_class); + selected = true; + } else { + region.removeClass(self.settings.active_class); + } + } else if (region.hasClass(self.settings.active_class)) { + selected = true; + } + }); + + if (!selected && !settings.deep_linking && (settings.one_up || !self.is_horizontal_nav(section) && + !self.is_vertical_nav(section) && !self.is_accordion(section))) + regions.filter(":visible").first().addClass(self.settings.active_class); + }); + }, + + reflow: function() { + var self = Foundation.libs.section; + + $(self.settings.section_selector).removeAttr(self.settings.resized_data_attr); + self.throttle(function() { self.resize(); }, 30)(); + }, + + small: function(el) { + var settings = $.extend({}, this.settings, this.data_options(el)); + + if (this.is_horizontal_tabs(el)) { + return false; + } + if (el && this.is_accordion(el)) { + return true; + } + if ($('html').hasClass('lt-ie9')) { + return true; + } + if ($('html').hasClass('ie8compat')) { + return true; + } + return $(this.scope).width() < settings.small_breakpoint; + }, + + off: function() { + $(this.scope).off('.fndtn.section'); + $(window).off('.fndtn.section'); + $(document).off('.fndtn.section'); + } + }; + + //resize selected sections + $.fn.reflow_section = function(ensure_has_active_region) { + var section = this, + self = Foundation.libs.section; + + section.removeAttr(self.settings.resized_data_attr); + self.throttle(function() { self.resize(section, ensure_has_active_region); }, 30)(); + return this; + }; + +}(Foundation.zj, window, document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.tooltips.js b/videodb/lib/foundation4/js/foundation/foundation.tooltips.js new file mode 100644 index 0000000..9398705 --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.tooltips.js @@ -0,0 +1,208 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.tooltips = { + name : 'tooltips', + + version : '4.2.2', + + settings : { + selector : '.has-tip', + additionalInheritableClasses : [], + tooltipClass : '.tooltip', + appendTo: 'body', + 'disable-for-touch': false, + tipTemplate : function (selector, content) { + return '' + content + ''; + } + }, + + cache : {}, + + init : function (scope, method, options) { + Foundation.inherit(this, 'data_options'); + var self = this; + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } else if (typeof options !== 'undefined') { + $.extend(true, this.settings, options); + } + + if (typeof method !== 'string') { + if (Modernizr.touch) { + $(this.scope) + .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', + '[data-tooltip]', function (e) { + var settings = $.extend({}, self.settings, self.data_options($(this))); + if (!settings['disable-for-touch']) { + e.preventDefault(); + $(settings.tooltipClass).hide(); + self.showOrCreateTip($(this)); + } + }) + .on('click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip', + this.settings.tooltipClass, function (e) { + e.preventDefault(); + $(this).fadeOut(150); + }); + } else { + $(this.scope) + .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip', + '[data-tooltip]', function (e) { + var $this = $(this); + + if (/enter|over/i.test(e.type)) { + self.showOrCreateTip($this); + } else if (e.type === 'mouseout' || e.type === 'mouseleave') { + self.hide($this); + } + }); + } + + // $(this.scope).data('fndtn-tooltips', true); + } else { + return this[method].call(this, options); + } + + }, + + showOrCreateTip : function ($target) { + var $tip = this.getTip($target); + + if ($tip && $tip.length > 0) { + return this.show($target); + } + + return this.create($target); + }, + + getTip : function ($target) { + var selector = this.selector($target), + tip = null; + + if (selector) { + tip = $('span[data-selector="' + selector + '"]' + this.settings.tooltipClass); + } + + return (typeof tip === 'object') ? tip : false; + }, + + selector : function ($target) { + var id = $target.attr('id'), + dataSelector = $target.attr('data-tooltip') || $target.attr('data-selector'); + + if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') { + dataSelector = 'tooltip' + Math.random().toString(36).substring(7); + $target.attr('data-selector', dataSelector); + } + + return (id && id.length > 0) ? id : dataSelector; + }, + + create : function ($target) { + var $tip = $(this.settings.tipTemplate(this.selector($target), $('
              ').html($target.attr('title')).html())), + classes = this.inheritable_classes($target); + + $tip.addClass(classes).appendTo(this.settings.appendTo); + if (Modernizr.touch) { + $tip.append('tap to close '); + } + $target.removeAttr('title').attr('title',''); + this.show($target); + }, + + reposition : function (target, tip, classes) { + var width, nub, nubHeight, nubWidth, column, objPos; + + tip.css('visibility', 'hidden').show(); + + width = target.data('width'); + nub = tip.children('.nub'); + nubHeight = this.outerHeight(nub); + nubWidth = this.outerHeight(nub); + + objPos = function (obj, top, right, bottom, left, width) { + return obj.css({ + 'top' : (top) ? top : 'auto', + 'bottom' : (bottom) ? bottom : 'auto', + 'left' : (left) ? left : 'auto', + 'right' : (right) ? right : 'auto', + 'width' : (width) ? width : 'auto' + }).end(); + }; + + objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', target.offset().left, width); + + if ($(window).width() < 767) { + objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', 12.5, $(this.scope).width()); + tip.addClass('tip-override'); + objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left); + } else { + var left = target.offset().left; + if (Foundation.rtl) { + left = target.offset().left + target.offset().width - this.outerWidth(tip); + } + objPos(tip, (target.offset().top + this.outerHeight(target) + 10), 'auto', 'auto', left, width); + tip.removeClass('tip-override'); + if (classes && classes.indexOf('tip-top') > -1) { + objPos(tip, (target.offset().top - this.outerHeight(tip)), 'auto', 'auto', left, width) + .removeClass('tip-override'); + } else if (classes && classes.indexOf('tip-left') > -1) { + objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left - this.outerWidth(tip) - nubHeight), width) + .removeClass('tip-override'); + } else if (classes && classes.indexOf('tip-right') > -1) { + objPos(tip, (target.offset().top + (this.outerHeight(target) / 2) - nubHeight*2.5), 'auto', 'auto', (target.offset().left + this.outerWidth(target) + nubHeight), width) + .removeClass('tip-override'); + } + } + + tip.css('visibility', 'visible').hide(); + }, + + inheritable_classes : function (target) { + var inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'noradius'].concat(this.settings.additionalInheritableClasses), + classes = target.attr('class'), + filtered = classes ? $.map(classes.split(' '), function (el, i) { + if ($.inArray(el, inheritables) !== -1) { + return el; + } + }).join(' ') : ''; + + return $.trim(filtered); + }, + + show : function ($target) { + var $tip = this.getTip($target); + + this.reposition($target, $tip, $target.attr('class')); + $tip.fadeIn(150); + }, + + hide : function ($target) { + var $tip = this.getTip($target); + + $tip.fadeOut(150); + }, + + // deprecate reload + reload : function () { + var $self = $(this); + + return ($self.data('fndtn-tooltips')) ? $self.foundationTooltips('destroy').foundationTooltips('init') : $self.foundationTooltips('init'); + }, + + off : function () { + $(this.scope).off('.fndtn.tooltip'); + $(this.settings.tooltipClass).each(function (i) { + $('[data-tooltip]').get(i).attr('title', $(this).text()); + }).remove(); + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/foundation/foundation.topbar.js b/videodb/lib/foundation4/js/foundation/foundation.topbar.js new file mode 100644 index 0000000..e9c172e --- /dev/null +++ b/videodb/lib/foundation4/js/foundation/foundation.topbar.js @@ -0,0 +1,300 @@ +/*jslint unparam: true, browser: true, indent: 2 */ + +;(function ($, window, document, undefined) { + 'use strict'; + + Foundation.libs.topbar = { + name : 'topbar', + + version: '4.3.1', + + settings : { + index : 0, + stickyClass : 'sticky', + custom_back_text: true, + back_text: 'Back', + is_hover: true, + mobile_show_parent_link: true, + scrolltop : true, // jump to top when sticky nav menu toggle is clicked + init : false + }, + + init : function (section, method, options) { + Foundation.inherit(this, 'data_options'); + var self = this; + + if (typeof method === 'object') { + $.extend(true, this.settings, method); + } else if (typeof options !== 'undefined') { + $.extend(true, this.settings, options); + } + + if (typeof method !== 'string') { + + $('.top-bar, [data-topbar]').each(function () { + $.extend(true, self.settings, self.data_options($(this))); + self.settings.$w = $(window); + self.settings.$topbar = $(this); + self.settings.$section = self.settings.$topbar.find('section'); + self.settings.$titlebar = self.settings.$topbar.children('ul').first(); + self.settings.$topbar.data('index', 0); + + var breakpoint = $("
              ").insertAfter(self.settings.$topbar); + self.settings.breakPoint = breakpoint.width(); + breakpoint.remove(); + + self.assemble(); + + if (self.settings.is_hover) { + self.settings.$topbar.find('.has-dropdown').addClass('not-click'); + } + + if (self.settings.$topbar.parent().hasClass('fixed')) { + $('body').css('padding-top', self.outerHeight(self.settings.$topbar)); + } + }); + + if (!self.settings.init) { + this.events(); + } + + return this.settings.init; + } else { + // fire method + return this[method].call(this, options); + } + }, + + timer : null, + + events : function () { + var self = this; + var offst = this.outerHeight($('.top-bar, [data-topbar]')); + $(this.scope) + .off('.fndtn.topbar') + .on('click.fndtn.topbar', '.top-bar .toggle-topbar, [data-topbar] .toggle-topbar', function (e) { + var topbar = $(this).closest('.top-bar, [data-topbar]'), + section = topbar.find('section, .section'), + titlebar = topbar.children('ul').first(); + + e.preventDefault(); + + if (self.breakpoint()) { + if (!self.rtl) { + section.css({left: '0%'}); + section.find('>.name').css({left: '100%'}); + } else { + section.css({right: '0%'}); + section.find('>.name').css({right: '100%'}); + } + + section.find('li.moved').removeClass('moved'); + topbar.data('index', 0); + + topbar + .toggleClass('expanded') + .css('height', ''); + } + + if (!topbar.hasClass('expanded')) { + if (topbar.hasClass('fixed')) { + topbar.parent().addClass('fixed'); + topbar.removeClass('fixed'); + $('body').css('padding-top',offst); + } + } else if (topbar.parent().hasClass('fixed')) { + topbar.parent().removeClass('fixed'); + topbar.addClass('fixed'); + $('body').css('padding-top','0'); + + if (self.settings.scrolltop) { + window.scrollTo(0,0); + } + } + }) + + .on('click.fndtn.topbar', '.top-bar li.has-dropdown', function (e) { + if (self.breakpoint()) return; + + var li = $(this), + target = $(e.target), + topbar = li.closest('[data-topbar], .top-bar'), + is_hover = topbar.data('topbar'); + + if (self.settings.is_hover && !Modernizr.touch) return; + + e.stopImmediatePropagation(); + + if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) { + e.preventDefault(); + } + + if (li.hasClass('hover')) { + li + .removeClass('hover') + .find('li') + .removeClass('hover'); + } else { + li.addClass('hover'); + } + }) + + .on('click.fndtn.topbar', '.top-bar .has-dropdown>a, [data-topbar] .has-dropdown>a', function (e) { + if (self.breakpoint()) { + e.preventDefault(); + + var $this = $(this), + topbar = $this.closest('.top-bar, [data-topbar]'), + section = topbar.find('section, .section'), + titlebar = topbar.children('ul').first(), + dropdownHeight = $this.next('.dropdown').outerHeight(), + $selectedLi = $this.closest('li'); + + topbar.data('index', topbar.data('index') + 1); + $selectedLi.addClass('moved'); + + if (!self.rtl) { + section.css({left: -(100 * topbar.data('index')) + '%'}); + section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); + } else { + section.css({right: -(100 * topbar.data('index')) + '%'}); + section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); + } + + topbar.css('height', self.outerHeight($this.siblings('ul'), true) + self.height(titlebar)); + } + }); + + $(window).on('resize.fndtn.topbar', function () { + if (!self.breakpoint()) { + $('.top-bar, [data-topbar]') + .css('height', '') + .removeClass('expanded') + .find('li') + .removeClass('hover'); + } + }.bind(this)); + + $('body').on('click.fndtn.topbar', function (e) { + var parent = $(e.target).closest('[data-topbar], .top-bar'); + + if (parent.length > 0) { + return; + } + + $('.top-bar li, [data-topbar] li').removeClass('hover'); + }); + + // Go up a level on Click + $(this.scope).on('click.fndtn', '.top-bar .has-dropdown .back, [data-topbar] .has-dropdown .back', function (e) { + e.preventDefault(); + + var $this = $(this), + topbar = $this.closest('.top-bar, [data-topbar]'), + titlebar = topbar.children('ul').first(), + section = topbar.find('section, .section'), + $movedLi = $this.closest('li.moved'), + $previousLevelUl = $movedLi.parent(); + + topbar.data('index', topbar.data('index') - 1); + + if (!self.rtl) { + section.css({left: -(100 * topbar.data('index')) + '%'}); + section.find('>.name').css({left: 100 * topbar.data('index') + '%'}); + } else { + section.css({right: -(100 * topbar.data('index')) + '%'}); + section.find('>.name').css({right: 100 * topbar.data('index') + '%'}); + } + + if (topbar.data('index') === 0) { + topbar.css('height', ''); + } else { + topbar.css('height', self.outerHeight($previousLevelUl, true) + self.height(titlebar)); + } + + setTimeout(function () { + $movedLi.removeClass('moved'); + }, 300); + }); + }, + + breakpoint : function () { + return $(document).width() <= this.settings.breakPoint || $('html').hasClass('lt-ie9'); + }, + + assemble : function () { + var self = this; + // Pull element out of the DOM for manipulation + this.settings.$section.detach(); + + this.settings.$section.find('.has-dropdown>a').each(function () { + var $link = $(this), + $dropdown = $link.siblings('.dropdown'), + url = $link.attr('href'); + + if (self.settings.mobile_show_parent_link && url && url.length > 1) { + var $titleLi = $('
            2. ' + $link.text() +'
            3. '); + } else { + var $titleLi = $('
            4. '); + } + + // Copy link to subnav + if (self.settings.custom_back_text == true) { + $titleLi.find('h5>a').html('« ' + self.settings.back_text); + } else { + $titleLi.find('h5>a').html('« ' + $link.html()); + } + $dropdown.prepend($titleLi); + }); + + // Put element back in the DOM + this.settings.$section.appendTo(this.settings.$topbar); + + // check for sticky + this.sticky(); + }, + + height : function (ul) { + var total = 0, + self = this; + + ul.find('> li').each(function () { total += self.outerHeight($(this), true); }); + + return total; + }, + + sticky : function () { + var klass = '.' + this.settings.stickyClass; + if ($(klass).length > 0) { + var distance = $(klass).length ? $(klass).offset().top: 0, + $window = $(window), + offst = this.outerHeight($('.top-bar')), + t_top; + + //Whe resize elements of the page on windows resize. Must recalculate distance + $(window).resize(function() { + clearTimeout(t_top); + t_top = setTimeout (function() { + distance = $(klass).offset().top; + },105); + }); + $window.scroll(function() { + if ($window.scrollTop() > (distance)) { + $(klass).addClass("fixed"); + $('body').css('padding-top',offst); + } else if ($window.scrollTop() <= distance) { + $(klass).removeClass("fixed"); + $('body').css('padding-top','0'); + } + }); + } + }, + + off : function () { + $(this.scope).off('.fndtn.topbar'); + $(window).off('.fndtn.topbar'); + }, + + reflow : function () {} + }; +}(Foundation.zj, this, this.document)); diff --git a/videodb/lib/foundation4/js/vendor/custom.modernizr.js b/videodb/lib/foundation4/js/vendor/custom.modernizr.js new file mode 100644 index 0000000..e5afa6c --- /dev/null +++ b/videodb/lib/foundation4/js/vendor/custom.modernizr.js @@ -0,0 +1,4 @@ +/* Modernizr 2.6.2 (Custom Build) | MIT & BSD + * Build: http://modernizr.com/download/#-inlinesvg-svg-svgclippaths-touch-shiv-mq-cssclasses-teststyles-prefixes-ie8compat-load + */ +;window.Modernizr=function(a,b,c){function y(a){j.cssText=a}function z(a,b){return y(m.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={svg:"http://www.w3.org/2000/svg"},o={},p={},q={},r=[],s=r.slice,t,u=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},v=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return u("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=s.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(s.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(s.call(arguments)))};return e}),o.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:u(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},o.svg=function(){return!!b.createElementNS&&!!b.createElementNS(n.svg,"svg").createSVGRect},o.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==n.svg},o.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(l.call(b.createElementNS(n.svg,"clipPath")))};for(var D in o)x(o,D)&&(t=D.toLowerCase(),e[t]=o[D](),r.push((e[t]?"":"no-")+t));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e.mq=v,e.testStyles=u,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+r.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.2", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.10.2 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-07-03 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent.attachEvent && parent !== parent.top ) { + parent.attachEvent( "onbeforeunload", function() { + setDocument(); + }); + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "
              "; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; + } + }); +} + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
              a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
              t
              "; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
              "; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
              ", "
              " ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
              " ], + tr: [ 2, "", "
              " ], + col: [ 2, "", "
              " ], + td: [ 3, "", "
              " ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
              ", "
              " ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
              " && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each(function() { + if ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery(" + \ No newline at end of file diff --git a/videodb/templates/nexgen/users.tpl b/videodb/templates/nexgen/users.tpl new file mode 100644 index 0000000..2c01a8e --- /dev/null +++ b/videodb/templates/nexgen/users.tpl @@ -0,0 +1,132 @@ +{* + Users template + $Id: users.tpl,v 1.6 2013/03/21 16:27:57 andig2 Exp $ +*} + + + +
              +
              + + {if $message} +
              + {$message} + × +
              + {/if} + + +

              {$lang.createuser}

              + +
              + + +
              +
              +
              + +
              + +
              + +
              + +
              + +
              + + +
              + +
              + +
              +
              + + + +

              {$lang.existingusers}

              + + {foreach item=user from=$userlist} +
              {$user.name}
              + +
              + + +
              +
              + +
              +
              + {if !$user.guest} + + {/if} +
              +
              + + {$lang.update} + {if !$user.guest} + {$lang.delete} + {/if} +
              +
              + +
              +
              + {if !$user.guest} + + {/if} +
              + +
              + + + {$lang.perm} + +
              +
              + + {/foreach} + +
              +
              + + +{foreach item=user from=$userlist} +
              +
              + + +

              {$user.name}

              +

              {$lang.delete_user}

              + {$lang.delete} + {$lang.cancel} + + × + +
              +{/foreach} diff --git a/videodb/templates/nexgen/xml.tpl b/videodb/templates/nexgen/xml.tpl new file mode 100644 index 0000000..58101b5 --- /dev/null +++ b/videodb/templates/nexgen/xml.tpl @@ -0,0 +1,32 @@ + + + + + + + videoDB{if $title} - {$title}{/if} + + + + + + {if !empty($rss)}{/if} + + + + + + + + + + + + + + + + + + {literal}{/literal} + diff --git a/videodb/test/index.php b/videodb/test/index.php new file mode 100644 index 0000000..94d92ef --- /dev/null +++ b/videodb/test/index.php @@ -0,0 +1,71 @@ += 0) + error_reporting(E_ALL ^ E_NOTICE ^ E_DEPRECATED); +else + error_reporting(E_ALL ^ E_NOTICE); + +localnet_or_die(); +permission_or_die(PERM_ADMIN); + +if (!defined('SIMPLE_TEST')) define('SIMPLE_TEST', './test/simpletest/'); + +require_once(SIMPLE_TEST . 'unit_tester.php'); +require_once(SIMPLE_TEST . 'reporter.php'); + +function findTestCases($dir, $pattern=null) +{ + $res = array(); + + if ($dh = @opendir($dir)) + { + while (($file = readdir($dh)) !== false) + { + if (preg_match("/^test_(.+)\.php$/", $file, $matches)) + { + if ($pattern && (stristr($file, $pattern) == false)) continue; + + $res[$matches[1]] = $dir.'/'.$file; + // get meta data +# require_once($dir.'/'.$file); +/* + $func = $engine.'Meta'; + + if (function_exists($func)) + { + $engines[$engine] = $func(); + + // required php version present? + if ($engines[$engine]['php'] && (version_compare(phpversion(), $engines[$engine]['php']) < 0)) + { + unset($engines[$engine]); + } + } +*/ + } + } + closedir($dh); + } + + return $res; +} + +$res = findTestCases('./test', $_REQUEST['test']); + +echo "Starting tests.
              "; + +foreach ($res as $case => $file) +{ + $test = new TestSuite($case); + $test->addFile($file); + $test->run(new HtmlReporter('utf-8')); +} + +echo "
              All tests completed.
              "; + +?> diff --git a/videodb/test/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE b/videodb/test/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE new file mode 100644 index 0000000..a65e83e --- /dev/null +++ b/videodb/test/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE @@ -0,0 +1,399 @@ +Simple Test interface changes +============================= +Because the SimpleTest tool set is still evolving it is likely that tests +written with earlier versions will fail with the newest ones. The most +dramatic changes are in the alpha releases. Here is a list of possible +problems and their fixes... + +assertText() no longer finds a string inside a ', 'js'); + $this->mapHandler('comment', 'ignore'); + $this->addEntryPattern('', 'comment'); + } + + /** + * Pattern matches to start and end a tag. + * @param string $tag Name of tag to scan for. + * @access private + */ + function _addTag($tag) { + $this->addSpecialPattern("", 'text', 'acceptEndToken'); + $this->addEntryPattern("<$tag", 'text', 'tag'); + } + + /** + * Pattern matches to parse the inside of a tag + * including the attributes and their quoting. + * @access private + */ + function _addInTagTokens() { + $this->mapHandler('tag', 'acceptStartToken'); + $this->addSpecialPattern('\s+', 'tag', 'ignore'); + $this->_addAttributeTokens(); + $this->addExitPattern('/>', 'tag'); + $this->addExitPattern('>', 'tag'); + } + + /** + * Matches attributes that are either single quoted, + * double quoted or unquoted. + * @access private + */ + function _addAttributeTokens() { + $this->mapHandler('dq_attribute', 'acceptAttributeToken'); + $this->addEntryPattern('=\s*"', 'tag', 'dq_attribute'); + $this->addPattern("\\\\\"", 'dq_attribute'); + $this->addExitPattern('"', 'dq_attribute'); + $this->mapHandler('sq_attribute', 'acceptAttributeToken'); + $this->addEntryPattern("=\s*'", 'tag', 'sq_attribute'); + $this->addPattern("\\\\'", 'sq_attribute'); + $this->addExitPattern("'", 'sq_attribute'); + $this->mapHandler('uq_attribute', 'acceptAttributeToken'); + $this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute'); + } +} + +/** + * Converts HTML tokens into selected SAX events. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleHtmlSaxParser { + var $_lexer; + var $_listener; + var $_tag; + var $_attributes; + var $_current_attribute; + + /** + * Sets the listener. + * @param SimpleSaxListener $listener SAX event handler. + * @access public + */ + function SimpleHtmlSaxParser(&$listener) { + $this->_listener = &$listener; + $this->_lexer = &$this->createLexer($this); + $this->_tag = ''; + $this->_attributes = array(); + $this->_current_attribute = ''; + } + + /** + * Runs the content through the lexer which + * should call back to the acceptors. + * @param string $raw Page text to parse. + * @return boolean False if parse error. + * @access public + */ + function parse($raw) { + return $this->_lexer->parse($raw); + } + + /** + * Sets up the matching lexer. Starts in 'text' mode. + * @param SimpleSaxParser $parser Event generator, usually $self. + * @return SimpleLexer Lexer suitable for this parser. + * @access public + * @static + */ + function &createLexer(&$parser) { + $lexer = &new SimpleHtmlLexer($parser); + return $lexer; + } + + /** + * Accepts a token from the tag mode. If the + * starting element completes then the element + * is dispatched and the current attributes + * set back to empty. The element or attribute + * name is converted to lower case. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptStartToken($token, $event) { + if ($event == LEXER_ENTER) { + $this->_tag = strtolower(substr($token, 1)); + return true; + } + if ($event == LEXER_EXIT) { + $success = $this->_listener->startElement( + $this->_tag, + $this->_attributes); + $this->_tag = ''; + $this->_attributes = array(); + return $success; + } + if ($token != '=') { + $this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token)); + $this->_attributes[$this->_current_attribute] = ''; + } + return true; + } + + /** + * Accepts a token from the end tag mode. + * The element name is converted to lower case. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptEndToken($token, $event) { + if (! preg_match('/<\/(.*)>/', $token, $matches)) { + return false; + } + return $this->_listener->endElement(strtolower($matches[1])); + } + + /** + * Part of the tag data. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptAttributeToken($token, $event) { + if ($this->_current_attribute) { + if ($event == LEXER_UNMATCHED) { + $this->_attributes[$this->_current_attribute] .= + SimpleHtmlSaxParser::decodeHtml($token); + } + if ($event == LEXER_SPECIAL) { + $this->_attributes[$this->_current_attribute] .= + preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token)); + } + } + return true; + } + + /** + * A character entity. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptEntityToken($token, $event) { + } + + /** + * Character data between tags regarded as + * important. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptTextToken($token, $event) { + return $this->_listener->addContent($token); + } + + /** + * Incoming data to be ignored. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function ignore($token, $event) { + return true; + } + + /** + * Decodes any HTML entities. + * @param string $html Incoming HTML. + * @return string Outgoing plain text. + * @access public + * @static + */ + function decodeHtml($html) { + return html_entity_decode($html, ENT_QUOTES); + } + + /** + * Turns HTML into text browser visible text. Images + * are converted to their alt text and tags are supressed. + * Entities are converted to their visible representation. + * @param string $html HTML to convert. + * @return string Plain text. + * @access public + * @static + */ + function normalise($html) { + $text = preg_replace('||', '', $html); + $text = preg_replace('|]*>.*?|', '', $text); + $text = preg_replace('|]*alt\s*=\s*"([^"]*)"[^>]*>|', ' \1 ', $text); + $text = preg_replace('|]*alt\s*=\s*\'([^\']*)\'[^>]*>|', ' \1 ', $text); + $text = preg_replace('|]*alt\s*=\s*([a-zA-Z_]+)[^>]*>|', ' \1 ', $text); + $text = preg_replace('|<[^>]*>|', '', $text); + $text = SimpleHtmlSaxParser::decodeHtml($text); + $text = preg_replace('|\s+|', ' ', $text); + return trim(trim($text), "\xA0"); // TODO: The \xAO is a  . Add a test for this. + } +} + +/** + * SAX event handler. + * @package SimpleTest + * @subpackage WebTester + * @abstract + */ +class SimpleSaxListener { + + /** + * Sets the document to write to. + * @access public + */ + function SimpleSaxListener() { + } + + /** + * Start of element event. + * @param string $name Element name. + * @param hash $attributes Name value pairs. + * Attributes without content + * are marked as true. + * @return boolean False on parse error. + * @access public + */ + function startElement($name, $attributes) { + } + + /** + * End of element event. + * @param string $name Element name. + * @return boolean False on parse error. + * @access public + */ + function endElement($name) { + } + + /** + * Unparsed, but relevant data. + * @param string $text May include unparsed tags. + * @return boolean False on parse error. + * @access public + */ + function addContent($text) { + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/php_parser.php b/videodb/test/simpletest/php_parser.php new file mode 100644 index 0000000..c0fcbf1 --- /dev/null +++ b/videodb/test/simpletest/php_parser.php @@ -0,0 +1,1054 @@ + $constant) { + if (! defined($constant)) { + define($constant, $i + 1); + } +} +/**#@-*/ + +/** + * Compounded regular expression. Any of + * the contained patterns could match and + * when one does, it's label is returned. + * @package SimpleTest + * @subpackage WebTester + */ +class ParallelRegex { + private $patterns; + private $labels; + private $regex; + private $case; + + /** + * Constructor. Starts with no patterns. + * @param boolean $case True for case sensitive, false + * for insensitive. + * @access public + */ + function __construct($case) { + $this->case = $case; + $this->patterns = array(); + $this->labels = array(); + $this->regex = null; + } + + /** + * Adds a pattern with an optional label. + * @param string $pattern Perl style regex, but ( and ) + * lose the usual meaning. + * @param string $label Label of regex to be returned + * on a match. + * @access public + */ + function addPattern($pattern, $label = true) { + $count = count($this->patterns); + $this->patterns[$count] = $pattern; + $this->labels[$count] = $label; + $this->regex = null; + } + + /** + * Attempts to match all patterns at once against + * a string. + * @param string $subject String to match against. + * @param string $match First matched portion of + * subject. + * @return boolean True on success. + * @access public + */ + function match($subject, &$match) { + if (count($this->patterns) == 0) { + return false; + } + if (! preg_match($this->getCompoundedRegex(), $subject, $matches)) { + $match = ''; + return false; + } + $match = $matches[0]; + for ($i = 1; $i < count($matches); $i++) { + if ($matches[$i]) { + return $this->labels[$i - 1]; + } + } + return true; + } + + /** + * Compounds the patterns into a single + * regular expression separated with the + * "or" operator. Caches the regex. + * Will automatically escape (, ) and / tokens. + * @param array $patterns List of patterns in order. + * @access private + */ + protected function getCompoundedRegex() { + if ($this->regex == null) { + for ($i = 0, $count = count($this->patterns); $i < $count; $i++) { + $this->patterns[$i] = '(' . str_replace( + array('/', '(', ')'), + array('\/', '\(', '\)'), + $this->patterns[$i]) . ')'; + } + $this->regex = "/" . implode("|", $this->patterns) . "/" . $this->getPerlMatchingFlags(); + } + return $this->regex; + } + + /** + * Accessor for perl regex mode flags to use. + * @return string Perl regex flags. + * @access private + */ + protected function getPerlMatchingFlags() { + return ($this->case ? "msS" : "msSi"); + } +} + +/** + * States for a stack machine. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleStateStack { + private $stack; + + /** + * Constructor. Starts in named state. + * @param string $start Starting state name. + * @access public + */ + function __construct($start) { + $this->stack = array($start); + } + + /** + * Accessor for current state. + * @return string State. + * @access public + */ + function getCurrent() { + return $this->stack[count($this->stack) - 1]; + } + + /** + * Adds a state to the stack and sets it + * to be the current state. + * @param string $state New state. + * @access public + */ + function enter($state) { + array_push($this->stack, $state); + } + + /** + * Leaves the current state and reverts + * to the previous one. + * @return boolean False if we drop off + * the bottom of the list. + * @access public + */ + function leave() { + if (count($this->stack) == 1) { + return false; + } + array_pop($this->stack); + return true; + } +} + +/** + * Accepts text and breaks it into tokens. + * Some optimisation to make the sure the + * content is only scanned by the PHP regex + * parser once. Lexer modes must not start + * with leading underscores. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleLexer { + private $regexes; + private $parser; + private $mode; + private $mode_handlers; + private $case; + + /** + * Sets up the lexer in case insensitive matching + * by default. + * @param SimpleSaxParser $parser Handling strategy by + * reference. + * @param string $start Starting handler. + * @param boolean $case True for case sensitive. + * @access public + */ + function __construct($parser, $start = "accept", $case = false) { + $this->case = $case; + $this->regexes = array(); + $this->parser = $parser; + $this->mode = new SimpleStateStack($start); + $this->mode_handlers = array($start => $start); + } + + /** + * Adds a token search pattern for a particular + * parsing mode. The pattern does not change the + * current mode. + * @param string $pattern Perl style regex, but ( and ) + * lose the usual meaning. + * @param string $mode Should only apply this + * pattern when dealing with + * this type of input. + * @access public + */ + function addPattern($pattern, $mode = "accept") { + if (! isset($this->regexes[$mode])) { + $this->regexes[$mode] = new ParallelRegex($this->case); + } + $this->regexes[$mode]->addPattern($pattern); + if (! isset($this->mode_handlers[$mode])) { + $this->mode_handlers[$mode] = $mode; + } + } + + /** + * Adds a pattern that will enter a new parsing + * mode. Useful for entering parenthesis, strings, + * tags, etc. + * @param string $pattern Perl style regex, but ( and ) + * lose the usual meaning. + * @param string $mode Should only apply this + * pattern when dealing with + * this type of input. + * @param string $new_mode Change parsing to this new + * nested mode. + * @access public + */ + function addEntryPattern($pattern, $mode, $new_mode) { + if (! isset($this->regexes[$mode])) { + $this->regexes[$mode] = new ParallelRegex($this->case); + } + $this->regexes[$mode]->addPattern($pattern, $new_mode); + if (! isset($this->mode_handlers[$new_mode])) { + $this->mode_handlers[$new_mode] = $new_mode; + } + } + + /** + * Adds a pattern that will exit the current mode + * and re-enter the previous one. + * @param string $pattern Perl style regex, but ( and ) + * lose the usual meaning. + * @param string $mode Mode to leave. + * @access public + */ + function addExitPattern($pattern, $mode) { + if (! isset($this->regexes[$mode])) { + $this->regexes[$mode] = new ParallelRegex($this->case); + } + $this->regexes[$mode]->addPattern($pattern, "__exit"); + if (! isset($this->mode_handlers[$mode])) { + $this->mode_handlers[$mode] = $mode; + } + } + + /** + * Adds a pattern that has a special mode. Acts as an entry + * and exit pattern in one go, effectively calling a special + * parser handler for this token only. + * @param string $pattern Perl style regex, but ( and ) + * lose the usual meaning. + * @param string $mode Should only apply this + * pattern when dealing with + * this type of input. + * @param string $special Use this mode for this one token. + * @access public + */ + function addSpecialPattern($pattern, $mode, $special) { + if (! isset($this->regexes[$mode])) { + $this->regexes[$mode] = new ParallelRegex($this->case); + } + $this->regexes[$mode]->addPattern($pattern, "_$special"); + if (! isset($this->mode_handlers[$special])) { + $this->mode_handlers[$special] = $special; + } + } + + /** + * Adds a mapping from a mode to another handler. + * @param string $mode Mode to be remapped. + * @param string $handler New target handler. + * @access public + */ + function mapHandler($mode, $handler) { + $this->mode_handlers[$mode] = $handler; + } + + /** + * Splits the page text into tokens. Will fail + * if the handlers report an error or if no + * content is consumed. If successful then each + * unparsed and parsed token invokes a call to the + * held listener. + * @param string $raw Raw HTML text. + * @return boolean True on success, else false. + * @access public + */ + function parse($raw) { + if (! isset($this->parser)) { + return false; + } + $length = strlen($raw); + while (is_array($parsed = $this->reduce($raw))) { + list($raw, $unmatched, $matched, $mode) = $parsed; + if (! $this->dispatchTokens($unmatched, $matched, $mode)) { + return false; + } + if ($raw === '') { + return true; + } + if (strlen($raw) == $length) { + return false; + } + $length = strlen($raw); + } + if (! $parsed) { + return false; + } + return $this->invokeParser($raw, LEXER_UNMATCHED); + } + + /** + * Sends the matched token and any leading unmatched + * text to the parser changing the lexer to a new + * mode if one is listed. + * @param string $unmatched Unmatched leading portion. + * @param string $matched Actual token match. + * @param string $mode Mode after match. A boolean + * false mode causes no change. + * @return boolean False if there was any error + * from the parser. + * @access private + */ + protected function dispatchTokens($unmatched, $matched, $mode = false) { + if (! $this->invokeParser($unmatched, LEXER_UNMATCHED)) { + return false; + } + if (is_bool($mode)) { + return $this->invokeParser($matched, LEXER_MATCHED); + } + if ($this->isModeEnd($mode)) { + if (! $this->invokeParser($matched, LEXER_EXIT)) { + return false; + } + return $this->mode->leave(); + } + if ($this->isSpecialMode($mode)) { + $this->mode->enter($this->decodeSpecial($mode)); + if (! $this->invokeParser($matched, LEXER_SPECIAL)) { + return false; + } + return $this->mode->leave(); + } + $this->mode->enter($mode); + return $this->invokeParser($matched, LEXER_ENTER); + } + + /** + * Tests to see if the new mode is actually to leave + * the current mode and pop an item from the matching + * mode stack. + * @param string $mode Mode to test. + * @return boolean True if this is the exit mode. + * @access private + */ + protected function isModeEnd($mode) { + return ($mode === "__exit"); + } + + /** + * Test to see if the mode is one where this mode + * is entered for this token only and automatically + * leaves immediately afterwoods. + * @param string $mode Mode to test. + * @return boolean True if this is the exit mode. + * @access private + */ + protected function isSpecialMode($mode) { + return (strncmp($mode, "_", 1) == 0); + } + + /** + * Strips the magic underscore marking single token + * modes. + * @param string $mode Mode to decode. + * @return string Underlying mode name. + * @access private + */ + protected function decodeSpecial($mode) { + return substr($mode, 1); + } + + /** + * Calls the parser method named after the current + * mode. Empty content will be ignored. The lexer + * has a parser handler for each mode in the lexer. + * @param string $content Text parsed. + * @param boolean $is_match Token is recognised rather + * than unparsed data. + * @access private + */ + protected function invokeParser($content, $is_match) { + if (($content === '') || ($content === false)) { + return true; + } + $handler = $this->mode_handlers[$this->mode->getCurrent()]; + return $this->parser->$handler($content, $is_match); + } + + /** + * Tries to match a chunk of text and if successful + * removes the recognised chunk and any leading + * unparsed data. Empty strings will not be matched. + * @param string $raw The subject to parse. This is the + * content that will be eaten. + * @return array/boolean Three item list of unparsed + * content followed by the + * recognised token and finally the + * action the parser is to take. + * True if no match, false if there + * is a parsing error. + * @access private + */ + protected function reduce($raw) { + if ($action = $this->regexes[$this->mode->getCurrent()]->match($raw, $match)) { + $unparsed_character_count = strpos($raw, $match); + $unparsed = substr($raw, 0, $unparsed_character_count); + $raw = substr($raw, $unparsed_character_count + strlen($match)); + return array($raw, $unparsed, $match, $action); + } + return true; + } +} + +/** + * Breaks HTML into SAX events. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleHtmlLexer extends SimpleLexer { + + /** + * Sets up the lexer with case insensitive matching + * and adds the HTML handlers. + * @param SimpleSaxParser $parser Handling strategy by + * reference. + * @access public + */ + function __construct($parser) { + parent::__construct($parser, 'text'); + $this->mapHandler('text', 'acceptTextToken'); + $this->addSkipping(); + foreach ($this->getParsedTags() as $tag) { + $this->addTag($tag); + } + $this->addInTagTokens(); + } + + /** + * List of parsed tags. Others are ignored. + * @return array List of searched for tags. + * @access private + */ + protected function getParsedTags() { + return array('a', 'base', 'title', 'form', 'input', 'button', 'textarea', 'select', + 'option', 'frameset', 'frame', 'label'); + } + + /** + * The lexer has to skip certain sections such + * as server code, client code and styles. + * @access private + */ + protected function addSkipping() { + $this->mapHandler('css', 'ignore'); + $this->addEntryPattern('addExitPattern('', 'css'); + $this->mapHandler('js', 'ignore'); + $this->addEntryPattern('addExitPattern('', 'js'); + $this->mapHandler('comment', 'ignore'); + $this->addEntryPattern('', 'comment'); + } + + /** + * Pattern matches to start and end a tag. + * @param string $tag Name of tag to scan for. + * @access private + */ + protected function addTag($tag) { + $this->addSpecialPattern("", 'text', 'acceptEndToken'); + $this->addEntryPattern("<$tag", 'text', 'tag'); + } + + /** + * Pattern matches to parse the inside of a tag + * including the attributes and their quoting. + * @access private + */ + protected function addInTagTokens() { + $this->mapHandler('tag', 'acceptStartToken'); + $this->addSpecialPattern('\s+', 'tag', 'ignore'); + $this->addAttributeTokens(); + $this->addExitPattern('/>', 'tag'); + $this->addExitPattern('>', 'tag'); + } + + /** + * Matches attributes that are either single quoted, + * double quoted or unquoted. + * @access private + */ + protected function addAttributeTokens() { + $this->mapHandler('dq_attribute', 'acceptAttributeToken'); + $this->addEntryPattern('=\s*"', 'tag', 'dq_attribute'); + $this->addPattern("\\\\\"", 'dq_attribute'); + $this->addExitPattern('"', 'dq_attribute'); + $this->mapHandler('sq_attribute', 'acceptAttributeToken'); + $this->addEntryPattern("=\s*'", 'tag', 'sq_attribute'); + $this->addPattern("\\\\'", 'sq_attribute'); + $this->addExitPattern("'", 'sq_attribute'); + $this->mapHandler('uq_attribute', 'acceptAttributeToken'); + $this->addSpecialPattern('=\s*[^>\s]*', 'tag', 'uq_attribute'); + } +} + +/** + * Converts HTML tokens into selected SAX events. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleHtmlSaxParser { + private $lexer; + private $listener; + private $tag; + private $attributes; + private $current_attribute; + + /** + * Sets the listener. + * @param SimplePhpPageBuilder $listener SAX event handler. + * @access public + */ + function __construct($listener) { + $this->listener = $listener; + $this->lexer = $this->createLexer($this); + $this->tag = ''; + $this->attributes = array(); + $this->current_attribute = ''; + } + + /** + * Runs the content through the lexer which + * should call back to the acceptors. + * @param string $raw Page text to parse. + * @return boolean False if parse error. + * @access public + */ + function parse($raw) { + return $this->lexer->parse($raw); + } + + /** + * Sets up the matching lexer. Starts in 'text' mode. + * @param SimpleSaxParser $parser Event generator, usually $self. + * @return SimpleLexer Lexer suitable for this parser. + * @access public + */ + static function createLexer(&$parser) { + return new SimpleHtmlLexer($parser); + } + + /** + * Accepts a token from the tag mode. If the + * starting element completes then the element + * is dispatched and the current attributes + * set back to empty. The element or attribute + * name is converted to lower case. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptStartToken($token, $event) { + if ($event == LEXER_ENTER) { + $this->tag = strtolower(substr($token, 1)); + return true; + } + if ($event == LEXER_EXIT) { + $success = $this->listener->startElement( + $this->tag, + $this->attributes); + $this->tag = ''; + $this->attributes = array(); + return $success; + } + if ($token != '=') { + $this->current_attribute = strtolower(html_entity_decode($token, ENT_QUOTES)); + $this->attributes[$this->current_attribute] = ''; + } + return true; + } + + /** + * Accepts a token from the end tag mode. + * The element name is converted to lower case. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptEndToken($token, $event) { + if (! preg_match('/<\/(.*)>/', $token, $matches)) { + return false; + } + return $this->listener->endElement(strtolower($matches[1])); + } + + /** + * Part of the tag data. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptAttributeToken($token, $event) { + if ($this->current_attribute) { + if ($event == LEXER_UNMATCHED) { + $this->attributes[$this->current_attribute] .= + html_entity_decode($token, ENT_QUOTES); + } + if ($event == LEXER_SPECIAL) { + $this->attributes[$this->current_attribute] .= + preg_replace('/^=\s*/' , '', html_entity_decode($token, ENT_QUOTES)); + } + } + return true; + } + + /** + * A character entity. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptEntityToken($token, $event) { + } + + /** + * Character data between tags regarded as + * important. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function acceptTextToken($token, $event) { + return $this->listener->addContent($token); + } + + /** + * Incoming data to be ignored. + * @param string $token Incoming characters. + * @param integer $event Lexer event type. + * @return boolean False if parse error. + * @access public + */ + function ignore($token, $event) { + return true; + } +} + +/** + * SAX event handler. Maintains a list of + * open tags and dispatches them as they close. + * @package SimpleTest + * @subpackage WebTester + */ +class SimplePhpPageBuilder { + private $tags; + private $page; + private $private_content_tag; + private $open_forms = array(); + private $complete_forms = array(); + private $frameset = false; + private $loading_frames = array(); + private $frameset_nesting_level = 0; + private $left_over_labels = array(); + + /** + * Frees up any references so as to allow the PHP garbage + * collection from unset() to work. + * @access public + */ + function free() { + unset($this->tags); + unset($this->page); + unset($this->private_content_tags); + $this->open_forms = array(); + $this->complete_forms = array(); + $this->frameset = false; + $this->loading_frames = array(); + $this->frameset_nesting_level = 0; + $this->left_over_labels = array(); + } + + /** + * This builder is always available. + * @return boolean Always true. + */ + function can() { + return true; + } + + /** + * Reads the raw content and send events + * into the page to be built. + * @param $response SimpleHttpResponse Fetched response. + * @return SimplePage Newly parsed page. + * @access public + */ + function parse($response) { + $this->tags = array(); + $this->page = $this->createPage($response); + $parser = $this->createParser($this); + $parser->parse($response->getContent()); + $this->acceptPageEnd(); + $page = $this->page; + $this->free(); + return $page; + } + + /** + * Creates an empty page. + * @return SimplePage New unparsed page. + * @access protected + */ + protected function createPage($response) { + return new SimplePage($response); + } + + /** + * Creates the parser used with the builder. + * @param SimplePhpPageBuilder $listener Target of parser. + * @return SimpleSaxParser Parser to generate + * events for the builder. + * @access protected + */ + protected function createParser(&$listener) { + return new SimpleHtmlSaxParser($listener); + } + + /** + * Start of element event. Opens a new tag. + * @param string $name Element name. + * @param hash $attributes Attributes without content + * are marked as true. + * @return boolean False on parse error. + * @access public + */ + function startElement($name, $attributes) { + $factory = new SimpleTagBuilder(); + $tag = $factory->createTag($name, $attributes); + if (! $tag) { + return true; + } + if ($tag->getTagName() == 'label') { + $this->acceptLabelStart($tag); + $this->openTag($tag); + return true; + } + if ($tag->getTagName() == 'form') { + $this->acceptFormStart($tag); + return true; + } + if ($tag->getTagName() == 'frameset') { + $this->acceptFramesetStart($tag); + return true; + } + if ($tag->getTagName() == 'frame') { + $this->acceptFrame($tag); + return true; + } + if ($tag->isPrivateContent() && ! isset($this->private_content_tag)) { + $this->private_content_tag = &$tag; + } + if ($tag->expectEndTag()) { + $this->openTag($tag); + return true; + } + $this->acceptTag($tag); + return true; + } + + /** + * End of element event. + * @param string $name Element name. + * @return boolean False on parse error. + * @access public + */ + function endElement($name) { + if ($name == 'label') { + $this->acceptLabelEnd(); + return true; + } + if ($name == 'form') { + $this->acceptFormEnd(); + return true; + } + if ($name == 'frameset') { + $this->acceptFramesetEnd(); + return true; + } + if ($this->hasNamedTagOnOpenTagStack($name)) { + $tag = array_pop($this->tags[$name]); + if ($tag->isPrivateContent() && $this->private_content_tag->getTagName() == $name) { + unset($this->private_content_tag); + } + $this->addContentTagToOpenTags($tag); + $this->acceptTag($tag); + return true; + } + return true; + } + + /** + * Test to see if there are any open tags awaiting + * closure that match the tag name. + * @param string $name Element name. + * @return boolean True if any are still open. + * @access private + */ + protected function hasNamedTagOnOpenTagStack($name) { + return isset($this->tags[$name]) && (count($this->tags[$name]) > 0); + } + + /** + * Unparsed, but relevant data. The data is added + * to every open tag. + * @param string $text May include unparsed tags. + * @return boolean False on parse error. + * @access public + */ + function addContent($text) { + if (isset($this->private_content_tag)) { + $this->private_content_tag->addContent($text); + } else { + $this->addContentToAllOpenTags($text); + } + return true; + } + + /** + * Any content fills all currently open tags unless it + * is part of an option tag. + * @param string $text May include unparsed tags. + * @access private + */ + protected function addContentToAllOpenTags($text) { + foreach (array_keys($this->tags) as $name) { + for ($i = 0, $count = count($this->tags[$name]); $i < $count; $i++) { + $this->tags[$name][$i]->addContent($text); + } + } + } + + /** + * Parsed data in tag form. The parsed tag is added + * to every open tag. Used for adding options to select + * fields only. + * @param SimpleTag $tag Option tags only. + * @access private + */ + protected function addContentTagToOpenTags(&$tag) { + if ($tag->getTagName() != 'option') { + return; + } + foreach (array_keys($this->tags) as $name) { + for ($i = 0, $count = count($this->tags[$name]); $i < $count; $i++) { + $this->tags[$name][$i]->addTag($tag); + } + } + } + + /** + * Opens a tag for receiving content. Multiple tags + * will be receiving input at the same time. + * @param SimpleTag $tag New content tag. + * @access private + */ + protected function openTag($tag) { + $name = $tag->getTagName(); + if (! in_array($name, array_keys($this->tags))) { + $this->tags[$name] = array(); + } + $this->tags[$name][] = $tag; + } + + /** + * Adds a tag to the page. + * @param SimpleTag $tag Tag to accept. + * @access public + */ + protected function acceptTag($tag) { + if ($tag->getTagName() == "a") { + $this->page->addLink($tag); + } elseif ($tag->getTagName() == "base") { + $this->page->setBase($tag->getAttribute('href')); + } elseif ($tag->getTagName() == "title") { + $this->page->setTitle($tag); + } elseif ($this->isFormElement($tag->getTagName())) { + for ($i = 0; $i < count($this->open_forms); $i++) { + $this->open_forms[$i]->addWidget($tag); + } + $this->last_widget = $tag; + } + } + + /** + * Opens a label for a described widget. + * @param SimpleFormTag $tag Tag to accept. + * @access public + */ + protected function acceptLabelStart($tag) { + $this->label = $tag; + unset($this->last_widget); + } + + /** + * Closes the most recently opened label. + * @access public + */ + protected function acceptLabelEnd() { + if (isset($this->label)) { + if (isset($this->last_widget)) { + $this->last_widget->setLabel($this->label->getText()); + unset($this->last_widget); + } else { + $this->left_over_labels[] = SimpleTestCompatibility::copy($this->label); + } + unset($this->label); + } + } + + /** + * Tests to see if a tag is a possible form + * element. + * @param string $name HTML element name. + * @return boolean True if form element. + * @access private + */ + protected function isFormElement($name) { + return in_array($name, array('input', 'button', 'textarea', 'select')); + } + + /** + * Opens a form. New widgets go here. + * @param SimpleFormTag $tag Tag to accept. + * @access public + */ + protected function acceptFormStart($tag) { + $this->open_forms[] = new SimpleForm($tag, $this->page); + } + + /** + * Closes the most recently opened form. + * @access public + */ + protected function acceptFormEnd() { + if (count($this->open_forms)) { + $this->complete_forms[] = array_pop($this->open_forms); + } + } + + /** + * Opens a frameset. A frameset may contain nested + * frameset tags. + * @param SimpleFramesetTag $tag Tag to accept. + * @access public + */ + protected function acceptFramesetStart($tag) { + if (! $this->isLoadingFrames()) { + $this->frameset = $tag; + } + $this->frameset_nesting_level++; + } + + /** + * Closes the most recently opened frameset. + * @access public + */ + protected function acceptFramesetEnd() { + if ($this->isLoadingFrames()) { + $this->frameset_nesting_level--; + } + } + + /** + * Takes a single frame tag and stashes it in + * the current frame set. + * @param SimpleFrameTag $tag Tag to accept. + * @access public + */ + protected function acceptFrame($tag) { + if ($this->isLoadingFrames()) { + if ($tag->getAttribute('src')) { + $this->loading_frames[] = $tag; + } + } + } + + /** + * Test to see if in the middle of reading + * a frameset. + * @return boolean True if inframeset. + * @access private + */ + protected function isLoadingFrames() { + return $this->frameset and $this->frameset_nesting_level > 0; + } + + /** + * Marker for end of complete page. Any work in + * progress can now be closed. + * @access public + */ + protected function acceptPageEnd() { + while (count($this->open_forms)) { + $this->complete_forms[] = array_pop($this->open_forms); + } + foreach ($this->left_over_labels as $label) { + for ($i = 0, $count = count($this->complete_forms); $i < $count; $i++) { + $this->complete_forms[$i]->attachLabelBySelector( + new SimpleById($label->getFor()), + $label->getText()); + } + } + $this->page->setForms($this->complete_forms); + $this->page->setFrames($this->loading_frames); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/recorder.php b/videodb/test/simpletest/recorder.php new file mode 100644 index 0000000..b6e8786 --- /dev/null +++ b/videodb/test/simpletest/recorder.php @@ -0,0 +1,101 @@ +time, $this->breadcrumb, $this->message) = + array(time(), $breadcrumb, $message); + } +} + +/** + * A single pass captured for later. + * @package SimpleTest + * @subpackage Extensions + */ +class SimpleResultOfPass extends SimpleResult { } + +/** + * A single failure captured for later. + * @package SimpleTest + * @subpackage Extensions + */ +class SimpleResultOfFail extends SimpleResult { } + +/** + * A single exception captured for later. + * @package SimpleTest + * @subpackage Extensions + */ +class SimpleResultOfException extends SimpleResult { } + +/** + * Array-based test recorder. Returns an array + * with timestamp, status, test name and message for each pass and failure. + * @package SimpleTest + * @subpackage Extensions + */ +class Recorder extends SimpleReporterDecorator { + public $results = array(); + + /** + * Stashes the pass as a SimpleResultOfPass + * for later retrieval. + * @param string $message Pass message to be displayed + * eventually. + */ + function paintPass($message) { + parent::paintPass($message); + $this->results[] = new SimpleResultOfPass(parent::getTestList(), $message); + } + + /** + * Stashes the fail as a SimpleResultOfFail + * for later retrieval. + * @param string $message Failure message to be displayed + * eventually. + */ + function paintFail($message) { + parent::paintFail($message); + $this->results[] = new SimpleResultOfFail(parent::getTestList(), $message); + } + + /** + * Stashes the exception as a SimpleResultOfException + * for later retrieval. + * @param string $message Exception message to be displayed + * eventually. + */ + function paintException($message) { + parent::paintException($message); + $this->results[] = new SimpleResultOfException(parent::getTestList(), $message); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/reflection_php4.php b/videodb/test/simpletest/reflection_php4.php new file mode 100644 index 0000000..bf1b5fd --- /dev/null +++ b/videodb/test/simpletest/reflection_php4.php @@ -0,0 +1,136 @@ +_interface = $interface; + } + + /** + * Checks that a class has been declared. + * @return boolean True if defined. + * @access public + */ + function classExists() { + return class_exists($this->_interface); + } + + /** + * Needed to kill the autoload feature in PHP5 + * for classes created dynamically. + * @return boolean True if defined. + * @access public + */ + function classExistsSansAutoload() { + return class_exists($this->_interface); + } + + /** + * Checks that a class or interface has been + * declared. + * @return boolean True if defined. + * @access public + */ + function classOrInterfaceExists() { + return class_exists($this->_interface); + } + + /** + * Needed to kill the autoload feature in PHP5 + * for classes created dynamically. + * @return boolean True if defined. + * @access public + */ + function classOrInterfaceExistsSansAutoload() { + return class_exists($this->_interface); + } + + /** + * Gets the list of methods on a class or + * interface. + * @returns array List of method names. + * @access public + */ + function getMethods() { + return get_class_methods($this->_interface); + } + + /** + * Gets the list of interfaces from a class. If the + * class name is actually an interface then just that + * interface is returned. + * @returns array List of interfaces. + * @access public + */ + function getInterfaces() { + return array(); + } + + /** + * Finds the parent class name. + * @returns string Parent class name. + * @access public + */ + function getParent() { + return strtolower(get_parent_class($this->_interface)); + } + + /** + * Determines if the class is abstract, which for PHP 4 + * will never be the case. + * @returns boolean True if abstract. + * @access public + */ + function isAbstract() { + return false; + } + + /** + * Determines if the the entity is an interface, which for PHP 4 + * will never be the case. + * @returns boolean True if interface. + * @access public + */ + function isInterface() { + return false; + } + + /** + * Scans for final methods, but as it's PHP 4 there + * aren't any. + * @returns boolean True if the class has a final method. + * @access public + */ + function hasFinal() { + return false; + } + + /** + * Gets the source code matching the declaration + * of a method. + * @param string $method Method name. + * @access public + */ + function getSignature($method) { + return "function &$method()"; + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/reflection_php5.php b/videodb/test/simpletest/reflection_php5.php new file mode 100644 index 0000000..d6c33a9 --- /dev/null +++ b/videodb/test/simpletest/reflection_php5.php @@ -0,0 +1,386 @@ +interface = $interface; + } + + /** + * Checks that a class has been declared. Versions + * before PHP5.0.2 need a check that it's not really + * an interface. + * @return boolean True if defined. + * @access public + */ + function classExists() { + if (! class_exists($this->interface)) { + return false; + } + $reflection = new ReflectionClass($this->interface); + return ! $reflection->isInterface(); + } + + /** + * Needed to kill the autoload feature in PHP5 + * for classes created dynamically. + * @return boolean True if defined. + * @access public + */ + function classExistsSansAutoload() { + return class_exists($this->interface, false); + } + + /** + * Checks that a class or interface has been + * declared. + * @return boolean True if defined. + * @access public + */ + function classOrInterfaceExists() { + return $this->classOrInterfaceExistsWithAutoload($this->interface, true); + } + + /** + * Needed to kill the autoload feature in PHP5 + * for classes created dynamically. + * @return boolean True if defined. + * @access public + */ + function classOrInterfaceExistsSansAutoload() { + return $this->classOrInterfaceExistsWithAutoload($this->interface, false); + } + + /** + * Needed to select the autoload feature in PHP5 + * for classes created dynamically. + * @param string $interface Class or interface name. + * @param boolean $autoload True totriggerautoload. + * @return boolean True if interface defined. + * @access private + */ + protected function classOrInterfaceExistsWithAutoload($interface, $autoload) { + if (function_exists('interface_exists')) { + if (interface_exists($this->interface, $autoload)) { + return true; + } + } + return class_exists($this->interface, $autoload); + } + + /** + * Gets the list of methods on a class or + * interface. + * @returns array List of method names. + * @access public + */ + function getMethods() { + return array_unique(get_class_methods($this->interface)); + } + + /** + * Gets the list of interfaces from a class. If the + * class name is actually an interface then just that + * interface is returned. + * @returns array List of interfaces. + * @access public + */ + function getInterfaces() { + $reflection = new ReflectionClass($this->interface); + if ($reflection->isInterface()) { + return array($this->interface); + } + return $this->onlyParents($reflection->getInterfaces()); + } + + /** + * Gets the list of methods for the implemented + * interfaces only. + * @returns array List of enforced method signatures. + * @access public + */ + function getInterfaceMethods() { + $methods = array(); + foreach ($this->getInterfaces() as $interface) { + $methods = array_merge($methods, get_class_methods($interface)); + } + return array_unique($methods); + } + + /** + * Checks to see if the method signature has to be tightly + * specified. + * @param string $method Method name. + * @returns boolean True if enforced. + * @access private + */ + protected function isInterfaceMethod($method) { + return in_array($method, $this->getInterfaceMethods()); + } + + /** + * Finds the parent class name. + * @returns string Parent class name. + * @access public + */ + function getParent() { + $reflection = new ReflectionClass($this->interface); + $parent = $reflection->getParentClass(); + if ($parent) { + return $parent->getName(); + } + return false; + } + + /** + * Trivially determines if the class is abstract. + * @returns boolean True if abstract. + * @access public + */ + function isAbstract() { + $reflection = new ReflectionClass($this->interface); + return $reflection->isAbstract(); + } + + /** + * Trivially determines if the class is an interface. + * @returns boolean True if interface. + * @access public + */ + function isInterface() { + $reflection = new ReflectionClass($this->interface); + return $reflection->isInterface(); + } + + /** + * Scans for final methods, as they screw up inherited + * mocks by not allowing you to override them. + * @returns boolean True if the class has a final method. + * @access public + */ + function hasFinal() { + $reflection = new ReflectionClass($this->interface); + foreach ($reflection->getMethods() as $method) { + if ($method->isFinal()) { + return true; + } + } + return false; + } + + /** + * Whittles a list of interfaces down to only the + * necessary top level parents. + * @param array $interfaces Reflection API interfaces + * to reduce. + * @returns array List of parent interface names. + * @access private + */ + protected function onlyParents($interfaces) { + $parents = array(); + $blacklist = array(); + foreach ($interfaces as $interface) { + foreach($interfaces as $possible_parent) { + if ($interface->getName() == $possible_parent->getName()) { + continue; + } + if ($interface->isSubClassOf($possible_parent)) { + $blacklist[$possible_parent->getName()] = true; + } + } + if (!isset($blacklist[$interface->getName()])) { + $parents[] = $interface->getName(); + } + } + return $parents; + } + + /** + * Checks whether a method is abstract or not. + * @param string $name Method name. + * @return bool true if method is abstract, else false + * @access private + */ + protected function isAbstractMethod($name) { + $interface = new ReflectionClass($this->interface); + if (! $interface->hasMethod($name)) { + return false; + } + return $interface->getMethod($name)->isAbstract(); + } + + /** + * Checks whether a method is the constructor. + * @param string $name Method name. + * @return bool true if method is the constructor + * @access private + */ + protected function isConstructor($name) { + return ($name == '__construct') || ($name == $this->interface); + } + + /** + * Checks whether a method is abstract in all parents or not. + * @param string $name Method name. + * @return bool true if method is abstract in parent, else false + * @access private + */ + protected function isAbstractMethodInParents($name) { + $interface = new ReflectionClass($this->interface); + $parent = $interface->getParentClass(); + while($parent) { + if (! $parent->hasMethod($name)) { + return false; + } + if ($parent->getMethod($name)->isAbstract()) { + return true; + } + $parent = $parent->getParentClass(); + } + return false; + } + + /** + * Checks whether a method is static or not. + * @param string $name Method name + * @return bool true if method is static, else false + * @access private + */ + protected function isStaticMethod($name) { + $interface = new ReflectionClass($this->interface); + if (! $interface->hasMethod($name)) { + return false; + } + return $interface->getMethod($name)->isStatic(); + } + + /** + * Writes the source code matching the declaration + * of a method. + * @param string $name Method name. + * @return string Method signature up to last + * bracket. + * @access public + */ + function getSignature($name) { + if ($name == '__set') { + return 'function __set($key, $value)'; + } + if ($name == '__call') { + return 'function __call($method, $arguments)'; + } + if (version_compare(phpversion(), '5.1.0', '>=')) { + if (in_array($name, array('__get', '__isset', $name == '__unset'))) { + return "function {$name}(\$key)"; + } + } + if ($name == '__toString') { + return "function $name()"; + } + + // This wonky try-catch is a work around for a faulty method_exists() + // in early versions of PHP 5 which would return false for static + // methods. The Reflection classes work fine, but hasMethod() + // doesn't exist prior to PHP 5.1.0, so we need to use a more crude + // detection method. + try { + $interface = new ReflectionClass($this->interface); + $interface->getMethod($name); + } catch (ReflectionException $e) { + return "function $name()"; + } + return $this->getFullSignature($name); + } + + /** + * For a signature specified in an interface, full + * details must be replicated to be a valid implementation. + * @param string $name Method name. + * @return string Method signature up to last + * bracket. + * @access private + */ + protected function getFullSignature($name) { + $interface = new ReflectionClass($this->interface); + $method = $interface->getMethod($name); + $reference = $method->returnsReference() ? '&' : ''; + $static = $method->isStatic() ? 'static ' : ''; + return "{$static}function $reference$name(" . + implode(', ', $this->getParameterSignatures($method)) . + ")"; + } + + /** + * Gets the source code for each parameter. + * @param ReflectionMethod $method Method object from + * reflection API + * @return array List of strings, each + * a snippet of code. + * @access private + */ + protected function getParameterSignatures($method) { + $signatures = array(); + foreach ($method->getParameters() as $parameter) { + $signature = ''; + $type = $parameter->getClass(); + if (is_null($type) && version_compare(phpversion(), '5.1.0', '>=') && $parameter->isArray()) { + $signature .= 'array '; + } elseif (!is_null($type)) { + $signature .= $type->getName() . ' '; + } + if ($parameter->isPassedByReference()) { + $signature .= '&'; + } + $signature .= '$' . $this->suppressSpurious($parameter->getName()); + if ($this->isOptional($parameter)) { + $signature .= ' = null'; + } + $signatures[] = $signature; + } + return $signatures; + } + + /** + * The SPL library has problems with the + * Reflection library. In particular, you can + * get extra characters in parameter names :(. + * @param string $name Parameter name. + * @return string Cleaner name. + * @access private + */ + protected function suppressSpurious($name) { + return str_replace(array('[', ']', ' '), '', $name); + } + + /** + * Test of a reflection parameter being optional + * that works with early versions of PHP5. + * @param reflectionParameter $parameter Is this optional. + * @return boolean True if optional. + * @access private + */ + protected function isOptional($parameter) { + if (method_exists($parameter, 'isOptional')) { + return $parameter->isOptional(); + } + return false; + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/remote.php b/videodb/test/simpletest/remote.php new file mode 100644 index 0000000..589a511 --- /dev/null +++ b/videodb/test/simpletest/remote.php @@ -0,0 +1,115 @@ +url = $url; + $this->dry_url = $dry_url ? $dry_url : $url; + $this->size = false; + } + + /** + * Accessor for the test name for subclasses. + * @return string Name of the test. + * @access public + */ + function getLabel() { + return $this->url; + } + + /** + * Runs the top level test for this class. Currently + * reads the data as a single chunk. I'll fix this + * once I have added iteration to the browser. + * @param SimpleReporter $reporter Target of test results. + * @returns boolean True if no failures. + * @access public + */ + function run($reporter) { + $browser = $this->createBrowser(); + $xml = $browser->get($this->url); + if (! $xml) { + trigger_error('Cannot read remote test URL [' . $this->url . ']'); + return false; + } + $parser = $this->createParser($reporter); + if (! $parser->parse($xml)) { + trigger_error('Cannot parse incoming XML from [' . $this->url . ']'); + return false; + } + return true; + } + + /** + * Creates a new web browser object for fetching + * the XML report. + * @return SimpleBrowser New browser. + * @access protected + */ + protected function createBrowser() { + return new SimpleBrowser(); + } + + /** + * Creates the XML parser. + * @param SimpleReporter $reporter Target of test results. + * @return SimpleTestXmlListener XML reader. + * @access protected + */ + protected function createParser($reporter) { + return new SimpleTestXmlParser($reporter); + } + + /** + * Accessor for the number of subtests. + * @return integer Number of test cases. + * @access public + */ + function getSize() { + if ($this->size === false) { + $browser = $this->createBrowser(); + $xml = $browser->get($this->dry_url); + if (! $xml) { + trigger_error('Cannot read remote test URL [' . $this->dry_url . ']'); + return false; + } + $reporter = new SimpleReporter(); + $parser = $this->createParser($reporter); + if (! $parser->parse($xml)) { + trigger_error('Cannot parse incoming XML from [' . $this->dry_url . ']'); + return false; + } + $this->size = $reporter->getTestCaseCount(); + } + return $this->size; + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/reporter.php b/videodb/test/simpletest/reporter.php new file mode 100644 index 0000000..3c7bf80 --- /dev/null +++ b/videodb/test/simpletest/reporter.php @@ -0,0 +1,445 @@ +character_set = $character_set; + } + + /** + * Paints the top of the web page setting the + * title to the name of the starting test. + * @param string $test_name Name class of test. + * @access public + */ + function paintHeader($test_name) { + $this->sendNoCacheHeaders(); + print ""; + print "\n\n$test_name\n"; + print "\n"; + print "\n"; + print "\n\n"; + print "

              $test_name

              \n"; + flush(); + } + + /** + * Send the headers necessary to ensure the page is + * reloaded on every request. Otherwise you could be + * scratching your head over out of date test data. + * @access public + */ + static function sendNoCacheHeaders() { + if (! headers_sent()) { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + } + } + + /** + * Paints the CSS. Add additional styles here. + * @return string CSS code as text. + * @access protected + */ + protected function getCss() { + return ".fail { background-color: inherit; color: red; }" . + ".pass { background-color: inherit; color: green; }" . + " pre { background-color: lightgray; color: inherit; }"; + } + + /** + * Paints the end of the test with a summary of + * the passes and failures. + * @param string $test_name Name class of test. + * @access public + */ + function paintFooter($test_name) { + $colour = ($this->getFailCount() + $this->getExceptionCount() > 0 ? "red" : "green"); + print "
              "; + print $this->getTestCaseProgress() . "/" . $this->getTestCaseCount(); + print " test cases complete:\n"; + print "" . $this->getPassCount() . " passes, "; + print "" . $this->getFailCount() . " fails and "; + print "" . $this->getExceptionCount() . " exceptions."; + print "
              \n"; + print "\n\n"; + } + + /** + * Paints the test failure with a breadcrumbs + * trail of the nesting test suites below the + * top level test. + * @param string $message Failure message displayed in + * the context of the other tests. + */ + function paintFail($message) { + parent::paintFail($message); + print "Fail: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + print " -> " . $this->htmlEntities($message) . "
              \n"; + } + + /** + * Paints a PHP error. + * @param string $message Message is ignored. + * @access public + */ + function paintError($message) { + parent::paintError($message); + print "Exception: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + print " -> " . $this->htmlEntities($message) . "
              \n"; + } + + /** + * Paints a PHP exception. + * @param Exception $exception Exception to display. + * @access public + */ + function paintException($exception) { + parent::paintException($exception); + print "Exception: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + $message = 'Unexpected exception of type [' . get_class($exception) . + '] with message ['. $exception->getMessage() . + '] in ['. $exception->getFile() . + ' line ' . $exception->getLine() . ']'; + print " -> " . $this->htmlEntities($message) . "
              \n"; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + parent::paintSkip($message); + print "Skipped: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + print " -> " . $this->htmlEntities($message) . "
              \n"; + } + + /** + * Paints formatted text such as dumped privateiables. + * @param string $message Text to show. + * @access public + */ + function paintFormattedMessage($message) { + print '
              ' . $this->htmlEntities($message) . '
              '; + } + + /** + * Character set adjusted entity conversion. + * @param string $message Plain text or Unicode message. + * @return string Browser readable message. + * @access protected + */ + protected function htmlEntities($message) { + return htmlentities($message, ENT_COMPAT, $this->character_set); + } +} + +/** + * Sample minimal test displayer. Generates only + * failure messages and a pass count. For command + * line use. I've tried to make it look like JUnit, + * but I wanted to output the errors as they arrived + * which meant dropping the dots. + * @package SimpleTest + * @subpackage UnitTester + */ +class TextReporter extends SimpleReporter { + + /** + * Does nothing yet. The first output will + * be sent on the first test start. + */ + function __construct() { + parent::__construct(); + } + + /** + * Paints the title only. + * @param string $test_name Name class of test. + * @access public + */ + function paintHeader($test_name) { + if (! SimpleReporter::inCli()) { + header('Content-type: text/plain'); + } + print "$test_name\n"; + flush(); + } + + /** + * Paints the end of the test with a summary of + * the passes and failures. + * @param string $test_name Name class of test. + * @access public + */ + function paintFooter($test_name) { + if ($this->getFailCount() + $this->getExceptionCount() == 0) { + print "OK\n"; + } else { + print "FAILURES!!!\n"; + } + print "Test cases run: " . $this->getTestCaseProgress() . + "/" . $this->getTestCaseCount() . + ", Passes: " . $this->getPassCount() . + ", Failures: " . $this->getFailCount() . + ", Exceptions: " . $this->getExceptionCount() . "\n"; + } + + /** + * Paints the test failure as a stack trace. + * @param string $message Failure message displayed in + * the context of the other tests. + * @access public + */ + function paintFail($message) { + parent::paintFail($message); + print $this->getFailCount() . ") $message\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print "\tin " . implode("\n\tin ", array_reverse($breadcrumb)); + print "\n"; + } + + /** + * Paints a PHP error or exception. + * @param string $message Message to be shown. + * @access public + * @abstract + */ + function paintError($message) { + parent::paintError($message); + print "Exception " . $this->getExceptionCount() . "!\n$message\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print "\tin " . implode("\n\tin ", array_reverse($breadcrumb)); + print "\n"; + } + + /** + * Paints a PHP error or exception. + * @param Exception $exception Exception to describe. + * @access public + * @abstract + */ + function paintException($exception) { + parent::paintException($exception); + $message = 'Unexpected exception of type [' . get_class($exception) . + '] with message ['. $exception->getMessage() . + '] in ['. $exception->getFile() . + ' line ' . $exception->getLine() . ']'; + print "Exception " . $this->getExceptionCount() . "!\n$message\n"; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print "\tin " . implode("\n\tin ", array_reverse($breadcrumb)); + print "\n"; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + parent::paintSkip($message); + print "Skip: $message\n"; + } + + /** + * Paints formatted text such as dumped privateiables. + * @param string $message Text to show. + * @access public + */ + function paintFormattedMessage($message) { + print "$message\n"; + flush(); + } +} + +/** + * Runs just a single test group, a single case or + * even a single test within that case. + * @package SimpleTest + * @subpackage UnitTester + */ +class SelectiveReporter extends SimpleReporterDecorator { + private $just_this_case = false; + private $just_this_test = false; + private $on; + + /** + * Selects the test case or group to be run, + * and optionally a specific test. + * @param SimpleScorer $reporter Reporter to receive events. + * @param string $just_this_case Only this case or group will run. + * @param string $just_this_test Only this test method will run. + */ + function __construct($reporter, $just_this_case = false, $just_this_test = false) { + if (isset($just_this_case) && $just_this_case) { + $this->just_this_case = strtolower($just_this_case); + $this->off(); + } else { + $this->on(); + } + if (isset($just_this_test) && $just_this_test) { + $this->just_this_test = strtolower($just_this_test); + } + parent::__construct($reporter); + } + + /** + * Compares criteria to actual the case/group name. + * @param string $test_case The incoming test. + * @return boolean True if matched. + * @access protected + */ + protected function matchesTestCase($test_case) { + return $this->just_this_case == strtolower($test_case); + } + + /** + * Compares criteria to actual the test name. If no + * name was specified at the beginning, then all tests + * can run. + * @param string $method The incoming test method. + * @return boolean True if matched. + * @access protected + */ + protected function shouldRunTest($test_case, $method) { + if ($this->isOn() || $this->matchesTestCase($test_case)) { + if ($this->just_this_test) { + return $this->just_this_test == strtolower($method); + } else { + return true; + } + } + return false; + } + + /** + * Switch on testing for the group or subgroup. + * @access private + */ + protected function on() { + $this->on = true; + } + + /** + * Switch off testing for the group or subgroup. + * @access private + */ + protected function off() { + $this->on = false; + } + + /** + * Is this group actually being tested? + * @return boolean True if the current test group is active. + * @access private + */ + protected function isOn() { + return $this->on; + } + + /** + * Veto everything that doesn't match the method wanted. + * @param string $test_case Name of test case. + * @param string $method Name of test method. + * @return boolean True if test should be run. + * @access public + */ + function shouldInvoke($test_case, $method) { + if ($this->shouldRunTest($test_case, $method)) { + return $this->reporter->shouldInvoke($test_case, $method); + } + return false; + } + + /** + * Paints the start of a group test. + * @param string $test_case Name of test or other label. + * @param integer $size Number of test cases starting. + * @access public + */ + function paintGroupStart($test_case, $size) { + if ($this->just_this_case && $this->matchesTestCase($test_case)) { + $this->on(); + } + $this->reporter->paintGroupStart($test_case, $size); + } + + /** + * Paints the end of a group test. + * @param string $test_case Name of test or other label. + * @access public + */ + function paintGroupEnd($test_case) { + $this->reporter->paintGroupEnd($test_case); + if ($this->just_this_case && $this->matchesTestCase($test_case)) { + $this->off(); + } + } +} + +/** + * Suppresses skip messages. + * @package SimpleTest + * @subpackage UnitTester + */ +class NoSkipsReporter extends SimpleReporterDecorator { + + /** + * Does nothing. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/scorer.php b/videodb/test/simpletest/scorer.php new file mode 100644 index 0000000..a9a8c7f --- /dev/null +++ b/videodb/test/simpletest/scorer.php @@ -0,0 +1,875 @@ +passes = 0; + $this->fails = 0; + $this->exceptions = 0; + $this->is_dry_run = false; + } + + /** + * Signals that the next evaluation will be a dry + * run. That is, the structure events will be + * recorded, but no tests will be run. + * @param boolean $is_dry Dry run if true. + * @access public + */ + function makeDry($is_dry = true) { + $this->is_dry_run = $is_dry; + } + + /** + * The reporter has a veto on what should be run. + * @param string $test_case_name name of test case. + * @param string $method Name of test method. + * @access public + */ + function shouldInvoke($test_case_name, $method) { + return ! $this->is_dry_run; + } + + /** + * Can wrap the invoker in preperation for running + * a test. + * @param SimpleInvoker $invoker Individual test runner. + * @return SimpleInvoker Wrapped test runner. + * @access public + */ + function createInvoker($invoker) { + return $invoker; + } + + /** + * Accessor for current status. Will be false + * if there have been any failures or exceptions. + * Used for command line tools. + * @return boolean True if no failures. + * @access public + */ + function getStatus() { + if ($this->exceptions + $this->fails > 0) { + return false; + } + return true; + } + + /** + * Paints the start of a group test. + * @param string $test_name Name of test or other label. + * @param integer $size Number of test cases starting. + * @access public + */ + function paintGroupStart($test_name, $size) { + } + + /** + * Paints the end of a group test. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintGroupEnd($test_name) { + } + + /** + * Paints the start of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseStart($test_name) { + } + + /** + * Paints the end of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseEnd($test_name) { + } + + /** + * Paints the start of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodStart($test_name) { + } + + /** + * Paints the end of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodEnd($test_name) { + } + + /** + * Increments the pass count. + * @param string $message Message is ignored. + * @access public + */ + function paintPass($message) { + $this->passes++; + } + + /** + * Increments the fail count. + * @param string $message Message is ignored. + * @access public + */ + function paintFail($message) { + $this->fails++; + } + + /** + * Deals with PHP 4 throwing an error. + * @param string $message Text of error formatted by + * the test case. + * @access public + */ + function paintError($message) { + $this->exceptions++; + } + + /** + * Deals with PHP 5 throwing an exception. + * @param Exception $exception The actual exception thrown. + * @access public + */ + function paintException($exception) { + $this->exceptions++; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + } + + /** + * Accessor for the number of passes so far. + * @return integer Number of passes. + * @access public + */ + function getPassCount() { + return $this->passes; + } + + /** + * Accessor for the number of fails so far. + * @return integer Number of fails. + * @access public + */ + function getFailCount() { + return $this->fails; + } + + /** + * Accessor for the number of untrapped errors + * so far. + * @return integer Number of exceptions. + * @access public + */ + function getExceptionCount() { + return $this->exceptions; + } + + /** + * Paints a simple supplementary message. + * @param string $message Text to display. + * @access public + */ + function paintMessage($message) { + } + + /** + * Paints a formatted ASCII message such as a + * privateiable dump. + * @param string $message Text to display. + * @access public + */ + function paintFormattedMessage($message) { + } + + /** + * By default just ignores user generated events. + * @param string $type Event type as text. + * @param mixed $payload Message or object. + * @access public + */ + function paintSignal($type, $payload) { + } +} + +/** + * Recipient of generated test messages that can display + * page footers and headers. Also keeps track of the + * test nesting. This is the main base class on which + * to build the finished test (page based) displays. + * @package SimpleTest + * @subpackage UnitTester + */ +class SimpleReporter extends SimpleScorer { + private $test_stack; + private $size; + private $progress; + + /** + * Starts the display with no results in. + * @access public + */ + function __construct() { + parent::__construct(); + $this->test_stack = array(); + $this->size = null; + $this->progress = 0; + } + + /** + * Gets the formatter for small generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return new SimpleDumper(); + } + + /** + * Paints the start of a group test. Will also paint + * the page header and footer if this is the + * first test. Will stash the size if the first + * start. + * @param string $test_name Name of test that is starting. + * @param integer $size Number of test cases starting. + * @access public + */ + function paintGroupStart($test_name, $size) { + if (! isset($this->size)) { + $this->size = $size; + } + if (count($this->test_stack) == 0) { + $this->paintHeader($test_name); + } + $this->test_stack[] = $test_name; + } + + /** + * Paints the end of a group test. Will paint the page + * footer if the stack of tests has unwound. + * @param string $test_name Name of test that is ending. + * @param integer $progress Number of test cases ending. + * @access public + */ + function paintGroupEnd($test_name) { + array_pop($this->test_stack); + if (count($this->test_stack) == 0) { + $this->paintFooter($test_name); + } + } + + /** + * Paints the start of a test case. Will also paint + * the page header and footer if this is the + * first test. Will stash the size if the first + * start. + * @param string $test_name Name of test that is starting. + * @access public + */ + function paintCaseStart($test_name) { + if (! isset($this->size)) { + $this->size = 1; + } + if (count($this->test_stack) == 0) { + $this->paintHeader($test_name); + } + $this->test_stack[] = $test_name; + } + + /** + * Paints the end of a test case. Will paint the page + * footer if the stack of tests has unwound. + * @param string $test_name Name of test that is ending. + * @access public + */ + function paintCaseEnd($test_name) { + $this->progress++; + array_pop($this->test_stack); + if (count($this->test_stack) == 0) { + $this->paintFooter($test_name); + } + } + + /** + * Paints the start of a test method. + * @param string $test_name Name of test that is starting. + * @access public + */ + function paintMethodStart($test_name) { + $this->test_stack[] = $test_name; + } + + /** + * Paints the end of a test method. Will paint the page + * footer if the stack of tests has unwound. + * @param string $test_name Name of test that is ending. + * @access public + */ + function paintMethodEnd($test_name) { + array_pop($this->test_stack); + } + + /** + * Paints the test document header. + * @param string $test_name First test top level + * to start. + * @access public + * @abstract + */ + function paintHeader($test_name) { + } + + /** + * Paints the test document footer. + * @param string $test_name The top level test. + * @access public + * @abstract + */ + function paintFooter($test_name) { + } + + /** + * Accessor for internal test stack. For + * subclasses that need to see the whole test + * history for display purposes. + * @return array List of methods in nesting order. + * @access public + */ + function getTestList() { + return $this->test_stack; + } + + /** + * Accessor for total test size in number + * of test cases. Null until the first + * test is started. + * @return integer Total number of cases at start. + * @access public + */ + function getTestCaseCount() { + return $this->size; + } + + /** + * Accessor for the number of test cases + * completed so far. + * @return integer Number of ended cases. + * @access public + */ + function getTestCaseProgress() { + return $this->progress; + } + + /** + * Static check for running in the comand line. + * @return boolean True if CLI. + * @access public + */ + static function inCli() { + return php_sapi_name() == 'cli'; + } +} + +/** + * For modifying the behaviour of the visual reporters. + * @package SimpleTest + * @subpackage UnitTester + */ +class SimpleReporterDecorator { + protected $reporter; + + /** + * Mediates between the reporter and the test case. + * @param SimpleScorer $reporter Reporter to receive events. + */ + function __construct($reporter) { + $this->reporter = $reporter; + } + + /** + * Signals that the next evaluation will be a dry + * run. That is, the structure events will be + * recorded, but no tests will be run. + * @param boolean $is_dry Dry run if true. + * @access public + */ + function makeDry($is_dry = true) { + $this->reporter->makeDry($is_dry); + } + + /** + * Accessor for current status. Will be false + * if there have been any failures or exceptions. + * Used for command line tools. + * @return boolean True if no failures. + * @access public + */ + function getStatus() { + return $this->reporter->getStatus(); + } + + /** + * The nesting of the test cases so far. Not + * all reporters have this facility. + * @return array Test list if accessible. + * @access public + */ + function getTestList() { + if (method_exists($this->reporter, 'getTestList')) { + return $this->reporter->getTestList(); + } else { + return array(); + } + } + + /** + * The reporter has a veto on what should be run. + * @param string $test_case_name Name of test case. + * @param string $method Name of test method. + * @return boolean True if test should be run. + * @access public + */ + function shouldInvoke($test_case_name, $method) { + return $this->reporter->shouldInvoke($test_case_name, $method); + } + + /** + * Can wrap the invoker in preparation for running + * a test. + * @param SimpleInvoker $invoker Individual test runner. + * @return SimpleInvoker Wrapped test runner. + * @access public + */ + function createInvoker($invoker) { + return $this->reporter->createInvoker($invoker); + } + + /** + * Gets the formatter for privateiables and other small + * generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return $this->reporter->getDumper(); + } + + /** + * Paints the start of a group test. + * @param string $test_name Name of test or other label. + * @param integer $size Number of test cases starting. + * @access public + */ + function paintGroupStart($test_name, $size) { + $this->reporter->paintGroupStart($test_name, $size); + } + + /** + * Paints the end of a group test. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintGroupEnd($test_name) { + $this->reporter->paintGroupEnd($test_name); + } + + /** + * Paints the start of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseStart($test_name) { + $this->reporter->paintCaseStart($test_name); + } + + /** + * Paints the end of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseEnd($test_name) { + $this->reporter->paintCaseEnd($test_name); + } + + /** + * Paints the start of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodStart($test_name) { + $this->reporter->paintMethodStart($test_name); + } + + /** + * Paints the end of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodEnd($test_name) { + $this->reporter->paintMethodEnd($test_name); + } + + /** + * Chains to the wrapped reporter. + * @param string $message Message is ignored. + * @access public + */ + function paintPass($message) { + $this->reporter->paintPass($message); + } + + /** + * Chains to the wrapped reporter. + * @param string $message Message is ignored. + * @access public + */ + function paintFail($message) { + $this->reporter->paintFail($message); + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text of error formatted by + * the test case. + * @access public + */ + function paintError($message) { + $this->reporter->paintError($message); + } + + /** + * Chains to the wrapped reporter. + * @param Exception $exception Exception to show. + * @access public + */ + function paintException($exception) { + $this->reporter->paintException($exception); + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + $this->reporter->paintSkip($message); + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text to display. + * @access public + */ + function paintMessage($message) { + $this->reporter->paintMessage($message); + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text to display. + * @access public + */ + function paintFormattedMessage($message) { + $this->reporter->paintFormattedMessage($message); + } + + /** + * Chains to the wrapped reporter. + * @param string $type Event type as text. + * @param mixed $payload Message or object. + * @return boolean Should return false if this + * type of signal should fail the + * test suite. + * @access public + */ + function paintSignal($type, $payload) { + $this->reporter->paintSignal($type, $payload); + } +} + +/** + * For sending messages to multiple reporters at + * the same time. + * @package SimpleTest + * @subpackage UnitTester + */ +class MultipleReporter { + private $reporters = array(); + + /** + * Adds a reporter to the subscriber list. + * @param SimpleScorer $reporter Reporter to receive events. + * @access public + */ + function attachReporter($reporter) { + $this->reporters[] = $reporter; + } + + /** + * Signals that the next evaluation will be a dry + * run. That is, the structure events will be + * recorded, but no tests will be run. + * @param boolean $is_dry Dry run if true. + * @access public + */ + function makeDry($is_dry = true) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->makeDry($is_dry); + } + } + + /** + * Accessor for current status. Will be false + * if there have been any failures or exceptions. + * If any reporter reports a failure, the whole + * suite fails. + * @return boolean True if no failures. + * @access public + */ + function getStatus() { + for ($i = 0; $i < count($this->reporters); $i++) { + if (! $this->reporters[$i]->getStatus()) { + return false; + } + } + return true; + } + + /** + * The reporter has a veto on what should be run. + * It requires all reporters to want to run the method. + * @param string $test_case_name name of test case. + * @param string $method Name of test method. + * @access public + */ + function shouldInvoke($test_case_name, $method) { + for ($i = 0; $i < count($this->reporters); $i++) { + if (! $this->reporters[$i]->shouldInvoke($test_case_name, $method)) { + return false; + } + } + return true; + } + + /** + * Every reporter gets a chance to wrap the invoker. + * @param SimpleInvoker $invoker Individual test runner. + * @return SimpleInvoker Wrapped test runner. + * @access public + */ + function createInvoker($invoker) { + for ($i = 0; $i < count($this->reporters); $i++) { + $invoker = $this->reporters[$i]->createInvoker($invoker); + } + return $invoker; + } + + /** + * Gets the formatter for privateiables and other small + * generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return new SimpleDumper(); + } + + /** + * Paints the start of a group test. + * @param string $test_name Name of test or other label. + * @param integer $size Number of test cases starting. + * @access public + */ + function paintGroupStart($test_name, $size) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintGroupStart($test_name, $size); + } + } + + /** + * Paints the end of a group test. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintGroupEnd($test_name) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintGroupEnd($test_name); + } + } + + /** + * Paints the start of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseStart($test_name) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintCaseStart($test_name); + } + } + + /** + * Paints the end of a test case. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintCaseEnd($test_name) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintCaseEnd($test_name); + } + } + + /** + * Paints the start of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodStart($test_name) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintMethodStart($test_name); + } + } + + /** + * Paints the end of a test method. + * @param string $test_name Name of test or other label. + * @access public + */ + function paintMethodEnd($test_name) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintMethodEnd($test_name); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $message Message is ignored. + * @access public + */ + function paintPass($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintPass($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $message Message is ignored. + * @access public + */ + function paintFail($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintFail($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text of error formatted by + * the test case. + * @access public + */ + function paintError($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintError($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param Exception $exception Exception to display. + * @access public + */ + function paintException($exception) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintException($exception); + } + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintSkip($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text to display. + * @access public + */ + function paintMessage($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintMessage($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $message Text to display. + * @access public + */ + function paintFormattedMessage($message) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintFormattedMessage($message); + } + } + + /** + * Chains to the wrapped reporter. + * @param string $type Event type as text. + * @param mixed $payload Message or object. + * @return boolean Should return false if this + * type of signal should fail the + * test suite. + * @access public + */ + function paintSignal($type, $payload) { + for ($i = 0; $i < count($this->reporters); $i++) { + $this->reporters[$i]->paintSignal($type, $payload); + } + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/selector.php b/videodb/test/simpletest/selector.php new file mode 100644 index 0000000..fe527df --- /dev/null +++ b/videodb/test/simpletest/selector.php @@ -0,0 +1,141 @@ +name = $name; + } + + /** + * Accessor for name. + * @returns string $name Name to match. + */ + function getName() { + return $this->name; + } + + /** + * Compares with name attribute of widget. + * @param SimpleWidget $widget Control to compare. + * @access public + */ + function isMatch($widget) { + return ($widget->getName() == $this->name); + } +} + +/** + * Used to extract form elements for testing against. + * Searches by visible label or alt text. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleByLabel { + private $label; + + /** + * Stashes the name for later comparison. + * @param string $label Visible text to match. + */ + function __construct($label) { + $this->label = $label; + } + + /** + * Comparison. Compares visible text of widget or + * related label. + * @param SimpleWidget $widget Control to compare. + * @access public + */ + function isMatch($widget) { + if (! method_exists($widget, 'isLabel')) { + return false; + } + return $widget->isLabel($this->label); + } +} + +/** + * Used to extract form elements for testing against. + * Searches dy id attribute. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleById { + private $id; + + /** + * Stashes the name for later comparison. + * @param string $id ID atribute to match. + */ + function __construct($id) { + $this->id = $id; + } + + /** + * Comparison. Compares id attribute of widget. + * @param SimpleWidget $widget Control to compare. + * @access public + */ + function isMatch($widget) { + return $widget->isId($this->id); + } +} + +/** + * Used to extract form elements for testing against. + * Searches by visible label, name or alt text. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleByLabelOrName { + private $label; + + /** + * Stashes the name/label for later comparison. + * @param string $label Visible text to match. + */ + function __construct($label) { + $this->label = $label; + } + + /** + * Comparison. Compares visible text of widget or + * related label or name. + * @param SimpleWidget $widget Control to compare. + * @access public + */ + function isMatch($widget) { + if (method_exists($widget, 'isLabel')) { + if ($widget->isLabel($this->label)) { + return true; + } + } + return ($widget->getName() == $this->label); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/shell_tester.php b/videodb/test/simpletest/shell_tester.php new file mode 100644 index 0000000..4b8a547 --- /dev/null +++ b/videodb/test/simpletest/shell_tester.php @@ -0,0 +1,330 @@ +output = false; + } + + /** + * Actually runs the command. Does not trap the + * error stream output as this need PHP 4.3+. + * @param string $command The actual command line + * to run. + * @return integer Exit code. + * @access public + */ + function execute($command) { + $this->output = false; + exec($command, $this->output, $ret); + return $ret; + } + + /** + * Accessor for the last output. + * @return string Output as text. + * @access public + */ + function getOutput() { + return implode("\n", $this->output); + } + + /** + * Accessor for the last output. + * @return array Output as array of lines. + * @access public + */ + function getOutputAsList() { + return $this->output; + } +} + +/** + * Test case for testing of command line scripts and + * utilities. Usually scripts that are external to the + * PHP code, but support it in some way. + * @package SimpleTest + * @subpackage UnitTester + */ +class ShellTestCase extends SimpleTestCase { + private $current_shell; + private $last_status; + private $last_command; + + /** + * Creates an empty test case. Should be subclassed + * with test methods for a functional test case. + * @param string $label Name of test case. Will use + * the class name if none specified. + * @access public + */ + function __construct($label = false) { + parent::__construct($label); + $this->current_shell = $this->createShell(); + $this->last_status = false; + $this->last_command = ''; + } + + /** + * Executes a command and buffers the results. + * @param string $command Command to run. + * @return boolean True if zero exit code. + * @access public + */ + function execute($command) { + $shell = $this->getShell(); + $this->last_status = $shell->execute($command); + $this->last_command = $command; + return ($this->last_status === 0); + } + + /** + * Dumps the output of the last command. + * @access public + */ + function dumpOutput() { + $this->dump($this->getOutput()); + } + + /** + * Accessor for the last output. + * @return string Output as text. + * @access public + */ + function getOutput() { + $shell = $this->getShell(); + return $shell->getOutput(); + } + + /** + * Accessor for the last output. + * @return array Output as array of lines. + * @access public + */ + function getOutputAsList() { + $shell = $this->getShell(); + return $shell->getOutputAsList(); + } + + /** + * Called from within the test methods to register + * passes and failures. + * @param boolean $result Pass on true. + * @param string $message Message to display describing + * the test state. + * @return boolean True on pass + * @access public + */ + function assertTrue($result, $message = false) { + return $this->assert(new TrueExpectation(), $result, $message); + } + + /** + * Will be true on false and vice versa. False + * is the PHP definition of false, so that null, + * empty strings, zero and an empty array all count + * as false. + * @param boolean $result Pass on false. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertFalse($result, $message = '%s') { + return $this->assert(new FalseExpectation(), $result, $message); + } + + /** + * Will trigger a pass if the two parameters have + * the same value only. Otherwise a fail. This + * is for testing hand extracted text, etc. + * @param mixed $first Value to compare. + * @param mixed $second Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertEqual($first, $second, $message = "%s") { + return $this->assert( + new EqualExpectation($first), + $second, + $message); + } + + /** + * Will trigger a pass if the two parameters have + * a different value. Otherwise a fail. This + * is for testing hand extracted text, etc. + * @param mixed $first Value to compare. + * @param mixed $second Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertNotEqual($first, $second, $message = "%s") { + return $this->assert( + new NotEqualExpectation($first), + $second, + $message); + } + + /** + * Tests the last status code from the shell. + * @param integer $status Expected status of last + * command. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertExitCode($status, $message = "%s") { + $message = sprintf($message, "Expected status code of [$status] from [" . + $this->last_command . "], but got [" . + $this->last_status . "]"); + return $this->assertTrue($status === $this->last_status, $message); + } + + /** + * Attempt to exactly match the combined STDERR and + * STDOUT output. + * @param string $expected Expected output. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertOutput($expected, $message = "%s") { + $shell = $this->getShell(); + return $this->assert( + new EqualExpectation($expected), + $shell->getOutput(), + $message); + } + + /** + * Scans the output for a Perl regex. If found + * anywhere it passes, else it fails. + * @param string $pattern Regex to search for. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertOutputPattern($pattern, $message = "%s") { + $shell = $this->getShell(); + return $this->assert( + new PatternExpectation($pattern), + $shell->getOutput(), + $message); + } + + /** + * If a Perl regex is found anywhere in the current + * output then a failure is generated, else a pass. + * @param string $pattern Regex to search for. + * @param $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertNoOutputPattern($pattern, $message = "%s") { + $shell = $this->getShell(); + return $this->assert( + new NoPatternExpectation($pattern), + $shell->getOutput(), + $message); + } + + /** + * File existence check. + * @param string $path Full filename and path. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertFileExists($path, $message = "%s") { + $message = sprintf($message, "File [$path] should exist"); + return $this->assertTrue(file_exists($path), $message); + } + + /** + * File non-existence check. + * @param string $path Full filename and path. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertFileNotExists($path, $message = "%s") { + $message = sprintf($message, "File [$path] should not exist"); + return $this->assertFalse(file_exists($path), $message); + } + + /** + * Scans a file for a Perl regex. If found + * anywhere it passes, else it fails. + * @param string $pattern Regex to search for. + * @param string $path Full filename and path. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertFilePattern($pattern, $path, $message = "%s") { + return $this->assert( + new PatternExpectation($pattern), + implode('', file($path)), + $message); + } + + /** + * If a Perl regex is found anywhere in the named + * file then a failure is generated, else a pass. + * @param string $pattern Regex to search for. + * @param string $path Full filename and path. + * @param string $message Message to display. + * @return boolean True if pass. + * @access public + */ + function assertNoFilePattern($pattern, $path, $message = "%s") { + return $this->assert( + new NoPatternExpectation($pattern), + implode('', file($path)), + $message); + } + + /** + * Accessor for current shell. Used for testing the + * the tester itself. + * @return Shell Current shell. + * @access protected + */ + protected function getShell() { + return $this->current_shell; + } + + /** + * Factory for the shell to run the command on. + * @return Shell New shell object. + * @access protected + */ + protected function createShell() { + return new SimpleShell(); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/simpletest.php b/videodb/test/simpletest/simpletest.php new file mode 100644 index 0000000..105716c --- /dev/null +++ b/videodb/test/simpletest/simpletest.php @@ -0,0 +1,391 @@ +getParent()) { + SimpleTest::ignore($parent); + } + } + } + } + + /** + * Puts the object to the global pool of 'preferred' objects + * which can be retrieved with SimpleTest :: preferred() method. + * Instances of the same class are overwritten. + * @param object $object Preferred object + * @see preferred() + */ + static function prefer($object) { + $registry = &SimpleTest::getRegistry(); + $registry['Preferred'][] = $object; + } + + /** + * Retrieves 'preferred' objects from global pool. Class filter + * can be applied in order to retrieve the object of the specific + * class + * @param array|string $classes Allowed classes or interfaces. + * @return array|object|null + * @see prefer() + */ + static function preferred($classes) { + if (! is_array($classes)) { + $classes = array($classes); + } + $registry = &SimpleTest::getRegistry(); + for ($i = count($registry['Preferred']) - 1; $i >= 0; $i--) { + foreach ($classes as $class) { + if (SimpleTestCompatibility::isA($registry['Preferred'][$i], $class)) { + return $registry['Preferred'][$i]; + } + } + } + return null; + } + + /** + * Test to see if a test case is in the ignore + * list. Quite obviously the ignore list should + * be a separate object and will be one day. + * This method is internal to SimpleTest. Don't + * use it. + * @param string $class Class name to test. + * @return boolean True if should not be run. + */ + static function isIgnored($class) { + $registry = &SimpleTest::getRegistry(); + return isset($registry['IgnoreList'][strtolower($class)]); + } + + /** + * Sets proxy to use on all requests for when + * testing from behind a firewall. Set host + * to false to disable. This will take effect + * if there are no other proxy settings. + * @param string $proxy Proxy host as URL. + * @param string $username Proxy username for authentication. + * @param string $password Proxy password for authentication. + */ + static function useProxy($proxy, $username = false, $password = false) { + $registry = &SimpleTest::getRegistry(); + $registry['DefaultProxy'] = $proxy; + $registry['DefaultProxyUsername'] = $username; + $registry['DefaultProxyPassword'] = $password; + } + + /** + * Accessor for default proxy host. + * @return string Proxy URL. + */ + static function getDefaultProxy() { + $registry = &SimpleTest::getRegistry(); + return $registry['DefaultProxy']; + } + + /** + * Accessor for default proxy username. + * @return string Proxy username for authentication. + */ + static function getDefaultProxyUsername() { + $registry = &SimpleTest::getRegistry(); + return $registry['DefaultProxyUsername']; + } + + /** + * Accessor for default proxy password. + * @return string Proxy password for authentication. + */ + static function getDefaultProxyPassword() { + $registry = &SimpleTest::getRegistry(); + return $registry['DefaultProxyPassword']; + } + + /** + * Accessor for default HTML parsers. + * @return array List of parsers to try in + * order until one responds true + * to can(). + */ + static function getParsers() { + $registry = &SimpleTest::getRegistry(); + return $registry['Parsers']; + } + + /** + * Set the list of HTML parsers to attempt to use by default. + * @param array $parsers List of parsers to try in + * order until one responds true + * to can(). + */ + static function setParsers($parsers) { + $registry = &SimpleTest::getRegistry(); + $registry['Parsers'] = $parsers; + } + + /** + * Accessor for global registry of options. + * @return hash All stored values. + */ + protected static function &getRegistry() { + static $registry = false; + if (! $registry) { + $registry = SimpleTest::getDefaults(); + } + return $registry; + } + + /** + * Accessor for the context of the current + * test run. + * @return SimpleTestContext Current test run. + */ + static function getContext() { + static $context = false; + if (! $context) { + $context = new SimpleTestContext(); + } + return $context; + } + + /** + * Constant default values. + * @return hash All registry defaults. + */ + protected static function getDefaults() { + return array( + 'Parsers' => false, + 'MockBaseClass' => 'SimpleMock', + 'IgnoreList' => array(), + 'DefaultProxy' => false, + 'DefaultProxyUsername' => false, + 'DefaultProxyPassword' => false, + 'Preferred' => array(new HtmlReporter(), new TextReporter(), new XmlReporter())); + } + + /** + * @deprecated + */ + static function setMockBaseClass($mock_base) { + $registry = &SimpleTest::getRegistry(); + $registry['MockBaseClass'] = $mock_base; + } + + /** + * @deprecated + */ + static function getMockBaseClass() { + $registry = &SimpleTest::getRegistry(); + return $registry['MockBaseClass']; + } +} + +/** + * Container for all components for a specific + * test run. Makes things like error queues + * available to PHP event handlers, and also + * gets around some nasty reference issues in + * the mocks. + * @package SimpleTest + */ +class SimpleTestContext { + private $test; + private $reporter; + private $resources; + + /** + * Clears down the current context. + * @access public + */ + function clear() { + $this->resources = array(); + } + + /** + * Sets the current test case instance. This + * global instance can be used by the mock objects + * to send message to the test cases. + * @param SimpleTestCase $test Test case to register. + */ + function setTest($test) { + $this->clear(); + $this->test = $test; + } + + /** + * Accessor for currently running test case. + * @return SimpleTestCase Current test. + */ + function getTest() { + return $this->test; + } + + /** + * Sets the current reporter. This + * global instance can be used by the mock objects + * to send messages. + * @param SimpleReporter $reporter Reporter to register. + */ + function setReporter($reporter) { + $this->clear(); + $this->reporter = $reporter; + } + + /** + * Accessor for current reporter. + * @return SimpleReporter Current reporter. + */ + function getReporter() { + return $this->reporter; + } + + /** + * Accessor for the Singleton resource. + * @return object Global resource. + */ + function get($resource) { + if (! isset($this->resources[$resource])) { + $this->resources[$resource] = new $resource(); + } + return $this->resources[$resource]; + } +} + +/** + * Interrogates the stack trace to recover the + * failure point. + * @package SimpleTest + * @subpackage UnitTester + */ +class SimpleStackTrace { + private $prefixes; + + /** + * Stashes the list of target prefixes. + * @param array $prefixes List of method prefixes + * to search for. + */ + function __construct($prefixes) { + $this->prefixes = $prefixes; + } + + /** + * Extracts the last method name that was not within + * Simpletest itself. Captures a stack trace if none given. + * @param array $stack List of stack frames. + * @return string Snippet of test report with line + * number and file. + */ + function traceMethod($stack = false) { + $stack = $stack ? $stack : $this->captureTrace(); + foreach ($stack as $frame) { + if ($this->frameLiesWithinSimpleTestFolder($frame)) { + continue; + } + if ($this->frameMatchesPrefix($frame)) { + return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']'; + } + } + return ''; + } + + /** + * Test to see if error is generated by SimpleTest itself. + * @param array $frame PHP stack frame. + * @return boolean True if a SimpleTest file. + */ + protected function frameLiesWithinSimpleTestFolder($frame) { + if (isset($frame['file'])) { + $path = substr(SIMPLE_TEST, 0, -1); + if (strpos($frame['file'], $path) === 0) { + if (dirname($frame['file']) == $path) { + return true; + } + } + } + return false; + } + + /** + * Tries to determine if the method call is an assert, etc. + * @param array $frame PHP stack frame. + * @return boolean True if matches a target. + */ + protected function frameMatchesPrefix($frame) { + foreach ($this->prefixes as $prefix) { + if (strncmp($frame['function'], $prefix, strlen($prefix)) == 0) { + return true; + } + } + return false; + } + + /** + * Grabs a current stack trace. + * @return array Fulle trace. + */ + protected function captureTrace() { + if (function_exists('debug_backtrace')) { + return array_reverse(debug_backtrace()); + } + return array(); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/socket.php b/videodb/test/simpletest/socket.php new file mode 100644 index 0000000..325ca1f --- /dev/null +++ b/videodb/test/simpletest/socket.php @@ -0,0 +1,312 @@ +clearError(); + } + + /** + * Test for an outstanding error. + * @return boolean True if there is an error. + * @access public + */ + function isError() { + return ($this->error != ''); + } + + /** + * Accessor for an outstanding error. + * @return string Empty string if no error otherwise + * the error message. + * @access public + */ + function getError() { + return $this->error; + } + + /** + * Sets the internal error. + * @param string Error message to stash. + * @access protected + */ + function setError($error) { + $this->error = $error; + } + + /** + * Resets the error state to no error. + * @access protected + */ + function clearError() { + $this->setError(''); + } +} + +/** + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleFileSocket extends SimpleStickyError { + private $handle; + private $is_open = false; + private $sent = ''; + private $block_size; + + /** + * Opens a socket for reading and writing. + * @param SimpleUrl $file Target URI to fetch. + * @param integer $block_size Size of chunk to read. + * @access public + */ + function __construct($file, $block_size = 1024) { + parent::__construct(); + if (! ($this->handle = $this->openFile($file, $error))) { + $file_string = $file->asString(); + $this->setError("Cannot open [$file_string] with [$error]"); + return; + } + $this->is_open = true; + $this->block_size = $block_size; + } + + /** + * Writes some data to the socket and saves alocal copy. + * @param string $message String to send to socket. + * @return boolean True if successful. + * @access public + */ + function write($message) { + return true; + } + + /** + * Reads data from the socket. The error suppresion + * is a workaround for PHP4 always throwing a warning + * with a secure socket. + * @return integer/boolean Incoming bytes. False + * on error. + * @access public + */ + function read() { + $raw = @fread($this->handle, $this->block_size); + if ($raw === false) { + $this->setError('Cannot read from socket'); + $this->close(); + } + return $raw; + } + + /** + * Accessor for socket open state. + * @return boolean True if open. + * @access public + */ + function isOpen() { + return $this->is_open; + } + + /** + * Closes the socket preventing further reads. + * Cannot be reopened once closed. + * @return boolean True if successful. + * @access public + */ + function close() { + if (!$this->is_open) return false; + $this->is_open = false; + return fclose($this->handle); + } + + /** + * Accessor for content so far. + * @return string Bytes sent only. + * @access public + */ + function getSent() { + return $this->sent; + } + + /** + * Actually opens the low level socket. + * @param SimpleUrl $file SimpleUrl file target. + * @param string $error Recipient of error message. + * @param integer $timeout Maximum time to wait for connection. + * @access protected + */ + protected function openFile($file, &$error) { + return @fopen($file->asString(), 'r'); + } +} + +/** + * Wrapper for TCP/IP socket. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleSocket extends SimpleStickyError { + private $handle; + private $is_open = false; + private $sent = ''; + private $lock_size; + + /** + * Opens a socket for reading and writing. + * @param string $host Hostname to send request to. + * @param integer $port Port on remote machine to open. + * @param integer $timeout Connection timeout in seconds. + * @param integer $block_size Size of chunk to read. + * @access public + */ + function __construct($host, $port, $timeout, $block_size = 255) { + parent::__construct(); + if (! ($this->handle = $this->openSocket($host, $port, $error_number, $error, $timeout))) { + $this->setError("Cannot open [$host:$port] with [$error] within [$timeout] seconds"); + return; + } + $this->is_open = true; + $this->block_size = $block_size; + SimpleTestCompatibility::setTimeout($this->handle, $timeout); + } + + /** + * Writes some data to the socket and saves alocal copy. + * @param string $message String to send to socket. + * @return boolean True if successful. + * @access public + */ + function write($message) { + if ($this->isError() || ! $this->isOpen()) { + return false; + } + $count = fwrite($this->handle, $message); + if (! $count) { + if ($count === false) { + $this->setError('Cannot write to socket'); + $this->close(); + } + return false; + } + fflush($this->handle); + $this->sent .= $message; + return true; + } + + /** + * Reads data from the socket. The error suppresion + * is a workaround for PHP4 always throwing a warning + * with a secure socket. + * @return integer/boolean Incoming bytes. False + * on error. + * @access public + */ + function read() { + if ($this->isError() || ! $this->isOpen()) { + return false; + } + $raw = @fread($this->handle, $this->block_size); + if ($raw === false) { + $this->setError('Cannot read from socket'); + $this->close(); + } + return $raw; + } + + /** + * Accessor for socket open state. + * @return boolean True if open. + * @access public + */ + function isOpen() { + return $this->is_open; + } + + /** + * Closes the socket preventing further reads. + * Cannot be reopened once closed. + * @return boolean True if successful. + * @access public + */ + function close() { + $this->is_open = false; + return fclose($this->handle); + } + + /** + * Accessor for content so far. + * @return string Bytes sent only. + * @access public + */ + function getSent() { + return $this->sent; + } + + /** + * Actually opens the low level socket. + * @param string $host Host to connect to. + * @param integer $port Port on host. + * @param integer $error_number Recipient of error code. + * @param string $error Recipoent of error message. + * @param integer $timeout Maximum time to wait for connection. + * @access protected + */ + protected function openSocket($host, $port, &$error_number, &$error, $timeout) { + return @fsockopen($host, $port, $error_number, $error, $timeout); + } +} + +/** + * Wrapper for TCP/IP socket over TLS. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleSecureSocket extends SimpleSocket { + + /** + * Opens a secure socket for reading and writing. + * @param string $host Hostname to send request to. + * @param integer $port Port on remote machine to open. + * @param integer $timeout Connection timeout in seconds. + * @access public + */ + function __construct($host, $port, $timeout) { + parent::__construct($host, $port, $timeout); + } + + /** + * Actually opens the low level socket. + * @param string $host Host to connect to. + * @param integer $port Port on host. + * @param integer $error_number Recipient of error code. + * @param string $error Recipient of error message. + * @param integer $timeout Maximum time to wait for connection. + * @access protected + */ + function openSocket($host, $port, &$error_number, &$error, $timeout) { + return parent::openSocket("tls://$host", $port, $error_number, $error, $timeout); + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/tag.php b/videodb/test/simpletest/tag.php new file mode 100644 index 0000000..d58cd19 --- /dev/null +++ b/videodb/test/simpletest/tag.php @@ -0,0 +1,1527 @@ + 'SimpleAnchorTag', + 'title' => 'SimpleTitleTag', + 'base' => 'SimpleBaseTag', + 'button' => 'SimpleButtonTag', + 'textarea' => 'SimpleTextAreaTag', + 'option' => 'SimpleOptionTag', + 'label' => 'SimpleLabelTag', + 'form' => 'SimpleFormTag', + 'frame' => 'SimpleFrameTag'); + $attributes = $this->keysToLowerCase($attributes); + if (array_key_exists($name, $map)) { + $tag_class = $map[$name]; + return new $tag_class($attributes); + } elseif ($name == 'select') { + return $this->createSelectionTag($attributes); + } elseif ($name == 'input') { + return $this->createInputTag($attributes); + } + return new SimpleTag($name, $attributes); + } + + /** + * Factory for selection fields. + * @param hash $attributes Element attributes. + * @return SimpleTag Tag object. + * @access protected + */ + protected function createSelectionTag($attributes) { + if (isset($attributes['multiple'])) { + return new MultipleSelectionTag($attributes); + } + return new SimpleSelectionTag($attributes); + } + + /** + * Factory for input tags. + * @param hash $attributes Element attributes. + * @return SimpleTag Tag object. + * @access protected + */ + protected function createInputTag($attributes) { + if (! isset($attributes['type'])) { + return new SimpleTextTag($attributes); + } + $type = strtolower(trim($attributes['type'])); + $map = array( + 'submit' => 'SimpleSubmitTag', + 'image' => 'SimpleImageSubmitTag', + 'checkbox' => 'SimpleCheckboxTag', + 'radio' => 'SimpleRadioButtonTag', + 'text' => 'SimpleTextTag', + 'hidden' => 'SimpleTextTag', + 'password' => 'SimpleTextTag', + 'file' => 'SimpleUploadTag'); + if (array_key_exists($type, $map)) { + $tag_class = $map[$type]; + return new $tag_class($attributes); + } + return false; + } + + /** + * Make the keys lower case for case insensitive look-ups. + * @param hash $map Hash to convert. + * @return hash Unchanged values, but keys lower case. + * @access private + */ + protected function keysToLowerCase($map) { + $lower = array(); + foreach ($map as $key => $value) { + $lower[strtolower($key)] = $value; + } + return $lower; + } +} + +/** + * HTML or XML tag. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTag { + private $name; + private $attributes; + private $content; + + /** + * Starts with a named tag with attributes only. + * @param string $name Tag name. + * @param hash $attributes Attribute names and + * string values. Note that + * the keys must have been + * converted to lower case. + */ + function __construct($name, $attributes) { + $this->name = strtolower(trim($name)); + $this->attributes = $attributes; + $this->content = ''; + } + + /** + * Check to see if the tag can have both start and + * end tags with content in between. + * @return boolean True if content allowed. + * @access public + */ + function expectEndTag() { + return true; + } + + /** + * The current tag should not swallow all content for + * itself as it's searchable page content. Private + * content tags are usually widgets that contain default + * values. + * @return boolean False as content is available + * to other tags by default. + * @access public + */ + function isPrivateContent() { + return false; + } + + /** + * Appends string content to the current content. + * @param string $content Additional text. + * @access public + */ + function addContent($content) { + $this->content .= (string)$content; + return $this; + } + + /** + * Adds an enclosed tag to the content. + * @param SimpleTag $tag New tag. + * @access public + */ + function addTag($tag) { + } + + /** + * Adds multiple enclosed tags to the content. + * @param array List of SimpleTag objects to be added. + */ + function addTags($tags) { + foreach ($tags as $tag) { + $this->addTag($tag); + } + } + + /** + * Accessor for tag name. + * @return string Name of tag. + * @access public + */ + function getTagName() { + return $this->name; + } + + /** + * List of legal child elements. + * @return array List of element names. + * @access public + */ + function getChildElements() { + return array(); + } + + /** + * Accessor for an attribute. + * @param string $label Attribute name. + * @return string Attribute value. + * @access public + */ + function getAttribute($label) { + $label = strtolower($label); + if (! isset($this->attributes[$label])) { + return false; + } + return (string)$this->attributes[$label]; + } + + /** + * Sets an attribute. + * @param string $label Attribute name. + * @return string $value New attribute value. + * @access protected + */ + protected function setAttribute($label, $value) { + $this->attributes[strtolower($label)] = $value; + } + + /** + * Accessor for the whole content so far. + * @return string Content as big raw string. + * @access public + */ + function getContent() { + return $this->content; + } + + /** + * Accessor for content reduced to visible text. Acts + * like a text mode browser, normalising space and + * reducing images to their alt text. + * @return string Content as plain text. + * @access public + */ + function getText() { + return SimplePage::normalise($this->content); + } + + /** + * Test to see if id attribute matches. + * @param string $id ID to test against. + * @return boolean True on match. + * @access public + */ + function isId($id) { + return ($this->getAttribute('id') == $id); + } +} + +/** + * Base url. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleBaseTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('base', $attributes); + } + + /** + * Base tag is not a block tag. + * @return boolean false + * @access public + */ + function expectEndTag() { + return false; + } +} + +/** + * Page title. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTitleTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('title', $attributes); + } +} + +/** + * Link. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleAnchorTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('a', $attributes); + } + + /** + * Accessor for URL as string. + * @return string Coerced as string. + * @access public + */ + function getHref() { + $url = $this->getAttribute('href'); + if (is_bool($url)) { + $url = ''; + } + return $url; + } +} + +/** + * Form element. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleWidget extends SimpleTag { + private $value; + private $label; + private $is_set; + + /** + * Starts with a named tag with attributes only. + * @param string $name Tag name. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($name, $attributes) { + parent::__construct($name, $attributes); + $this->value = false; + $this->label = false; + $this->is_set = false; + } + + /** + * Accessor for name submitted as the key in + * GET/POST privateiables hash. + * @return string Parsed value. + * @access public + */ + function getName() { + return $this->getAttribute('name'); + } + + /** + * Accessor for default value parsed with the tag. + * @return string Parsed value. + * @access public + */ + function getDefault() { + return $this->getAttribute('value'); + } + + /** + * Accessor for currently set value or default if + * none. + * @return string Value set by form or default + * if none. + * @access public + */ + function getValue() { + if (! $this->is_set) { + return $this->getDefault(); + } + return $this->value; + } + + /** + * Sets the current form element value. + * @param string $value New value. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + $this->value = $value; + $this->is_set = true; + return true; + } + + /** + * Resets the form element value back to the + * default. + * @access public + */ + function resetValue() { + $this->is_set = false; + } + + /** + * Allows setting of a label externally, say by a + * label tag. + * @param string $label Label to attach. + * @access public + */ + function setLabel($label) { + $this->label = trim($label); + return $this; + } + + /** + * Reads external or internal label. + * @param string $label Label to test. + * @return boolean True is match. + * @access public + */ + function isLabel($label) { + return $this->label == trim($label); + } + + /** + * Dispatches the value into the form encoded packet. + * @param SimpleEncoding $encoding Form packet. + * @access public + */ + function write($encoding) { + if ($this->getName()) { + $encoding->add($this->getName(), $this->getValue()); + } + } +} + +/** + * Text, password and hidden field. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTextTag extends SimpleWidget { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + if ($this->getAttribute('value') === false) { + $this->setAttribute('value', ''); + } + } + + /** + * Tag contains no content. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * Sets the current form element value. Cannot + * change the value of a hidden field. + * @param string $value New value. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + if ($this->getAttribute('type') == 'hidden') { + return false; + } + return parent::setValue($value); + } +} + +/** + * Submit button as input tag. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleSubmitTag extends SimpleWidget { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + if ($this->getAttribute('value') === false) { + $this->setAttribute('value', 'Submit'); + } + } + + /** + * Tag contains no end element. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * Disables the setting of the button value. + * @param string $value Ignored. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + return false; + } + + /** + * Value of browser visible text. + * @return string Visible label. + * @access public + */ + function getLabel() { + return $this->getValue(); + } + + /** + * Test for a label match when searching. + * @param string $label Label to test. + * @return boolean True on match. + * @access public + */ + function isLabel($label) { + return trim($label) == trim($this->getLabel()); + } +} + +/** + * Image button as input tag. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleImageSubmitTag extends SimpleWidget { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + } + + /** + * Tag contains no end element. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * Disables the setting of the button value. + * @param string $value Ignored. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + return false; + } + + /** + * Value of browser visible text. + * @return string Visible label. + * @access public + */ + function getLabel() { + if ($this->getAttribute('title')) { + return $this->getAttribute('title'); + } + return $this->getAttribute('alt'); + } + + /** + * Test for a label match when searching. + * @param string $label Label to test. + * @return boolean True on match. + * @access public + */ + function isLabel($label) { + return trim($label) == trim($this->getLabel()); + } + + /** + * Dispatches the value into the form encoded packet. + * @param SimpleEncoding $encoding Form packet. + * @param integer $x X coordinate of click. + * @param integer $y Y coordinate of click. + * @access public + */ + function write($encoding, $x = 1, $y = 1) { + if ($this->getName()) { + $encoding->add($this->getName() . '.x', $x); + $encoding->add($this->getName() . '.y', $y); + } else { + $encoding->add('x', $x); + $encoding->add('y', $y); + } + } +} + +/** + * Submit button as button tag. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleButtonTag extends SimpleWidget { + + /** + * Starts with a named tag with attributes only. + * Defaults are very browser dependent. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('button', $attributes); + } + + /** + * Check to see if the tag can have both start and + * end tags with content in between. + * @return boolean True if content allowed. + * @access public + */ + function expectEndTag() { + return true; + } + + /** + * Disables the setting of the button value. + * @param string $value Ignored. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + return false; + } + + /** + * Value of browser visible text. + * @return string Visible label. + * @access public + */ + function getLabel() { + return $this->getContent(); + } + + /** + * Test for a label match when searching. + * @param string $label Label to test. + * @return boolean True on match. + * @access public + */ + function isLabel($label) { + return trim($label) == trim($this->getLabel()); + } +} + +/** + * Content tag for text area. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTextAreaTag extends SimpleWidget { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('textarea', $attributes); + } + + /** + * Accessor for starting value. + * @return string Parsed value. + * @access public + */ + function getDefault() { + return $this->wrap(html_entity_decode($this->getContent(), ENT_QUOTES)); + } + + /** + * Applies word wrapping if needed. + * @param string $value New value. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + return parent::setValue($this->wrap($value)); + } + + /** + * Test to see if text should be wrapped. + * @return boolean True if wrapping on. + * @access private + */ + function wrapIsEnabled() { + if ($this->getAttribute('cols')) { + $wrap = $this->getAttribute('wrap'); + if (($wrap == 'physical') || ($wrap == 'hard')) { + return true; + } + } + return false; + } + + /** + * Performs the formatting that is peculiar to + * this tag. There is strange behaviour in this + * one, including stripping a leading new line. + * Go figure. I am using Firefox as a guide. + * @param string $text Text to wrap. + * @return string Text wrapped with carriage + * returns and line feeds + * @access private + */ + protected function wrap($text) { + $text = str_replace("\r\r\n", "\r\n", str_replace("\n", "\r\n", $text)); + $text = str_replace("\r\n\n", "\r\n", str_replace("\r", "\r\n", $text)); + if (strncmp($text, "\r\n", strlen("\r\n")) == 0) { + $text = substr($text, strlen("\r\n")); + } + if ($this->wrapIsEnabled()) { + return wordwrap( + $text, + (integer)$this->getAttribute('cols'), + "\r\n"); + } + return $text; + } + + /** + * The content of textarea is not part of the page. + * @return boolean True. + * @access public + */ + function isPrivateContent() { + return true; + } +} + +/** + * File upload widget. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleUploadTag extends SimpleWidget { + + /** + * Starts with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + } + + /** + * Tag contains no content. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * Dispatches the value into the form encoded packet. + * @param SimpleEncoding $encoding Form packet. + * @access public + */ + function write($encoding) { + if (! file_exists($this->getValue())) { + return; + } + $encoding->attach( + $this->getName(), + implode('', file($this->getValue())), + basename($this->getValue())); + } +} + +/** + * Drop down widget. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleSelectionTag extends SimpleWidget { + private $options; + private $choice; + + /** + * Starts with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('select', $attributes); + $this->options = array(); + $this->choice = false; + } + + /** + * Adds an option tag to a selection field. + * @param SimpleOptionTag $tag New option. + * @access public + */ + function addTag($tag) { + if ($tag->getTagName() == 'option') { + $this->options[] = $tag; + } + } + + /** + * Text within the selection element is ignored. + * @param string $content Ignored. + * @access public + */ + function addContent($content) { + return $this; + } + + /** + * Scans options for defaults. If none, then + * the first option is selected. + * @return string Selected field. + * @access public + */ + function getDefault() { + for ($i = 0, $count = count($this->options); $i < $count; $i++) { + if ($this->options[$i]->getAttribute('selected') !== false) { + return $this->options[$i]->getDefault(); + } + } + if ($count > 0) { + return $this->options[0]->getDefault(); + } + return ''; + } + + /** + * Can only set allowed values. + * @param string $value New choice. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + for ($i = 0, $count = count($this->options); $i < $count; $i++) { + if ($this->options[$i]->isValue($value)) { + $this->choice = $i; + return true; + } + } + return false; + } + + /** + * Accessor for current selection value. + * @return string Value attribute or + * content of opton. + * @access public + */ + function getValue() { + if ($this->choice === false) { + return $this->getDefault(); + } + return $this->options[$this->choice]->getValue(); + } +} + +/** + * Drop down widget. + * @package SimpleTest + * @subpackage WebTester + */ +class MultipleSelectionTag extends SimpleWidget { + private $options; + private $values; + + /** + * Starts with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('select', $attributes); + $this->options = array(); + $this->values = false; + } + + /** + * Adds an option tag to a selection field. + * @param SimpleOptionTag $tag New option. + * @access public + */ + function addTag($tag) { + if ($tag->getTagName() == 'option') { + $this->options[] = &$tag; + } + } + + /** + * Text within the selection element is ignored. + * @param string $content Ignored. + * @access public + */ + function addContent($content) { + return $this; + } + + /** + * Scans options for defaults to populate the + * value array(). + * @return array Selected fields. + * @access public + */ + function getDefault() { + $default = array(); + for ($i = 0, $count = count($this->options); $i < $count; $i++) { + if ($this->options[$i]->getAttribute('selected') !== false) { + $default[] = $this->options[$i]->getDefault(); + } + } + return $default; + } + + /** + * Can only set allowed values. Any illegal value + * will result in a failure, but all correct values + * will be set. + * @param array $desired New choices. + * @return boolean True if all allowed. + * @access public + */ + function setValue($desired) { + $achieved = array(); + foreach ($desired as $value) { + $success = false; + for ($i = 0, $count = count($this->options); $i < $count; $i++) { + if ($this->options[$i]->isValue($value)) { + $achieved[] = $this->options[$i]->getValue(); + $success = true; + break; + } + } + if (! $success) { + return false; + } + } + $this->values = $achieved; + return true; + } + + /** + * Accessor for current selection value. + * @return array List of currently set options. + * @access public + */ + function getValue() { + if ($this->values === false) { + return $this->getDefault(); + } + return $this->values; + } +} + +/** + * Option for selection field. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleOptionTag extends SimpleWidget { + + /** + * Stashes the attributes. + */ + function __construct($attributes) { + parent::__construct('option', $attributes); + } + + /** + * Does nothing. + * @param string $value Ignored. + * @return boolean Not allowed. + * @access public + */ + function setValue($value) { + return false; + } + + /** + * Test to see if a value matches the option. + * @param string $compare Value to compare with. + * @return boolean True if possible match. + * @access public + */ + function isValue($compare) { + $compare = trim($compare); + if (trim($this->getValue()) == $compare) { + return true; + } + return trim(strip_tags($this->getContent())) == $compare; + } + + /** + * Accessor for starting value. Will be set to + * the option label if no value exists. + * @return string Parsed value. + * @access public + */ + function getDefault() { + if ($this->getAttribute('value') === false) { + return strip_tags($this->getContent()); + } + return $this->getAttribute('value'); + } + + /** + * The content of options is not part of the page. + * @return boolean True. + * @access public + */ + function isPrivateContent() { + return true; + } +} + +/** + * Radio button. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleRadioButtonTag extends SimpleWidget { + + /** + * Stashes the attributes. + * @param array $attributes Hash of attributes. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + if ($this->getAttribute('value') === false) { + $this->setAttribute('value', 'on'); + } + } + + /** + * Tag contains no content. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * The only allowed value sn the one in the + * "value" attribute. + * @param string $value New value. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + if ($value === false) { + return parent::setValue($value); + } + if ($value != $this->getAttribute('value')) { + return false; + } + return parent::setValue($value); + } + + /** + * Accessor for starting value. + * @return string Parsed value. + * @access public + */ + function getDefault() { + if ($this->getAttribute('checked') !== false) { + return $this->getAttribute('value'); + } + return false; + } +} + +/** + * Checkbox widget. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleCheckboxTag extends SimpleWidget { + + /** + * Starts with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('input', $attributes); + if ($this->getAttribute('value') === false) { + $this->setAttribute('value', 'on'); + } + } + + /** + * Tag contains no content. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } + + /** + * The only allowed value in the one in the + * "value" attribute. The default for this + * attribute is "on". If this widget is set to + * true, then the usual value will be taken. + * @param string $value New value. + * @return boolean True if allowed. + * @access public + */ + function setValue($value) { + if ($value === false) { + return parent::setValue($value); + } + if ($value === true) { + return parent::setValue($this->getAttribute('value')); + } + if ($value != $this->getAttribute('value')) { + return false; + } + return parent::setValue($value); + } + + /** + * Accessor for starting value. The default + * value is "on". + * @return string Parsed value. + * @access public + */ + function getDefault() { + if ($this->getAttribute('checked') !== false) { + return $this->getAttribute('value'); + } + return false; + } +} + +/** + * A group of multiple widgets with some shared behaviour. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleTagGroup { + private $widgets = array(); + + /** + * Adds a tag to the group. + * @param SimpleWidget $widget + * @access public + */ + function addWidget($widget) { + $this->widgets[] = $widget; + } + + /** + * Accessor to widget set. + * @return array All widgets. + * @access protected + */ + protected function &getWidgets() { + return $this->widgets; + } + + /** + * Accessor for an attribute. + * @param string $label Attribute name. + * @return boolean Always false. + * @access public + */ + function getAttribute($label) { + return false; + } + + /** + * Fetches the name for the widget from the first + * member. + * @return string Name of widget. + * @access public + */ + function getName() { + if (count($this->widgets) > 0) { + return $this->widgets[0]->getName(); + } + } + + /** + * Scans the widgets for one with the appropriate + * ID field. + * @param string $id ID value to try. + * @return boolean True if matched. + * @access public + */ + function isId($id) { + for ($i = 0, $count = count($this->widgets); $i < $count; $i++) { + if ($this->widgets[$i]->isId($id)) { + return true; + } + } + return false; + } + + /** + * Scans the widgets for one with the appropriate + * attached label. + * @param string $label Attached label to try. + * @return boolean True if matched. + * @access public + */ + function isLabel($label) { + for ($i = 0, $count = count($this->widgets); $i < $count; $i++) { + if ($this->widgets[$i]->isLabel($label)) { + return true; + } + } + return false; + } + + /** + * Dispatches the value into the form encoded packet. + * @param SimpleEncoding $encoding Form packet. + * @access public + */ + function write($encoding) { + $encoding->add($this->getName(), $this->getValue()); + } +} + +/** + * A group of tags with the same name within a form. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleCheckboxGroup extends SimpleTagGroup { + + /** + * Accessor for current selected widget or false + * if none. + * @return string/array Widget values or false if none. + * @access public + */ + function getValue() { + $values = array(); + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if ($widgets[$i]->getValue() !== false) { + $values[] = $widgets[$i]->getValue(); + } + } + return $this->coerceValues($values); + } + + /** + * Accessor for starting value that is active. + * @return string/array Widget values or false if none. + * @access public + */ + function getDefault() { + $values = array(); + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if ($widgets[$i]->getDefault() !== false) { + $values[] = $widgets[$i]->getDefault(); + } + } + return $this->coerceValues($values); + } + + /** + * Accessor for current set values. + * @param string/array/boolean $values Either a single string, a + * hash or false for nothing set. + * @return boolean True if all values can be set. + * @access public + */ + function setValue($values) { + $values = $this->makeArray($values); + if (! $this->valuesArePossible($values)) { + return false; + } + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + $possible = $widgets[$i]->getAttribute('value'); + if (in_array($widgets[$i]->getAttribute('value'), $values)) { + $widgets[$i]->setValue($possible); + } else { + $widgets[$i]->setValue(false); + } + } + return true; + } + + /** + * Tests to see if a possible value set is legal. + * @param string/array/boolean $values Either a single string, a + * hash or false for nothing set. + * @return boolean False if trying to set a + * missing value. + * @access private + */ + protected function valuesArePossible($values) { + $matches = array(); + $widgets = &$this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + $possible = $widgets[$i]->getAttribute('value'); + if (in_array($possible, $values)) { + $matches[] = $possible; + } + } + return ($values == $matches); + } + + /** + * Converts the output to an appropriate format. This means + * that no values is false, a single value is just that + * value and only two or more are contained in an array. + * @param array $values List of values of widgets. + * @return string/array/boolean Expected format for a tag. + * @access private + */ + protected function coerceValues($values) { + if (count($values) == 0) { + return false; + } elseif (count($values) == 1) { + return $values[0]; + } else { + return $values; + } + } + + /** + * Converts false or string into array. The opposite of + * the coercian method. + * @param string/array/boolean $value A single item is converted + * to a one item list. False + * gives an empty list. + * @return array List of values, possibly empty. + * @access private + */ + protected function makeArray($value) { + if ($value === false) { + return array(); + } + if (is_string($value)) { + return array($value); + } + return $value; + } +} + +/** + * A group of tags with the same name within a form. + * Used for radio buttons. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleRadioGroup extends SimpleTagGroup { + + /** + * Each tag is tried in turn until one is + * successfully set. The others will be + * unchecked if successful. + * @param string $value New value. + * @return boolean True if any allowed. + * @access public + */ + function setValue($value) { + if (! $this->valueIsPossible($value)) { + return false; + } + $index = false; + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if (! $widgets[$i]->setValue($value)) { + $widgets[$i]->setValue(false); + } + } + return true; + } + + /** + * Tests to see if a value is allowed. + * @param string Attempted value. + * @return boolean True if a valid value. + * @access private + */ + protected function valueIsPossible($value) { + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if ($widgets[$i]->getAttribute('value') == $value) { + return true; + } + } + return false; + } + + /** + * Accessor for current selected widget or false + * if none. + * @return string/boolean Value attribute or + * content of opton. + * @access public + */ + function getValue() { + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if ($widgets[$i]->getValue() !== false) { + return $widgets[$i]->getValue(); + } + } + return false; + } + + /** + * Accessor for starting value that is active. + * @return string/boolean Value of first checked + * widget or false if none. + * @access public + */ + function getDefault() { + $widgets = $this->getWidgets(); + for ($i = 0, $count = count($widgets); $i < $count; $i++) { + if ($widgets[$i]->getDefault() !== false) { + return $widgets[$i]->getDefault(); + } + } + return false; + } +} + +/** + * Tag to keep track of labels. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleLabelTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('label', $attributes); + } + + /** + * Access for the ID to attach the label to. + * @return string For attribute. + * @access public + */ + function getFor() { + return $this->getAttribute('for'); + } +} + +/** + * Tag to aid parsing the form. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleFormTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('form', $attributes); + } +} + +/** + * Tag to aid parsing the frames in a page. + * @package SimpleTest + * @subpackage WebTester + */ +class SimpleFrameTag extends SimpleTag { + + /** + * Starts with a named tag with attributes only. + * @param hash $attributes Attribute names and + * string values. + */ + function __construct($attributes) { + parent::__construct('frame', $attributes); + } + + /** + * Tag contains no content. + * @return boolean False. + * @access public + */ + function expectEndTag() { + return false; + } +} +?> \ No newline at end of file diff --git a/videodb/test/simpletest/test_case.php b/videodb/test/simpletest/test_case.php new file mode 100644 index 0000000..a31609c --- /dev/null +++ b/videodb/test/simpletest/test_case.php @@ -0,0 +1,658 @@ +label = $label; + } + } + + /** + * Accessor for the test name for subclasses. + * @return string Name of the test. + * @access public + */ + function getLabel() { + return $this->label ? $this->label : get_class($this); + } + + /** + * This is a placeholder for skipping tests. In this + * method you place skipIf() and skipUnless() calls to + * set the skipping state. + * @access public + */ + function skip() { + } + + /** + * Will issue a message to the reporter and tell the test + * case to skip if the incoming flag is true. + * @param string $should_skip Condition causing the tests to be skipped. + * @param string $message Text of skip condition. + * @access public + */ + function skipIf($should_skip, $message = '%s') { + if ($should_skip && ! $this->should_skip) { + $this->should_skip = true; + $message = sprintf($message, 'Skipping [' . get_class($this) . ']'); + $this->reporter->paintSkip($message . $this->getAssertionLine()); + } + } + + /** + * Accessor for the private variable $_shoud_skip + * @access public + */ + function shouldSkip() { + return $this->should_skip; + } + + /** + * Will issue a message to the reporter and tell the test + * case to skip if the incoming flag is false. + * @param string $shouldnt_skip Condition causing the tests to be run. + * @param string $message Text of skip condition. + * @access public + */ + function skipUnless($shouldnt_skip, $message = false) { + $this->skipIf(! $shouldnt_skip, $message); + } + + /** + * Used to invoke the single tests. + * @return SimpleInvoker Individual test runner. + * @access public + */ + function createInvoker() { + return new SimpleErrorTrappingInvoker( + new SimpleExceptionTrappingInvoker(new SimpleInvoker($this))); + } + + /** + * Uses reflection to run every method within itself + * starting with the string "test" unless a method + * is specified. + * @param SimpleReporter $reporter Current test reporter. + * @return boolean True if all tests passed. + * @access public + */ + function run($reporter) { + $context = SimpleTest::getContext(); + $context->setTest($this); + $context->setReporter($reporter); + $this->reporter = $reporter; + $started = false; + foreach ($this->getTests() as $method) { + if ($reporter->shouldInvoke($this->getLabel(), $method)) { + $this->skip(); + if ($this->should_skip) { + break; + } + if (! $started) { + $reporter->paintCaseStart($this->getLabel()); + $started = true; + } + $invoker = $this->reporter->createInvoker($this->createInvoker()); + $invoker->before($method); + $invoker->invoke($method); + $invoker->after($method); + } + } + if ($started) { + $reporter->paintCaseEnd($this->getLabel()); + } + unset($this->reporter); + $context->setTest(null); + return $reporter->getStatus(); + } + + /** + * Gets a list of test names. Normally that will + * be all internal methods that start with the + * name "test". This method should be overridden + * if you want a different rule. + * @return array List of test names. + * @access public + */ + function getTests() { + $methods = array(); + foreach (get_class_methods(get_class($this)) as $method) { + if ($this->isTest($method)) { + $methods[] = $method; + } + } + return $methods; + } + + /** + * Tests to see if the method is a test that should + * be run. Currently any method that starts with 'test' + * is a candidate unless it is the constructor. + * @param string $method Method name to try. + * @return boolean True if test method. + * @access protected + */ + protected function isTest($method) { + if (strtolower(substr($method, 0, 4)) == 'test') { + return ! SimpleTestCompatibility::isA($this, strtolower($method)); + } + return false; + } + + /** + * Announces the start of the test. + * @param string $method Test method just started. + * @access public + */ + function before($method) { + $this->reporter->paintMethodStart($method); + $this->observers = array(); + } + + /** + * Sets up unit test wide variables at the start + * of each test method. To be overridden in + * actual user test cases. + * @access public + */ + function setUp() { + } + + /** + * Clears the data set in the setUp() method call. + * To be overridden by the user in actual user test cases. + * @access public + */ + function tearDown() { + } + + /** + * Announces the end of the test. Includes private clean up. + * @param string $method Test method just finished. + * @access public + */ + function after($method) { + for ($i = 0; $i < count($this->observers); $i++) { + $this->observers[$i]->atTestEnd($method, $this); + } + $this->reporter->paintMethodEnd($method); + } + + /** + * Sets up an observer for the test end. + * @param object $observer Must have atTestEnd() + * method. + * @access public + */ + function tell($observer) { + $this->observers[] = &$observer; + } + + /** + * @deprecated + */ + function pass($message = "Pass") { + if (! isset($this->reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->reporter->paintPass( + $message . $this->getAssertionLine()); + return true; + } + + /** + * Sends a fail event with a message. + * @param string $message Message to send. + * @access public + */ + function fail($message = "Fail") { + if (! isset($this->reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->reporter->paintFail( + $message . $this->getAssertionLine()); + return false; + } + + /** + * Formats a PHP error and dispatches it to the + * reporter. + * @param integer $severity PHP error code. + * @param string $message Text of error. + * @param string $file File error occoured in. + * @param integer $line Line number of error. + * @access public + */ + function error($severity, $message, $file, $line) { + if (! isset($this->reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->reporter->paintError( + "Unexpected PHP error [$message] severity [$severity] in [$file line $line]"); + } + + /** + * Formats an exception and dispatches it to the + * reporter. + * @param Exception $exception Object thrown. + * @access public + */ + function exception($exception) { + $this->reporter->paintException($exception); + } + + /** + * For user defined expansion of the available messages. + * @param string $type Tag for sorting the signals. + * @param mixed $payload Extra user specific information. + */ + function signal($type, $payload) { + if (! isset($this->reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->reporter->paintSignal($type, $payload); + } + + /** + * Runs an expectation directly, for extending the + * tests with new expectation classes. + * @param SimpleExpectation $expectation Expectation subclass. + * @param mixed $compare Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assert($expectation, $compare, $message = '%s') { + if ($expectation->test($compare)) { + return $this->pass(sprintf( + $message, + $expectation->overlayMessage($compare, $this->reporter->getDumper()))); + } else { + return $this->fail(sprintf( + $message, + $expectation->overlayMessage($compare, $this->reporter->getDumper()))); + } + } + + /** + * Uses a stack trace to find the line of an assertion. + * @return string Line number of first assert* + * method embedded in format string. + * @access public + */ + function getAssertionLine() { + $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip')); + return $trace->traceMethod(); + } + + /** + * Sends a formatted dump of a variable to the + * test suite for those emergency debugging + * situations. + * @param mixed $variable Variable to display. + * @param string $message Message to display. + * @return mixed The original variable. + * @access public + */ + function dump($variable, $message = false) { + $dumper = $this->reporter->getDumper(); + $formatted = $dumper->dump($variable); + if ($message) { + $formatted = $message . "\n" . $formatted; + } + $this->reporter->paintFormattedMessage($formatted); + return $variable; + } + + /** + * Accessor for the number of subtests including myelf. + * @return integer Number of test cases. + * @access public + */ + function getSize() { + return 1; + } +} + +/** + * Helps to extract test cases automatically from a file. + * @package SimpleTest + * @subpackage UnitTester + */ +class SimpleFileLoader { + + /** + * Builds a test suite from a library of test cases. + * The new suite is composed into this one. + * @param string $test_file File name of library with + * test case classes. + * @return TestSuite The new test suite. + * @access public + */ + function load($test_file) { + $existing_classes = get_declared_classes(); + $existing_globals = get_defined_vars(); + include_once($test_file); + $new_globals = get_defined_vars(); + $this->makeFileVariablesGlobal($existing_globals, $new_globals); + $new_classes = array_diff(get_declared_classes(), $existing_classes); + if (empty($new_classes)) { + $new_classes = $this->scrapeClassesFromFile($test_file); + } + $classes = $this->selectRunnableTests($new_classes); + return $this->createSuiteFromClasses($test_file, $classes); + } + + /** + * Imports new variables into the global namespace. + * @param hash $existing Variables before the file was loaded. + * @param hash $new Variables after the file was loaded. + * @access private + */ + protected function makeFileVariablesGlobal($existing, $new) { + $globals = array_diff(array_keys($new), array_keys($existing)); + foreach ($globals as $global) { + $GLOBALS[$global] = $new[$global]; + } + } + + /** + * Lookup classnames from file contents, in case the + * file may have been included before. + * Note: This is probably too clever by half. Figuring this + * out after a failed test case is going to be tricky for us, + * never mind the user. A test case should not be included + * twice anyway. + * @param string $test_file File name with classes. + * @access private + */ + protected function scrapeClassesFromFile($test_file) { + preg_match_all('~^\s*class\s+(\w+)(\s+(extends|implements)\s+\w+)*\s*\{~mi', + file_get_contents($test_file), + $matches ); + return $matches[1]; + } + + /** + * Calculates the incoming test cases. Skips abstract + * and ignored classes. + * @param array $candidates Candidate classes. + * @return array New classes which are test + * cases that shouldn't be ignored. + * @access public + */ + function selectRunnableTests($candidates) { + $classes = array(); + foreach ($candidates as $class) { + if (TestSuite::getBaseTestCase($class)) { + $reflection = new SimpleReflection($class); + if ($reflection->isAbstract()) { + SimpleTest::ignore($class); + } else { + $classes[] = $class; + } + } + } + return $classes; + } + + /** + * Builds a test suite from a class list. + * @param string $title Title of new group. + * @param array $classes Test classes. + * @return TestSuite Group loaded with the new + * test cases. + * @access public + */ + function createSuiteFromClasses($title, $classes) { + if (count($classes) == 0) { + $suite = new BadTestSuite($title, "No runnable test cases in [$title]"); + return $suite; + } + SimpleTest::ignoreParentsIfIgnored($classes); + $suite = new TestSuite($title); + foreach ($classes as $class) { + if (! SimpleTest::isIgnored($class)) { + $suite->add($class); + } + } + return $suite; + } +} + +/** + * This is a composite test class for combining + * test cases and other RunnableTest classes into + * a group test. + * @package SimpleTest + * @subpackage UnitTester + */ +class TestSuite { + private $label; + private $test_cases; + + /** + * Sets the name of the test suite. + * @param string $label Name sent at the start and end + * of the test. + * @access public + */ + function TestSuite($label = false) { + $this->label = $label; + $this->test_cases = array(); + } + + /** + * Accessor for the test name for subclasses. If the suite + * wraps a single test case the label defaults to the name of that test. + * @return string Name of the test. + * @access public + */ + function getLabel() { + if (! $this->label) { + return ($this->getSize() == 1) ? + get_class($this->test_cases[0]) : get_class($this); + } else { + return $this->label; + } + } + + /** + * Adds a test into the suite by instance or class. The class will + * be instantiated if it's a test suite. + * @param SimpleTestCase $test_case Suite or individual test + * case implementing the + * runnable test interface. + * @access public + */ + function add($test_case) { + if (! is_string($test_case)) { + $this->test_cases[] = $test_case; + } elseif (TestSuite::getBaseTestCase($test_case) == 'testsuite') { + $this->test_cases[] = new $test_case(); + } else { + $this->test_cases[] = $test_case; + } + } + + /** + * Builds a test suite from a library of test cases. + * The new suite is composed into this one. + * @param string $test_file File name of library with + * test case classes. + * @access public + */ + function addFile($test_file) { + $extractor = new SimpleFileLoader(); + $this->add($extractor->load($test_file)); + } + + /** + * Delegates to a visiting collector to add test + * files. + * @param string $path Path to scan from. + * @param SimpleCollector $collector Directory scanner. + * @access public + */ + function collect($path, $collector) { + $collector->collect($this, $path); + } + + /** + * Invokes run() on all of the held test cases, instantiating + * them if necessary. + * @param SimpleReporter $reporter Current test reporter. + * @access public + */ + function run($reporter) { + $reporter->paintGroupStart($this->getLabel(), $this->getSize()); + for ($i = 0, $count = count($this->test_cases); $i < $count; $i++) { + if (is_string($this->test_cases[$i])) { + $class = $this->test_cases[$i]; + $test = new $class(); + $test->run($reporter); + unset($test); + } else { + $this->test_cases[$i]->run($reporter); + } + } + $reporter->paintGroupEnd($this->getLabel()); + return $reporter->getStatus(); + } + + /** + * Number of contained test cases. + * @return integer Total count of cases in the group. + * @access public + */ + function getSize() { + $count = 0; + foreach ($this->test_cases as $case) { + if (is_string($case)) { + if (! SimpleTest::isIgnored($case)) { + $count++; + } + } else { + $count += $case->getSize(); + } + } + return $count; + } + + /** + * Test to see if a class is derived from the + * SimpleTestCase class. + * @param string $class Class name. + * @access public + */ + static function getBaseTestCase($class) { + while ($class = get_parent_class($class)) { + $class = strtolower($class); + if ($class == 'simpletestcase' || $class == 'testsuite') { + return $class; + } + } + return false; + } +} + +/** + * This is a failing group test for when a test suite hasn't + * loaded properly. + * @package SimpleTest + * @subpackage UnitTester + */ +class BadTestSuite { + private $label; + private $error; + + /** + * Sets the name of the test suite and error message. + * @param string $label Name sent at the start and end + * of the test. + * @access public + */ + function BadTestSuite($label, $error) { + $this->label = $label; + $this->error = $error; + } + + /** + * Accessor for the test name for subclasses. + * @return string Name of the test. + * @access public + */ + function getLabel() { + return $this->label; + } + + /** + * Sends a single error to the reporter. + * @param SimpleReporter $reporter Current test reporter. + * @access public + */ + function run($reporter) { + $reporter->paintGroupStart($this->getLabel(), $this->getSize()); + $reporter->paintFail('Bad TestSuite [' . $this->getLabel() . + '] with error [' . $this->error . ']'); + $reporter->paintGroupEnd($this->getLabel()); + return $reporter->getStatus(); + } + + /** + * Number of contained test cases. Always zero. + * @return integer Total count of cases in the group. + * @access public + */ + function getSize() { + return 0; + } +} +?> diff --git a/videodb/test/simpletest/tidy_parser.php b/videodb/test/simpletest/tidy_parser.php new file mode 100644 index 0000000..4e21112 --- /dev/null +++ b/videodb/test/simpletest/tidy_parser.php @@ -0,0 +1,382 @@ +free(); + } + + /** + * Frees up any references so as to allow the PHP garbage + * collection from unset() to work. + */ + private function free() { + unset($this->page); + $this->forms = array(); + $this->labels = array(); + } + + /** + * This builder is only available if the 'tidy' extension is loaded. + * @return boolean True if available. + */ + function can() { + return extension_loaded('tidy'); + } + + /** + * Reads the raw content the page using HTML Tidy. + * @param $response SimpleHttpResponse Fetched response. + * @return SimplePage Newly parsed page. + */ + function parse($response) { + $this->page = new SimplePage($response); + $tidied = tidy_parse_string($input = $this->insertGuards($response->getContent()), + array('output-xml' => false, 'wrap' => '0', 'indent' => 'no'), + 'latin1'); + $this->walkTree($tidied->html()); + $this->attachLabels($this->widgets_by_id, $this->labels); + $this->page->setForms($this->forms); + $page = $this->page; + $this->free(); + return $page; + } + + /** + * Stops HTMLTidy stripping content that we wish to preserve. + * @param string The raw html. + * @return string The html with guard tags inserted. + */ + private function insertGuards($html) { + return $this->insertEmptyTagGuards($this->insertTextareaSimpleWhitespaceGuards($html)); + } + + /** + * Removes the extra content added during the parse stage + * in order to preserve content we don't want stripped + * out by HTMLTidy. + * @param string The raw html. + * @return string The html with guard tags removed. + */ + private function stripGuards($html) { + return $this->stripTextareaWhitespaceGuards($this->stripEmptyTagGuards($html)); + } + + /** + * HTML tidy strips out empty tags such as
              '.$only_gd); +$Examples[] = array('getstrings' => array('src='.$img['landscape'].'&w=300&fltr[]=wmt|phpThumb|18|C|FF0000|loki.ttf|100|5|20&f=png', 'src='.$img['landscape'].'&w=300&fltr[]=wmt|'.rawurlencode('☺♫ǖڞ').'|40|L|FF0000|arial.ttf|100&f=png', 'src='.$img['landscape'].'&w=300&fltr[]=wmt|copyright+'.date('Y').'|3|BR|00FF00||50&f=png', 'src='.$img['landscape'].'&w=300&fltr[]=wmt|copyright+'.date('Y').'%0AphpThumb()|3|L|00FFFF&f=png'), 'description' => 'Text overlay, TTF and built-in fonts, unicode characters (rawurlencoded HTMLentities), multiple lines, metacharacters (height, width)'.$only_gd); +$Examples[] = array('getstrings' => array('src='.$img['landscape'].'&w=300&fltr[]=wmt|thumbnail+=+^Xx^Y|3|BR|00FFFF||50&f=png', 'src='.$img['landscape'].'&w=300&fltr[]=wmt|click%0Ahere%0A^FkkB|10|L|FF00FF|arial.ttf|100|0||333399|50|y&f=png', 'src='.$img['landscape'].'&w=300&fltr[]=wmt|resized:+^Xx^Y+to+^xx^y|10|B|FFFFFF|arial.ttf|100|0||000000|100|x&f=png'), 'description' => 'metacharacters (height, width), background color, background extend'.$only_gd); +$Examples[] = array('getstrings' => array('new=FF0000&w=100&h=50&fltr[]=bvl|10&fltr[]=wmt|hello|14|C|00FFFF|arial.ttf&f=png', 'new=FF0000|25&w=150&h=50&fltr[]=bvl|10&fltr[]=wmt|25%+opaque|14|C|0066FF|arial.ttf&f=png'), 'description' => 'Image created with "new", red background, bevel, TTF text'.$only_gd); + +$Examples[] = array('getstrings' => array('src='.$img['bmp'].'&w=300'), 'description' => 'BMP source, width=300px'); +$Examples[] = array('getstrings' => array('src='.$img['tiff'], 'src='.$img['tiff'].'&w=300&aoe=1'), 'description' => 'TIFF source, width=300px'.$only_im.$only_im_tiff); +$Examples[] = array('getstrings' => array('src='.$img['wmf'].'&w=300'), 'description' => 'WMF source, width=300px'.$only_im); +$Examples[] = array('getstrings' => array('src='.$img['pdf'].'&w=300'), 'description' => 'PDF source, width=300px'.$only_im.$only_gs); +//$Examples[] = array('getstrings' => array(''), 'description' => ''); + +foreach ($Examples as $key => $ExamplesArray) { + echo '#'.$key.''; + echo '
              '; + $text = ''; + foreach ($ExamplesArray['getstrings'] as $dummy => $GETstring) { + if ($GETstring == "\n") { + echo '
              '; + $text .= "\n"; + } else { + echo ''; + $text .= ''."\n"; + } + } + echo '
              '; + echo '
              '.htmlentities($text).'
              '; + echo $ExamplesArray['description'].'
              '; + echo '


              '; +} + +$PATH_INFO_examples = array( + 'fltr[]=sep;200x200;'.$img['portrait'], + 'f=png;fltr[]=wmt|hello;fltr[]=flip|y;fltr[]=wmt|hello;200x100;new=FF00FF', +); + +echo '#pathinfo'; +echo '
              '; +foreach ($PATH_INFO_examples as $key => $value) { + echo ' '; +} +echo '
              '; +echo '
              ';
              +foreach ($PATH_INFO_examples as $key => $value) {
              +	echo htmlentities('  ')."\n";
              +}
              +echo '
              '; +echo 'PATH_INFO example
              '; +echo '


              '; + +?> + + +
              + + + + + + + + + + + + + + + + +
              + Illustration of potential difference between GD1.x and GD2.x
              + In most cases the thumbnails produced by phpThumb() on GD v1.x are perfectly + acceptable, but in some cases it may look ugly. Diagonal lines and reducing a + very large source image increase chance for bad results (the house/sky picture + has both problems). Here are three static examples: +
              GD v2.0.15kayak.jpg generated with phpThumb() on GD v2.0.15bottle.jpg generated with phpThumb() on GD v2.0.15sky.jpg generated with phpThumb() on GD v2.0.15
              GD v1.6.2kayak.jpg generated with phpThumb() on GD v1.6.2bottle.jpg generated with phpThumb() on GD v1.6.2sky.jpg generated with phpThumb() on GD v1.6.2

              +
              +
              + +Demo of phpThumb.demo.showpic.php
              +
              +'; +echo '(mouse-over to see calling parameters)
              '; +echo '
              '; +$SmallParams = array( + 'unmodified' => '', + 'text watermark' => '&fltr[]=wmt|Watermark|20|C|FF0000|arial.ttf|100', +); +foreach ($SmallParams as $description => $moreparams) { + echo ''.htmlentities($description).' '; +} +?> +
              +
              +'; +echo '(mouse-over to see calling parameters)
              '; +echo '
              '; +$BigParams = array( + 'unmodified' => '', + 'width=800' => '&w=800', + 'width=200, grayscale' => '&w=300&fltr[]=gray', +); +foreach ($BigParams as $description => $moreparams) { + echo ''.htmlentities($description).' '; +} +?> +
              +
              +'; +foreach ($img as $key => $value) { + echo '
            5. '.basename($value).'
            6. '; +} +echo '


        '; +?> + + \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.gallery.php b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.gallery.php new file mode 100644 index 0000000..9ad8756 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.gallery.php @@ -0,0 +1,105 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpThumb.demo.gallery.php // +// James Heinrich // +// // +// Demo showing basic usage of phpThumb in a photo gallery // +// // +////////////////////////////////////////////////////////////// +die('For security reasons, this demo is disabled by default. Please comment out line '.__LINE__.' in '.basename(__FILE__)); +?> + + + + phpThumb :: sample photo gallery demo + + +This is a demo of how you can use phpThumb() in an image gallery.
        +
        + $value) { + @list($photo, $caption) = explode("\t", $value); + $CAPTIONS[$photo] = $caption; + } +} + +if (!empty($_REQUEST['pic'])) { + + $alt = @$CAPTIONS[$_REQUEST['pic']] ? $CAPTIONS[$_REQUEST['pic']] : $_REQUEST['pic']; + echo ''.htmlentities($alt, ENT_QUOTES).'
        '; + echo '
        '.htmlentities(@$CAPTIONS[$_REQUEST['pic']]).'
        '; + +} else { + + $currentdir = realpath($docroot.'/'.$imgdir.@$_REQUEST['dir']); + if (!preg_match('#^'.preg_quote($dirlimit).'#', $currentdir)) { + echo 'Cannot browse to "'.htmlentities($currentdir).'"
        '; + } elseif ($dh = @opendir($currentdir)) { + $folders = array(); + $pictures = array(); + while ($file = readdir($dh)) { + if (is_dir($currentdir.'/'.$file) && ($file{0} != '.')) { + $folders[] = $file; + } elseif (preg_match('#\\.(jpe?g|gif|png|bmp|tiff?)$#i', $file)) { + $pictures[] = $file; + } + } + closedir($dh); + if (preg_match('#^'.preg_quote($dirlimit).'#', realpath($currentdir.'/..'))) { + echo 'Parent directory
        '; + } + if (!empty($folders)) { + echo ''; + } + if (!empty($pictures)) { + foreach ($pictures as $file) { + $alt = (!empty($CAPTIONS[$file]) ? $CAPTIONS[$file] : $file); + echo ''.(!empty($CAPTIONS[$file]) ? '' : '').'
        '.htmlentities($CAPTIONS[$file]).'
        '; + if ($use_popup) { + echo ''; + } else { + echo ''; + } + echo ''.htmlentities($alt, ENT_QUOTES).''; + echo '
        '; + } + echo '
        '; + } else { + echo 'No pictures in "'.htmlentities(str_replace(realpath($docroot), '', realpath($docroot.'/'.$imgdir.@$_REQUEST['dir']))).'"'; + } + } else { + echo 'failed to open "'.htmlentities($currentdir).'"'; + } + +} +?> + + \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.php b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.php new file mode 100644 index 0000000..92b9d23 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.php @@ -0,0 +1,85 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpThumb.demo.object.php // +// James Heinrich // +// // +// Example of how to use phpthumb.class.php as an object // +// // +////////////////////////////////////////////////////////////// + +// Note: phpThumb.php is where the caching code is located, if +// you instantiate your own phpThumb() object that code is +// bypassed and it's up to you to handle the reading and +// writing of cached files, if appropriate. + +die('For security reasons, this demo is disabled by default. Please comment out line '.__LINE__.' in '.basename(__FILE__)); + +require_once '../phpthumb.class.php'; + +// create phpThumb object +$phpThumb = new phpThumb(); + +// create 3 sizes of thumbnail +$thumbnail_widths = array(160, 320, 640); +$capture_raw_data = false; // set to true to insert to database rather than render to screen or file (see below) +foreach ($thumbnail_widths as $thumbnail_width) { + // this is very important when using a single object to process multiple images + $phpThumb->resetObject(); + + // set data source -- do this first, any settings must be made AFTER this call + $phpThumb->setSourceFilename('images/loco.jpg'); // for static demo only + //$phpThumb->setSourceFilename($_FILES['userfile']['tmp_name']); + // or $phpThumb->setSourceData($binary_image_data); + // or $phpThumb->setSourceImageResource($gd_image_resource); + + // PLEASE NOTE: + // You must set any relevant config settings here. The phpThumb + // object mode does NOT pull any settings from phpThumb.config.php + //$phpThumb->setParameter('config_document_root', '/home/groups/p/ph/phpthumb/htdocs/'); + //$phpThumb->setParameter('config_cache_directory', '/tmp/persistent/phpthumb/cache/'); + + // set parameters (see "URL Parameters" in phpthumb.readme.txt) + $phpThumb->setParameter('w', $thumbnail_width); + //$phpThumb->setParameter('h', 100); + //$phpThumb->setParameter('fltr', 'gam|1.2'); + //$phpThumb->setParameter('fltr', 'wmi|../watermark.jpg|C|75|20|20'); + + // set options (see phpThumb.config.php) + // here you must preface each option with "config_" + $phpThumb->setParameter('config_output_format', 'jpeg'); + $phpThumb->setParameter('config_imagemagick_path', '/usr/local/bin/convert'); + //$phpThumb->setParameter('config_allow_src_above_docroot', true); // needed if you're working outside DOCUMENT_ROOT, in a temp dir for example + + // generate & output thumbnail + $output_filename = './thumbnails/'.basename($_FILES['userfile']['name']).'_'.$thumbnail_width.'.'.$phpThumb->config_output_format; + if ($phpThumb->GenerateThumbnail()) { // this line is VERY important, do not remove it! + $output_size_x = imagesx($phpThumb->gdimg_output); + $output_size_y = imagesy($phpThumb->gdimg_output); + if ($output_filename || $capture_raw_data) { + if ($capture_raw_data && $phpThumb->RenderOutput()) { + // RenderOutput renders the thumbnail data to $phpThumb->outputImageData, not to a file or the browser + $mysqli->query("INSERT INTO `table` (`thumbnail`) VALUES ('".mysqli_real_escape_string($phpThumb->outputImageData)."') WHERE (`id` = '".mysqli_real_escape_string($id)."')"); + } elseif ($phpThumb->RenderToFile($output_filename)) { + // do something on success + echo 'Successfully rendered:
        '; + } else { + // do something with debug/error messages + echo 'Failed (size='.$thumbnail_width.'):
        '.implode("\n\n", $phpThumb->debugmessages).'
        '; + } + $phpThumb->purgeTempFiles(); + } else { + $phpThumb->OutputThumbnail(); + } + } else { + // do something with debug/error messages + echo 'Failed (size='.$thumbnail_width.').
        '; + echo '
        '.$phpThumb->fatalerror.'
        '; + echo '

        '; + } + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.simple.php b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.simple.php new file mode 100644 index 0000000..9fe0d14 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.object.simple.php @@ -0,0 +1,63 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpThumb.demo.object.simple.php // +// James Heinrich // +// // +// Simplified example of how to use phpthumb.class.php as // +// an object -- please also see phpThumb.demo.object.php // +// // +////////////////////////////////////////////////////////////// + +// Note: phpThumb.php is where the caching code is located, if +// you instantiate your own phpThumb() object that code is +// bypassed and it's up to you to handle the reading and +// writing of cached files, if appropriate. + +die('For security reasons, this demo is disabled by default. Please comment out line '.__LINE__.' in '.basename(__FILE__)); + +require_once '../phpthumb.class.php'; + +// create phpThumb object +$phpThumb = new phpThumb(); + +$thumbnail_width = 100; + +// set data source -- do this first, any settings must be made AFTER this call +if (is_uploaded_file(@$_FILES['userfile']['tmp_name'])) { + $phpThumb->setSourceFilename($_FILES['userfile']['tmp_name']); + $output_filename = './thumbnails/'.basename($_FILES['userfile']['name']).'_'.$thumbnail_width.'.'.$phpThumb->config_output_format; +} else { + $phpThumb->setSourceData(file_get_contents('..\images\disk.jpg')); + $output_filename = './thumbnails/disk_small.jpg'; +} + +// PLEASE NOTE: +// You must set any relevant config settings here. The phpThumb +// object mode does NOT pull any settings from phpThumb.config.php +//$phpThumb->setParameter('config_document_root', '/home/groups/p/ph/phpthumb/htdocs/'); +//$phpThumb->setParameter('config_cache_directory', '/tmp/persistent/phpthumb/cache/'); + +// set parameters (see "URL Parameters" in phpthumb.readme.txt) +$phpThumb->setParameter('w', $thumbnail_width); +//$phpThumb->setParameter('fltr', 'gam|1.2'); +//$phpThumb->setParameter('fltr', 'wmi|../watermark.jpg|C|75|20|20'); + +// generate & output thumbnail +if ($phpThumb->GenerateThumbnail()) { // this line is VERY important, do not remove it! + if ($phpThumb->RenderToFile($output_filename)) { + // do something on success + echo 'Successfully rendered to "'.$output_filename.'"'; + } else { + // do something with debug/error messages + echo 'Failed:
        '.implode("\n\n", $phpThumb->debugmessages).'
        '; + } + $phpThumb->purgeTempFiles(); +} else { + // do something with debug/error messages + echo 'Failed:
        '.$phpThumb->fatalerror."\n\n".implode("\n\n", $phpThumb->debugmessages).'
        '; +} diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.random.php b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.random.php new file mode 100644 index 0000000..47368c7 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.random.php @@ -0,0 +1,96 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpThumb.demo.random.php // +// James Heinrich // +// // +// Display a random image from a specified directory. // +// Run with no parameters for usage instructions. // +// // +////////////////////////////////////////////////////////////// +die('For security reasons, this demo is disabled by default. Please comment out line '.__LINE__.' in '.basename(__FILE__)); + +function SelectRandomImage($dirname='.', $portrait=true, $landscape=true, $square=true) { + // return a random image filename from $dirname + // the last 3 parameters determine what aspect ratio of images + // may be returned + $possibleimages = array(); + if ($dh = opendir($dirname)) { + while ($file = readdir($dh)) { + if (is_file($dirname.'/'.$file) && preg_match('#\\.(jpg|jpeg|gif|png|tiff|bmp)$#i', $file)) { + if ($gis = @getimagesize($dirname.'/'.$file)) { + if ($portrait && ($gis[0] < $gis[1])) { + // portrait + $possibleimages[] = $file; + } elseif ($landscape && ($gis[0] > $gis[1])) { + // landscape + $possibleimages[] = $file; + } elseif ($square) { + // square + $possibleimages[] = $file; + } + } + } + } + closedir($dh); + } + if (empty($possibleimages)) { + return false; + } + if (PHP_VERSION < '4.2.0') { + mt_srand(time()); + } + $randkey = mt_rand(0, count($possibleimages) - 1); + return realpath($dirname.'/'.$possibleimages[$randkey]); +} + +if (@$_REQUEST['dir']) { + if (is_dir($_REQUEST['dir'])) { + + if (!@$_REQUEST['o']) { + $_REQUEST['o'] = 'PLS'; + } + $_REQUEST['o'] = strtoupper($_REQUEST['o']); + $portrait = (strpos(@$_REQUEST['o'], 'P') !== false); + $landscape = (strpos(@$_REQUEST['o'], 'L') !== false); + $square = (strpos(@$_REQUEST['o'], 'S') !== false); + $randomSRC = SelectRandomImage($_REQUEST['dir'], $portrait, $landscape, $square); + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + $randomSRC = str_replace('\\', '/', preg_replace('#^'.realpath(@$_SERVER['DOCUMENT_ROOT']).'#i', '', realpath($randomSRC))); + } else { + $randomSRC = str_replace(realpath(@$_SERVER['DOCUMENT_ROOT']), '', realpath($randomSRC)); + } + + $otherParams = array(); + foreach ($_GET as $key => $value) { + if (($key == 'dir') || ($key == 'o')) { + continue; + } + if (is_array($value)) { + foreach ($value as $vkey => $vvalue) { + $otherParams[] = urlencode($key).'['.urlencode($vkey).']='.urlencode($vvalue); + } + } else { + $otherParams[] = urlencode($key).'='.urlencode($value); + } + } + header('Location: ../phpThumb.php?src='.urlencode($randomSRC).'&'.implode('&', $otherParams)); + exit; + + } else { + echo htmlentities($_REQUEST['dir']).' is not a directory'; + exit; + } + +} else { + + echo 'Usage: '.basename($_SERVER['PHP_SELF']).'?dir=<directory>&<phpThumb parameters>&o=(P|L|S)

        Examples:
          '; + echo '
        • '.basename($_SERVER['PHP_SELF']).'?./images/&o=L (landscape images only)
        • '; + echo '
        • '.basename($_SERVER['PHP_SELF']).'?./images/&o=PS (portrait or square images only)
        • '; + echo '
        '; + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.showpic.php b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.showpic.php new file mode 100644 index 0000000..8fb4328 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/phpThumb.demo.showpic.php @@ -0,0 +1,140 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +// // +// phpThumb.demo.showpic.php // +// James Heinrich // +// 23 Feb 2004 // +// // +// This code is useful for popup pictures (e.g. thumbnails // +// you want to show larger, such as a larger version of a // +// product photo for example) but you don't know the image // +// dimensions before popping up. This script displays the // +// image with no window border, and resizes the window to // +// the size it needs to be (usually better to spawn it // +// large (600x400 for example) and let it auto-resize it // +// smaller), and if the image is larger than 90% of the // +// current screen area the window respawns itself with // +// scrollbars. // +// // +// Usage: // +// window.open('showpic.php?src=big.jpg&title=Big+picture', // +// 'popupwindowname', // +// 'width=600,height=400,menubar=no,toolbar=no') // +// // +// See demo linked from http://phpthumb.sourceforge.net /// +////////////////////////////////////////////////////////////// +die('For security reasons, this demo is disabled by default. Please comment out line '.__LINE__.' in '.basename(__FILE__)); + +$phpThumbLocation = '../phpThumb.php'; +require_once '../phpThumb.config.php'; + +echo ''; +echo ''; +if (isset($_GET['title'])) { + echo ''.htmlentities($_GET['title']).''; + unset($_GET['title']); +} else { + echo ''.htmlentities('phpThumb :: popup window resizing demo').''; +} +?> + + + + + + $value) { + if (!in_array($key, $allowedGETparameters)) { + continue; + } + if (is_array($value)) { + if ($key != 'fltr') { + continue; + } + foreach ($value as $key2 => $value2) { + @$additionalparameters[$key][] = preg_replace('#[^A-Za-z0-9\\. _:/]#', '', $value2); + } + } else { + if ($key == 'src') { + // allow as passed + $additionalparameters[$key] = $value; + } else { + $additionalparameters[$key] = preg_replace('#[^A-Za-z0-9\\. _:/]#', '', $value); + } + } +} +$imagesrc = phpThumbURL($additionalparameters, $phpThumbLocation); + +echo ''; + +echo '
        '; + +if (!empty($_GET['src'])) { + + echo ''; + +} else { + + echo '
        ';
        +	echo 'Usage:

        '.basename(__FILE__).'?src=filename&title=Picture+Title'; + echo '
        '; + +} + +?>
        + \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/demo/readme.demos.txt b/videodb/vendor/james-heinrich/phpthumb/demo/readme.demos.txt new file mode 100644 index 0000000..7bc2569 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/demo/readme.demos.txt @@ -0,0 +1,50 @@ +////////////////////////////////////////////////////////////////// +// phpThumb() by James Heinrich // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////////// +/// // +// phpThumb() included and related demos // +// /// +////////////////////////////////////////////////////////////////// + +The phpThumb() distribution includes several demos, and it +should be self-evident what they do when you run them: + +* phpThumb.demo.check.php - configuration checker, will check + your config file and server configuration and warn of any + potential problems + +* phpThumb.demo.demo.php - shows a wide variety of samples, + basically all the different features and filters of phpThumb. + Note: sample image files are not included in the distribution + but can be downloaded from http://phpthumb.sourceforge.net/demo + +* phpThumb.demo.gallery.php - basic demonstration of a photo + gallery with ability to browse subdirectories. May use + phpThumb.demo.showpic.php for fullsize popup display + +* phpThumb.demo.showpic.php - auto-resizes a popup window to + match the dimensions of the image it is displaying + +* phpThumb.demo.object.php - example of how to call phpThumb + as an object. + +* phpThumb.demo.object.simple.php - simplified version of + phpThumb.demo.object.php with fewer options shown. + +* phpThumb.demo.random.php - select & display a random thumbnail + from a directory of images. + + + +Other people have created useful demos and/or extensions to +phpThumb(). Some of these I know of are: +(also see http://phpthumb.sourceforge.net for an updated list) + +* iManager/iBrowser - http://www.j-cons.com/news/ +* ThumbnailSelector - http://www.silisoftware.com/scripts/ThumbnailSelector +* Applejuice Build_Gallery - http://twofivethreetwo.com/?nav=scripts + +If you know of any others you think should be mentioned here +please let me know: info@silisoftware.com \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.changelog.txt b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.changelog.txt new file mode 100644 index 0000000..3b7dc6b --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.changelog.txt @@ -0,0 +1,1573 @@ +////////////////////////////////////////////////////////////////// +// phpThumb() by James Heinrich // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////////// + +¤ = structure change or important new feature +* = less important new feature or change + +v1.7.19-202210110924 + ¤ #193 Composer - script hsp-generate for generate High Security Password + * [bugfix: #196] PHP8 compatibility + * [bugfix: #191] IMAGETYPE_AVIF not defined + +v1.7.18-202208061319 + ¤ add support for AVIF image format (requires PHP 8.1.0) for native PHP support. + * [bugfix: G190] ensure that all used IMG_ and IMAGETYPE_ constants are always defined. + * [bugfix: G188] fix some potential null-parameter warnings + * [bugfix: G187] PHP8 compatibility + * [bugfix: G185] PHP8 compatibility + * [bugfix: G183] better message auth + +v1.7.17-202109221111 + * [bugfix: G171] problem with class_exists and autoloading + * [bugfix: G173] file_get_contents instead of readfile + * [bugfix: G178] missing quality parameter in webp + * [bugfix: G181] missing webp in ImageCreateFromFilename + +v1.7.16-202012161640 + ¤ PHP 8.0 compatibility + * [bugfix: G168] bad call to $this inside static function + * [bugfix: G167] realPathSafe() issues on Windows + * [bugfix: G164] open_basedir on Windows prevents caching + * [bugfix: G155] PHP 7.4 compatibility + * [bugfix: G154] regex for external image source + +v1.7.15-202004301145 + ¤ PHP 7.4 curly brace substring compatibility + ¤ add basic support for WebP + * [bugfix: G18] fopen() fails when using remote images from SSL + * [bugfix: G65] ImageMagick -flatten vs PNG transparency + * [bugfix: G75] improved InitializeTempDirSetting + * [bugfix: G76] fix memory limit detection + * [bugfix: G78] AllowOutputEnlargement and Zoom-Crop + * [bugfix: G90] missing $PHPTHUMB_CONFIG + * [bugfix: G91] missing config_ prefix in allow_local_http_src + * [bugfix: G93] ImageMagick unsharp amount/threshold + * [bugfix: G94] cannot declare class phpthumb_functions + * [bugfix: G96] language level migration fixes + * [bugfix: G108] add 'line-height' for text in image + * [bugfix: G113] fix PNGs losing transparency + * [bugfix: G123] improved default thumbnail filename + * [bugfix: G126] overlay on transparent image + * [bugfix: G133] fix 'wmi' and 'wmt' filters + * [bugfix: G134] fix fsockopen http status text + * [bugfix: G140] parse_url return user/pass + * [bugfix: G142] allow transparent background when using 'far' + * [bugfix: G144] fix class_exists + * [bugfix: G149] magic quotes deprecated in PHP 7.4.2 + * bugfix: ImageMagick v7+ parameter order (in, op, out) + +v1.7.14-201608101311 + ¤ improved PHP7 compatability + * [bugfix: #1021] demo.gallery updates for phpThumbURL + * [bugfix: G65] PNG transparency + * [bugfix: G70] set a default timezone if one is not set + * [bugfix: G69] overlay margins + * [bugfix: G66] lowercase all PHP function names + * [bugfix: G64] Can't put thumbnail into a frame + * [bugfix: #1014] PDF thumbnail not rendered correctly + * [bugfix: #1008] phpThumbURL accept array parameters + * [bugfix: #988] mysqli support in demo + * [bugfix: G41] fail when memory_limit = -1 + * [bugfix: G35] no temp directory set + * [bugfix: G33] realpath not working on some systems + * [bugfix: #991] ImprovedImageRotate not static + * set Last-Modified header on all requests + * [bugfix: G19] phpThumbURL() rawurlencode + * [bugfix: G26] ImageInterlace in RenderOutput() + * [bugfix: #775] file_exists_ignoreopenbasedir + * escapeshellarg disabled workaround + * [bugfix: #977] $PHPTHUMB_DEFAULTS not working + * [bugfix: G24] enable ImagePNG quality + * [bugfix: G25] escapeshellarg may exist but be disabled + +v1.7.13-201406261000 - June 26, 2014 + * Bugfix: incorrect variable name in phpThumb.php could prevent + all hash values from matching + + +v1.7.12-201406260900 - June 26, 2014 + ¤ high_security_mode enabled by default and strongly recommended + ¤ debug mode must be disabled to allow thumbnail generation + Thanks to Rafay Baloch (www.rafayhackingarticles.net) for + recommending some security configuration changes + * default config_cache_directory_depth is now 2 (was 4) + * Bugfix: temp files used with ImageMagick might not get deleted + from cache directory + * Bugfix (G:17): 'far' parameter not working as intended + * Bugfix (G:16): $PHPTHUMB_DEFAULTS configuration ignored + * Bugfix (G:15): $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE logic wrong + * Bugfix (G:11): config_cache_maxsize does not count wasted space + * Bugfix (G:10): error images ignored relevant config options + * Bugfix (#962): support symlinked subfolders for image source + * Bugfix (#934): source image region (sx/sy/sw/sh) did not work + when using ImageMagick + * Bugfix (#925): add configurable URL separator + * Bugfix (#921): non-static phpUnsharpMask::applyUnsharpMask() + * Bugfix (#920): $PHPTHUMB_DEFAULTS with array value gives + array-to-string PHP error + * Bugfix (#909): unnecessary calls of shell commands when cached + * Bugfix (#618): cached files inside subdirs won't be deleted + * Bugfix (#594): ImageMagick variants not detected (e.g. OpenMP) + + +v1.7.11-201108081344 - August 8, 2011 + ¤ Added even more explicit escaping of command line parameters + ¤ $PHPTHUMB_CONFIG['high_security_password'] needs to be more + complex than before. Previously 5 characters length was + required, now password strength calculated based on character + type (a-z, A-Z, 0-9, other) and count. + See PasswordStrength() function in phpThumb.php + ¤ deliberate delay inserted on failed hash in high_security mode + to slow down brute force attempts + ¤ 'file' paramter removed (use object mode instead) + ¤ 'goto' paramter removed (use object mode instead) + ¤ Disable all demos by default, user will need to comment out a + line to run the demo + ¤ CleanUpCacheDirectory() is now only called once per hour, and + skipped entirely if cache directory is not writable + * Bugfix: crop broken if width or height set to 1.00 + * Bugfix: gamma filter for ImageMagick was broken + * Bugfix: newer versions of ImageMagick have changed "-gaussian" + filter to "-gaussian-blur" + * Bugfix: newer versions of ImageMagick have changed date format + from DD/MM/YY to YYYY-MM-DD + * Bugfix: image not found errors when using virtual subdirectories + * Bugfix: assorted errors in phpThumb.demo.check.php + * Bugfix (#265): source images other than JPEG/PNG/GIF would not + generate thumbnails for remote (http) images but work locally + * Bugfix (#253): ImageMagick not found if outside open_basedir + * Bugfix (#243): port ignored in URLreadFsock() + * Bugfix (#240): Any temporary file created through + phpThumb_tempnam() is automatically deleted at the end of the + script via phpThumb object destructor (PHP5 only) + * Bugfix (#216): uninitialized string offset + * Bugfix (#209): don't try ImageMagick first if + config_prefer_imagemagick == false + * Bugfix (#127): CleanUpURLencoding() ignored port number + * Bugfix (#38): CleanUpCacheDirectory() ignored config_cache_prefix + * Bugfix (#31): ExtractEXIFgetImageSize() may throw errors on non- + JPEG/TIFF images when calling exif_read_data() + + +v1.7.10 - April 24, 2011 + * ImageMagickVersion() returned unknown-version for versions + with hyphenated subversion numbers + (thanks r34wangØuwaterloo*ca) + * replace all ereg* functions with preg* equivalents for + PHP v5.3.0+ compatability + * Bugfix: security vulnerabilities when used with ImageMagick + + +v1.7.9 - May 28, 2008 + * "ra" (rotation) now handled by ImageMagick directly. + Note: If output to transparent-supporting format (PNG,GIF) + them ImageMagick support only enabled for v6.3.7 and above + when rotating to nonmultiples of 90° (e.g. rotate by 30°). + Transparent area behind rotate image buggy in v6.2.9 and + working fine in v6.3.7 (exact version where fixed is + unknown, reports welcome) + * Debug now shows server software version + * Debug now checks for GetImageSize() availability + * Better error message when neither ImageMagick nor GD are + installed on server (thanks kevinmØbuenacg*com) + * Bugfix: uncaught image-too-large exception when using + HTTP source image and ImageMagick is unavailable or fails + (thanks jslaggerØjsamdirect*com) + * Bugfix: phpThumb could sometimes die without showing an + error message when using an HTTP/data image source that is + too large for GD processing with PHP memory limit + (thanks jslaggerØjsamdirect*com) + * Bugfix: prevent error images from clobbering debug output + * Bugfix: temp file for ImageMagick processing not created + correctly when source image is HTTP or from non-file data + (thanks jonahØschwartzdev*com, tsolanØgmail*com) + * Bugfix: Filters are only removed from the processing queue + if ImageMagick supports them AND returns successfully + (thanks tsolanØgmail*com) + * Bugfix: ImageCreateFromStringReplacement() doesn't need to + fail in safe_mode if can still write temp file somewhere + (thanks avatar1983Øgmail*com) + * Bugfix: ImageMagickThumbnailToGD() doesn't need to + fail in safe_mode if can still write temp file somewhere + (thanks avatar1983Øgmail*com) + * Bugfix: better error messages when safe_mode enabled and + using MySQL database for image source + (thanks avatar1983Øgmail*com) + * Bugfix: "wmt" alignment broken for built-in fonts + (thanks mailØmmjaeger*com) + * Bugfix: CleanUpCacheDirectory() has some undefined + variables that might have been the cause of the images- + don't-show-on-first-page-load bug + (thanks hgØdynweb*net) + * Bugfix: phpThumb.php would not correctly skip unneeded + processing if source image was smaller than target size + (thanks adam*kingØcarnegiecycling*com*au) + * Bugfix: phpThumb.demo.check.php incorrectly calculated + age of ImageMagick version (for versions < 180 days old) + + +v1.7.8 - September 16, 2007 + ¤ Added 'stc' (Source Transparent Color) filter + (Feature Request #1672440) + ¤ 'zc' now supports directed crop (top/bottom/left/right) + (thanks leeØvirtualinkdesign*com, chrisØsostreassoc*com) + ¤ new class function resetObject() should be used to reset + default class paramters to default to allow reusing a + single object on multiple images + (see phpThumb.demo.object.php) + (thanks bjohnsonØaccomplishhosting*com) + ¤ "wmt" and "wmi" filters now support absolute positioning + using alignment values: {xoffset}x{yoffset} (eg: "10x20") + (Feature Request #1567113) + (thanks mailØpedrocandeias*com; + squidlibertyØusers*sourceforge*net) + ¤ "wmi" filter now supports watermark scaling & rotation + (Feature Request #1788063) + (thanks glen*fiebichØ2020promo*com) + ¤ Added "size" filter. NOTE: try not to use this filter, + it is inefficient and for special purposes only, please + see phpthumb.readme.txt entry before using. + (thanks glen*fiebichØ2020promo*com) + * Added wildcard domain options for config settings + nooffsitelink_valid_domains and nohotlink_valid_domains + (thanks meØjohannes*jarolim*com) + * Output image format defaults to input format (based on + filename) (thanks bjohnsonØaccomplishhosting*com) + * Sepia filter now uses ImageFilter under PHP5 + (thanks richard*comleyØmademedia*co*uk) + * "f" parameters now accepts formats with leading "." + (eg "&f=.png") to allow you to fool things that look at + the last characters of the URL to determine image type + (thanks alexØalexderas*nl) + * Added section to phpthumb.demo.check.php to test cache + file creation and deletion + * Bugfix [#1715256]: ImageMagick output not always used + if fltr set incorrectly + (thanks danakiØusers*sourceforge*net) + * Bugfix [#1541673]: ResolveFilenameToAbsolute() fails in + RenderToFile() if target file does not exist + (thanks jasonpellØusers*sourceforge*net) + * Bugfix [#1703373]: CleanUpURLencoding() broken if path + element equals "0" + (thanks izeronØusers*sourceforge*net) + * Bugfix [#1573399]: phpthumb_filters::Colorize() broken + under PHP5 (thanks cerwØusers*sourceforge*net) + * Bugfix [#1573399]: ImageMagick version of colorize filter + missing quotes (thanks cerwØusers*sourceforge*net) + * Bugfix [#1733462]: SafeExec broken + (thanks hansfnØusers*sourceforge*net) + * Bugfix [#1751821]: open_basedir restriction gives warning + when trying to initialize config_temp_dir + (thanks k-mystikØusers*sourceforge*net) + * Bugfix: [#1751880]: open_basedirs not initialized properly + under Windows (thanks k-mystikØusers*sourceforge*net) + * Bugfix: "A non well formed numeric value encountered" error + when using "cont" filter in PHP5 + (thanks isnbitØyahoo*com) + * Bugfix: Colorize filter ignored $amount under PHP5 + (thanks richard*comleyØmademedia*co*uk) + * Bugfix: double-drawn color in corders of Bevel filter + (thanks m*westmijzeØstudent*utwente*nl) + * Bugfix: 'nocache' parameter was forbidden + (thanks andreasØkringelstan*se) + * Bugfix: disabled functions (set_time_limit, shell_exec, etc) + not always correctly detected + (thanks ryandemmerØgmail*com, dr*creatorØgmail*com) + * Bugfix: phpThumbURL() was broken if __FILE__ and + $PHPTHUMB_CONFIG['document_root'] had different slash + styles (\ vs /) (thanks jobbetØskarin*com) + * Bugfix: 'wmt' watermark text not properly right- or center- + aligned with TTF fonts + * Bugfix: force stderr output to stdout in SafeExec + * Bugfix: filter-generated debug messages not passed back + * Bugfix: /demo/phpThumb.demo.gallery.php was not getting + correct DOCUMENT_ROOT value on some servers + (thanks latex*dragonØgmail*com) + * Bugfix: HTTP source images with no parameters were not + being passed through directly (thanks chrisØmegawap*net) + * Bugfix: "unknown image type" errors on non-GD-supported + remote image sources (thanks chrisØgorillachicago*com) + * Bugfix: remote images not found if server returned 302 + message with text other than "Found" + (thanks cveleØdefault*co*yu, kevinØkevinevans*net) + * Bugfix: resetObject() was clobbering debug messages + (thanks kevinØkevinevans*net) + * Bugfix: CleanUpURLencoding() was broken + (thanks kevinØkevinevans*net) + * Bugfix: Cache auto-purging was generally broken, especially + if the cache contained a directory or filename of "0" + (thanks hannoØxcalak*info; webdesignØweisshart*de) + + + +v1.7.7 - December 25, 2006 + ¤ Added phpThumb.demo.gallery.php -- basic image gallery demo + ¤ Added option for image watermark for anti-hotlinking. See + phpThumb.config.php "nooffsitelink_watermark_src" + (thanks rik_helsenØrad*be) + ¤ Added config option "imagemagick_use_thumbnail" to control + use of ImageMagick's "-thumbnail" command vs "-resize" (the + former discards non-visible metadata, the latter retains it) + Most times you want the smallest filesize so keep the default + setting (imagemagick_use_thumbnail==true) + (thanks niklasØmysticalgarden*se) + * Bugfix [#1620056]: EnsureDirectoryExists() failed to create + some directory structures (thanks jeromyØlocallinux*com) + * Bugfix [#1620056]: CleanUpCacheDirectory() would not purge + files from subdirectories if (cache_directory_depth > 0) + (thanks kingsquareØusers*sourceforge*net) + * Bugfix: nooffsitelink_* broken when running on non-standard + HTTP port (thanks marianbucur17Øyahoo*com) + + +v1.7.6 - December 09, 2006 + ¤ Added "sia" (Save Image As) parameter to provide default + filename when saving generated thumbnails + (thanks web_mkØhotmail*com) + ¤ Changed "lvl" filter parameters, and added option to choose + between four different methods (two internal, two from + ImageMagick). Added configurable threshold parameter. + (thanks publicØwoktiny*com for code for new internal method) + ¤ "wb" (White Balance) filter now processed by ImageMagick + * Changed GD max source pixels to 1/6 (from 1/5) available + memory, and changed SourceImageIsTooLarge() to account for + already-used memory (if known) + * More graceful error handling in object mode when source + larger than PHP memory limit and ImageMagick unavailable + (thanks djasonØdjason*com) + * Added ImageMagickFormatsList() + * CleanUpCacheDirectory() now also purges zero-byte orphan + files left over from aborted thumbnailings + * Bugfix [#1606069]: Changed default urlencoded space + character from "+" to "%20" + * Bugfix [#1608664]: "Unsupported operand types" in "wmi" + filter (thanks haydurØhaydur*com; squidliberty) + * Bugfix: debug mode now supports text output in all modes + * Bugfix: ImageMagick creation attempted before GD creation + * Bugfix: no longer fails silently if GD creation runs out of + memory + * Bugfix: poor alpha rendering from PNG to GIF with ImageMagick + * Bugfix: "wb" filter had no effect when "lvl" filter applied + in ImageMagick instead of GD + + +v1.7.5 - October 03, 2006 + ¤ Deprecated parameters "err", "file", "goto" + ¤ Added broad cache directory feature (see phpThumb.config.php + "cache_directory_depth") to spread cache files across many + directories for improved performance and ability to store + very large numbers (30000+) of cached files + (thanks despoixØopenxtrem*com) + ¤ phpThumb now follows HTTP redirects (status=302) to new + HTTP image URLs (configurable: config_http_follow_redirect) + (thanks shannahØsfu*ca) + ¤ Added "rot" (ROTate) filter which is similar to "ra" parameter + but now allows rotation of output of other filters (for + example, rotate image after fltr[]=mask is applied) + (thanks markØwoodjoint*ca) + ¤ Added WBMP output support (built-in GD or via ImageMagick) + (code was already mostly present, just was not allowed format) + ¤ [#1567113] "wmi" filter now accepts both x and y margins + and can be in pixels or percent of thumbnail dimensions + (thanks squidlibertyØusers*sourceforge*net) + * "hist" filter now accepts both X and Y margins + * Added config variables: config_http_fopen_timeout = 10; + config_http_follow_redirect = true + * Changed MIME type for WBMP to image/vnd.wap.wbmp + * Bugfix: Check for GD format support before attempting output + * Bugfix: Opening HTTP source images with URL query parameters + was broken (eg: http://host/show?id=123) + (thanks ivo*beckersØinfopractica*nl) + + +v1.7.4 - August 17, 2006 + ¤ Improved version of /demo/phpThumb.demo.showpic.php uses + phpThumb.php so any filters can be applied, as well as + resized image popups. + New file: /demo/javascript_api.js + Uses Javascript API by James Austin + (http://aspandjavascript.co.uk/javascript/javascript_api/) + ¤ Added "sfn" (Source Frame Number) parameter to specify + source frame in multi-frame/multi-page source formats, such + as GIF, TIFF, PDF, etc (thanks despoixØopenxtrem*com) + ¤ Added "dpi" (Dots Per Inch) parameter to specify + rasterising resolution for vector source formats (PDF, WMF) + (requires ImageMagick) (thanks despoixØopenxtrem*com) + * Added /demo/phpThumb.demo.object.simple.php + * Added debug points inside GenerateThumbnail + * Explicit error message for unsupported PDF source images + * Bugfix: SafeURLread broken with GETstring parameters in + fsockopen mode + * Bugfix: [#1540523] CleanUpCacheDirectory() does not delete + files as expected (thanks patricksleeØusers*sourceforge*net) + * Bugfix: added useful error message when no source specified + in object mode (thanks infoØdoepud*co*uk) + * Bugfix: timeout value ignored in URLreadFsock() + * Bugfix: timeout missing in SafeURLread CURL part + * Bugfix: ImageMagick now checked with --version (not -version) + * Bugfix: better ImageMagick (numeric) version number matching + * Bugfix: noGD errors showing up when GD imagecreate functions + fail and ImageMagick not available + (thanks caseyyØgmail*com) + * Bugfix: "-thumbnail" parameter not present in older versions + of ImageMagick, now using -resize if -thumbnail unavailable + (thanks atombomb96Øbtopenworld*com) + * Bugfix: "-resize" parameter requires explicit dimensions + ("100x100" instead of "100x") in older ImageMagick versions + (thanks atombomb96Øbtopenworld*com) + * Bugfix: phpThumb crashed with no output if ImageMagick failed + to resize but output image larger than max_source_pixels + (thanks atombomb96Øbtopenworld*com) + * Bugfix: phpThumb might die with no output on some large source + images when ImageMagick unavailable. + (thanks atombomb96Øbtopenworld*com) + + +v1.7.3 - July 11, 2006 + * Now returns useful message for HTTP sources if 404-file- + not-found (or similar) errors occur + * Added new fsockopen() section in SafeURLread() + * Removed PNG alpha warning for IE 7 (alpha PNGs now work) + * Bugfix: ImageMagick failing version check and dumping text + output (thanks infoØdevsystem*net) + * Bugfix: curl_exec failing with text output + (thanks infoØdevsystem*net) + * Bugfix: workaround for PHP Bug #36102 (fopen(http) crash + in PHP v4.4.2 + * Bugfix: "Unknown image type identified by..." problem when + opening http:// source images + (thanks webmasterØdanceage*com) + + +v1.7.2 - June 22, 2006 + ¤ [#1256693] Added $this->exif_raw_data which is returned + data from exif_read_data() on source image. + Requires PHP v4.2.0+ (thanks tebiØusers*sourceforge*net) + ¤ Added $this->outputImageData and RenderOutput() to allow + easy outputting of thumbnail data to a database or such. + Call RenderOutput() instead of RenderToFile() and then + access raw data in $this->outputImageData + (thanks r*cremerØswitch*nl) + ¤ Added 'crop' filter, which is applied after resizing (as + opposed to sx,sy,sw,sh which are before resizing) + (thanks scottØscottjehl*com) + * Enable creating new images with PATH_INFO style call + (thanks edenØinstyleit*com*au) + * Added warning message to encourage users not to use + full HTTP paths for "src" parameter + * Added fallback 'preg_quote' to phpthumb.functions.php in + case your PHP installation does not have preg_* functions + (thanks mortenØemeskay*dk) + * Added fallback 'imagesavealpha' if GD < v2.0.1 + (thanks oliver*heegerØweb*de) + * Added fallback 'imagealphablending' if GD < v2.0.1 + (thanks oliver*heegerØweb*de) + * Added 'nocache' parameter that suppresses writing to cache + file, but only if high_security_enabled is set + (thanks federicoØdonelleschi*com) + * Attempt to detect supported ImageMagick features + (thanks simonØjapancentre*com) + * Added temp dir detection to phpThumb.demo.check.php + * Added ImageMagick dir to phpThumb.demo.check.php + * Added ImageMagick features to phpThumb.demo.check.php + * Default (config_allow_src_above_docroot = true) when PHP + running in "cli" mode (thanks flobeeØgmail*com) + * Bugfix: [#1470791] 'iar' not working properly with + ImageMagick (thanks w1xØusers*sourceforge*net) + * Bugfix: [#1498564] illegal characters in cache filenames + (thanks carl-evertØusers*sourceforge*net) + * Bugfix: 'sx','sy','sw','sh','zc' cache parameters broken + (thanks federicoØdonelleschi*com) + * Bugfix: 'config_max_source_pixels' incorrectly handled + (thanks oliver*heegerØweb*de) + * Bugfix: 'aoe' not working properly + (thanks w1xØusers*sourceforge*net) + * Bugfix: setParameter() was broken for arrays + * Bugfix: setSourceFilename() wasn't setting 'src' + * Bugfix: suppress stat()-related file permission + notices (thanks lanceØmainecoastdesign*com) + * Bugfix: image format now initialized during ErrorImage() + * Bugfix: domain matching now case-insensitive + * Bugfix: some versions of ImageMagick not detected + (thanks arvidØfys*ku*dk) + * Bugfix: sometimes no image returned in safe_mode + (thanks bkainersØgmail*com) + * Bugfix: 'far' not always handled correctly + (thanks matthew*newtonØrealworldweb*com) + * Bugfix: PATH_INFO method not working if no filters specified + (thanks jjimenezØpracticaldata*com) + * Bugfix: first (internal) call to ImageMagickVersion() failed + under Windows + * Bugfix: Images source-cropped AND resized with ImageMagick were + wrong size (cropped size, not resized size) + (thanks joao*saleiroØwebfuel*pt) + * Bugfix: stat() warnings in CleanUpCacheDirectory() + (thanks christianØhss-haage*de) + * Bugfix: $PHPTHUMB_DEFAULTS not working when no other processing + parameters specified (thanks tbittnersØcox*net) + + +v1.7.1 - March 16, 2006 + * /demo/phpThumb.demo.check.php now checks: + - server software + - local and master config values (with ini_get and + get_cfg_var respectively) (thanks nEUTRonØgmx*tm) + - existance of assorted PHP functions and explains their + importance + * Bugfix: config_error_die_on_error now defaults to FALSE to + prevent object-mode errors dying in an error image + (thanks moshØtobt*de; riteshgupta1974Øgmail*com) + * Bugfix: setParameter() now handles array parameters (such + as 'fltr') by appending $value to $parameter + * Bugfix: /demo/phpThumb.demo.check.php incorrect CURL value + under PHP5 (thanks nEUTRonØgmx*tm) + * Bugfix: [#1439110] Limit fread() calls to 8kB + (see http://bugs.php.net/bug.php?id=35859) + (thanks andig2Øusers*sourceforge*net) + * Bugfix: Prevent RenderToFilename() from trying to render + to URLs (thanks Tim*MasseyØitrm*co*uk) + * Bugfix: [#1438657] missing path in phpThumbURL() + (thanks terracesØusers*sourceforge*net) + * Bugfix: zoomcrop was broken for non-square output + (thanks alisonØsemidivine*com, federicoØdonelleschi*com) + * Bugfix: suppress error messages when stat access to temp + dir is disabled (thanks rfineØvnuinc*com) + * Bugfix: ImageMagick processing was broken for source + images of types not supported by GetImageSize + (thanks rfineØvnuinc*com) + +v1.7.0 - February 15, 2006 + ¤ ImageMagick output is used directly far more frequently + for much improved speed and minor quality improvement. + ¤ ImageMagick now processes most of the image filters if + possible (will fall back to GD methods if unavailable) + ¤ GD support is now optional if ImageMagick is installed. + Known limitations include: + - no support for HTTP source images + - ICO output is buggy (in some ImageMagick versions) + - most &fltr[] filters don't work at all + - 'ar', 'ra', 'far' don't work + ¤ Added output support for ICO (icon) format (&f=ico). + Currently only supports single-image icons, but multi- + resolution support may be added in future versions + New file: phpthumb.ico.php + ¤ Added output support for BMP (bitmap) format (&f=bmp). + Currently only supports 24-bit RGB format (for simplicity) + ¤ Added new configuration & compatability checker + New file: demo/phpThumb.demo.check.php + * ImageMagick-generated thumbnails now have extra hidden + contents (EXIF data, etc) stripped (by using -thumbnail + instead of -resize) resulting in smaller filesizes + * Added background fill color, opacity and extent options to + 'wmt' filter (thanks craigØpc-fanatics*com) + * Added metacharacter (^*) support for 'wmt', currently: + source filesize in bytes (^Fb), kB (^Fk), MB (^Fm), + source image width (^X), source image height (^Y), + thumbnail width (^x), thumbnail height (^y) and caret (^^) + (Feature Request #1357815) + (thanks ticklemeozmoØusers*sourceforge*net) + * Moved ImageDestroy call from OutputThumbnail to end of + phpThumb.php to allow multiple calls to OutputThumbnail + * Added config_http_user_agent for site with browsersniffers + (thanks redrobØgmail*com) + * Added $PHPTHUMB_CONFIG['disable_pathinfo_parsing'] (default + false) which disables parsing $_SERVER[PATH_INFO] for + parameters. If you want to parse PATH_INFO, set to false + * Added $PHPTHUMB_CONFIG['disable_imagecopyresampled'] (default + false) which replaces ImageCopyResampled with + ImageCopyResampleBicubic for buggy PHP-GD versions + (thanks g*pelagattiØnetface*it) + * Added $PHPTHUMB_CONFIG['cache_prefix'] to allow sharing of + cache files across virtual servers (Feature Request #1395332) + (thanks doggyfrØusers*sourceforge*net) + * Added $PHPTHUMB_CONFIG['disable_onlycreateable_passthru'] with + default=true (increased speed) to allow direct passthru of + images that don't have GD support. (Feature Request #1396446) + (thanks zedboyØusers*sourceforge*net) + * Removed $PHPTHUMB_CONFIG['cache_differentiate_offsite'] because + it is now automatically tied in with nooffsitelink_enabled + (thanks doggysworldØlibertysurf*fr) + * Removed phpThumb.demo.cacheconvert2.php + * Debug messages are now passed back from filters + * $PHPTHUMB_CONFIG['cache_source_filemtime_ignore_remote'] now + defaults to true for much-improved cached performance + (thanks redrobØgmail*com) + * $PHPTHUMB_CONFIG['cache_differentiate_offsite'] now defaults + to false + * Added $PHPTHUMB_DEFAULTS['ar']='x' to phpThumb.config.php.default + * Added ImageDestroy($this->gdimg_source) to GenerateThumbnail() + to save memory before applying image filters + * gd_info() no longer member of phpthumb_functions + * cache files now default to using SERVER_NAME without 'www.' + * phpUnsharpMask::applyUnsharpMask() should be faster under PHP5 by + using ImageFilter(IMG_FILTER_GAUSSIAN_BLUR) when radius==1 + * Added alternate CURL method for HTTP source images if + allow_url_fopen is disabled (thanks webweberØmotiondraw*com) + * Replaced $this->osslash with DIRECTORY_SEPARATOR constant + * Bugfix: [#1398327] 'new' got broken (1x1 images) + * Bugfix: [#1412552] HTTP source images with special characters were + not getting urlencoded + * Bugfix: ImageSaveAlpha errors on GD v2.0.0 + * Bugfix: phpThumbDebug now entirely disabled if high_security=true + * Bugfix: source images with transparency lost transparency when + rotated (thanks roalklØyahoo*com) + * Bugfix: square source images were not resized when only (w|h)(p|l) + parameters passed + * Bugfix: source images are passed through unmodified in more cases + * Bugfix: ImageMagick not used on systems where it exists outside + defined open_basedir + * Bugfix: ImageMagickVersion() now returns correct versionstring + * Bugfix: ImageMagick warnings no longer cause ImageMagick to fail + * Bugfix: ErrorImage no longer fatal to phpThumbDebug + * Bugfix: "Array to string conversion" in foreach($a as $v) loops + (thanks zeeshanØtargetedmedia*co*uk) + * Bugfix: safe mode warnings in ImageCreateFromStringReplacement + (thanks adminØalex-home*net) + * Bugfix: nooffsitelink broken if !nooffsitelink_require_refer + (thanks depronØgmx*net) + * Bugfix: phpThumb failed when magic_quotes_runtime=true + (thanks stansawyerØyahoo*com) + * Bugfix: several issues with HTTP image sources + (thanks redrobØgmail*com) + * Bugfix: phpThumb_tempnam() would return incomplete temp filenames + under Windows, which may result in orphaned zero-byte temp files + in C:\ if multiple drives exist + +v1.6.2 - November 24, 2005 + ¤ Animated GIF output is now possible if ImageMagick is + available and no filters (other than resize) are applied + (thanks brandenbassØgmail*com for idea) + * Added $PHPTHUMB_CONFIG['cache_force_passthru'] to work + around cached-image-only-works-second-time issue + (thanks yakoØ11y11*com) + * Bugfix: black borders on some image edges + (thanks atelierØdelirius*ch && chuckØcatalyststudio*com) + * Bugfix: uncaught PHP warning in RenderToFile DebugMessage + * Bugfix: allow phpThumbDebug in noGD PHP installations + * Bugfix: 'hash' warning in high_security mode + (thanks bernhardØwtf*at) + * Bugfix: non-TTF rotated text watermarks now work (unrotated) + with no warnings if ImageRotate is unavailable + (thanks aparviaiØusers*sourceforge*net) + +v1.6.1 - August 26, 2005 + ¤ Filters now use GD functions where available (using + ImageFilter, only available in PHP v5.0.0+ with bundled + version of GD). Enabled for: colorize, negative, + grayscale, brightness, contrast, gaussian blur, selective + blur, mean removal (thanks donlaurØmac*com) + ¤ Added config_prefer_imagemagick (defaults=true) + ¤ Added phpthumb_filters::Grayscale() 'gray' + ¤ Added phpthumb_filters::ReduceColorDepth() 'rcd' + ¤ Added phpthumb_filters::Brightness() 'brit' + ¤ Added phpthumb_filters::Contrast() 'cont' + ¤ Added phpthumb_filters::Saturation() 'sat' + ¤ Added phpthumb_filters::EdgeDetect() 'edge' [PHP5 only] + ¤ Added phpthumb_filters::BlurGaussian() 'gblr' [PHP5 only] + ¤ Added phpthumb_filters::BlurSelective() 'gblr' [PHP5 only] + ¤ Added phpthumb_filters::MeanRemoval() 'mean' [PHP5 only] + ¤ Added phpthumb_filters::Smooth() 'smth' [PHP5 only] + * New timing debug info in phpThumbDebug + * Added config_cache_differentiate_offsite + * config_die_on_error now defaults to false + * ResolveSource works better + * cache filenames with 'fltr' parameters have changed + * Filters now skip processing if amount=0 or similar + * [#1263051] 'far' now accepts L,R,T,B,C as values giving + alignment of left/right/top/bottom/center respectively. + Old value of '1' defaults to centered + (thanks webgrappaØusers*sourceforge*net) + * Bugfix: RenderToFile() now fails properly when output format + is unknown + * Bugfix: PNG transparency wasn't working with 'far' + * Bugfix: source images with EXIF thumbnails that differ in + aspect ratio no longer use EXIF thumbnails as source unless + no other options exist + * Bugfix: setting 'src' with setParameter now invokes + setSourceFilename to properly set $this->sourceFilename + (thanks Gazou) + * Bugfix: 'zc' had poor quality when used with ImageMagick + * Bugfix: 'aoe' parameter broken when not using ImageMagick + (thanks frankieali4Øhotmail*com) + * Bugfix: fixed issue with symbolic links + (thanks hornet136Øgmail*com) + * Bugfix: config_max_source_pixels now defaults to same + calculation as used in phpThumb.config.php + (thanks vukshaØhotmail*com) + * Bugfix: Offsite cached thumbnails no longer use unique + referer (now either nothing or "_offsite") + (thanks swaayeØyahoo*com) + * Bugfix: "Unknown image type identified by “] + where is the target hex color to white balance + on, this color is what "should be" white, or light + gray. The filter attempts to maintain brightness so + any gray color can theoretically be used. If is + omitted the filter guesses based on brightest pixels + in each of RGB + ¤ Cached files are used by a Location header instead of + being passed through phpThumb.php using readfile + (thanks newtnØthrillnerds*com) + * Added 'cache_source_filemtime_ignore_local' and + 'cache_source_filemtime_ignore_remote' configurations + to ignore source modification and/or removal + (thanks raynerapeØgmail*com) + * Added 'md5s' parameter, which is the MD5 hash of the + source image -- if this parameter is passed with the + hash of the source image then the source image is not + checked for existance or modification and the cached + file is used (if available). If 'md5s' is passed an + empty string then phpThumb.php dies and outputs the + correct MD5 hash value. This parameter is the single- + file equivalent of 'cache_source_filemtime_ignore_*' + configuration paramters (thanks raynerapeØgmail*com) + * Added /demo/phpThumb.demo.object.php + * Unused parameter 'bgt' removed + * Added empty /cache/source/ directory to distribution + * Added /demo/ and /docs/ and /fonts/ directories + * Set default config_use_exif_thumbnail_for_speed = false + * Bugfix: Wrapped output buffering around all + include_once calls to prevent headers getting sent + accidentally + * Bugfix: md5_file and imagecolorallocatealpha calls + were undefined under PHP v4.1.x (thanks tomØemile*com) + * Bugfix: default 'f' parameter ('jpeg') overrode + config_output_format in object mode + (thanks mailØmmjaeger*com) + * Bugfix: suppressed error message for IIS shell_exec + errors (thanks tomØemile*com) + * Bugfix: Added PHP version check for stream_set_timeout + for HTTP sources (thanks raynerapeØgmail*com) + * Bugfix: overlay margins of 0.5-1.0 cause invalid image + dimensions error (thanks mailØmmjaeger*com) + * Bugfix: underlay margins were not working + (thanks mailØmmjaeger*com) + * Bugfix: [#1187735] EXIF thumbnails were incorrectly + output to the browser directly if requested thumbnail + exactly matched EXIF dimensions + (thanks rebootØusers*sourceforge*net) + +v1.5.2 - April 20, 2005 + ¤ phpThumb.config.php is renamed to + phpThumb.config.php.default to prevent accidental + overwriting. Please migrate your old settings to the new + file, delete your old config and rename the default to + phpThumb.config.php + ¤ Added new filters: + - 'blur' (Blur) [ex: &fltr[]=blur|] + where (0 < < 25) (default = 1) + (thanks thoensiØnetcom*no for code) + - 'hist' (Histogram) + [ex: &fltr[]=hist|||||||] + Where is the color band(s) to display, from back + to front (one or more of "rgba*" for Red Green Blue + Alpha and Grayscale respectively); + is a semicolon-seperated list of hex colors to + use for each graph band (defaults to FF0000, 00FF00, + 0000FF, 999999, FFFFFF respectively); + and are the width and height of the overlaid + histogram in pixels, or if <= 1 then percentage of + source image width/height; + is the alignment (same as for "wmi" and "wmt"); + is opacity from 0 to 100; + is the edge (and inter-tile) margin in percent + - 'over' (OVERlay/underlay image) overlays an image on + the thumbnail, or overlays the thumbnail on another + image (to create a picture frame for example) + [ex: &fltr[]=over||||] + where is the image filename; is "0" (default) + for overlay the image on top of the thumbnail or "1" + for overlay the thumbnail on top of the image; is + the margin - can be absolute pixels, or if < 1 is a + percentage of the thumbnail size [must be < 0.5] + (default is 0 for overlay and 10% for underlay); + is opacity (0 = transparent, 100 = opaque) + (thanks raynerapeØgmail*com, shabazz3Ømsu*edu) + - 'gray' (GRAYscale) [ex: &fltr[]=gray] + is an alias to 100% desaturation + * New configuration 'cache_source_directory' allows the + unprocessed source image to be cached when source is + HTTP or from a database (thanks raynerapeØgmail*com) + * Added 'cache' subdirectory to phpThumb distribution + since this is the default location for the cache + folder. + * Default value for config_error_die_on_source_failure + changed to true (thanks shabazz3Ømsu*edu) + * Added checks to make sure $this->gdimg_output is a + resource before allowing calls to RenderToFile or + OutputThumbnail + * Better error messages when phpThumb.config.php missing + * Bugfix: watermark overlay margins were wrong + * Bugfix: 'lvl' filter no longer processes if not needed + * Bugfix: off-server thumbnail error message was wrong + * Bugfix: several PHP safe mode fixes + (thanks virginiaØalertbutnotalarmed*com) + * Bugfix: cache filenames broken for filter parameters + with paths (thanks srcericØusers.sourceforge.net) + +v1.5.1 - April 06, 2005 + * Added some security upgrades: + - 'config_*' parameters cannot be passed by GETstring + - 'config_nooffsitelink_require_refer' is a new option + (disabled by default) that only allows calls to + phpThumb() from a refering domain listed in + 'config_nooffsitelink_valid_domains' + - disallowed paramters now generate an error image if + present in the GETstring + - 'high_security_enabled' if set to true enabled new + mode of verification, and requires a small function + to generate a hash for calls to phpThumb: + echo ''; + This function is supplied at the bottom of + phpThumb.config.php (thanks paulØstonie*co*uk) + ¤ Added new parameter "new" (phpThumb.php only) which can + create a new image without using "src" parameter. Set + "&new=|" where is the background hex color, + is (optional) opacity (0=transparent, 100=opaque). + (thanks mailØmmjaeger*com) + ¤ Added new filters: + - 'sep' (Sepia) [ex: &fltr[]=sep||] + where is a number between 0 and 100 for the + amount of colorization (default=50), and is + the hex color to colorize to (default=A28065). + (thanks mailØmmjaeger*com) + - 'lvl' (Levels) [ex: &fltr[]=lvl||| + where can be one of 'r', 'g', 'b', 'a' (for + Red, Green, Blue, Alpha respectively), or '*' for all + channels based on average grayscale value (default). + and are the clip points for the levels + (range = 0-255) and are set to clip 0.1% of each end + by default. Use -1 for min and/or max to invoke auto- + detect mode. Using default parameters (&fltr[]=lvl) + is similar to Auto Contrast in Adobe Photoshop. + * Bugfix: Image MIME header was incorrect for cached + images. + * Bugfix: Cache was broken for images pulled from a + database in phpThumb.php + (thanks dragutin*cvetkovicØdragontech-ltd*com) + * Bugfix: Hotlink/Offsite prevention was broken when + image was already cached. + * Bugfix: ImageMagick path was incorrect in some cases + (thanks joshgØtwcny*rr*com) + * Bugfix: ProportionalResize() in phpthumb.functions.php + had a broken check for default values + (thanks Bert*ClaeysØarinso*com) + * Bugfix: transparency now preserved for GIF & PNG input + (thanks tristanØcyrax*ch) + * Bugfix: transparency now supported for GIF output + (thanks j_ivanovØabv*bg) + * Bugfix: alpha transparency could be lost in ApplyMask() + (thanks analyzerxØgmail*com) + * Bugfix: errors on 16/32-bit BMPs + (thanks mattØhellstrominc*com) + * Bugfix: Added datestamp to cached filenames for remote + (HTTP) files, and better warning for caching + (thanks a*gambinoØabramo*it) + * Faster BMP parsing (thanks sgeppertØmail*utexas*edu) + * Added 'error_die_on_source_failure' configuration to + allow invalid source images to show an error rather + than output unmodified source image. + (thanks mindpixelØgmail*com) + * Added $phpThumb->fatalerror which will contain the + text of the fatal error if 'error_die_on_error' is + false. (thanks mindpixelØgmail*com) + +v1.5.0 - February 4, 2005 + * Added new filter parameter 'fltr' that is an array and + can apply multiple effects in sequence. Current filters + that can be called are: + - 'gam' (Gamma Correction) [ex: &fltr[]=gam|] + where can be a number >0 to 10+ (default 1.0) + - 'ds' (DeSaturate) [ex: &fltr[]=ds|] + where is a number between zero (no change) + and 100 (complete desaturation -- grayscale), or it + can be a negative number for saturation boost. + (thanks mailØmmjaeger*com) + - 'clr' (Colorize) [ex: &fltr[]=clr||] + where is a number between 0 and 100 for the + amount of colorization, and is the hex color + to colorize to. (thanks mailØmmjaeger*com) + - 'neg' (Negative) [ex: &fltr[]=neg] + inverts the color + - 'th' (ThresHold) [ex: &fltr[]=th|] (range 0-255) + every grayscale pixel brighter than is set to + white, every darker pixel is set to black + (thanks mailØmmjaeger*com) + - 'usm' (UnSharpMask) [ex: &fltr[]=usm|||] + where is the amount (default = 80), is the + radius (default = 0.5), is the threshold + (default = 3). + - 'wmi' (WaterMarkImage) + [ex: &fltr[]=wmi||||] where is the + filename of the image to overlay, is the + alignment (one of BR, BL, TR, TL, C, R, L, T, B, * + where B=bottom, T=top, L=left, R=right, C=centre, + *=tile), is opacity from 0 to 100, is the + edge (and inter-tile) margin in percent + - 'wmt' (WaterMarkText) + [ex: &fltr[]=wmt||||||||] + where: + is the text to use as a watermark, + is the font size (1-5 for built-in font, or point + size for TrueType fonts), + is the alignment (one of BR, BL, TR, TL, C, R, L, + T, B, * where B=bottom, T=top, L=left, R=right, + C=centre, *=tile), + is the hex color of the text + is the filename of the TTF file (optional, if + omitted a built-in font will be used) + is opacity from 0 to 100, + is the edge (and inter-tile) margin in percent + is the angle + (thanks mailØmmjaeger*com) + - 'flip' [ex: &fltr[]=flip|x or &fltr[]=flip|y] + flip image on X or Y axis + (thanks mailØmmjaeger*com) + - 'elip' [ex: &fltr[]=elip] + similar to rounded corners but more extreme + (thanks mailØmmjaeger*com) + - 'mask' [ex: &fltr[]=mask|filename.png] + greyscale values of mask are applied as the alpha + channel to the main image. White is opaque, black + is transparent. + - 'bvl' (BeVeL) [ex: &fltr[]=bvl|||] + where is the bevel width, is the hex color + for the top and left shading, is the hex color + for the bottom and right shading + (thanks mailØmmjaeger*com) + - 'fram' (FRAMe) draws a frame, similar to border but + more configurable (thanks mailØmmjaeger*com) + [ex: &fltr[]=fram|||||] + where is the width of the main border, is + the width of each side of the bevel part, is the + hex color of the main border, is the highlight + bevel color, is the shadow bevel color + - 'drop' (DROP shadow) + [ex: &fltr[]=drop||||] + where is distance from image to shadow, is + width of shadow fade (not yet implemented), is + the hex color of the shadow, and is the angle of + the shadow (default=225) + - 'ric' (Rounded Image Corners) + [ex: &fltr[]=ric||] + where is the horizontal corner radius, + is the vertical corner radius + * Split out filter functions into phpthumb.filters.php + * 'usa','usr','ust' parameters have been removed and + replaced with the 'fltr' call (see above) + * 'wmf','wma','wmp','wmm' parameters have been removed + and replaced with the 'fltr' call (see above) + * 'brx','bry','bw' parameters have been removed + and replaced with the 'fltr' call (see above) + * 'bw=0' to force aspect ratio has been replaced by + 'far=1' (force aspect ratio) + * Filters that produce transparent sections (such as + Rounded Corners, Ellipse, Mask, Rotate) are now output + as 32-bit/alpha PNG, or flattened with "bg" background + color for JPEG/GIF output (thanks mailØmmjaeger*com) + * Added 'zc' (Zoom Crop) parameter + (thanks arcookeØgmail*com, mailØmmjaeger*com, + pl16056Ømacnews*de, kezzasmØusers*sourceforge*net, etc) + * AutoRotate now can use EXIF orientation tag ('ar=x') + * Added 'ttf_directory' configuration parameter for + TrueType watermarks (thanks mailØmmjaeger*com) + * Added "Last-Modified" header to cache portion of + phpThumb.php which should allow better user-side + caching of thumbnails. (thanks derekØnetsimple*net) + * Added 'cache_disable_warning' configuration which will + cause an error image to be displayed if the cache + directory isn't configured, unless explicitly disabled + * Added 'nooffsitelink_enabled' configuration which + prevents linking to thumbnails on your server from + another domain. Defaults to watermaking linked images + with text warning message. + (thanks anteØabstraktmedia*com) + * Added 'error_image_width' & 'error_image_height' + config variables (thanks mailØmmjaeger*com) + * Rounded image corners now requires GD v2.0.1 and PHP + v4.3.2. Corners are transparent (for PNG output) and + antialiased. + * Rotate by arbitary angle ('ra') now has a transparent + background for PNG output + * Cached filenames now have an additional component for + applied filters + * Cached filenames now have an additional component for + HTTP referer, but only if the refering domain does not + match the domain of the server (designed to prevent + imaged linked from offsite with error message being + cached the same as the local cached version) + * Added setSourceImageResource() to allow use of an + existing GD image resource for thumbnailing + (thanks danØgonmad*co*uk) + * Now including phpThumb.demo.demo1.php (main demo page) + and phpThumb.demo.demo2.php (configurable demo page) + in the phpThumb() distribution + (thanks mailØmmjaeger*com) + * Added many more debugging/tracing message points + * Added set_time_limit(30) to phpThumb.php + * Bugfix: ImageMagick not used if `which convert` points + to a link and not a file (thanks bkainersØgmail*com) + * Bugfix: 'bgt' parameter was sometimes misspelled 'bct' + * Bugfix: 'wmm' couldn't be set to zero + * Bugfix: 'wmm' parameter was only applied to top/left of + image + * Bugfix: auto-detection of document_root failed on + Windows (thanks xbartvØhotmail*com) + * Bugfix: phpThumbDebug could be bypassed if EXIF + thumbnail present (thanks olgradinØcheckfree*com) + * Bugfix: cache file wasn't being written if EXIF data + was used directly (thanks olgradinØcheckfree*com) + * Bugfix: phpThumb.demo.showpic.php was broken by popup + blockers for images larger than the screen. + (thanks mailØmmjaeger*com) + +v1.4.11 - October 11, 2004 + * Changed sx/sy/sw/sh parameters to allow decimal values + (>0 but <1) to represent percent of source image + (thanks mordorØdefault*co*yu) + * Added config_error_silent_die_on_error for no-output + die on fatal errors (thanks johannesØformformat*se) + * Added auto-detection of probable 'document_root' if + that key is not available in $_SERVER + * Bugfix: Check `which convert` failing with error + message (thanks chadØchadshome*com) + * Bugfix: Image cropping to invalid areas outside source + image caused text output (thanks mordorØdefault*co*yu) + +v1.4.10 - August 22, 2004 + * Bugfix: cached files not written in most cases + (thanks kizerØcourtkizer*com, snuffØinbox*ru) + * Bugfix: ApacheLookupURIarray() crashes in CGI mode + (thanks hanskrentelØyahoo*de) + * Bugfix: phpthumb_bmpfile2gd() was broken + (thanks iØmindlace*net) + +v1.4.9 - August 9, 2004 + * Bugfix: changed destination filename in RenderToFile() + (thanks alextkØwalla*com) + * Bugfix: problems with HTTP image source when called as + an object (thanks alextkØwalla*com) + +v1.4.8 - August 4, 2004 + * $this->error has changed to $this->errors and is now + an array of strings (instead of a single string) + * A lot more error conditions (invalid cache directory, + etc) are now reported in $this->errors + (thanks aidan*slingsbyØlineone*net) + * Removed all define(CONSTANT) in the phpThumb() + constructor - you can now access: + - PHPTHUMB_VERSION == $this->phpthumb_version; + - PHPTHUMB_OSSLASH == $this->osslash; + - PHPTHUMB_ISWINDOWS == $this->iswindows; + * Bugfix: Error message from apache_lookup_uri() failing + under Apache2 now reported cleanly + (thanks derbaffØyahoo*com) + * Bugfix: missing phpthumb_functions:: class name for + ImageTypeToMIMEtype() call in ExtractEXIFgetImageSize() + (thanks aidan*slingsbyØlineone*net) + * Bugfix: ImageTypeToMIMEtype() was broken for PHP older + than v4.3.0 (thanks georg*schreiberØbatch-pc*es) + * Bugfix: RenderToFile() now returns false if it fails + (thanks phpthumbØsendthemtomir*com) + * Bugfix: Corrupt JPEG/PNG/GIF files that failed + ImageCreateFrom*() were not being passed to ImageMagick + for fallback, nor passed through unmodified if IM was + unavailable or failed (thanks r*chongØmogenic*net) + * Bugfix: Improved backtick safe-mode limit detection + (thanks 1Øadamcarrington*com) + * Bugfix: EXIF thumbnails were being used as source when + they should not be (thanks aidan*slingsbyØlineone*net) + * Bugfix: Cached files were not being created or used + properly (thanks aidan*slingsbyØlineone*net) + * Bugfix: max_source_pixels not set correct on some PHP + versions (thanks derbaffØyahoo*com) + * Bugfix: 'down' parameter ignored for unprocessed and + cached files (thanks aidan*slingsbyØlineone*net) + +v1.4.7 - July 27, 2004 + * Included a modified version of "module.graphic.bmp.php" + from getID3() [http://getid3.sourceforge.net] as + "phpthumb.bmp.php" for BMP reading support without + ImageMagick. It works, but it's *very* slow, especially + for large images (as in 640x480 or larger). + * Added check to prevent error messages when shell_exec + is disabled (thanks webmasterØneester*com) + +v1.4.6 - July 22, 2004 + * Added new section to phpthumb.config.php where you can + easily specify defaults for any parameter you can set + in the URL. Normally URL parameters override these + default values, unless you set + $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE to false + * Renamed phpthumb.config.php to phpThumb.config.php + since it's part of phpThumb.php, not part of + phpthumb.class.php (change of case only, will not + affect Windows servers, but will affect *nix) + * Changed cached filename of rawImageData-source images + from urlencode('') to md5(rawImageData). This should + make caching thumbnails from non-file sources more + reliable. + * Added ImageMagick debugging information + * Removed unneccesary default values from cached + filenames. This may invalidate some previously cached + files. phpthumb.demo.cacheconvert.php has been updated + to handle v1.4.1-1.4.5 => v1.4.6+ cache filenames. + * Bugfix: Cached filename did not have file-modified + datestamp when used as implmented in phpThumb.php + * Bugfix: RenderToFile() now accepts relative filenames + (thanks aidan*slingsbyØlineone*net) + * Bugfix: AllowOutputEnlargment setting was ignored when + falling back to ImageMagick + * Bugfix: IgnoreAspectRatio setting was ignored when + falling back to ImageMagick + * Bugfix: config_temp_directory was ignored in gd_info() + in PHP < v4.3.0 when phpinfo() returns no GD + information (due to safe mode restrictions) + (thanks mimyrtekØmyrtek*com) + +v1.4.5 - June 28, 2004 + * Added new parameter 'down' where you can specify a + filename and OutputThumbnail() will cause the file + to be downloaded rather than displayed in the browser. + Demo images on silisoftware.com/scripts/phpThumb/demo/ + can all be downloaded to show off this feature. + (thanks stuartscrumpØyahoo*co*uk) + * Added ability to remove old files from cache directory + based on last-access time and/or number of cached files + and/or total size of cached files + (thanks jrmhaigØyahoo*co*uk) + * Added public CleanUpCacheDirectory() for cache cleaning + (see above) if you need to call it manually + * Included new file phpThumb.demo.cacheconvert.php to + convert old-style cache names to the current (and + hopefully last!) standard naming convention. + (thanks joshgØtwcny*rr*com) + * Added configuration value 'document_root' for rare case + when $_SERVER['DOCUMENT_ROOT'] return incorrect value + (thanks joshgØtwcny*rr*com) + * Now tries to create thumbnail with ImageMagick if + ImageCreateFromJPEG etc fails, before falling back to + outputting unmodified source data. + * Bugfix: HTTP image sources were broken + (thanks fritz*weisshartØt-online*de) + * Bugfix: ImageMagick callout wasn't being used if EXIF + thumbnail was available + (thanks joshgØtwcny*rr*com) + * Bugfix: HTTP src with space in filename was broken + (thanks drØrhodes360*com) + * Bugfix: version_compare_replacement() was broken for + PHP v4.1.0+ + +v1.4.4 - June 8, 2004 + * Bugfix: network-share (Windows) source filenames were + not possible. Now works, but you must use the network + name and not a mapped drive name, for example: + \\othercomputer\file.jpg - good + \\192.168.2.1\file.jpg - good + z:\file.jpg - won't work + This is a PHP limitation (see www.php.net/file-exists) + Note: you may want to use "/" slashes instead of "\" if + you have magic_quotes_gpc enabled to avoid stripslashes + problems. + (thanks drØrhodes360*com) + * Bugfix: missing "phpthumb_functions::" in + ImageCreateFromStringReplacement() + (thanks zapletalØsoftwaremedia*cz) + +v1.4.3 - May 25, 2004 + * Added new configuration variable 'config_temp_directory' + to allow you to specify a writable directory name for + temp files if you do not have access to the system temp + directory on your server (Safe Mode restrictions etc) + (thanks nickØregenmag*com) + * Added new configuration variable + 'config_error_die_on_error' which can be set to false if + you want to retrieve the error message without having it + dumped as an image - the error message is now available + in $phpThumb->error + * Images are passed through directly with no processing + and no caching if no parameters are passed to alter the + image (resize, crop, sharpening, etc) + (thanks nchmuraØusers*sourceforge*net) + * Added new configuration variable 'config_disable_debug' + which disabled phpThumbDebug from working if you have + security concerns about the displayed information + * Bugfix: Added detection at the top of phpThumb.php for + no-GD errors to avoid parse errors later in the code + (thanks nickØregenmag*com) + * Bugfix: RoundedImageCorners() had some off-by-1 errors + (thanks ola*thunbergØhome*se) + +v1.4.2 - May 10, 2004 + * Added IE-compatability mode for transparent corners + (set 'bct=256') + * Bugfix: version_compare_replacement() was broken in PHP + older than 4.1.0 + (thanks nickØregenmag*com) + +v1.4.1.1 - May 9, 2004 + * Bugfix: Removed ImageTrueColorToPalette hack. + See http://bugs.php.net/bug.php?id=28341 + * Bugfix: 'maxb' option for PNG/GIF output incorrect + bit depth under some circumstances + +v1.4.1 - May 9, 2004 + * Added 'maxb' (MAXimum Bytes) option to auto-set the + output image quality (JPEG) or bit depth (PNG/GIF) so + that the output thumbnail is less than 'maxb' bytes + (thanks e_belleØhotmail*com) + * Added 'bgt' parameter to make rounded corners from + 'brx'/'bry' option transparent when used with PNG + output. Note: PHP/GD appears buggy at this time, so this + option must force output to 256-color mode for this + to work. The feature will be updated when a non-broken + version of PHP/GD is released. + (thanks javierØircorion*net) + * Bugfix: Caching was broken + (thanks mikeØgdaymate*nl, jurewiczØgo3*pl) + +v1.4.0 - April 30, 2004 + * Rewritten as a PHP class. Split into several files: + - phpthumb.class.php = most processing code + - phpthumb.functions.php = support functions + - phpthumb.readme.txt = usage instructions + - phpthumb.changelog.txt = this file + - phpthumb.config.php = configuration file + - phpthumb.gif.php = Non-GD GIF reading support + - phpthumb.unsharp.php = Unsharp Masking support + - phpThumb.php = demo script that works + exactly as previous versions; this is a drop-in + replacement for existing phpThumb() installations + - phpThumb.demo.showpic.php = demo script that auto- + resizes a popup window to the size of the image + shown. Useful if you want popup images but do not + know the large image size beforehand + * Added optional call-out to ImageMagick (if avaible) if + source image is larger than PHP memory restrictions + allow. ImageMagick installation should be auto-detected + under *nix, but you should configure 'imagemagick_path' + for use under Windows. + * 'max_source_pixels' is now auto-calculated from PHP + configuration settings. Due to various server-level + restrictions that may override PHP settings this + calculated value may not always be correct, and you may + have to specify the value manually. + * Added rounded-corner border option. You must specify + both 'brx' (horizontal radius) and 'bry' (vertical + radius) as well as 'bw' (border width). If 'bw' is + greater than zero, the image will be shrunk to fit + inside the border with a margin of background color. + If 'bw' is zero, the corners of the image will be + cut off and filled with background color. + (thanks javierØircorion*net) + * Minor speed improvement for unsharp masking + +v1.3.7 - March 28, 2004 + * Bugfix: GD version detection was broken on PHP <4.3.0 + on servers where phpinfo() was disabled + (thanks javierØircorion*net) + * Bugfix: Non-GD GIF support was broken on restricted + PHP configurations + (thanks javierØircorion*net) + * Bugfix: phpThumb.gif.php output error messages if PHP + was running in Safe Mode + * Added 'iar' parameter (Ignore Aspect Ratio) to allow + non-proportional resizing (stretch image to fit). + You must specify 'h' and 'w' to use this option. + (thanks javierØircorion*net) + +v1.3.6 - March 14, 2004 + * Bugfix: was broken when register_globals turned on + (thanks joshgØtwcny*rr*com) + * Bugfix: Images with transparent backgrounds now have + the background color filled with the color specified + by the 'bg' parameter + * Bugfix: ImageCreateFromString() is broken in the + non-bundled GD. Added workaround, but please use + the bundled version of GD if possible + (thanks dnØxbe*ch) + * Bugfix: EXIF thumbnail caching was broken + * Bugfix: EXIF thumbnail handling was broken for PHP + v4.2.x + (thanks smithk1Øshaw*ca) + * Bugfix: Image borders with GD2 were misaligned + * Bugfix: virtual paths/filenames like /~user/foo.jpg + should now work properly, if PHP is installed as an + Apache module (see www.php.net/apache-lookup-uri) + * Bugfix: contents of any non-image file could be + displayed (including PHP & HTML files) + (thanks arsyanØarsyan*com) + * Added rotation parameters 'ra' and 'ar' + (thanks drØrhodes360*com) + * Added $CONFIG['output_allow_enlarging'], defaulted + to false, to prevent smaller-than-max-size images + from being enlarged beyond their original size. If + you want to be able to enlarge images, set this to + false. Can be overridden with the 'aoe' parameter + (thanks dnØxbe*ch) + * Changed all configuration variables to be under one + array named $CONFIG + * Moved color and font options for ErrorImage() to + $CONFIG variables + * Changed cached filename structure (again) to a more + flexible format that can handle future expansion + (old cached files are invalid and will be recreated) + * Added more debugging code to phpThumbDebug + +v1.3.5 - February 29, 2004 + * Added capability to use EXIF thumbnail that may be + embedded in source image (often is in digital camera + JPEGs) and source image dimensions are larger than + $config_max_source_pixels. This will overcome the + limitation where PHP runs out of memory processing + large images (usually >1600x1200). EXIF thumbnail + extraction requires PHP v4.2.0 or higher and EXIF + support compiled into PHP (or php_exif extension) + * Eliminated intermediate read-file-to-memory stage if + image is created from local file. Should allow + larger images to be processed without running out of + memory. + * Added optional 'goto' parameter to be used with the + 'file' parameter, where 'goto' is a URL that is + redirected to after image is rendered to file + (thanks wimbleØwebdonors*com) + * Added optional 'xto' parameter that will bypass all + processing and just return the embedded EXIF + thumbnail, if available. + * Added error-handling if ImageTypes() is unavailable + +v1.3.4 - February 15, 2004 + * Custom error image option (&err=img.jpg) which can + also be set as $config_error_message_image_default + (thanks carlØ4thstar*net) + * &f=text will now output plain-text error messages + * ErrorImage() now used for anti-hotlink messages (if + $config_nohotlink_erase_image is true) + +v1.3.3 - February 5, 2004 + * Bugfix: Added stripslashes() to filenames if + magic_quotes_gpc is enabled + (thanks arsyanØarsyan*com) + * Output can now be rendered to a file only (not to + browser) specified by the 'file' parameter + (thanks arsyanØarsyan*com) + * JPEG quality now has a maximum of 95%, as specified + in the GD documentation + +v1.3.2.1 - February 3, 2004 + * Bugfix: gd_version() was broken for GD v2.0+ + * Bugfix: removed debugging code + +v1.3.2 - February 3, 2004 + * Bugfix: when borders are enabled, portait images + with no width constraint, or landscape images with + no height constraint were smaller than neccesary by + double the border width + (thanks jjjØxs4all*nl) + * Added unsharp mask option thanks to Torstein Hønsi: + http://www.vikjavev.com/hovudsida/umtestside.php + Note: requires GD v2.x to function + (thanks jjjØxs4all*nl) + * Updated cache filenames to reflect new parameters, + this means old cached files will need to be deleted + (or not, they just will never get called again) and + new cached versions will be created. + * Added caching to gd_info() calls for minor speedup + +v1.3.1 - February 2, 2004 + * Added optional border (width and color configurable) + (thanks arsyanØarsyan*com) + * Added option to create fixed-dimension thumbnails + regardless of source aspect ration. Set the 'bw' + (BorderWidth) parameter (even to 0) and this will be + enabled. Outside the actual image will be filled + with 'bg' color (default FFFFFF) + (thanks arsyanØarsyan*com) + +v1.3.0 - January 27, 2004 + * Added watermarking option to overlay thumbnails with + a semi-transparent watermark image (copied from a + seperate source watermark image) + (thanks arsyanØarsyan*com) + * Added option for absolute filenames (on both Windows + and *nix) outside the DOCUMENT_ROOT directory + * Added debug output dump for diagnosing problems) + +v1.2.8 - January 19, 2004 + * added ability to specify relative pathnames as well + as absolute pathnames (pathname is relative to the + location of phpThumb.php if the passed source does + not begin with "/" + +v1.2.7 - January 7, 2004 + * Added patch to allow use of PHP older than 4.1.0 + (or GD without PNG support) for non-GD GIF support + (thanks hostwebserverØhotmail*com) + +v1.2.6 - January 4, 2004 + * Added patch to allow use of PHP older than 4.1.0 + (without the superglobals arrays) + +v1.2.5 - December 26, 2003 + * Added configuration options for default output image + format and max width/height + +v1.2.4 - December 20, 2003 + * Bugfix: temp directory for non-native GD support not + always returning valid directory + * Caching feature reintroduced (see configuration) + +v1.2.3 - December 19, 2003 + * Added anti-hotlink code so the thumbnail script on + one domain cannot be used by another domain. The + list of allowed domains defaults to the current + domain but is configurable below as + $config_nohotlink_valid_domains. The message, text + size, colors and whether to blank the image or not + are also configurable + * Bugfix: URL image sources were not able to use the + non-GD GIF-reading functions + +v1.2.2 - December 17, 2003 + * Added option to use http:// URL as image source + +v1.2.1 - December 11, 2003 + * Added option to get source data from a database + rather than a physical file + * Bugfix: resize not proportional when wide image + limited more by max height than max width + Thanks mathias_strasserØgmx*net + * Removed caching code + +v1.2.0 - December 10, 2003 + * Added GIF support for versions of GD that do not + have built-in GIF support (v1.6.x) via the "GIF + Util" class by Fabien Ezber (www.yamasoft.com) + GD's built-in GIF-reading functions are faster, and + are present in PHP v4.3.0 or newer, but all versions + of GD can display resized GIF thumbnails now. + +v1.1.2 - October 26, 2003 + * check for source image existance to prevent text + error messages + * if GD not available, a GIF saying "no GD" is shown + instead of showing the original image + * Cache feature introduced + +v1.1.1 - September 28, 2003 + * better resize code by sfisher10Øcox*net + +v1.1.0 - September 1, 2003 + * initial public release + * thumbnails can now be larger than source image + * graphical error messages + +v1.0.0 - January 7, 2002 + * initial private release diff --git a/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.faq.txt b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.faq.txt new file mode 100644 index 0000000..e873dc8 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.faq.txt @@ -0,0 +1,372 @@ +////////////////////////////////////////////////////////////////// +// phpThumb() by James Heinrich // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////////// +/// // +// Frequently Asked Questions (FAQ) about phpThumb() // +// /// +////////////////////////////////////////////////////////////////// + + +Q: My question isn't answered here, how do I get support? +A: Please visit http://support.silisoftware.com for any + questions, suggestions, bugs, etc. + + +Q: I think I found a bug, what's the first thing I should do? +A: Please make sure you're using the latest version: + https://github.com/JamesHeinrich/phpThumb + There's a good chance I may have already fixed the bug, so + please make sure you can reproduce it with the latest version + before reporting the bug. + + +Q: phpThumb doesn't work as expected, and it may be a server + configuration issue -- how do I check? +A: Please run /demo/demo.check.php to find out how your server + matches up with the recommended configuration and for + suggestions on what to change for improved performance. + + +Q: What is the GPL? Can I use this for commercial sites? +A: See the GPL FAQ: http://www.gnu.org/licenses/gpl-faq.html + In general, if you just want to call phpThumb.php in the + standard manner + then there is no problem, you're free to do this no matter + if you site is commercial or not, or what license your code + is released under. + If you're calling phpThumb() as an object then you will + probably run into license issues, so consult the above FAQ + and the GPL itself. + No matter if you use phpThumb() commercially or not, no + payment is required. However, donations are always welcome + and can be made at http://phpthumb.sourceforge.net + + +Q: Some images generate thumbnails, but some fail (the original + non-resized image is output instead). +A: Your PHP installation does not have a high enough memory_limit + and ImageMagick is not installed on the server. The PHP memory + required is 5 times the number of pixels in the image. + For example: + 640x480x5 = 1.5MB + 1600x1200x5 = 9.2MB + You can adjust the PHP memory limit in php.ini (if you have + permission on your server to do so), or (better yet) install + ImageMagick on the server and that will bypass the memory limit + issue. If you can't do either of the above, you can resize the + images manually (with your favourite image editor) to a size + that your memory_limit setting can handle, and/or you can + re-save the images with an image editor that can embed an EXIF + thumbnail (Photoshop for example) which phpThumb can use as an + image source (lower image quality, but perhaps better than + nothing). + + +Q: Is there are way to determine the new height and width of the + generated thumbnail (so I can put it in the width/height)? +A: The problem is that phpThumb.php returns an image -- there is no + way to pass on any additional info such as width/height. + However, you can do something like this: + require_once('phpthumb.functions.php'); + $pic = 'picture.jpg'; + list($source_w, $source_h) = GetImageSize($pic); + $max_w = 375; + $max_h = 400; + list($newW, $newH) = phpthumb_functions::ProportionalResize( + $source_w, $source_h, $max_w, $max_h); + $url = 'phpThumb.php?src='.$pic.'&w='.$max_w.'&h='.$max_h; + echo "'; + + +Q: I'm getting is this error message: + Failed: RenderToFile() failed because + !is_resource($this->gdimg_output) +A: You missed the call to GenerateThumbnail() before + RenderToFile() or OutputThumbnail. + See /demo/phpThumb.demo.object.php for an example. + + +Q: I'm trying to save a phpThumb-generated image in Internet + Explorer and it saves in BMP format, why? +A: This is not phpThumb's fault, it is an IE issue: + http://support.microsoft.com/default.aspx?scid=kb;en-us;810978 + http://support.microsoft.com/default.aspx?scid=kb;en-us;260650 + + +Q: PNG images with transparent areas show up with gray background + in the areas that are supposed to be transparent. +A: Internet Explorer has had a broken PNG alpha-channel display + implementation for a decade, so it may never get fixed. Other + major browsers generally handle alpha-transparent PNGs fine. + See http://www.silisoftware.com/png_transparency/ + For an alpha-channel PNG display in IE hack, see this page: + http://www.koivi.com/ie-png-transparency/ + + +Q: I'm getting " does not exist" when I know the + file does exist +A: Check that these two values are present and properly + configured in phpThumb.config.php (introduced in v1.6.0): + $PHPTHUMB_CONFIG['allow_src_above_docroot'] (default=false) + $PHPTHUMB_CONFIG['allow_src_above_phpthumb'] (default=true) + If your images are outside DOCUMENT_ROOT (this includes if + you have an image upload form, most likely the images will + get uploaded to "/tmp/" or similar) then you will have + to configure 'allow_src_above_docroot' to true. + Make sure whatever user the webserver is running as has read + permission to the file/directory you're reading from + + +Q: Should I use phpThumb.php, or use phpThumb() as an object? +A: phpThumb.php is easier to use (less coding) for basic uses. + phpThumb.php handles all caching; your own object will need + to have its own caching code. If you just want to display a + thumbnailed version of an existing image, use phpThumb.php + If you want to render one (or more) thumbnails to static + files (during upload, for example), that's an appropriate + use for the object mode. Also, phpThumb.config.php is only + used by phpThumb.php, so if you instantiate your own object + you need to manually set all configuration options because + phpThumb.config.php has NO effect. So, to repeat: + **always use phpThumb.php unless you NEED to have an object** + + +Q: The first time I go to a page which contains thumbnails I + don't actually see the thumbnail, I just get a browser image + placeholder (or no image). As soon as I hit refresh, all the + thumbnail images pop into place really fast. +A: You can try and see if it works better with + $PHPTHUMB_CONFIG['cache_force_passthru'] = false; + but typically the default setting works better. + Note: There were some maybe-undefined variables prior to + v1.7.9 that contributed to this behavior. If you notice + this happening in v1.7.9 or newer please email me at + info@silisoftware.com + + +Q: Are there any front-end GUI interfaces to phpThumb()? +A: See /demo/readme.demo.txt + + +Q: Are there / have there been any security issues in phpThumb? +A: http://secunia.com/product/5199/ + + +Q: Why can't Flash work with images output from phpThumb()? +A: Flash doesn't like progressive JPEG. Set: + $PHPTHUMB_CONFIG['output_interlace'] = false; + + +Q: Image quality is not very good - why? +A: If you're using GD v1.x, no way around it. Upgrade to GD v2.x + + +Q: Image quality is very bad, very pixelated -- why? +A: You may be trying to resize images larger than the available + PHP memory, so phpThumb is simply extracting and using the + EXIF thumbnail as the image source, which is usually about + 160x120 (so if you resize it to 640x480 it will look very bad). + To calculate the required size for memory_limit in php.ini, + calculate the number of pixels in the image and multiply by 5: + For example, 1600x1200 = 1600 * 1200 * 5 = 9600000 = 10M + Easy solution: install ImageMagick + + +Q: Can I save the generated thumbnail to a file? +A: Yes, there are several ways to do so; the best way is to call + phpThumb as an object and call RenderToFile() to save the + thumbnail to whatever filename you want. + See /demo/phpThumb.demo.object.php for an example. + The other way is to use the 'file' parameter (see + /docs/phpthumb.readme.txt) but this parameter is deprecated + and does not work in phpThumb v1.7.5 and newer. + + +Q: "Off-server thumbnailing is not allowed" -- how do I enable it? +A: By default, phpThumb() only makes thumbnails for the same + domain that it is running on. To allow it to make thumbnails + for a limited number of other domains, add them + (in phpThumb.config.php) like this: + $PHPTHUMB_CONFIG['nohotlink_valid_domains'] = array( + @$_SERVER['HTTP_HOST'], 'example.com', 'www.example.com', + 'subdomain.example.net', 'example.org'); + To disable off-server thumbnail blocking, just set: + $PHPTHUMB_CONFIG['nohotlink_enabled'] = false; + + +Q: Is it possible to set the parameters (like w/h/fltr[]) in + the config, so that they can't be changed over the URL? +A: Take a look at $PHPTHUMB_DEFAULTS at the bottom of + phpThumb.config.php You'll want to set + $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE = false + possibly also + $PHPTHUMB_DEFAULTS_DISABLEGETPARAMS = true + You may also want to investigate + $PHPTHUMB_CONFIG['high_security_enabled'] = true + (see the example at the bottom of phpThumb.config.php + for how to call images in HighSecurity mode) + + +Q: Is there a way to use phpThumb() object to create thumbnails + without the parameters in the URL showing the location of + the image etc? +A: There is a demo in /demo/phpThumb.demo.object.php. You could + modify this into your own file, but there still remains the + problem of passing parameters to the file, whether it's + phpThumb.php or your own instantiation of a phpThumb() object. + I would suggest is putting as many of the common parameters + into phpThumb.config.php as possible under $PHPTHUMB_DEFAULTS, + so you then don't have to pass them for each image. If you + don't want people modifying the parameters, turn on + $PHPTHUMB_CONFIG['high_security_enabled'] and set a password + (you'll need to generate the tags with phpThumbURL() + provided at the bottom of phpThumb.config.php). If you don't + want people accessing your source images at all, you can + place them outside DOCUMENT_ROOT on your server (as long as + phpThumb/PHP has read access to the directory). The other + option is to put your source images in a MySQL database + and set $PHPTHUMB_CONFIG['mysql_query'] and related + parameters in phpThumb.config.php to pull your source images + from the database. That way it's impossible to retrieve the + images except through phpThumb.php, and if high_security is + enabled, then nobody can modify the parameters to view + anything except what you want to show. So, yes, it's possible + to use your own object, but it's probably better to use + phpThumb.php if possible -- one notable issue is that + phpThumb.php handles all the caching, so you're on your own + to deal with that if you create your own object. + + +Q: How do I write the output thumbnail back to a database instead + of outputting to the browser or a file? +A: See /demo/phpThumb.demo.object.php Basically you need to call + $this->GenerateThumbnail() then $this->RenderOutput() and then + the output raw image data is found in $this->outputImageData + + +Q: phpThumb runs slowly, as if the images aren't cached, when I use HTTP source + images (not on my server). How can I make it go faster? +A: $PHPTHUMB_CONFIG['cache_source_filemtime_ignore_remote'] = true; + // if true, remote source images will not be checked for modification date and + // cached image will be used if available, even if source image is changed or removed + + +Q: What does the "cache_default_only_suffix" configuration option do? +A: Cache files are normally created with big ugly names like + "phpThumb_cache_www.example.com_src1a482c2c760463795ff18faf073b389f_par3e099041c2f4a73041a7f5d7e7fc481a_dat1119952152.jpeg" + but if cache_default_only_suffix is enabled, cache filenames are simplified to + "pic_thumb.jpg" (for example). The problem is that only one version of that + thumbnail is possible, and you can never call it again with a different size, + or different filters, etc. Generally you don't want that enabled, but it's + there because some people asked for it. + + +Q: Why is the visual size of rotated images smaller than the unrotated images? +A: phpThumb fits the rotated image into the 'w' and 'h' dimensions. + Try not specifying a 'w' parameter: phpThumb.php?src=file.png&ra=15 + That should leave the image the apparent same size as the unrotated image + (in actual fact the canvas size is enlarged to fit the rotated image in it). + + +Q: phpThumb.demo.check.php says Safe Mode is off for Master but on for Local, + and I checked php.ini and it's already set off. How do I disable safe mode? +A: Your PHP was probably installed as an Apache module. If so, you have to set + php_admin_value safe_mode "Off" + in your domain settings (usually between tags in httpd.conf). + Then you have to restart Apache. + + +Q: How can I purge cached files when I delete the source image? +A: You can either let phpThumb's built-in cache purging features (see phpThumb.config.php) + take effect, or you can manually walk through your source images to delete and find + the matching cache files and delete them: + if ($dh = opendir($sourcedir)) { + while ($file = readddir($dh)) { + if ($file == $WhatIwantToDelete) { + $md5 = md5_file($sourcedir.'/'.$file); + unlink($phpthumb_cache_dir.'/phpThumb_cache_www.example.com_src'.$md5.'*.*'); + } + } + closedir($dh); + } + + +Q: Is it safe to delete cache files? +A: Yes, it is safe to delete any cache files and/or directories. phpThumb will + automatically re-create them as needed. Also, take a look at the "cache_max*" + settings in phpThumb.config.php for automatic cache purging. + + +Q: How can I find the filename that phpThumb.php will use to + cache a particular image? +A: It's not easily possible to get the cache filename. You can + see the method used to calculate it in SetCacheFilename() + in phpthumb.class.php (around line 2991-3090). If you need + to know where an image will be rendered to, it may be + easier and better to call phpThumb as an object and handle + your own caching. See /demo/phpThumb.demo.object.simple.php + for an example. + + +Q: Can I make thumbnails from a PDF? +A: Yes, as long as you have both ImageMagick and GhostScript + installed. The AFPL version of GhostScript seems to work + better than the GNU version (at least for me it does). + http://www.imagemagick.org + http://www.cs.wisc.edu/~ghost/ + You may want to use the "sfn" (Source Frame Number) + parameter of phpThumb to specify which page to thumbnail. + + +Q: Can I make a thumbnail of a webpage? +A: Possibly, but it's not easy. Theoretically, if you have + html2ps, GhostScript and ImageMagick all installed it should + be possible, but I have not tested that. Other projects that + attempt to generate thumbnails from webpages include: + http://www.boutell.com/webthumb/ + + +Q: When I resize an animated GIF the new "smaller" version is + actually a larger file size -- why? +A: Animated GIFs use a variety of temporal and spatial compression + techniques. The source GIF is probably very well optimized, but + when each frame is resized some of the desirable compression + properties could be negatively affected (the number of colours + can increase; dithered areas compress very poorly compared to + solid areas of colour). ImageMagick may also not produce the + most filesize-optimized animated GIF possible. + + +Q: Can I use source images from (same or another) server that uses + a script with parameters to display images? For example: + http://sourceforge.net/sflogo.php?group_id=106407&type=5 + (displays a PNG image, 210x62) +A: Yes, you should be able to use phpThumb like that no problem. + If the source image is on a different server you need to set + $PHPTHUMB_CONFIG['nohotlink_valid_domains'] to contain the source + domain(s) [eg: sourceforge.net] if it's a small list of possible + source domains, or make sure $PHPTHUMB_CONFIG['nohotlink_enabled'] + is set to false to allow creating source images from any domain/IP. + You will also need to properly encode the image source (using PHP + function rawurlencode): + /phpThumb.php?src=http%3A%2F%2Fsourceforge.net%2Fsflogo.php%3Fgroup_id%3D106407%26type%3D5&w=100 + + +Q: phpThumb is the best software in the world, how can I donate? +A: There's a handy "Support this project" button at the top of + http://phpthumb.sourceforge.net which will take you through + the process (and give SourceForge a ~5% cut), or if you prefer + you can send PayPal donations directly to info@silisoftware.com + + +Q: What is the proper name for this script/program/library? +A: The official name is "phpThumb()" but it may be written + as simply "phpThumb" in short form (or where parentheses + are not permitted), or "phpthumb" in case-insensitive + environments. The following is a non-exhaustive sample of + unacceptable forms: PHPthumb; phpThumbs; phpthump; + phpthumbnailer; phpThumbnail; PHP Thumb; Phpthumb; etc. + + diff --git a/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.readme.txt b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.readme.txt new file mode 100644 index 0000000..7aecc3e --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/docs/phpthumb.readme.txt @@ -0,0 +1,682 @@ +////////////////////////////////////////////////////////////////// +// phpThumb() by James Heinrich // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////////// +/// // +// This code is released under the GNU GPL: // +// http://www.gnu.org/copyleft/gpl.html // +// // +// phpThumb() is free to use according to the terms of the GPL. // +// GPL FAQ: http://gnu.org/licenses/gpl-faq.html // +// // +// Donations are gratefully accepted from happy users :) // +// See http://phpthumb.sourceforge.net // +// /// +////////////////////////////////////////////////////////////////// + +============ +Description: +============ + +phpThumb() uses the GD library to create thumbnails from images (GIF, PNG +or JPEG) on the fly. The output size is configurable (can be larger or +smaller than the source), and the source may be the entire image or only a +portion of the original image. True color and resampling is used if +GD v2.0+ is available, otherwise low-color and simple resizing is used. +Source image can be a physical file on the server or can be retrieved from +a database. GIFs are supported on all versions of GD even if GD does not +have native GIF support thanks to the GIFutil class by Fabien Ezber. + +AntiHotlinking feature prevents other people from using your server to +resize their thumbnails, or link to your images from another server. The +cache feature reduces server load. + + +======== +Support: +======== +First, read this file. +Then read phpthumb.faq.txt +Then run /demo/phpThumb.demo.check.php +If you still think it's a bug, or can't figure it out, please go to +http://support.silisoftware.com + + +============= +Installation: +============= + +1) Download from either official site: + * https://github.com/JamesHeinrich/phpThumb (current development version) + * http://phpthumb.sourceforge.net (occasional releases + documentation) +2) unzip to a location of your choice on your server, putting it in its + own subdirectory (e.g. /phpThumb/ is useful but not required) +3) rename phpThumb.config.php.default -> phpThumb.config.php +4) edit phpThumb.config.php as needed to suit your server configuration. + * the only setting you must set is 'high_security_password' + * most other values are auto-detected, but your particular server config + may necessitate setting other values such as 'document_root' or + 'imagemagick_path' + * see also "Configuration" section below +5) Check your server configuration by opening + /phpThumb/demo/phpThumb.demo.check.php in your browser. Settings that are + highlighted green are good; yellow/orange/red may need to be adjusted. + + +====== +Usage: +====== + +Call phpThumb() just like you would a normal image (i.e. as the SRC attribute +of an IMG tag): + +To generate the hash value you must include the phpThumb.config.php file and use the +phpThumbURL function to generate the URL including the hash value: + echo ''; +The hash is calculated with the 'high_security_password' config value, so you +must generate a complex password value for that setting in phpThumb.config.php +(once, when installing phpThumb). + +See the "demo" link on http://phpthumb.sourceforge.net for more usage examples. +Parameters that can be passed are listed below under "URL Parameters". + +NOTE: It's recommended you use the local image filename +wherever possible (rather than http://) because performance +is much better, less (or no) use of temp files, and the +last-modified check for cached files doesn't work for +remote files. + +To access files over a LAN with Windows share names you +must use the network name (or IP) and not a mapped drive +name, for example: + //othercomputer/file.jpg - good + //192.168.2.1/file.jpg - good + z:/file.jpg - won't work +This is a PHP limitation (see www.php.net/file-exists) +Note: you may want to use "/" slashes instead of "\" if +you have magic_quotes_gpc enabled to avoid stripslashes +problems, although either slash should work if +magic_quotes_gpc is disabled + + +================================ +Alternate PATH_INFO-style Usage: +================================ + +phpThumb.php can also be called by passing parameters not +after the usual "?" but like this: + phpThumb.php/=;x; +For example: + phpThumb.php/100;pic.jpg + phpThumb.php/100;images/pic.jpg + phpThumb.php/100;/images/pic.jpg + phpThumb.php/100x200;pic.jpg + phpThumb.php/x200;pic.jpg + phpThumb.php/f=jpeg;q=50;100x200;pic.jpg + phpThumb.php/fltr[]=usm;100;pic.jpg + + must be the last item. Dimensions must be the second- +last item. As many key/value pairs for parameters can be +passed before those last two items, with each pair joined by +equals ("=") and separated by semicolon (";") + + +============================================== +Calling as an object (not using phpThumb.php): +============================================== + +NOTE: most people don't need to and should not do this. If you just want to +display resized images, please just use phpThumb.php, not the object mode. +To render output to one (or more) files instead of the browser, you should +skip phpThumb.php and instantiate your own object. Please take a look at +/demo/phpThumb.demo.object.php for details. + +Note: phpThumb.php is where the caching code is located, if you instantiate + your own phpThumb() object that code is bypassed and it's up to you to + handle the reading and writing of cached files. + + +============== +Configuration: +============== + +There are some configuration options you may (but are not required to) change. +Most configuration options can be set when you call phpThumb() - see list below), +but default configuration options (such as cache directory) are in +phpThumb.config.php - this is the only file you should ever modify. + + +IMPORTANT: +The configuration file is distributed as phpThumb.config.php.default to prevent +accidental overwriting of old configuration settings. Please migrate your old +settings to the new file (if upgrading), or delete your old config and rename +the default to phpThumb.config.php since newer releases may include settings not +present in your older configuration file. + + +The configuration options you should maybe modify are: +* high_security_password - required to generate the secure hashed URLs for + phpThumb using phpThumbURL() in phpThumb.config.php + Password must be sufficiently complex (typically means at least 20 mixed + alpha/numeric/punctuation characters). You can generate a good password + at http://www.silisoftware.com/tools/password-random.php +* cache_directory - thumbnailing is slow and processor-intensive. Enabling + caching will dramatically speed up future thumbnail serving +* imagemagick_path - Most image-processing functions can be processed faster + if ImageMagick is available on the server. +* max_source_pixels - This should be auto-detected, but if auto-detection + fails and you get an invalid image from large source images, set this to + about 20% of your available PHP memory limit. + The amount of memory required for phpThumb depends on several factors: + * the dimensions of the source image + * the dimensions of the output image + * whether unsharp masking is applied + * whether watermarks are applied, etc. + The auto-detection of memory limits works as a general "safe" value. You + may be able to exceed the auto value by a small or large amount, depending + on whether you apply watermarks and/or sharpening, and the output size of + your thumbnails. If ImageMagick is available then the amount of available + PHP memory is usually not an issue. + + +/////////////////////////////////////////////////////////////////////////// +Note: High-Security mode is VERY STRONGLY recommended enabled in all cases. +Each call to phpThumb.php needs to be made through the function supplied +at the bottom of phpThumb.config.php which create the hash: + require_once('phpThumb.config.php'); + echo ''; +/////////////////////////////////////////////////////////////////////////// + + +=========== +Parameters: +=========== + + src = filename of source image + new = create new image, not thumbnail of existing image. + Requires "w" and "h" parameters set. + [ex: &new=FF0000|75] - red background, 75% opacity + Set to hex color string of background. Opacity is + optional (defaults to 100% opaque). + w = max width of output thumbnail in pixels + h = max height of output thumbnail in pixels + wp = max width for portrait images + hp = max height for portrait images + wl = max width for landscape images + hl = max height for landscape images + ws = max width for square images + hs = max height for square images + f = output image format, one of: + ("jpeg", "png", "gif", "webp", "wbmp", "ico", "bmp") + q = JPEG compression (1=worst, 95=best, 75=default) + sx = left side of source rectangle (default = 0) + (values 0 < sx < 1 represent percentage) + sy = top side of source rectangle (default = 0) + (values 0 < sy < 1 represent percentage) + sw = width of source rectangle (default = fullwidth) + (values 0 < sw < 1 represent percentage) + sh = height of source rectangle (default = fullheight) + (values 0 < sh < 1 represent percentage) + zc = zoom-crop. Will auto-crop off the larger dimension + so that the image will fill the smaller dimension + (requires both "w" and "h", overrides "iar", "far") + Set to "1" or "C" to zoom-crop towards the center, + or set to "T", "B", "L", "R", "TL", "TR", "BL", "BR" + to gravitate towards top/left/bottom/right directions + (requies ImageMagick for values other than "C" or "1") + ica = ImageCropAuto, requires (PHP 5 >= 5.5.0, PHP 7) + https://www.php.net/manual/en/function.imagecropauto.php + value can be 0-4 (IMG_CROP_DEFAULT, IMG_CROP_TRANSPARENT, + IMG_CROP_BLACK, IMG_CROP_WHITE, IMG_CROP_SIDES) or can be + "5||" where is between 0 + and 1, and is the hex background color + bg = background hex color (default = FFFFFF) + bc = border hex color (default = 000000) +fltr = filter system. Call as an array as follows: + - "brit" (Brightness) [ex: &fltr[]=brit|] + where is the amount +/- to adjust brightness + (range -255 to 255) + Available in PHP5 with bundled GD only. + - "cont" (Constrast) [ex: &fltr[]=cont|] + where is the amount +/- to adjust contrast + (range -255 to 255) + Available in PHP5 with bundled GD only. + - "gam" (Gamma Correction) [ex: &fltr[]=gam|] + where can be a number 0.01 to 10 (default 1.0) + Must be >0 (zero gives no effect). There is no max, + although beyond 10 is pretty useless. Negative + numbers actually do something, maybe not quite the + desired effect, but interesting nonetheless. + - "sat" (SATuration) [ex: &fltr[]=sat|] + where is a number between zero (no change) + and -100 (complete desaturation = grayscale), or it + can be any positive number for increased saturation. + - "ds" (DeSaturate) [ex: &fltr[]=ds|] + is an alias for "sat" except values are inverted + (positive values remove color, negative values boost + saturation) + - "gray" (Grayscale) [ex: &fltr[]=gray] + remove all color from image, make it grayscale + - "th" (Threshold) [ex: &fltr[]=th|] + makes image greyscale, then sets all pixels brighter + than (range 0-255) to white, and all pixels + darker than to black + - "rcd" (Reduce Color Depth) [ex: &fltr[]=rcd||] + where is the number of colors (2-256) you want + in the output image, and is "1" for dithering + (deault) or "0" for no dithering + - "clr" (Colorize) [ex: &fltr[]=clr||] + where is a number between 0 and 100 for the + amount of colorization, and is the hex color + to colorize to. + - "sep" (Sepia) [ex: &fltr[]=sep||] + where is a number between 0 and 100 for the + amount of colorization (default=50), and is + the hex color to colorize to (default=A28065). + Note: this behaves differently when applied by + ImageMagick, in which case 80 is default, and lower + values give brighter/yellower images and higher + values give darker/bluer images + - "usm" (UnSharpMask) [ex: &fltr[]=usm|||] + where is the amount (default = 80, range 0-255), + is the radius (default = 0.5, range 0.0-10.0), + is the threshold (default = 3, range 0-50). + - "blur" (Blur) [ex: &fltr[]=blur|] + where (0 < < 25) (default = 1) + - "gblr" (Gaussian Blur) [ex: &fltr[]=gblr] + Available in PHP5 with bundled GD only. + - "sblr" (Selective Blur) [ex: &fltr[]=gblr] + Available in PHP5 with bundled GD only. + - "smth" (Smooth) [ex: &fltr[]=smth|] + where is the weighting value for the matrix + (range -10 to 10, default 6) + Available in PHP5 with bundled GD only. + - "lvl" (Levels) + [ex: &fltr[]=lvl||| + where can be one of 'r', 'g', 'b', 'a' (for + Red, Green, Blue, Alpha respectively), or '*' for all + RGB channels (default) based on grayscale average. + ImageMagick methods can support multiple channels + (eg "lvl|rg|3") but internal methods cannot (they will + use first character of channel string as channel) + can be one of: + 0=Internal RGB; + 1=Internal Grayscale; + 2=ImageMagick Contrast-Stretch (default) + 3=ImageMagick Normalize (may appear over-saturated) + is how much of brightest/darkest pixels + will be clipped in percent (default = 0.1%) + Using default parameters (&fltr[]=lvl) is similar to + Auto Contrast in Adobe Photoshop. + - "wb" (White Balance) [ex: &fltr[]=wb|] + where is the target hex color to white balance + on, this color is what "should be" white, or light + gray. The filter attempts to maintain brightness so + any gray color can theoretically be used. If is + omitted the filter guesses based on brightest pixels + in each of RGB + OR can be the percent of white clipping used + to calculate auto-white-balance (default = 0.1%) + NOTE: "wb" in default settings already gives an effect + similar to "lvl", there is usually no need to use "lvl" + if "wb" is already used. + - "hist" (Histogram) + [ex: &fltr[]=hist||||||||] + Where is the color band(s) to display, from back + to front (one or more of "rgba*" for Red Green Blue + Alpha and Grayscale respectively); + is a semicolon-separated list of hex colors to + use for each graph band (defaults to FF0000, 00FF00, + 0000FF, 999999, FFFFFF respectively); + and are the width and height of the overlaid + histogram in pixels, or if <= 1 then percentage of + source image width/height; + is the alignment (same as for "wmi" and "wmt"); + is opacity from 0 (transparent) to 100 (opaque) + (requires PHP v4.3.2, otherwise 100% opaque); + and are the edge margin in pixels (or percent + if 0 < (x|y) < 1) + - "over" (OVERlay/underlay image) overlays an image on + the thumbnail, or overlays the thumbnail on another + image (to create a picture frame for example) + [ex: &fltr[]=over||||] + where is the image filename; is "0" (default) + for overlay the image on top of the thumbnail or "1" + for overlay the thumbnail on top of the image; is + the margin - can be absolute pixels, or if < 1 is a + percentage of the thumbnail size [must be < 0.5] + (default is 0 for overlay and 10% for underlay); + is opacity (0 = transparent, 100 = opaque) + (requires PHP v4.3.2, otherwise 100% opaque); + (thanks raynerapegmail*com, shabazz3msu*edu) + - "wmi" (WaterMarkImage) + [ex: &fltr[]=wmi||||||] where + is the filename of the image to overlay; + is the alignment (one of BR, BL, TR, TL, C, + R, L, T, B, *) where B=bottom, T=top, L=left, + R=right, C=centre, *=tile) + *or* + an absolute position in pixels (from top-left + corner of canvas to top-left corner of overlay) + in format {xoffset}x{yoffset} (eg: "10x20") + note: this is center position of image if + and are set + is opacity from 0 (transparent) to 100 (opaque) + (requires PHP v4.3.2, otherwise 100% opaque); + and are the edge (and inter-tile) margin in + pixels (or percent if 0 < (x|y) < 1) + *or* + if is absolute-position format then and + represent maximum width and height that the + watermark image will be scaled to fit inside + is rotation angle of overlaid watermark + - "wmt" (WaterMarkText) + [ex: &fltr[]=wmt||||||||||||] + where: + is the text to use as a watermark; + URLencoded Unicode HTMLentities must be used for + characters beyond chr(127). For example, the + "eighth note" character (U+266A) is represented + as "♪" and then urlencoded to "%26%239834%3B" + Any instance of metacharacters will be replaced + with their calculated value. Currently supported: + ^Fb = source image filesize in bytes + ^Fk = source image filesize in kilobytes + ^Fm = source image filesize in megabytes + ^X = source image width in pixels + ^Y = source image height in pixels + ^x = thumbnail width in pixels + ^y = thumbnail height in pixels + ^^ = the character ^ + is the font size (1-5 for built-in font, or point + size for TrueType fonts); + is the alignment (one of BR, BL, TR, TL, C, R, L, + T, B, * where B=bottom, T=top, L=left, R=right, + C=centre, *=tile); + note: * does not work for built-in font "wmt" + *or* + an absolute position in pixels (from top-left + corner of canvas to top-left corner of overlay) + in format {xoffset}x{yoffset} (eg: "10x20") + is the hex color of the text; + is the filename of the TTF file (optional, if + omitted a built-in font will be used); + is opacity from 0 (transparent) to 100 (opaque) + (requires PHP v4.3.2, otherwise 100% opaque); + is the edge (and inter-tile) margin in percent; + is the angle + is the hex color of the background; + is background opacity from 0 (transparent) to + 100 (opaque) + (requires PHP v4.3.2, otherwise 100% opaque); + is the direction(s) in which the background is + extended (either 'x' or 'y' (or both, but both + will obscure entire image)) + Note: works with TTF fonts only, not built-in + is the scale multiplier for line height/spacing + default is 1.0 + - "flip" [ex: &fltr[]=flip|x or &fltr[]=flip|y] + flip image on X or Y axis + - "ric" [ex: &fltr[]=ric||] + rounds off the corners of the image (to transparent + for PNG output), where is the horizontal radius + of the curve and is the vertical radius + - "elip" [ex: &fltr[]=elip] + similar to rounded corners but more extreme + - "mask" [ex: &fltr[]=mask|filename.png|] + greyscale values of mask are applied as the alpha + channel to the main image. White is opaque, black + is transparent, unless the (invert) parameter is + set to 1 in which case black is opaque and white is + transparent + - "bvl" (BeVeL) [ex: &fltr[]=bvl|||] + where is the bevel width, is the hex color + for the top and left shading, is the hex color + for the bottom and right shading + - "bord" (BORDer) [ex: &fltr[]=bord|||| + where is the width in pixels, and are + horizontal and vertical radii for rounded corners, + and is the hex color of the border + - "fram" (FRAMe) draws a frame, similar to "bord" but + more configurable + [ex: &fltr[]=fram|||||] + where is the width of the main border, is + the width of each side of the bevel part, is the + hex color of the main border, is the highlight + bevel color, is the shadow bevel color + - "drop" (DROP shadow) + [ex: &fltr[]=drop|||||] + where is distance from image to shadow, is + width of shadow fade (not yet implemented), is + the hex color of the shadow, is the angle of the + shadow (default=225), is opacity (0=transparent, + 100=opaque, default=100) (not yet implemented) + - "crop" (CROP image) + [ex: &fltr[]=crop||||] + where is the number of pixels to crop from the left + side of the resized image; , , are for right, + top and bottom respectively. Where (0 < x < 1) the + value will be used as a percentage of width/height. + Left and top crops take precedence over right and + bottom values. Cropping will be limited such that at + least 1 pixel of width and height always remains. + - "rot" (ROTate) + [ex: &fltr[]=rot||] + where is the rotation angle in degrees; is the + background hex color. Similar to regular "ra" parameter + but is applied in filter order after regular processing + so you can rotate output of other filters. + - "size" (reSIZE) + [ex: &fltr[]=size|||] + where is the horizontal dimension in pixels, is + the vertical dimension in pixels, is boolean whether + to stretch (if 1) or resize proportionately (0, default) + and will be interpreted as percentage of current + output image size if values are (0 < X < 1) + NOTE: do NOT use this filter unless absolutely necessary. + It is only provided for cases where other filters need to + have absolute positioning based on source image and the + resultant image should be resized after other filters are + applied. This filter is less efficient than the standard + resizing procedures. + - "stc" (Source Transparent Color) + [ex: &fltr[]=stc|||] + where is the hex color of the target color to be made + transparent; is the minimum threshold in percent (all + pixels within % of the target color will be 100% + transparent, default =5); is the maximum threshold + in percent (all pixels more than % from the target + color will be 100% opaque, default =10); pixels between + the two thresholds will be partially transparent. +md5s = MD5 hash of the source image -- if this parameter is + passed with the hash of the source image then the + source image is not checked for existence or + modification and the cached file is used (if + available). If 'md5s' is passed an empty string then + phpThumb.php dies and outputs the correct MD5 hash + value. This parameter is the single-file equivalent + of 'cache_source_filemtime_ignore_*' configuration + parameters + xto = EXIF Thumbnail Only - set to only extract EXIF + thumbnail and not do any additional processing + ra = Rotate by Angle: angle of rotation in degrees + positive = counterclockwise, negative = clockwise + ar = Auto Rotate: set to "x" to use EXIF orientation + stored by camera. Can also be set to "l" or "L" + for landscape, or "p" or "P" for portrait. "l" + and "P" rotate the image clockwise, "L" and "p" + rotate the image counter-clockwise. + sfn = Source Frame Number - use this frame/page number for + multi-frame/multi-page source images (GIF, TIFF, etc) + aoe = Output Allow Enlarging - override the setting for + $CONFIG['output_allow_enlarging'] (1=on, 0=off) + ("far" and "iar" both override this and allow output + larger than input) + iar = Ignore Aspect Ratio - disable proportional resizing + and stretch image to fit "h" & "w" (which must both + be set). (1=on, 0=off) (overrides "far") + far = Force Aspect Ratio - image will be created at size + specified by "w" and "h" (which must both be set). + Alignment: L=left,R=right,T=top,B=bottom,C=center + BL,BR,TL,TR use the appropriate direction if the + image is landscape or portrait. + dpi = Dots Per Inch - input DPI setting when importing from + vector image format such as PDF, WMF, etc + sia = Save Image As - default filename to save generated + image as. Specify the base filename, the extension + (eg: ".png") will be automatically added +maxb = MAXimum Byte size - output quality is auto-set to + fit thumbnail into "maxb" bytes (compression + quality is adjusted for JPEG, bit depth is adjusted + for PNG and GIF) +down = filename to save image to. If this is set the + browser will prompt to save to this filename rather + than display the image + +// Deprecated: +file = if set then thumbnail will be rendered to this + filename, not output and not cached. + (Deprecated. Disabled by default since v1.6.0, + unavailable in v1.7.5 and later. You should + instantiate your own object instead) +goto = URL to redirect to after rendering image to file + * Must begin with "http://" + * Requires file parameter set + (Deprecated. Disabled by default since v1.6.0, + unavailable in v1.7.5 and later. You should + instantiate your own object instead) + err = custom error image filename instead of showing + error messages (for use on production sites) + (Deprecated. Disabled by default since v1.6.0, + unavailable in v1.7.5 and later. You should + instantiate your own object instead) + + +============== +General Notes: +============== + +* Always use the local image filename wherever possible + rather than a full http:// URL because performance is + much better, less (or no) use of temp files, and the + last-modified check for cached files doesn't work for + remote files. For example: + good: phpThumb.php?src=/images/nicepic.jpg + bad: phpThumb.php?src=/home/httpd/example/images/nicepic.jpg + worse: phpThumb.php?src=http://example.com/images/nicepic.jpg + +* Thumbnails will be scaled proportionately to fit in a + box of at most (width * height) pixels + (unless "iar" is set) + +* Thumbnail caching for URL or database sources requires + an absolute directory name for $config_cache_directory + Physical file cached thumbnails will be recreated if + the source file changes, but remote/database files + cannot (modification time isn't readily available) + +* If you need a GUI interface to phpThumb(), or for a user + to specify crop settings, or something like that please + see the list of known programs in /demo/readme.demos.txt + +* Cropping images can be specified with either exact pixel + values for sx/sy/sw/sh parameters, or if those are set + to a value >0 and <1 then these are interpreted as a + percentage of the source image width/height. For example, + to crop 25% off all sides, you would specify parameters: + phpThumb.php?src=pic.jpg&sx=.25&sy=.25&sw=.5&sh=.5 + +* phpThumb() may have tempfile access issues on servers + where Safe Mode is enabled, specificly when accessing + a file over HTTP, or when a non-bundled version of GD + is in use. Specifying "config_temp_directory" may help + +* Properly resolving /~user/ style filenames requires + apache_lookup_uri(), which is missing or broken in + Apache2, or if PHP is not installed as an Apache module. + phpThumb() does try and work around this if it is + unavailable, but you may have to specify a full filename + for "src" if you encounter problems. + +* phpThumb() should work with PHP v4.0.6+, but seems to + have a few quirks before v4.1.0 + EXIF thumbnail extraction requires PHP v4.2.0+ + Image rotation requires PHP v4.3.0+. There have been + reports of problems with PHP older than v4.3.3 + Partial transparency for overlays requires PHP v4.3.2+ + Some image filters require PHP v5.0.0+ + Run /demo/phpThumb.demo.check.php to examine your server + +* phpThumb() works better and faster when ImageMagick is + available. Most functions will work with only GD2, but + speed is much faster with ImageMagick, and much larger + images can be processed with ImageMagick than GD. + +* phpThumb() works with GD v1.x, but works better with + GD v2.0+ because of the true-color image support + and ImageCopyResampled(). Also, there appears to be a + bug in ImageCopyResized() which is used with GD v1.x + where the bottom and/or right line of pixels is set + to the background color (due to a rounding error?) + NOTE: Please use the bundled version of GD if at all + possible (with PHP v4.3.0+) because the non-bundled + version has bugs which may cause PHP to crash: + * http://bugs.php.net/bug.php?id=21518 + * http://bugs.php.net/bug.php?id=24174 + phpThumb() has a workaround for the above bug but + there may be other bugs, and the workaround is slow. + Alpha transparent output requires GD >= 2.0.1 and + PHP >= 4.3.2 + Most (if not all) filters require GD v2.x to function + at all. But many filters can be handled by ImageMagick + instead of GD. + +* Filters handled by ImageMagick or GD: + - brit;cont;ds;sat;gray;clr;sep;gam;neg;th;rcd;flip;edge; + emb;lvl;blur;gblr;usm;wb; +* Filters handled only by ImageMagick: + - none yet +* Filters handled only by GD + PHP5: + - sblr;mean;smth; +* Filters handled only by GD2: + - bvl;wmi;wmt;over;hist;fram;drop;mask;elip;ric;bord; + +* Some browsers, notably Internet Explorer (before v7.0), + have problems with PNG transparency. See description: + http://www.silisoftware.com/png_alpha_transparency/ + http://trific.ath.cx/web/png/ + There are some work-around fixes for this IE problem, eg: + http://www.twinhelix.com/css/iepngfix/demo/ + + +=============== +Thanks & Links: +=============== + +* Original image used in phpThumb logo provided by + Mark James: http://www.famfamfam.com/lab/icons/ + + + +======================================== +== phpThumb Commercial License (pTCL) == +======================================== + +See /docs/phpthumb.license.commercial.txt +for details on the terms of the license. + +Current list of pTCL licensees: +(updated list at http://www.phpthumb.com/) + +1) Accomplish Hosting, LLC + http://www.accomplishhosting.com + effective 2007-Apr-28 (lifetime license) + +2) Eric Pujol + http://www.kaprikorn.fr/ + effective 2007-Sep-29 (lifetime license) + +3) CalemEAM Inc. + http://www.calemeam.com/ + effective 2010-Jun-14 (lifetime license) \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/fonts/readme.txt b/videodb/vendor/james-heinrich/phpthumb/fonts/readme.txt new file mode 100644 index 0000000..2e5ef72 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/fonts/readme.txt @@ -0,0 +1,5 @@ +This is the default location for TTF fonts. + +You can safely delete or ignore this directory if you're not using +TTF fonts for text watermarks. You can also specify an alternate +directory in phpThumb.config.php \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/images/readme.txt b/videodb/vendor/james-heinrich/phpthumb/images/readme.txt new file mode 100644 index 0000000..2640965 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/images/readme.txt @@ -0,0 +1,4 @@ +If you're looking for the demo images, they can be downloaded from +the bottom of the phpThumb demo page on SourceForge: + +http://phpthumb.sourceforge.net/demo/demo/phpThumb.demo.demo.php \ No newline at end of file diff --git a/videodb/vendor/james-heinrich/phpthumb/index.php b/videodb/vendor/james-heinrich/phpthumb/index.php new file mode 100644 index 0000000..8a666e0 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/index.php @@ -0,0 +1,10 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// + +***************************************************************** +***************************************************************** + + phpThumb is released under multiple licenses. You may choose + from the following licenses, and use getID3 according to the + terms of the license most suitable to your project. + +GNU GPL: https://gnu.org/licenses/gpl.html (v3) + https://gnu.org/licenses/old-licenses/gpl-2.0.html (v2) + https://gnu.org/licenses/old-licenses/gpl-1.0.html (v1) + +GNU LGPL: https://gnu.org/licenses/lgpl.html (v3) + +Mozilla MPL: https://www.mozilla.org/MPL/2.0/ (v2) + +phpThumb Commercial License (pTCL): +http://phpthumb.sourceforge.net/#pTCL (payment required) + +***************************************************************** +***************************************************************** + +Copies of each of the above licenses are included in the 'licenses' +directory of the phpThumb distribution. diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-10.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-10.txt new file mode 100644 index 0000000..8de98af --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-10.txt @@ -0,0 +1,251 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-20.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-20.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-20.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-30.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-30.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.gpl-30.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.lgpl-30.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.lgpl-30.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.lgpl-30.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.mpl-20.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.mpl-20.txt new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.mpl-20.txt @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/videodb/vendor/james-heinrich/phpthumb/licenses/license.ptcl.txt b/videodb/vendor/james-heinrich/phpthumb/licenses/license.ptcl.txt new file mode 100644 index 0000000..37c845d --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/licenses/license.ptcl.txt @@ -0,0 +1,27 @@ + phpThumb() Commercial License + ============================= + +phpThumb() is licensed under the "GNU Public License" (GPL) and/or the +"phpThumb() Commercial License" (pTCL). This document describes the pTCL. + +--------------------------------------------------------------------- + +The license is non-exclusively granted to a single person or company, +per payment of the license fee, for the lifetime of that person or +company. The license is non-transferrable. + +The pTCL grants the licensee the right to use phpThumb() in commercial +closed-source projects. Modifications may be made to phpThumb() with no +obligation to release the modified source code. phpThumb() (or pieces +thereof) may be included in any number of projects authored (in whole +or in part) by the licensee. + +The licensee may use any version of phpThumb(), past, present or future, +as is most convenient. This license does not entitle the licensee to +receive any technical support, updates or bugfixes, except as such are +made publicly available to all phpThumb() users. + +The licensee may not sub-license phpThumb() itself, meaning that any +commercially released product containing all or parts of phpThumb() must +have added functionality beyond what is available in phpThumb(); +phpThumb() itself may not be re-licensed by the licensee. diff --git a/videodb/vendor/james-heinrich/phpthumb/phpThumb.config.php.default b/videodb/vendor/james-heinrich/phpthumb/phpThumb.config.php.default new file mode 100644 index 0000000..82001ec --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpThumb.config.php.default @@ -0,0 +1,277 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// See: phpthumb.readme.txt for usage instructions // +// NOTE: THIS FILE HAS NO EFFECT IN OBJECT MODE! // +// THIS CONFIG FILE ONLY APPLIES TO phpThumb.php // +// /// +////////////////////////////////////////////////////////////// + +define('phpThumbConfigFileVersion', '1.7.16'); +ob_start(); +if (!class_exists('phpthumb_functions', false)) { // normally include_once should take care of this, but see https://github.com/JamesHeinrich/phpThumb/issues/94 + if (!file_exists( __DIR__ .'/phpthumb.functions.php') || !include_once( __DIR__ .'/phpthumb.functions.php')) { + ob_end_flush(); + die('failed to include_once(phpthumb.functions.php) - realpath="'.realpath( __DIR__ .'/phpthumb.functions.php').'"'); + } +} +ob_end_clean(); + + + +/****************************************************************************************/ +/* START USER CONFIGURATION SECTION: */ +global $PHPTHUMB_CONFIG; // declare as global to prevent scope issues (when including phpThumb.config.php inside functions inside included files, etc) +$PHPTHUMB_CONFIG = array(); + +// * DocumentRoot configuration +// phpThumb() depends on $_SERVER['DOCUMENT_ROOT'] to resolve path/filenames. This value is usually correct, +// but has been known to be broken on some servers. This value allows you to override the default value. +// Do not modify from the auto-detect default value unless you are having problems. +//$PHPTHUMB_CONFIG['document_root'] = '/home/httpd/httpdocs'; +//$PHPTHUMB_CONFIG['document_root'] = 'c:\\webroot\\example.com\\www'; +//$PHPTHUMB_CONFIG['document_root'] = $_SERVER['DOCUMENT_ROOT']; +//$PHPTHUMB_CONFIG['document_root'] = realpath((@$_SERVER['DOCUMENT_ROOT'] && file_exists(@$_SERVER['DOCUMENT_ROOT'].$_SERVER['PHP_SELF'])) ? $_SERVER['DOCUMENT_ROOT'] : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', realpath('.')))); +$PHPTHUMB_CONFIG['document_root'] = realpath((getenv('DOCUMENT_ROOT') && preg_match('#^'.preg_quote(realpath(getenv('DOCUMENT_ROOT'))).'#', realpath(__FILE__))) ? getenv('DOCUMENT_ROOT') : str_replace(dirname(@$_SERVER['PHP_SELF']), '', str_replace(DIRECTORY_SEPARATOR, '/', __DIR__ ))); + + +// * Security configuration +$PHPTHUMB_CONFIG['disable_debug'] = true; // DO NOT DISABLE THIS ON ANY PUBLIC-ACCESSIBLE SERVER. Prevents phpThumb from displaying any information about your system. If true, phpThumbDebug and error messages will be disabled. If set to false (debug messages enabled) then debug mode will be FORCED -- ONLY debug output will be presented, no actual thumbnail (to avoid accidentally leaving debug mode enabled on a production server) +$PHPTHUMB_CONFIG['high_security_enabled'] = true; // DO NOT DISABLE THIS ON ANY PUBLIC-ACCESSIBLE SERVER. If disabled, your server is more vulnerable to hacking attempts, both on your server and via your server to other servers. When enabled, requires 'high_security_password' set to be set and requires the use of phpThumbURL() function (at the bottom of phpThumb.config.php) to generate hashed URLs +$PHPTHUMB_CONFIG['high_security_password'] = '__HSP_KEY__'; // required if 'high_security_enabled' is true, and must be at complex (uppercase, lowercase, numbers, punctuation, etc -- punctuation is strongest, lowercase is weakest; see PasswordStrength() in phpthumb.functions.php). You can use a password generator like http://silisoftware.com/tools/password-random.php to generate a strong password + +$PHPTHUMB_CONFIG['high_security_url_separator'] = '&'; // should almost always be left as '&'. Must be a single character. Do not change to '&' -- htmlspecialchars wrapped around phpThumbURL() takes care of this without breaking the hash +$PHPTHUMB_CONFIG['allow_src_above_docroot'] = false; // if false (default) only allow src within document_root; if true, allow src to be anywhere in filesystem +$PHPTHUMB_CONFIG['allow_src_above_phpthumb'] = true; // if true (default), allow src to be anywhere in filesystem; if false only allow src within sub-directory of phpThumb installation +$PHPTHUMB_CONFIG['auto_allow_symlinks'] = true; // if true (default), allow symlink target directories without explicitly whitelisting them +$PHPTHUMB_CONFIG['additional_allowed_dirs'] = array(); // array of additional directories to allow source images to be read from + + +// * Cache directory configuration (choose only one of these - leave the other lines commented-out): +// Note: this directory must be writable (usually chmod 777 is neccesary) for caching to work. +// If the directory is not writable no error will be generated but caching will be disabled. +$PHPTHUMB_CONFIG['cache_directory'] = __DIR__.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR; // set the cache directory relative to the phpThumb() installation +//$PHPTHUMB_CONFIG['cache_directory'] = $PHPTHUMB_CONFIG['document_root'].DIRECTORY_SEPARATOR.'phpthumb'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR; // set the cache directory to an absolute directory for all source images +//$PHPTHUMB_CONFIG['cache_directory'] = '.'.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR; // set the cache directory relative to the source image - must start with '.' (will not work to cache URL- or database-sourced images, please use an absolute directory name) +//$PHPTHUMB_CONFIG['cache_directory'] = null; // disable thumbnail caching (not recommended) +//if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { +// $PHPTHUMB_CONFIG['cache_directory'] = __DIR__.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR; // set the cache directory to an absolute directory for all source images +// $PHPTHUMB_CONFIG['cache_directory'] = '/tmp/persistent/phpthumb/cache/'; +//} + +$PHPTHUMB_CONFIG['cache_disable_warning'] = false; // If [cache_directory] is non-existant or not writable, and [cache_disable_warning] is false, an error image will be generated warning to either set the cache directory or disable the warning (to avoid people not knowing about the cache) +$PHPTHUMB_CONFIG['cache_directory_depth'] = 2; // If this larger than zero, cache structure will be broken into a broad directory structure based on cache filename. For example "cache_src012345..." will be stored in "/0/01/012/0123/cache_src012345..." when (cache_directory_depth = 4). Caution: larger values can lead to an exponentially larger number of subdirectories which will also affect disk space due to (typically) 4kB used per directory entry: "2" gives a maximum of 16^2=256 subdirectories (up to 1MB wasted space), "3": 16^3=4096 subdirs (up to 16MB wasted), "4": 16^4=65536 subdirs (256MB wasted space), etc. + +// * Cache culling: phpThumb can automatically limit the contents of the cache directory +// based on last-access date and/or number of files and/or total filesize. + +//$PHPTHUMB_CONFIG['cache_maxage'] = null; // never delete cached thumbnails based on last-access time +$PHPTHUMB_CONFIG['cache_maxage'] = 86400 * 30; // delete cached thumbnails that haven't been accessed in more than [30 days] (value is maximum time since last access in seconds to avoid deletion) + +//$PHPTHUMB_CONFIG['cache_maxsize'] = null; // never delete cached thumbnails based on byte size of cache directory +$PHPTHUMB_CONFIG['cache_maxsize'] = 10 * 1024 * 1024; // delete least-recently-accessed cached thumbnails when more than [10MB] of cached files are present (value is maximum bytesize of all cached files). Note: this only counts file size, does not count space "wasted" by directory entries in the cache structure -- see notes under $PHPTHUMB_CONFIG['cache_directory_depth'] + +//$PHPTHUMB_CONFIG['cache_maxfiles'] = null; // never delete cached thumbnails based on number of cached files +$PHPTHUMB_CONFIG['cache_maxfiles'] = 200; // delete least-recently-accessed cached thumbnails when more than [200] cached files are present (value is maximum number of cached files to keep) + + +// * Source image cache configuration +$PHPTHUMB_CONFIG['cache_source_enabled'] = false; // if true, source images obtained via HTTP are cached to $PHPTHUMB_CONFIG['cache_source_directory'] +$PHPTHUMB_CONFIG['cache_source_directory'] = __DIR__.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; // set the cache directory for unprocessed source images + +// * cache source modification date configuration +$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_local'] = false; // if true, local source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed +$PHPTHUMB_CONFIG['cache_source_filemtime_ignore_remote'] = true; // if true, remote source images will not be checked for modification date and cached image will be used if available, even if source image is changed or removed. WARNING: cached performance MUCH slower if this is set to false. + + +// * Simplified cache filename configuration +// Instead of creating unique cache filenames for all parameter combinations, create "simple" cache files (eg: "pic_thumb.jpg") +// If cache_default_only_suffix is non-empty, GETstring parameters (except 'src') are ignored and only $PHPTHUMB_DEFAULTS +// parameters (set at the bottom of phpThumb.config.php) are used for processing. +// The '*' character MUST be used to represent the source image name +$PHPTHUMB_CONFIG['cache_default_only_suffix'] = ''; // cached in normal phpThumb manner +//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = '*_thumb'; // cache 'pic.jpg' becomes 'pic_thumb.jpg' (or 'pic_thumb.png' if PNG output is selected, etc) +//$PHPTHUMB_CONFIG['cache_default_only_suffix'] = 'small-*'; // cache 'pic.jpg' becomes 'small-pic.jpg' (or 'small-pic.png' if PNG output is selected, etc) + +$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache_'.(isset($_SERVER['SERVER_NAME']) ? str_replace('www.', '', $_SERVER['SERVER_NAME']).'_' : ''); // keep cache file separate by domain +//$PHPTHUMB_CONFIG['cache_prefix'] = 'phpThumb_cache'; // allow phpThumb to share 1 set of cached files even if accessed under different servername/domains on same server + +$PHPTHUMB_CONFIG['cache_force_passthru'] = true; // if true, cached image data will always be passed to browser; if false, HTTP redirect will be used instead + + + +// * Temp directory configuration +// phpThumb() may need to create temp files. Usually the system temp dir is writable and can be used. +// Leave this value as NULL in most cases. If you get errors about "failed to open for writing" +// you should change this to a full pathname to a directory you do have write access to. +//$PHPTHUMB_CONFIG['temp_directory'] = null; // attempt to auto-detect +//$PHPTHUMB_CONFIG['temp_directory'] = '/tmp/persistent/phpthumb/cache/'; // set to absolute path +$PHPTHUMB_CONFIG['temp_directory'] = $PHPTHUMB_CONFIG['cache_directory']; // set to same as cache directory + + +// ImageMagick configuration +$PHPTHUMB_CONFIG['prefer_imagemagick'] = true; // If true, use ImageMagick to resize thumbnails if possible, since it is usually faster than GD functions; if false only use ImageMagick if PHP memory limit is too low. +$PHPTHUMB_CONFIG['imagemagick_use_thumbnail'] = true; // If true, use ImageMagick's "-thumbnail" resizing parameter (if available) which removes extra non-image metadata (profiles, EXIF info, etc) resulting in much smaller filesize; if false, use "-resize" paramter which retains this info +if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + // Windows: set absolute pathname + $PHPTHUMB_CONFIG['imagemagick_path'] = 'C:/ImageMagick/convert.exe'; +} else { + // *nix: set absolute pathname to "convert", or leave as null if "convert" is in the path (location detected with `which`) + //$PHPTHUMB_CONFIG['imagemagick_path'] = '/usr/local/bin/convert'; + $PHPTHUMB_CONFIG['imagemagick_path'] = null; +} + + +// NOTE: "max_source_pixels" only affects GD-resized thumbnails. If you have ImageMagick installed it will bypass most of these limits +// maximum number of pixels in source image to attempt to process entire image in GD mode. +// If this is zero then no limit on source image dimensions. +// If this is nonzero then this is the maximum number of pixels the source image can have to be processed normally, otherwise the +// embedded EXIF thumbnail will be used (if available) or an "image too large" notice will be displayed. This is to be used for large +// source images (>2Mpx) and low PHP memory limits. If PHP runs out of memory the script will usually just die with no output. +// To calculate this number, multiply the dimensions of the largest image you can process with your memory limitation (e.g. 1600 * 1200 = 1920000) +// As a general guideline, this number will be about 20% of your PHP memory configuration, so 8M = 1,677,722; 16M = 3,355,443; 32M = 6,710,886; etc. +if (phpthumb_functions::version_compare_replacement(phpversion(), '4.3.2', '>=') && !defined('memory_get_usage') && !@ini_get('memory_limit')) { + // memory_get_usage() will only be defined if your PHP is compiled with the --enable-memory-limit configuration option. + $PHPTHUMB_CONFIG['max_source_pixels'] = 0; // no memory limit +} else { + // calculate default max_source_pixels as 1/6 of memory limit configuration + $PHPTHUMB_CONFIG['max_source_pixels'] = round(max(intval(ini_get('memory_limit')), intval(get_cfg_var('memory_limit'))) * 1048576 / 6); + //$PHPTHUMB_CONFIG['max_source_pixels'] = 0; // no memory limit + //$PHPTHUMB_CONFIG['max_source_pixels'] = 1920000; // allow 1600x1200 images (2Mpx), no larger (about 12MB memory required) + //$PHPTHUMB_CONFIG['max_source_pixels'] = 2795000; // 16MB memory limit + //$PHPTHUMB_CONFIG['max_source_pixels'] = 3871488; // allow 2272x1704 images (4Mpx), no larger (about 24MB memory required) +} + + +// * Default output configuration: +$PHPTHUMB_CONFIG['output_format'] = 'jpeg'; // default output format ('jpeg', 'png' or 'gif') - thumbnail will be output in this format (if available in your version of GD or ImageMagick). This is only used if the "f" parameter is not specified, and if the thumbnail can't be output in the input format. +$PHPTHUMB_CONFIG['output_maxwidth'] = 0; // default maximum thumbnail width. If this is zero then default width is the width of the source image. This is always overridden by ?w=___ GETstring parameter +$PHPTHUMB_CONFIG['output_maxheight'] = 0; // default maximum thumbnail height. If this is zero then default height is the height of the source image. This is always overridden by ?h=___ GETstring parameter +$PHPTHUMB_CONFIG['output_interlace'] = true; // if true: interlaced output for GIF/PNG, progressive output for JPEG; if false: non-interlaced for GIF/PNG, baseline for JPEG. + +// * Error message configuration +$PHPTHUMB_CONFIG['error_image_width'] = 400; // default width for error images +$PHPTHUMB_CONFIG['error_image_height'] = 100; // default height for error images +$PHPTHUMB_CONFIG['error_message_image_default'] = ''; // Set this to the name of a generic error image (e.g. '/images/error.png') that you want displayed in place of any error message that may occur. This setting is overridden by the 'err' parameter, which does the same thing. +$PHPTHUMB_CONFIG['error_bgcolor'] = 'CCCCFF'; // background color of error message images +$PHPTHUMB_CONFIG['error_textcolor'] = 'FF0000'; // color of text in error messages +$PHPTHUMB_CONFIG['error_fontsize'] = 1; // size of text in error messages, from 1 (smallest) to 5 (largest) +$PHPTHUMB_CONFIG['error_die_on_error'] = true; // die with error message on any fatal error (recommended with standalone phpThumb.php) +$PHPTHUMB_CONFIG['error_silent_die_on_error'] = false; // simply die with no output of any kind on fatal errors (not recommended) +$PHPTHUMB_CONFIG['error_die_on_source_failure'] = true; // die with error message if source image cannot be processed by phpThumb() (usually because source image is corrupt in some way). If false the source image will be passed through unprocessed, if true (default) an error message will be displayed. + +// * Off-server Thumbnailing Configuration: +$PHPTHUMB_CONFIG['nohotlink_enabled'] = true; // If false will allow thumbnailing from any source domain, if true then only domains in 'nohotlink_valid_domains' will be accepted +$PHPTHUMB_CONFIG['nohotlink_valid_domains'] = array(@$_SERVER['HTTP_HOST']); // This is the list of domains for which thumbnails are allowed to be created. Note: domain only, do not include port numbers. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format "www.example.com" +$PHPTHUMB_CONFIG['nohotlink_erase_image'] = true; // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail +$PHPTHUMB_CONFIG['nohotlink_text_message'] = 'Off-server thumbnailing is not allowed'; // text of error message + +// * Off-server Linking Configuration: +$PHPTHUMB_CONFIG['nooffsitelink_enabled'] = true; // If false will allow thumbnails to be linked to from any domain, if true only domains listed below in 'nooffsitelink_valid_domains' will be allowed. +$PHPTHUMB_CONFIG['nooffsitelink_valid_domains'] = array(@$_SERVER['HTTP_HOST']); // This is the list of domains for which thumbnails are allowed to be created. The default value of the current domain should be fine in most cases, but if neccesary you can add more domains in here, in the format 'www.example.com' +$PHPTHUMB_CONFIG['nooffsitelink_require_refer'] = false; // If false will allow standalone calls to phpThumb(). If true then only requests with a $_SERVER['HTTP_REFERER'] value in 'nooffsitelink_valid_domains' are allowed. +$PHPTHUMB_CONFIG['nooffsitelink_erase_image'] = false; // if true thumbnail is covered up with $PHPTHUMB_CONFIG['nohotlink_fill_color'] before text is applied, if false text is written over top of thumbnail +$PHPTHUMB_CONFIG['nooffsitelink_watermark_src'] = '/demo/images/watermark.png'; // webroot-relative image to overlay on hotlinked images +$PHPTHUMB_CONFIG['nooffsitelink_text_message'] = 'Image taken from '.@$_SERVER['HTTP_HOST']; // text of error message (used if [nooffsitelink_watermark_src] is not a valid image) + + +// * Border & Background default colors +$PHPTHUMB_CONFIG['border_hexcolor'] = '000000'; // Default border color - usual HTML-style hex color notation (overidden with 'bc' parameter) +$PHPTHUMB_CONFIG['background_hexcolor'] = 'FFFFFF'; // Default background color when thumbnail aspect ratio does not match fixed-dimension box - usual HTML-style hex color notation (overridden with 'bg' parameter) + +// * Watermark configuration +$PHPTHUMB_CONFIG['ttf_directory'] = __DIR__ .DIRECTORY_SEPARATOR.'fonts'; // Base directory for TTF font files +//$PHPTHUMB_CONFIG['ttf_directory'] = 'c:/windows/fonts'; + + +// * MySQL configuration +// You may want to pull data from a database rather than a physical file +// If so, modify the $PHPTHUMB_CONFIG['mysql_query'] line to suit your database structure +// Note: the data retrieved must be the actual binary data of the image, not a URL or filename +$PHPTHUMB_CONFIG['mysql_extension'] = 'mysqli'; // either "mysqli" or "mysql" + +$PHPTHUMB_CONFIG['mysql_query'] = ''; +//$PHPTHUMB_CONFIG['mysql_query'] = 'SELECT `picture` FROM `products` WHERE (`id` = \''.mysqli_real_escape_string(@$_GET['id']).'\')'; + +// These 4 values must be modified if $PHPTHUMB_CONFIG['mysql_query'] is not empty, but may be ignored if $PHPTHUMB_CONFIG['mysql_query'] is blank. +$PHPTHUMB_CONFIG['mysql_hostname'] = 'localhost'; +$PHPTHUMB_CONFIG['mysql_username'] = ''; +$PHPTHUMB_CONFIG['mysql_password'] = ''; +$PHPTHUMB_CONFIG['mysql_database'] = ''; + + +// * HTTP UserAgent configuration +//$PHPTHUMB_CONFIG['http_user_agent'] = ''; // PHP default: none +//$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'; // Windows XP, Internet Explorer +//$PHPTHUMB_CONFIG['http_user_agent'] = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7'; // Windows XP, Firefox +$PHPTHUMB_CONFIG['http_user_agent'] = @$_SERVER['HTTP_USER_AGENT']; // use client user-agent + + +// * Compatability settings +$PHPTHUMB_CONFIG['disable_pathinfo_parsing'] = true; // if true, $_SERVER[PATH_INFO] is not parsed. May be needed on some server configurations to allow normal behavior. +$PHPTHUMB_CONFIG['disable_imagecopyresampled'] = false; // if true, imagecopyresampled is replaced with ImageCopyResampleBicubic. May be needed for buggy versions of PHP-GD. +$PHPTHUMB_CONFIG['disable_onlycreateable_passthru'] = true; // if true, any image that can be parsed by getimagesize() can be passed through; if false, only images that can be converted to GD by ImageCreateFrom(JPEG|GIF|PNG) functions are allowed +$PHPTHUMB_CONFIG['disable_realpath'] = false; // PHP realpath() function requires that "the running script must have executable permissions on all directories in the hierarchy, otherwise realpath() will return FALSE". Set config_disable_realpath=false to enable alternate filename-parsing that does not use realpath() function (but also does not resolve symbolic links) + + +// * HTTP remote file opening settings +$PHPTHUMB_CONFIG['http_fopen_timeout'] = 10; // timeout (in seconds) for fopen / curl / fsockopen +$PHPTHUMB_CONFIG['http_follow_redirect'] = true; // if true (default), follow "302 Found" redirects to new URL; if false, return error message + + +// * Speed optimizations configuration +$PHPTHUMB_CONFIG['allow_local_http_src'] = false; // If true, 'src' parameter can be "http:///path/image.ext" instead of just "/path/image.ext"; if false then display warning message to encourage more efficient local-filename calling. +$PHPTHUMB_CONFIG['use_exif_thumbnail_for_speed'] = false; // If true, and EXIF thumbnail is available, and is larger or equal to output image dimensions, use EXIF thumbnail rather than actual source image for generating thumbnail. Benefit is only speed, avoiding resizing large image. + +/* END USER CONFIGURATION SECTION */ + + + + +/* START DEFAULT PARAMETERS SECTION */ +// If any parameters are constant across ALL images, you can set them here + +$PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE = true; // if true, any parameters in the URL will override the defaults set here; if false, any parameters set here cannot be overridden in the URL +$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS = false; // if true, GETstring parameters will be ignored (except for 'src') and only below default parameters will be used; if false, both default and GETstring parameters will be used (depending on $PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE). Will be auto-set true if !empty($PHPTHUMB_CONFIG['cache_default_only_suffix']) + +//$PHPTHUMB_DEFAULTS['w'] = 200; +//$PHPTHUMB_DEFAULTS['fltr'] = array('blur|10'); +//$PHPTHUMB_DEFAULTS['q'] = 90; + + +/* END DEFAULT PARAMETERS SECTION */ + + + +////////////////////////////////////////////////////////////////////////////// +// Function for generating hashed calls to phpThumb if 'high_security_enabled' +// example: +// require_once('phpThumb/phpThumb.config.php'); +// echo ''; + +$GLOBALS['PHPTHUMB_CONFIG'] = $PHPTHUMB_CONFIG; +function phpThumbURL($ParameterString, $path_to_phpThumb='phpThumb.php') { + global $PHPTHUMB_CONFIG; + if (is_array($ParameterString)) { + $ParameterStringArray = $ParameterString; + } else { + parse_str($ParameterString, $ParameterStringArray); + } + $ParamterStringEncodedArray = array(); + foreach ($ParameterStringArray as $key => $value) { + if (is_array($value)) { + // e.g. fltr[] is passed as an array + foreach ($value as $subvalue) { + $ParamterStringEncodedArray[] = $key.'[]='.rawurlencode($subvalue); + } + } else { + $ParamterStringEncodedArray[] = $key.'='.rawurlencode($value); + } + } + $ParameterString = implode($PHPTHUMB_CONFIG['high_security_url_separator'], $ParamterStringEncodedArray); + return $path_to_phpThumb.'?'.$ParameterString.$PHPTHUMB_CONFIG['high_security_url_separator'].'hash='.hash_hmac('sha256', $ParameterString, $PHPTHUMB_CONFIG['high_security_password']); +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpThumb.php b/videodb/vendor/james-heinrich/phpthumb/phpThumb.php new file mode 100644 index 0000000..08717f1 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpThumb.php @@ -0,0 +1,717 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// See: phpthumb.changelog.txt for recent changes // +// See: phpthumb.readme.txt for usage instructions // +// /// +////////////////////////////////////////////////////////////// + +error_reporting(E_ALL); +ini_set('display_errors', '1'); + +// check for magic quotes in PHP < 7.4.0 (when these functions became deprecated) +if (version_compare(PHP_VERSION, '7.4.0', '<')) { + ini_set('magic_quotes_runtime', '0'); + if (ini_get('magic_quotes_runtime')) { + die('"magic_quotes_runtime" is set in php.ini, cannot run phpThumb with this enabled'); + } +} +// Set a default timezone if web server has not done already in php.ini +if (!ini_get('date.timezone') && function_exists('date_default_timezone_set')) { // PHP >= 5.1.0 + date_default_timezone_set('UTC'); +} +$starttime = array_sum(explode(' ', microtime())); // could be called as microtime(true) for PHP 5.0.0+ + +// this script relies on the superglobal arrays, fake it here for old PHP versions +if (PHP_VERSION < '4.1.0') { + $_SERVER = $HTTP_SERVER_VARS; + $_GET = $HTTP_GET_VARS; +} + +function SendSaveAsFileHeaderIfNeeded($getimagesize=false) { + if (headers_sent()) { + return false; + } + global $phpThumb; + $downloadfilename = phpthumb_functions::SanitizeFilename(!empty($_GET['sia']) ? $_GET['sia'] : (!empty($_GET['down']) ? $_GET['down'] : 'phpThumb_generated_thumbnail.'.(!empty($_GET['f']) ? $_GET['f'] : 'jpg'))); + //if (empty($_GET['sia']) && empty($_GET['down']) && !empty($phpThumb->thumbnail_image_width) && !empty($phpThumb->thumbnail_image_height)) { + if (empty($_GET['sia']) && empty($_GET['down']) && !empty($getimagesize[0]) && !empty($getimagesize[1])) { + // if we know the output image dimensions we can generate a better default filename + $downloadfilename = phpthumb_functions::SanitizeFilename((!empty($phpThumb->src) ? basename($phpThumb->src) : md5($phpThumb->rawImageData)).'-'.intval($getimagesize[0]).'x'.intval($getimagesize[1]).'.'.(!empty($_GET['f']) ? $_GET['f'] : 'jpg')); + } + if (!empty($downloadfilename)) { + $phpThumb->DebugMessage('SendSaveAsFileHeaderIfNeeded() sending header: Content-Disposition: '.(!empty($_GET['down']) ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"', __FILE__, __LINE__); + header('Content-Disposition: '.(!empty($_GET['down']) ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"'); + } + return true; +} + +function RedirectToCachedFile() { + global $phpThumb; + + $nice_cachefile = str_replace(DIRECTORY_SEPARATOR, '/', $phpThumb->cache_filename); + $nice_docroot = str_replace(DIRECTORY_SEPARATOR, '/', rtrim($phpThumb->config_document_root, '/\\')); + + $parsed_url = phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']); + + $nModified = filemtime($phpThumb->cache_filename); + + if ($phpThumb->config_nooffsitelink_enabled && !empty($_SERVER['HTTP_REFERER']) && !in_array(@$parsed_url['host'], $phpThumb->config_nooffsitelink_valid_domains)) { + + $phpThumb->DebugMessage('Would have used cached (image/'.$phpThumb->thumbnailFormat.') file "'.$phpThumb->cache_filename.'" (Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT), but skipping because $_SERVER[HTTP_REFERER] ('.@$_SERVER['HTTP_REFERER'].') is not in $phpThumb->config_nooffsitelink_valid_domains ('.implode(';', $phpThumb->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__); + + } elseif ($phpThumb->phpThumbDebug) { + + $phpThumb->DebugTimingMessage('skipped using cached image', __FILE__, __LINE__); + $phpThumb->DebugMessage('Would have used cached file, but skipping due to phpThumbDebug', __FILE__, __LINE__); + $phpThumb->DebugMessage('* Would have sent headers (1): Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT', __FILE__, __LINE__); + if ($getimagesize = @getimagesize($phpThumb->cache_filename)) { + $phpThumb->DebugMessage('* Would have sent headers (2): Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2]), __FILE__, __LINE__); + } + if (preg_match('#^'.preg_quote($nice_docroot).'(.*)$#', $nice_cachefile, $matches)) { + $phpThumb->DebugMessage('* Would have sent headers (3): Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1])), __FILE__, __LINE__); + } else { + $phpThumb->DebugMessage('* Would have sent data: file_get_contents('.$phpThumb->cache_filename.')', __FILE__, __LINE__); + } + + } else { + + if (headers_sent()) { + $phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')'); + exit; + } + $getimagesize = @getimagesize($phpThumb->cache_filename); + SendSaveAsFileHeaderIfNeeded($getimagesize); + + header('Pragma: private'); + header('Cache-Control: max-age='.$phpThumb->getParameter('config_cache_maxage')); + header('Expires: '.date(DATE_RFC1123, time() + $phpThumb->getParameter('config_cache_maxage'))); + if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($nModified == strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) && !empty($_SERVER['SERVER_PROTOCOL'])) { + header('Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT'); + header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified'); + exit; + } + header('Last-Modified: '.gmdate('D, d M Y H:i:s', $nModified).' GMT'); + header('ETag: "'.md5_file($phpThumb->cache_filename).'"'); + if (!empty($getimagesize[2])) { + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($getimagesize[2])); + } elseif (preg_match('#\\.ico$#i', $phpThumb->cache_filename)) { + header('Content-Type: image/x-icon'); + } + header('Content-Length: '.filesize($phpThumb->cache_filename)); + if (empty($phpThumb->config_cache_force_passthru) && preg_match('#^'.preg_quote($nice_docroot).'(.*)$#', $nice_cachefile, $matches)) { + header('Location: '.dirname($matches[1]).'/'.urlencode(basename($matches[1]))); + } else { + echo file_get_contents($phpThumb->cache_filename); + } + exit; + + } + return true; +} + + + +// instantiate a new phpThumb() object +ob_start(); +if (!include_once __DIR__ .'/phpthumb.class.php' ) { + ob_end_flush(); + die('failed to include_once("'.realpath( __DIR__ .'/phpthumb.class.php').'")'); +} +ob_end_clean(); +$phpThumb = new phpThumb(); +$phpThumb->DebugTimingMessage('phpThumb.php start', __FILE__, __LINE__, $starttime); +$phpThumb->setParameter('config_error_die_on_error', true); + +if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) { + set_time_limit(60); // shouldn't take nearly this long in most cases, but with many filters and/or a slow server... +} + +// phpThumbDebug[0] used to be here, but may reveal too much +// info when high_security_mode should be enabled (not set yet) + +if (file_exists( __DIR__ .'/phpThumb.config.php')) { + ob_start(); + if (include_once __DIR__ .'/phpThumb.config.php' ) { + // great + } else { + ob_end_flush(); + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('failed to include_once('. __DIR__ .'/phpThumb.config.php) - realpath="'.realpath( __DIR__ .'/phpThumb.config.php').'"'); + } + ob_end_clean(); +} elseif (file_exists( __DIR__ .'/phpThumb.config.php.default')) { + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('Please rename "phpThumb.config.php.default" to "phpThumb.config.php"'); +} else { + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('failed to include_once('. __DIR__ .'/phpThumb.config.php) - realpath="'.realpath( __DIR__ .'/phpThumb.config.php').'"'); +} + +if (!empty($PHPTHUMB_CONFIG)) { + foreach ($PHPTHUMB_CONFIG as $key => $value) { + $keyname = 'config_'.$key; + $phpThumb->setParameter($keyname, $value); + if (!preg_match('#(password|mysql)#i', $key)) { + $phpThumb->DebugMessage('setParameter('.$keyname.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__); + } + } + if (!$phpThumb->config_disable_debug) { + // if debug mode is enabled, force phpThumbDebug output, do not allow normal thumbnails to be generated + $_GET['phpThumbDebug'] = (!empty($_GET['phpThumbDebug']) ? max(1, (int) $_GET[ 'phpThumbDebug']) : 9); + $phpThumb->setParameter('phpThumbDebug', $_GET['phpThumbDebug']); + } +} else { + $phpThumb->DebugMessage('$PHPTHUMB_CONFIG is empty', __FILE__, __LINE__); +} + +if (empty($phpThumb->config_disable_pathinfo_parsing) && (empty($_GET) || isset($_GET['phpThumbDebug'])) && !empty($_SERVER['PATH_INFO'])) { + $_SERVER['PHP_SELF'] = str_replace($_SERVER['PATH_INFO'], '', @$_SERVER['PHP_SELF']); + + $args = explode(';', substr($_SERVER['PATH_INFO'], 1)); + $phpThumb->DebugMessage('PATH_INFO.$args set to ('.implode(')(', $args).')', __FILE__, __LINE__); + if (!empty($args)) { + $_GET['src'] = @$args[count($args) - 1]; + $phpThumb->DebugMessage('PATH_INFO."src" = "'.$_GET['src'].'"', __FILE__, __LINE__); + if (preg_match('#^new\=([a-z0-9]+)#i', $_GET['src'], $matches)) { + unset($_GET['src']); + $_GET['new'] = $matches[1]; + } + } + if (preg_match('#^([\d]*)x?([\d]*)$#i', @$args[count($args) - 2], $matches)) { + $_GET['w'] = $matches[1]; + $_GET['h'] = $matches[2]; + $phpThumb->DebugMessage('PATH_INFO."w"x"h" set to "'.$_GET['w'].'"x"'.$_GET['h'].'"', __FILE__, __LINE__); + } + for ($i = 0; $i < count($args) - 2; $i++) { + @list($key, $value) = explode('=', @$args[$i]); + if (substr($key, -2) == '[]') { + $array_key_name = substr($key, 0, -2); + $_GET[$array_key_name][] = $value; + $phpThumb->DebugMessage('PATH_INFO."'.$array_key_name.'[]" = "'.$value.'"', __FILE__, __LINE__); + } else { + $_GET[$key] = $value; + $phpThumb->DebugMessage('PATH_INFO."'.$key.'" = "'.$value.'"', __FILE__, __LINE__); + } + } +} + +if (!empty($phpThumb->config_high_security_enabled)) { + if (empty($_GET['hash'])) { + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('ERROR: missing hash'); + } elseif (phpthumb_functions::PasswordStrength($phpThumb->config_high_security_password) < 20) { + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('ERROR: $PHPTHUMB_CONFIG[high_security_password] is not complex enough'); + } elseif ($_GET['hash'] != hash_hmac('sha256', str_replace($phpThumb->config_high_security_url_separator.'hash='.$_GET['hash'], '', $_SERVER['QUERY_STRING']), $phpThumb->config_high_security_password)) { + header('HTTP/1.0 403 Forbidden'); + $phpThumb->ErrorImage('ERROR: invalid hash'); + } +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[0]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '0')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +// check for magic quotes in PHP < 7.4.0 (when these functions became deprecated) +if (version_compare(PHP_VERSION, '7.4.0', '<')) { + // returned the fixed string if the evil "magic_quotes_gpc" setting is on + if (get_magic_quotes_gpc()) { + // deprecated: 'err', 'file', 'goto', + $RequestVarsToStripSlashes = array('src', 'wmf', 'down'); + foreach ($RequestVarsToStripSlashes as $key) { + if (isset($_GET[$key])) { + if (is_string($_GET[$key])) { + $_GET[$key] = stripslashes($_GET[$key]); + } else { + unset($_GET[$key]); + } + } + } + } +} + +if (empty($_SERVER['PATH_INFO']) && empty($_SERVER['QUERY_STRING'])) { + $phpThumb->config_disable_debug = false; // otherwise error message won't print + $phpThumb->ErrorImage('ERROR: no parameters specified'); +} + +if (!empty($_GET['src']) && isset($_GET['md5s']) && empty($_GET['md5s'])) { + $md5s = ''; + if (preg_match('#^([a-z0-9]+)://#i', $_GET['src'], $protocol_matches)) { + if (preg_match('#^(f|ht)tps?://#i', $_GET['src'])) { + if ($rawImageData = phpthumb_functions::SafeURLread($_GET['src'], $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) { + $md5s = md5($rawImageData); + } + } else { + $phpThumb->ErrorImage('only FTP and HTTP/HTTPS protocols are allowed, "'.$protocol_matches[1].'" is not'); + } + } else { + $SourceFilename = $phpThumb->ResolveFilenameToAbsolute($_GET['src']); + if (is_readable($SourceFilename)) { + $md5s = phpthumb_functions::md5_file_safe($SourceFilename); + } else { + $phpThumb->ErrorImage('ERROR: "'.$SourceFilename.'" cannot be read'); + } + } + if (!empty($_SERVER['HTTP_REFERER'])) { + $phpThumb->ErrorImage('&md5s='.$md5s); + } else { + die('&md5s='.$md5s); + } +} + +if (!empty($_GET['src']) && empty($phpThumb->config_allow_local_http_src) && preg_match('#^http://'.@$_SERVER['HTTP_HOST'].'(.+)#i', $_GET['src'], $matches)) { + $phpThumb->ErrorImage('It is MUCH better to specify the "src" parameter as "'.$matches[1].'" instead of "'.$matches[0].'".'."\n\n".'If you really must do it this way, enable "allow_local_http_src" in phpThumb.config.php'); +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[1]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '1')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +$parsed_url_referer = phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']); +if ($phpThumb->config_nooffsitelink_require_refer && !in_array(@$parsed_url_referer['host'], $phpThumb->config_nohotlink_valid_domains)) { + $phpThumb->ErrorImage('config_nooffsitelink_require_refer enabled and '.(@$parsed_url_referer['host'] ? '"'.$parsed_url_referer['host'].'" is not an allowed referer' : 'no HTTP_REFERER exists')); +} +$parsed_url_src = phpthumb_functions::ParseURLbetter(@$_GET['src']); +if ($phpThumb->config_nohotlink_enabled && $phpThumb->config_nohotlink_erase_image && preg_match('#^(f|ht)tps?://#i', @$_GET['src']) && !in_array(@$parsed_url_src['host'], $phpThumb->config_nohotlink_valid_domains)) { + $phpThumb->ErrorImage($phpThumb->config_nohotlink_text_message); +} + +if ($phpThumb->config_mysql_query) { + if ($phpThumb->config_mysql_extension == 'mysqli') { + + $found_missing_function = false; + foreach (array('mysqli_connect') as $required_mysqli_function) { + if (!function_exists($required_mysqli_function)) { + $found_missing_function = $required_mysqli_function; + break; + } + } + if ($found_missing_function) { + $phpThumb->ErrorImage('SQL function unavailable: '.$found_missing_function); + } else { + $mysqli = new mysqli($phpThumb->config_mysql_hostname, $phpThumb->config_mysql_username, $phpThumb->config_mysql_password, $phpThumb->config_mysql_database); + if ($mysqli->connect_error) { + $phpThumb->ErrorImage('MySQLi connect error ('.$mysqli->connect_errno.') '.$mysqli->connect_error); + } else { + if ($result = $mysqli->query($phpThumb->config_mysql_query)) { + if ($row = $result->fetch_array()) { + + $result->free(); + $mysqli->close(); + $phpThumb->setSourceData($row[0]); + unset($row); + + } else { + $result->free(); + $mysqli->close(); + $phpThumb->ErrorImage('no matching data in database.'); + } + } else { + $mysqli->close(); + $phpThumb->ErrorImage('Error in MySQL query: "'.$mysqli->error.'"'); + } + } + unset($_GET['id']); + } + + } elseif ($phpThumb->config_mysql_extension == 'mysql') { + + $found_missing_function = false; + //foreach (array('mysql_connect', 'mysql_select_db', 'mysql_query', 'mysql_fetch_array', 'mysql_free_result', 'mysql_close', 'mysql_error') as $required_mysql_function) { + foreach (array('mysql_connect') as $required_mysql_function) { + if (!function_exists($required_mysql_function)) { + $found_missing_function = $required_mysql_function; + break; + } + } + if ($found_missing_function) { + $phpThumb->ErrorImage('SQL function unavailable: '.$found_missing_function); + } else { + if ($cid = @mysql_connect($phpThumb->config_mysql_hostname, $phpThumb->config_mysql_username, $phpThumb->config_mysql_password)) { + if (@mysql_select_db($phpThumb->config_mysql_database, $cid)) { + if ($result = @mysql_query($phpThumb->config_mysql_query, $cid)) { + if ($row = @mysql_fetch_array($result)) { + + mysql_free_result($result); + mysql_close($cid); + $phpThumb->setSourceData($row[0]); + unset($row); + + } else { + mysql_free_result($result); + mysql_close($cid); + $phpThumb->ErrorImage('no matching data in database.'); + } + } else { + mysql_close($cid); + $phpThumb->ErrorImage('Error in MySQL query: "'.mysql_error($cid).'"'); + } + } else { + mysql_close($cid); + $phpThumb->ErrorImage('cannot select MySQL database: "'.mysql_error($cid).'"'); + } + } else { + $phpThumb->ErrorImage('cannot connect to MySQL server'); + } + unset($_GET['id']); + } + + } else { + $phpThumb->ErrorImage('config_mysql_extension not supported'); + } +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[2]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '2')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +$PHPTHUMB_DEFAULTS_DISABLEGETPARAMS = (bool) ($phpThumb->config_cache_default_only_suffix && (strpos($phpThumb->config_cache_default_only_suffix, '*') !== false)); + +// deprecated: 'err', 'file', 'goto', +$allowedGETparameters = array('src', 'new', 'w', 'h', 'wp', 'hp', 'wl', 'hl', 'ws', 'hs', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'zc', 'ica', 'bc', 'bg', 'bgt', 'fltr', 'xto', 'ra', 'ar', 'aoe', 'far', 'iar', 'maxb', 'down', 'phpThumbDebug', 'hash', 'md5s', 'sfn', 'dpi', 'sia', 'nocache'); +foreach ($_GET as $key => $value) { + if (!empty($PHPTHUMB_DEFAULTS_DISABLEGETPARAMS) && ($key != 'src')) { + // disabled, do not set parameter + $phpThumb->DebugMessage('ignoring $_GET['.$key.'] because of $PHPTHUMB_DEFAULTS_DISABLEGETPARAMS', __FILE__, __LINE__); + } elseif (in_array($key, $allowedGETparameters)) { + $phpThumb->DebugMessage('setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).')', __FILE__, __LINE__); + $phpThumb->setParameter($key, $value); + } else { + $phpThumb->ErrorImage('Forbidden parameter: '.$key); + } +} + +if (!empty($PHPTHUMB_DEFAULTS) && is_array($PHPTHUMB_DEFAULTS)) { + $phpThumb->DebugMessage('setting $PHPTHUMB_DEFAULTS['.implode(';', array_keys($PHPTHUMB_DEFAULTS)).']', __FILE__, __LINE__); + foreach ($PHPTHUMB_DEFAULTS as $key => $value) { + if (!$PHPTHUMB_DEFAULTS_GETSTRINGOVERRIDE || !isset($_GET[$key])) { // set parameter to default value if config is set to allow _GET to override default, OR if no value is passed via _GET for this parameter + //$_GET[$key] = $value; + //$phpThumb->DebugMessage('PHPTHUMB_DEFAULTS assigning ('.(is_array($value) ? print_r($value, true) : $value).') to $_GET['.$key.']', __FILE__, __LINE__); + $phpThumb->setParameter($key, $value); + $phpThumb->DebugMessage('setParameter('.$key.', '.$phpThumb->phpThumbDebugVarDump($value).') from $PHPTHUMB_DEFAULTS', __FILE__, __LINE__); + } + } +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[3]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '3')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +//if (!@$_GET['phpThumbDebug'] && !is_file($phpThumb->sourceFilename) && !phpthumb_functions::gd_version()) { +// if (!headers_sent()) { +// // base64-encoded error image in GIF format +// $ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7'; +// header('Content-Type: image/gif'); +// echo base64_decode($ERROR_NOGD); +// } else { +// echo '*** ERROR: No PHP-GD support available ***'; +// } +// exit; +//} + +// check to see if file can be output from source with no processing or caching +$CanPassThroughDirectly = true; +if ($phpThumb->rawImageData) { + // data from SQL, should be fine +} elseif (preg_match('#^https?\\://[^\\?&]+\\.(jpe?g|gif|png|webp|avif)$#i', $phpThumb->src)) { + // assume is ok to passthru if no other parameters specified +} elseif (preg_match('#^(f|ht)tps?\\://#i', $phpThumb->src)) { + $phpThumb->DebugMessage('$CanPassThroughDirectly=false because preg_match("#^(f|ht)tps?://#i", '.$phpThumb->src.')', __FILE__, __LINE__); + $CanPassThroughDirectly = false; +} elseif (!@is_readable($phpThumb->sourceFilename)) { + $phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_readable('.$phpThumb->sourceFilename.')', __FILE__, __LINE__); + $CanPassThroughDirectly = false; +} elseif (!@is_file($phpThumb->sourceFilename)) { + $phpThumb->DebugMessage('$CanPassThroughDirectly=false because !@is_file('.$phpThumb->sourceFilename.')', __FILE__, __LINE__); + $CanPassThroughDirectly = false; +} +foreach ($_GET as $key => $value) { + switch ($key) { + case 'src': + // allowed + break; + + case 'w': + case 'h': + // might be OK if exactly matches original + if (preg_match('#^https?\\://[^\\?&]+\\.(jpe?g|gif|png|webp|avif)$#i', $phpThumb->src)) { + // assume it is not ok for direct-passthru of remote image + $CanPassThroughDirectly = false; + } + break; + + case 'phpThumbDebug': + // handled in direct-passthru code + break; + + default: + // all other parameters will cause some processing, + // therefore cannot pass through original image unmodified + $CanPassThroughDirectly = false; + $UnAllowedGET[] = $key; + break; + } +} +if (!empty($UnAllowedGET)) { + $phpThumb->DebugMessage('$CanPassThroughDirectly=false because $_GET['.implode(';', array_unique($UnAllowedGET)).'] are set', __FILE__, __LINE__); +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[4]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '4')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +$phpThumb->DebugMessage('$CanPassThroughDirectly="'. (int) $CanPassThroughDirectly .'" && $phpThumb->src="'.$phpThumb->src.'"', __FILE__, __LINE__); +while ($CanPassThroughDirectly && $phpThumb->src) { + // no parameters set, passthru + + if (preg_match('#^https?\\://[^\\?&]+\.(jpe?g|gif|png|webp|avif)$#i', $phpThumb->src)) { + $phpThumb->DebugMessage('Passing HTTP source through directly as Location: redirect ('.$phpThumb->src.')', __FILE__, __LINE__); + header('Location: '.$phpThumb->src); + exit; + } + + $SourceFilename = $phpThumb->ResolveFilenameToAbsolute($phpThumb->src); + + // security and size checks + if ($phpThumb->getimagesizeinfo = @getimagesize($SourceFilename)) { + $phpThumb->DebugMessage('Direct passthru getimagesize() returned [w='.$phpThumb->getimagesizeinfo[0].';h='.$phpThumb->getimagesizeinfo[1].';t='.$phpThumb->getimagesizeinfo[2].']', __FILE__, __LINE__); + + if (!@$_GET['w'] && !@$_GET['wp'] && !@$_GET['wl'] && !@$_GET['ws'] && !@$_GET['h'] && !@$_GET['hp'] && !@$_GET['hl'] && !@$_GET['hs']) { + // no resizing needed + $phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'")', __FILE__, __LINE__); + } elseif (($phpThumb->getimagesizeinfo[0] <= @$_GET['w']) && ($phpThumb->getimagesizeinfo[1] <= @$_GET['h']) && ((@$_GET['w'] == $phpThumb->getimagesizeinfo[0]) || (@$_GET['h'] == $phpThumb->getimagesizeinfo[1]))) { + // image fits into 'w'x'h' box, and at least one dimension matches exactly, therefore no resizing needed + $phpThumb->DebugMessage('Passing "'.$SourceFilename.'" through directly, no resizing required ("'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" fits inside "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__); + } else { + $phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because resizing required (from "'.$phpThumb->getimagesizeinfo[0].'"x"'.$phpThumb->getimagesizeinfo[1].'" to "'.@$_GET['w'].'"x"'.@$_GET['h'].'")', __FILE__, __LINE__); + break; + } + switch ($phpThumb->getimagesizeinfo[2]) { + case IMAGETYPE_GIF: + case IMAGETYPE_JPEG: + case IMAGETYPE_PNG: + case IMAGETYPE_WEBP: + case IMAGETYPE_AVIF: + // great, let it through + break; + default: + // browser probably can't handle format, remangle it to JPEG/PNG/GIF + $phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because $phpThumb->getimagesizeinfo[2] = "'.$phpThumb->getimagesizeinfo[2].'"', __FILE__, __LINE__); + break 2; + } + + $ImageCreateFunctions = array( + IMAGETYPE_GIF => 'imagecreatefromgif', + IMAGETYPE_JPEG => 'imagecreatefromjpeg', + IMAGETYPE_PNG => 'imagecreatefrompng', + IMAGETYPE_WEBP => 'imagecreatefromwebp', + IMAGETYPE_AVIF => 'imagecreatefromavif', + ); + $theImageCreateFunction = @$ImageCreateFunctions[$phpThumb->getimagesizeinfo[2]]; + $dummyImage = false; + if ($phpThumb->config_disable_onlycreateable_passthru || (function_exists($theImageCreateFunction) && ($dummyImage = @$theImageCreateFunction($SourceFilename)))) { + + // great + if (@is_resource($dummyImage) || (@is_object($dummyImage) && $dummyImage instanceOf \GdImage)) { + unset($dummyImage); + } + + if (headers_sent()) { + $phpThumb->ErrorImage('Headers already sent ('.basename(__FILE__).' line '.__LINE__.')'); + exit; + } + if (!empty($_GET['phpThumbDebug'])) { + $phpThumb->DebugTimingMessage('skipped direct $SourceFilename passthru', __FILE__, __LINE__); + $phpThumb->DebugMessage('Would have passed "'.$SourceFilename.'" through directly, but skipping due to phpThumbDebug', __FILE__, __LINE__); + break; + } + + SendSaveAsFileHeaderIfNeeded($phpThumb->getimagesizeinfo); + header('Last-Modified: '.gmdate('D, d M Y H:i:s', @filemtime($SourceFilename)).' GMT'); + if ($contentType = phpthumb_functions::ImageTypeToMIMEtype(@$phpThumb->getimagesizeinfo[2])) { + header('Content-Type: '.$contentType); + } + echo file_get_contents($SourceFilename); + exit; + + } else { + $phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because ($phpThumb->config_disable_onlycreateable_passthru = "'.$phpThumb->config_disable_onlycreateable_passthru.'") and '.$theImageCreateFunction.'() failed', __FILE__, __LINE__); + break; + } + + } else { + $phpThumb->DebugMessage('Not passing "'.$SourceFilename.'" through directly because getimagesize() failed', __FILE__, __LINE__); + break; + } + break; +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[5]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '5')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +// check to see if file already exists in cache, and output it with no processing if it does +$phpThumb->SetCacheFilename(); +if (@is_readable($phpThumb->cache_filename)) { + RedirectToCachedFile(); +} else { + $phpThumb->DebugMessage('Cached file "'.$phpThumb->cache_filename.'" does not exist, processing as normal', __FILE__, __LINE__); +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[6]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '6')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +if ($phpThumb->rawImageData) { + + // great + +} elseif (!empty($_GET['new'])) { + + // generate a blank image resource of the specified size/background color/opacity + if (($phpThumb->w <= 0) || ($phpThumb->h <= 0)) { + $phpThumb->ErrorImage('"w" and "h" parameters required for "new"'); + } + @list($bghexcolor, $opacity) = explode('|', $_GET['new']); + if (!phpthumb_functions::IsHexColor($bghexcolor)) { + $phpThumb->ErrorImage('BGcolor parameter for "new" is not valid'); + } + $opacity = ('' !== $opacity ? $opacity : 100); + if ($phpThumb->gdimg_source = phpthumb_functions::ImageCreateFunction($phpThumb->w, $phpThumb->h)) { + $alpha = (100 - min(100, max(0, $opacity))) * 1.27; + if ($alpha) { + $phpThumb->setParameter('is_alpha', true); + imagealphablending($phpThumb->gdimg_source, false); + imagesavealpha($phpThumb->gdimg_source, true); + } + $new_background_color = phpthumb_functions::ImageHexColorAllocate($phpThumb->gdimg_source, $bghexcolor, false, $alpha); + imagefilledrectangle($phpThumb->gdimg_source, 0, 0, $phpThumb->w, $phpThumb->h, $new_background_color); + } else { + $phpThumb->ErrorImage('failed to create "new" image ('.$phpThumb->w.'x'.$phpThumb->h.')'); + } + +} elseif (!$phpThumb->src) { + + $phpThumb->ErrorImage('Usage: '.$_SERVER['PHP_SELF'].'?src=/path/and/filename.jpg'."\n".'read Usage comments for details'); + +} elseif (preg_match('#^([a-z0-9]+)://#i', $_GET['src'], $protocol_matches)) { + + if (preg_match('#^(f|ht)tps?://#i', $_GET['src'])) { + $phpThumb->DebugMessage('$phpThumb->src ('.$phpThumb->src.') is remote image, attempting to download', __FILE__, __LINE__); + if ($phpThumb->config_http_user_agent) { + $phpThumb->DebugMessage('Setting "user_agent" to "'.$phpThumb->config_http_user_agent.'"', __FILE__, __LINE__); + ini_set('user_agent', $phpThumb->config_http_user_agent); + } + $cleanedupurl = phpthumb_functions::CleanUpURLencoding($phpThumb->src); + $phpThumb->DebugMessage('CleanUpURLencoding('.$phpThumb->src.') returned "'.$cleanedupurl.'"', __FILE__, __LINE__); + $phpThumb->src = $cleanedupurl; + unset($cleanedupurl); + if ($rawImageData = phpthumb_functions::SafeURLread($phpThumb->src, $error, $phpThumb->config_http_fopen_timeout, $phpThumb->config_http_follow_redirect)) { + $phpThumb->DebugMessage('SafeURLread('.$phpThumb->src.') succeeded'.($error ? ' with messages: "'.$error.'"' : ''), __FILE__, __LINE__); + $phpThumb->DebugMessage('Setting source data from URL "'.$phpThumb->src.'"', __FILE__, __LINE__); + $phpThumb->setSourceData($rawImageData, urlencode($phpThumb->src)); + } else { + $phpThumb->ErrorImage($error); + } + } else { + $phpThumb->ErrorImage('only FTP and HTTP/HTTPS protocols are allowed, "'.$protocol_matches[1].'" is not'); + } + +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[7]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '7')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +$phpThumb->GenerateThumbnail(); + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[8]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '8')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +if (!empty($phpThumb->config_high_security_enabled) && !empty($_GET['nocache'])) { + + // cache disabled, don't write cachefile + +} else { + + phpthumb_functions::EnsureDirectoryExists(dirname($phpThumb->cache_filename)); + if (is_writable(dirname($phpThumb->cache_filename)) || (file_exists($phpThumb->cache_filename) && is_writable($phpThumb->cache_filename))) { + + $phpThumb->CleanUpCacheDirectory(); + if ($phpThumb->RenderToFile($phpThumb->cache_filename) && is_readable($phpThumb->cache_filename)) { + chmod($phpThumb->cache_filename, 0644); + RedirectToCachedFile(); + } else { + $phpThumb->DebugMessage('Failed: RenderToFile('.$phpThumb->cache_filename.')', __FILE__, __LINE__); + } + + } else { + + $phpThumb->DebugMessage('Cannot write to $phpThumb->cache_filename ('.$phpThumb->cache_filename.') because that directory ('.dirname($phpThumb->cache_filename).') is not writable', __FILE__, __LINE__); + + } + +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[9]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '9')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// + +if (!$phpThumb->OutputThumbnail()) { + $phpThumb->ErrorImage('Error in OutputThumbnail():'."\n". $phpThumb->debugmessages[ count($phpThumb->debugmessages) - 1 ]); +} + +//////////////////////////////////////////////////////////////// +// Debug output, to try and help me diagnose problems +$phpThumb->DebugTimingMessage('phpThumbDebug[10]', __FILE__, __LINE__); +if (isset($_GET['phpThumbDebug']) && ($_GET['phpThumbDebug'] == '10')) { + $phpThumb->phpThumbDebug(); +} +//////////////////////////////////////////////////////////////// diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.bmp.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.bmp.php new file mode 100644 index 0000000..55a28a1 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.bmp.php @@ -0,0 +1,870 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.graphic.bmp.php // +// module for analyzing BMP Image files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// +// // +// Modified for use in phpThumb() - James Heinrich 2004.07.27 // +// // +///////////////////////////////////////////////////////////////// + + +class phpthumb_bmp { + + public function phpthumb_bmp2gd(&$BMPdata, $truecolor=true) { + $ThisFileInfo = array(); + if ($this->getid3_bmp($BMPdata, $ThisFileInfo, true, true)) { + $gd = $this->PlotPixelsGD($ThisFileInfo['bmp'], $truecolor); + return $gd; + } + return false; + } + + public function phpthumb_bmpfile2gd($filename, $truecolor=true) { + if ($fp = @fopen($filename, 'rb')) { + $BMPdata = fread($fp, filesize($filename)); + fclose($fp); + return $this->phpthumb_bmp2gd($BMPdata, $truecolor); + } + return false; + } + + public function GD2BMPstring(&$gd_image) { + $imageX = imagesx($gd_image); + $imageY = imagesy($gd_image); + + $BMP = ''; + for ($y = ($imageY - 1); $y >= 0; $y--) { + $thisline = ''; + for ($x = 0; $x < $imageX; $x++) { + $argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y); + $thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']); + } + while (strlen($thisline) % 4) { + $thisline .= "\x00"; + } + $BMP .= $thisline; + } + + $bmpSize = strlen($BMP) + 14 + 40; + // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp + $BITMAPFILEHEADER = 'BM'; // WORD bfType; + $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String($bmpSize, 4); // DWORD bfSize; + $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 0, 2); // WORD bfReserved1; + $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 0, 2); // WORD bfReserved2; + $BITMAPFILEHEADER .= phpthumb_functions::LittleEndian2String( 54, 4); // DWORD bfOffBits; + + // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp + $BITMAPINFOHEADER = phpthumb_functions::LittleEndian2String( 40, 4); // DWORD biSize; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageX, 4); // LONG biWidth; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( $imageY, 4); // LONG biHeight; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 1, 2); // WORD biPlanes; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 24, 2); // WORD biBitCount; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biCompression; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biSizeImage; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 2835, 4); // LONG biXPelsPerMeter; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 2835, 4); // LONG biYPelsPerMeter; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biClrUsed; + $BITMAPINFOHEADER .= phpthumb_functions::LittleEndian2String( 0, 4); // DWORD biClrImportant; + + return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP; + } + + public function getid3_bmp(&$BMPdata, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) { + + // shortcuts + $ThisFileInfo['bmp']['header']['raw'] = array(); + $thisfile_bmp = &$ThisFileInfo['bmp']; + $thisfile_bmp_header = &$thisfile_bmp['header']; + $thisfile_bmp_header_raw = &$thisfile_bmp_header['raw']; + + // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp + // all versions + // WORD bfType; + // DWORD bfSize; + // WORD bfReserved1; + // WORD bfReserved2; + // DWORD bfOffBits; + + $offset = 0; + $overalloffset = 0; + $BMPheader = substr($BMPdata, $overalloffset, 14 + 40); + $overalloffset += (14 + 40); + + $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2); + $offset += 2; + + if ($thisfile_bmp_header_raw['identifier'] != 'BM') { + $ThisFileInfo['error'][] = 'Expecting "BM" at offset '. (int) (@$ThisFileInfo[ 'avdataoffset']) .', found "'. $thisfile_bmp_header_raw[ 'identifier'].'"'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['bmp']); + return false; + } + + $thisfile_bmp_header_raw['filesize'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['reserved1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['reserved2'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['header_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + + // check if the hardcoded-to-1 "planes" is at offset 22 or 26 + $planes22 = $this->LittleEndian2Int(substr($BMPheader, 22, 2)); + $planes26 = $this->LittleEndian2Int(substr($BMPheader, 26, 2)); + if (($planes22 == 1) && ($planes26 != 1)) { + $thisfile_bmp['type_os'] = 'OS/2'; + $thisfile_bmp['type_version'] = 1; + } elseif (($planes26 == 1) && ($planes22 != 1)) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 12) { + $thisfile_bmp['type_os'] = 'OS/2'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 40) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 1; + } elseif ($thisfile_bmp_header_raw['header_size'] == 84) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 4; + } elseif ($thisfile_bmp_header_raw['header_size'] == 100) { + $thisfile_bmp['type_os'] = 'Windows'; + $thisfile_bmp['type_version'] = 5; + } else { + $ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)'; + unset($ThisFileInfo['fileformat']); + unset($ThisFileInfo['bmp']); + return false; + } + + $ThisFileInfo['fileformat'] = 'bmp'; + $ThisFileInfo['video']['dataformat'] = 'bmp'; + $ThisFileInfo['video']['lossless'] = true; + $ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1; + + if ($thisfile_bmp['type_os'] == 'OS/2') { + + // OS/2-format BMP + // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm + + // DWORD Size; /* Size of this structure in bytes */ + // DWORD Width; /* Bitmap width in pixels */ + // DWORD Height; /* Bitmap height in pixel */ + // WORD NumPlanes; /* Number of bit planes (color depth) */ + // WORD BitsPerPixel; /* Number of bits per pixel per plane */ + + $thisfile_bmp_header_raw['width'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['height'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['planes'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + + $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $ThisFileInfo['video']['codec'] = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + + if ($thisfile_bmp['type_version'] >= 2) { + // DWORD Compression; /* Bitmap compression scheme */ + // DWORD ImageDataSize; /* Size of bitmap data in bytes */ + // DWORD XResolution; /* X resolution of display device */ + // DWORD YResolution; /* Y resolution of display device */ + // DWORD ColorsUsed; /* Number of color table indices used */ + // DWORD ColorsImportant; /* Number of important color indices */ + // WORD Units; /* Type of units used to measure resolution */ + // WORD Reserved; /* Pad structure to 4-byte boundary */ + // WORD Recording; /* Recording algorithm */ + // WORD Rendering; /* Halftoning algorithm used */ + // DWORD Size1; /* Reserved for halftoning algorithm use */ + // DWORD Size2; /* Reserved for halftoning algorithm use */ + // DWORD ColorEncoding; /* Color model used in bitmap */ + // DWORD Identifier; /* Reserved for application use */ + + $thisfile_bmp_header_raw['compression'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['bmp_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_h'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_v'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_used'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_units'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['reserved1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['recording'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['rendering'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['size1'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['size2'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['color_encoding'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['identifier'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']); + + $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + } + + } elseif ($thisfile_bmp['type_os'] == 'Windows') { + + // Windows-format BMP + + // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp + // all versions + // DWORD biSize; + // LONG biWidth; + // LONG biHeight; + // WORD biPlanes; + // WORD biBitCount; + // DWORD biCompression; + // DWORD biSizeImage; + // LONG biXPelsPerMeter; + // LONG biYPelsPerMeter; + // DWORD biClrUsed; + // DWORD biClrImportant; + + $thisfile_bmp_header_raw['width'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['height'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['planes'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['bits_per_pixel'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 2)); + $offset += 2; + $thisfile_bmp_header_raw['compression'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['bmp_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_h'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['resolution_v'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_used'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['colors_important'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']); + $ThisFileInfo['video']['resolution_x'] = $thisfile_bmp_header_raw['width']; + $ThisFileInfo['video']['resolution_y'] = $thisfile_bmp_header_raw['height']; + $ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit'; + $ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel']; + + if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) { + // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen + $BMPheader .= substr($BMPdata, $overalloffset, 44); + $overalloffset += 44; + + // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp + // Win95+, WinNT4.0+ + // DWORD bV4RedMask; + // DWORD bV4GreenMask; + // DWORD bV4BlueMask; + // DWORD bV4AlphaMask; + // DWORD bV4CSType; + // CIEXYZTRIPLE bV4Endpoints; + // DWORD bV4GammaRed; + // DWORD bV4GammaGreen; + // DWORD bV4GammaBlue; + $thisfile_bmp_header_raw['red_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['green_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['blue_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['alpha_mask'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['cs_type'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4); + $offset += 4; + $thisfile_bmp_header_raw['gamma_red'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['gamma_green'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['gamma_blue'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + + $thisfile_bmp_header['ciexyz_red'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red'])); + $thisfile_bmp_header['ciexyz_green'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green'])); + $thisfile_bmp_header['ciexyz_blue'] = $this->FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue'])); + } + + if ($thisfile_bmp['type_version'] >= 5) { + $BMPheader .= substr($BMPdata, $overalloffset, 16); + $overalloffset += 16; + + // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp + // Win98+, Win2000+ + // DWORD bV5Intent; + // DWORD bV5ProfileData; + // DWORD bV5ProfileSize; + // DWORD bV5Reserved; + $thisfile_bmp_header_raw['intent'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['profile_data_offset'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['profile_data_size'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + $thisfile_bmp_header_raw['reserved3'] = $this->LittleEndian2Int(substr($BMPheader, $offset, 4)); + $offset += 4; + } + + } else { + + $ThisFileInfo['error'][] = 'Unknown BMP format in header.'; + return false; + + } + + if ($ExtractPalette || $ExtractData) { + $PaletteEntries = 0; + if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) { + $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']); + } elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) { + $PaletteEntries = $thisfile_bmp_header_raw['colors_used']; + } + if ($PaletteEntries > 0) { + $BMPpalette = substr($BMPdata, $overalloffset, 4 * $PaletteEntries); + $overalloffset += 4 * $PaletteEntries; + + $paletteoffset = 0; + for ($i = 0; $i < $PaletteEntries; $i++) { + // RGBQUAD - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp + // BYTE rgbBlue; + // BYTE rgbGreen; + // BYTE rgbRed; + // BYTE rgbReserved; + $blue = $this->LittleEndian2Int($BMPpalette[ $paletteoffset++ ]); + $green = $this->LittleEndian2Int($BMPpalette[ $paletteoffset++ ]); + $red = $this->LittleEndian2Int($BMPpalette[ $paletteoffset++ ]); + if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) { + // no padding byte + } else { + $paletteoffset++; // padding byte + } + $thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue); + } + } + } + + if ($ExtractData) { + $RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundary + + $BMPpixelData = substr($BMPdata, $thisfile_bmp_header_raw['data_offset'], $thisfile_bmp_header_raw['height'] * $RowByteLength); + $overalloffset = $thisfile_bmp_header_raw['data_offset'] + ($thisfile_bmp_header_raw['height'] * $RowByteLength); + + $pixeldataoffset = 0; + switch (@$thisfile_bmp_header_raw['compression']) { + + case 0: // BI_RGB + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 1: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { + $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]); + for ($i = 7; $i >= 0; $i--) { + $paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i; + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $col++; + } + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 4: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) { + $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]); + for ($i = 1; $i >= 0; $i--) { + $paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $col++; + } + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 8: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $paletteindex = ord($BMPpixelData[$pixeldataoffset++]); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 24: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]); + $pixeldataoffset += 3; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 32: + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+3]) << 24) | (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]); + $pixeldataoffset += 4; + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + case 16: + // ? + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 8: + $pixelcounter = 0; + while ($pixeldataoffset < strlen($BMPpixelData)) { + $firstbyte = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $secondbyte = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + if ($firstbyte == 0) { + + // escaped/absolute mode - the first byte of the pair can be set to zero to + // indicate an escape character that denotes the end of a line, the end of + // a bitmap, or a delta, depending on the value of the second byte. + switch ($secondbyte) { + case 0: + // end of line + // no need for special processing, just ignore + break; + + case 1: + // end of bitmap + $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case + break; + + case 2: + // delta - The 2 bytes following the escape contain unsigned values + // indicating the horizontal and vertical offsets of the next pixel + // from the current position. + $colincrement = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $rowincrement = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; + $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; + $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; + break; + + default: + // In absolute mode, the first byte is zero and the second byte is a + // value in the range 03H through FFH. The second byte represents the + // number of bytes that follow, each of which contains the color index + // of a single pixel. Each run must be aligned on a word boundary. + for ($i = 0; $i < $secondbyte; $i++) { + $paletteindex = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $pixelcounter++; + } + while (($pixeldataoffset % 2) != 0) { + // Each run must be aligned on a word boundary. + $pixeldataoffset++; + } + break; + } + + } else { + + // encoded mode - the first byte specifies the number of consecutive pixels + // to be drawn using the color index contained in the second byte. + for ($i = 0; $i < $firstbyte; $i++) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte]; + $pixelcounter++; + } + + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + + case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 4: + $pixelcounter = 0; + while ($pixeldataoffset < strlen($BMPpixelData)) { + $firstbyte = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $secondbyte = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + if ($firstbyte == 0) { + + // escaped/absolute mode - the first byte of the pair can be set to zero to + // indicate an escape character that denotes the end of a line, the end of + // a bitmap, or a delta, depending on the value of the second byte. + switch ($secondbyte) { + case 0: + // end of line + // no need for special processing, just ignore + break; + + case 1: + // end of bitmap + $pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case + break; + + case 2: + // delta - The 2 bytes following the escape contain unsigned values + // indicating the horizontal and vertical offsets of the next pixel + // from the current position. + $colincrement = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $rowincrement = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement; + $row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement; + $pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col; + break; + + default: + // In absolute mode, the first byte is zero. The second byte contains the number + // of color indexes that follow. Subsequent bytes contain color indexes in their + // high- and low-order 4 bits, one color index for each pixel. In absolute mode, + // each run must be aligned on a word boundary. + $paletteindexes = array(); + for ($i = 0, $iMax = ceil($secondbyte / 2); $i < $iMax; $i++) { + $paletteindexbyte = $this->LittleEndian2Int($BMPpixelData[ $pixeldataoffset++ ]); + $paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4; + $paletteindexes[] = ($paletteindexbyte & 0x0F); + } + while (($pixeldataoffset % 2) != 0) { + // Each run must be aligned on a word boundary. + $pixeldataoffset++; + } + + foreach ($paletteindexes as $dummy => $paletteindex) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex]; + $pixelcounter++; + } + break; + } + + } else { + + // encoded mode - the first byte of the pair contains the number of pixels to be + // drawn using the color indexes in the second byte. The second byte contains two + // color indexes, one in its high-order 4 bits and one in its low-order 4 bits. + // The first of the pixels is drawn using the color specified by the high-order + // 4 bits, the second is drawn using the color in the low-order 4 bits, the third + // is drawn using the color in the high-order 4 bits, and so on, until all the + // pixels specified by the first byte have been drawn. + $paletteindexes[0] = ($secondbyte & 0xF0) >> 4; + $paletteindexes[1] = ($secondbyte & 0x0F); + for ($i = 0; $i < $firstbyte; $i++) { + $col = $pixelcounter % $thisfile_bmp_header_raw['width']; + $row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']); + $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][ $paletteindexes[ $i % 2 ]]; + $pixelcounter++; + } + + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + case 3: // BI_BITFIELDS + switch ($thisfile_bmp_header_raw['bits_per_pixel']) { + case 16: + case 32: + $redshift = 0; + $greenshift = 0; + $blueshift = 0; + if (!$thisfile_bmp_header_raw['red_mask'] || !$thisfile_bmp_header_raw['green_mask'] || !$thisfile_bmp_header_raw['blue_mask']) { + $ThisFileInfo['error'][] = 'missing $thisfile_bmp_header_raw[(red|green|blue)_mask]'; + return false; + } + while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) { + $redshift++; + } + while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) { + $greenshift++; + } + while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) { + $blueshift++; + } + for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) { + for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) { + $pixelvalue = $this->LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8)); + $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8; + + $red = (int) round(((($pixelvalue & $thisfile_bmp_header_raw[ 'red_mask']) >> $redshift) / ($thisfile_bmp_header_raw[ 'red_mask'] >> $redshift)) * 255); + $green = (int) round(((($pixelvalue & $thisfile_bmp_header_raw[ 'green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw[ 'green_mask'] >> $greenshift)) * 255); + $blue = (int) round(((($pixelvalue & $thisfile_bmp_header_raw[ 'blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw[ 'blue_mask'] >> $blueshift)) * 255); + $thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | $blue); + } + while (($pixeldataoffset % 4) != 0) { + // lines are padded to nearest DWORD + $pixeldataoffset++; + } + } + break; + + default: + $ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data'; + break; + } + break; + + + default: // unhandled compression type + $ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data'; + break; + } + } + + return true; + } + + public function IntColor2RGB($color) { + $red = ($color & 0x00FF0000) >> 16; + $green = ($color & 0x0000FF00) >> 8; + $blue = ($color & 0x000000FF); + return array($red, $green, $blue); + } + + public function PlotPixelsGD(&$BMPdata, $truecolor=true) { + $imagewidth = $BMPdata['header']['raw']['width']; + $imageheight = $BMPdata['header']['raw']['height']; + + if ($truecolor) { + + $gd = @imagecreatetruecolor($imagewidth, $imageheight); + + } else { + + $gd = @imagecreate($imagewidth, $imageheight); + if (!empty($BMPdata['palette'])) { + // create GD palette from BMP palette + foreach ($BMPdata['palette'] as $dummy => $color) { + list($r, $g, $b) = $this->IntColor2RGB($color); + imagecolorallocate($gd, $r, $g, $b); + } + } else { + // create 216-color websafe palette + for ($r = 0x00; $r <= 0xFF; $r += 0x33) { + for ($g = 0x00; $g <= 0xFF; $g += 0x33) { + for ($b = 0x00; $b <= 0xFF; $b += 0x33) { + imagecolorallocate($gd, $r, $g, $b); + } + } + } + } + + } + if (!is_resource($gd) && !(is_object($gd) && $gd instanceOf \GdImage)) { + return false; + } + + foreach ($BMPdata['data'] as $row => $colarray) { + if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) { + set_time_limit(30); + } + foreach ($colarray as $col => $color) { + list($red, $green, $blue) = $this->IntColor2RGB($color); + if ($truecolor) { + $pixelcolor = imagecolorallocate($gd, $red, $green, $blue); + } else { + $pixelcolor = imagecolorclosest($gd, $red, $green, $blue); + } + imagesetpixel($gd, $col, $row, $pixelcolor); + } + } + return $gd; + } + + public function PlotBMP(&$BMPinfo) { + $starttime = time(); + if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) { + echo 'ERROR: no pixel data
        '; + return false; + } + if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) { + set_time_limit((int) round($BMPinfo[ 'resolution_x'] * $BMPinfo[ 'resolution_y'] / 10000)); + } + $im = $this->PlotPixelsGD($BMPinfo['bmp']); + if (headers_sent()) { + echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds
        '; + imagedestroy($im); + exit; + } + header('Content-Type: image/png'); + imagepng($im); + imagedestroy($im); + return true; + } + + public function BMPcompressionWindowsLookup($compressionid) { + static $BMPcompressionWindowsLookup = array( + 0 => 'BI_RGB', + 1 => 'BI_RLE8', + 2 => 'BI_RLE4', + 3 => 'BI_BITFIELDS', + 4 => 'BI_JPEG', + 5 => 'BI_PNG' + ); + return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid'); + } + + public function BMPcompressionOS2Lookup($compressionid) { + static $BMPcompressionOS2Lookup = array( + 0 => 'BI_RGB', + 1 => 'BI_RLE8', + 2 => 'BI_RLE4', + 3 => 'Huffman 1D', + 4 => 'BI_RLE24', + ); + return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid'); + } + + + // from getid3.lib.php + + public function trunc($floatnumber) { + // truncates a floating-point number at the decimal point + // returns int (if possible, otherwise float) + if ($floatnumber >= 1) { + $truncatednumber = floor($floatnumber); + } elseif ($floatnumber <= -1) { + $truncatednumber = ceil($floatnumber); + } else { + $truncatednumber = 0; + } + if ($truncatednumber <= 1073741824) { // 2^30 + $truncatednumber = (int) $truncatednumber; + } + return $truncatednumber; + } + + public function LittleEndian2Int($byteword) { + $intvalue = 0; + $byteword = strrev($byteword); + $bytewordlen = strlen($byteword); + for ($i = 0; $i < $bytewordlen; $i++) { + $intvalue += ord($byteword[$i]) * pow(256, $bytewordlen - 1 - $i); + } + return $intvalue; + } + + public function BigEndian2Int($byteword) { + return $this->LittleEndian2Int(strrev($byteword)); + } + + public function BigEndian2Bin($byteword) { + $binvalue = ''; + $bytewordlen = strlen($byteword); + for ($i = 0; $i < $bytewordlen; $i++) { + $binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT); + } + return $binvalue; + } + + public function FixedPoint2_30($rawdata) { + $binarystring = $this->BigEndian2Bin($rawdata); + return $this->Bin2Dec(substr($binarystring, 0, 2)) + (float) ($this->Bin2Dec(substr($binarystring, 2, 30)) / 1073741824); + } + + public function Bin2Dec($binstring, $signed=false) { + $signmult = 1; + if ($signed) { + if ($binstring[0] == '1') { + $signmult = -1; + } + $binstring = substr($binstring, 1); + } + $decvalue = 0; + for ($i = 0, $iMax = strlen($binstring); $i < $iMax; $i++) { + $decvalue += ((int) $binstring[ strlen($binstring) - $i - 1 ]) * pow(2, $i); + } + return $this->CastAsInt($decvalue * $signmult); + } + + public function CastAsInt($floatnum) { + // convert to float if not already + $floatnum = (float) $floatnum; + + // convert a float to type int, only if possible + if ($this->trunc($floatnum) == $floatnum) { + // it's not floating point + if ($floatnum <= 1073741824) { // 2^30 + // it's within int range + $floatnum = (int) $floatnum; + } + } + return $floatnum; + } + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.class.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.class.php new file mode 100644 index 0000000..e84d030 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.class.php @@ -0,0 +1,4598 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// See: phpthumb.readme.txt for usage instructions // +// /// +////////////////////////////////////////////////////////////// + +if (!class_exists('phpthumb_functions', false)) { + ob_start(); + if (!include_once __DIR__ . '/phpthumb.functions.php') { + ob_end_flush(); + die('failed to include_once("' . __DIR__ . '/phpthumb.functions.php")'); + } + ob_end_clean(); +} + +// make sure all image type constants are defined, even in older PHP versions that don't natively support them +$predefined_IMG_constants = array( + 'IMG_GIF' => 1, + 'IMG_JPG' => 2, // not a typo, both IMG_JPG and IMG_JPEG have a value of "2" + 'IMG_JPEG' => 2, // not a typo, both IMG_JPG and IMG_JPEG have a value of "2" + 'IMG_PNG' => 4, + 'IMG_WBMP' => 8, + 'IMG_XPM' => 16, + 'IMG_WEBP' => 32, // PHP 7.0.10 + 'IMG_BMP' => 64, // PHP 7.2.0 + 'IMG_WEBP_LOSSLESS' => 101, // PHP 8.1.0 + 'IMG_AVIF' => 256, // PHP 8.1.0 +); +$predefined_IMAGETYPE_constants = array( + 'IMAGETYPE_GIF' => 1, + 'IMAGETYPE_JPEG' => 2, + 'IMAGETYPE_PNG' => 3, + 'IMAGETYPE_SWF' => 4, + 'IMAGETYPE_PSD' => 5, + 'IMAGETYPE_BMP' => 6, + 'IMAGETYPE_TIFF_II' => 7, + 'IMAGETYPE_TIFF_MM' => 8, + 'IMAGETYPE_JPC' => 9, + 'IMAGETYPE_JP2' => 10, + 'IMAGETYPE_JPX' => 11, + 'IMAGETYPE_JB2' => 12, + 'IMAGETYPE_SWC' => 13, + 'IMAGETYPE_IFF' => 14, + 'IMAGETYPE_WBMP' => 15, + 'IMAGETYPE_XBM' => 16, + 'IMAGETYPE_ICO' => 17, + 'IMAGETYPE_WEBP' => 18, // PHP 7.0.10 + 'IMAGETYPE_AVIF' => 19, // PHP 8.1.0 +); +foreach ($predefined_IMG_constants as $PHP_constant_name => $PHP_constant_value) { + if (!defined($PHP_constant_name)) { + define($PHP_constant_name, $PHP_constant_value); + } +} +foreach ($predefined_IMAGETYPE_constants as $PHP_constant_name => $PHP_constant_value) { + if (!defined($PHP_constant_name)) { + define($PHP_constant_name, $PHP_constant_value); + } +} +unset($predefined_IMG_constants, $predefined_IMAGETYPE_constants, $PHP_constant_name, $PHP_constant_value); + + +class phpthumb { + + // public: + // START PARAMETERS (for object mode and phpThumb.php) + // See phpthumb.readme.txt for descriptions of what each of these values are + public $src = null; // SouRCe filename + public $new = null; // NEW image (phpThumb.php only) + public $w = null; // Width + public $h = null; // Height + public $wp = null; // Width (Portrait Images Only) + public $hp = null; // Height (Portrait Images Only) + public $wl = null; // Width (Landscape Images Only) + public $hl = null; // Height (Landscape Images Only) + public $ws = null; // Width (Square Images Only) + public $hs = null; // Height (Square Images Only) + public $f = null; // output image Format + public $q = 75; // jpeg output Quality + public $sx = null; // Source crop top-left X position + public $sy = null; // Source crop top-left Y position + public $sw = null; // Source crop Width + public $sh = null; // Source crop Height + public $zc = null; // Zoom Crop + public $ica = null; // Image Crop Auto + public $bc = null; // Border Color + public $bg = null; // BackGround color + public $fltr = array(); // FiLTeRs + public $goto = null; // GO TO url after processing + public $err = null; // default ERRor image filename + public $xto = null; // extract eXif Thumbnail Only + public $ra = null; // Rotate by Angle + public $ar = null; // Auto Rotate + public $aoe = null; // Allow Output Enlargement + public $far = null; // Fixed Aspect Ratio + public $iar = null; // Ignore Aspect Ratio + public $maxb = null; // MAXimum Bytes + public $down = null; // DOWNload thumbnail filename + public $md5s = null; // MD5 hash of Source image + public $sfn = 0; // Source Frame Number + public $dpi = 150; // Dots Per Inch for vector source formats + public $sia = null; // Save Image As filename + + public $file = null; // >>>deprecated, DO NOT USE, will be removed in future versions<<< + + public $phpThumbDebug = null; + // END PARAMETERS + + + // public: + // START CONFIGURATION OPTIONS (for object mode only) + // See phpThumb.config.php for descriptions of what each of these settings do + + // * Directory Configuration + public $config_cache_directory = null; + public $config_cache_directory_depth = 0; + public $config_cache_disable_warning = true; + public $config_cache_source_enabled = false; + public $config_cache_source_directory = null; + public $config_temp_directory = null; + public $config_document_root = null; + + // * Default output configuration: + public $config_output_format = 'jpeg'; + public $config_output_maxwidth = 0; + public $config_output_maxheight = 0; + public $config_output_interlace = true; + + // * Error message configuration + public $config_error_image_width = 400; + public $config_error_image_height = 100; + public $config_error_message_image_default = ''; + public $config_error_bgcolor = 'CCCCFF'; + public $config_error_textcolor = 'FF0000'; + public $config_error_fontsize = 1; + public $config_error_die_on_error = false; + public $config_error_silent_die_on_error = false; + public $config_error_die_on_source_failure = true; + + // * Anti-Hotlink Configuration: + public $config_nohotlink_enabled = true; + public $config_nohotlink_valid_domains = array(); + public $config_nohotlink_erase_image = true; + public $config_nohotlink_text_message = 'Off-server thumbnailing is not allowed'; + // * Off-server Linking Configuration: + public $config_nooffsitelink_enabled = false; + public $config_nooffsitelink_valid_domains = array(); + public $config_nooffsitelink_require_refer = false; + public $config_nooffsitelink_erase_image = true; + public $config_nooffsitelink_watermark_src = ''; + public $config_nooffsitelink_text_message = 'Off-server linking is not allowed'; + + // * Border & Background default colors + public $config_border_hexcolor = '000000'; + public $config_background_hexcolor = 'FFFFFF'; + + // * TrueType Fonts + public $config_ttf_directory = './fonts'; + + public $config_max_source_pixels = null; + public $config_use_exif_thumbnail_for_speed = false; + public $config_allow_local_http_src = false; + + public $config_imagemagick_path = null; + public $config_prefer_imagemagick = true; + public $config_imagemagick_use_thumbnail = true; + + public $config_cache_maxage = null; + public $config_cache_maxsize = null; + public $config_cache_maxfiles = null; + public $config_cache_source_filemtime_ignore_local = false; + public $config_cache_source_filemtime_ignore_remote = true; + public $config_cache_default_only_suffix = false; + public $config_cache_force_passthru = true; + public $config_cache_prefix = ''; // default value set in the constructor below + + // * MySQL + public $config_mysql_extension = null; + public $config_mysql_query = null; + public $config_mysql_hostname = null; + public $config_mysql_username = null; + public $config_mysql_password = null; + public $config_mysql_database = null; + + // * Security + public $config_high_security_enabled = true; + public $config_high_security_password = null; + public $config_high_security_url_separator = '&'; + public $config_disable_debug = true; + public $config_allow_src_above_docroot = false; + public $config_allow_src_above_phpthumb = true; + public $config_auto_allow_symlinks = true; // allow symlink target directories without explicitly whitelisting them + public $config_additional_allowed_dirs = array(); // additional directories to allow source images to be read from + public $config_file_create_mask = 0755; + public $config_dir_create_mask = 0755; + + // * HTTP fopen + public $config_http_fopen_timeout = 10; + public $config_http_follow_redirect = true; + + // * Compatability + public $config_disable_pathinfo_parsing = false; + public $config_disable_imagecopyresampled = false; + public $config_disable_onlycreateable_passthru = false; + public $config_disable_realpath = false; + + public $config_http_user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7'; + + // END CONFIGURATION OPTIONS + + + // public: error messages (read-only; persistant) + public $debugmessages = array(); + public $debugtiming = array(); + public $fatalerror = null; + + + // private: (should not be modified directly) + public $thumbnailQuality = 75; + public $thumbnailFormat = null; + + public $sourceFilename = null; + public $rawImageData = null; + public $IMresizedData = null; + public $outputImageData = null; + + public $useRawIMoutput = false; + + public $gdimg_output = null; + public $gdimg_source = null; + + public $getimagesizeinfo = null; + + public $source_width = null; + public $source_height = null; + + public $thumbnailCropX = null; + public $thumbnailCropY = null; + public $thumbnailCropW = null; + public $thumbnailCropH = null; + + public $exif_thumbnail_width = null; + public $exif_thumbnail_height = null; + public $exif_thumbnail_type = null; + public $exif_thumbnail_data = null; + public $exif_raw_data = null; + + public $thumbnail_width = null; + public $thumbnail_height = null; + public $thumbnail_image_width = null; + public $thumbnail_image_height = null; + + public $tempFilesToDelete = array(); + public $cache_filename = null; + + public $AlphaCapableFormats = array( 'png', 'ico', 'gif', 'webp', 'avif'); + public $is_alpha = false; + + public $iswindows = null; + public $issafemode = null; + public $php_memory_limit = null; + + public $phpthumb_version = '1.7.19-202210110924'; + + ////////////////////////////////////////////////////////////////////// + + // public: constructor + public function __construct() { + $this->phpThumb(); + } + + public function phpThumb() { + $this->DebugTimingMessage('phpThumb() constructor', __FILE__, __LINE__); + $this->DebugMessage('phpThumb() v'.$this->phpthumb_version, __FILE__, __LINE__); + + foreach (array(ini_get('memory_limit'), get_cfg_var('memory_limit')) as $php_config_memory_limit) { + if (!empty($php_config_memory_limit)) { + if (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'G') { // PHP memory limit expressed in Gigabytes + $php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1073741824; + } elseif (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'M') { // PHP memory limit expressed in Megabytes + $php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1048576; + } + $this->php_memory_limit = max($this->php_memory_limit, $php_config_memory_limit); + } + } + if ($this->php_memory_limit > 0) { // could be "-1" for "no limit" + $this->config_max_source_pixels = round($this->php_memory_limit * 0.20); // 20% of memory_limit + } + + $this->iswindows = (bool) (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'); + $this->issafemode = (bool) preg_match('#(1|ON)#i', ini_get('safe_mode')); + $this->config_document_root = (!empty($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : $this->config_document_root); + $this->config_cache_prefix = ( isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'].'_' : ''); + + $this->purgeTempFiles(); // purge existing temp files if re-initializing object + + $php_sapi_name = strtolower(function_exists('php_sapi_name') ? PHP_SAPI : ''); + if ($php_sapi_name == 'cli') { + $this->config_allow_src_above_docroot = true; + } + + if (!$this->config_disable_debug) { + // if debug mode is enabled, force phpThumbDebug output, do not allow normal thumbnails to be generated + $this->phpThumbDebug = (null === $this->phpThumbDebug ? 9 : max(1, (int) $this->phpThumbDebug)); + } + } + + public function __destruct() { + $this->purgeTempFiles(); + } + + // public: + public function purgeTempFiles() { + foreach ($this->tempFilesToDelete as $tempFileToDelete) { + if (file_exists($tempFileToDelete)) { + $this->DebugMessage('Deleting temp file "'.$tempFileToDelete.'"', __FILE__, __LINE__); + @unlink($tempFileToDelete); + } + } + $this->tempFilesToDelete = array(); + return true; + } + + // public: + public function setSourceFilename($sourceFilename) { + //$this->resetObject(); + //$this->rawImageData = null; + $this->sourceFilename = $sourceFilename; + $this->src = $sourceFilename; + if (null === $this->config_output_format) { + $sourceFileExtension = strtolower(substr(strrchr($sourceFilename, '.'), 1)); + if (preg_match('#^[a-z]{3,4}$#', $sourceFileExtension)) { + $this->config_output_format = $sourceFileExtension; + $this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->config_output_format to "'.$sourceFileExtension.'"', __FILE__, __LINE__); + } else { + $this->DebugMessage('setSourceFilename('.$sourceFilename.') did NOT set $this->config_output_format to "'.$sourceFileExtension.'" because it did not seem like an appropriate image format', __FILE__, __LINE__); + } + } + $this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->sourceFilename to "'.$this->sourceFilename.'"', __FILE__, __LINE__); + return true; + } + + // public: + public function setSourceData($rawImageData, $sourceFilename='') { + //$this->resetObject(); + //$this->sourceFilename = null; + $this->rawImageData = $rawImageData; + $this->DebugMessage('setSourceData() setting $this->rawImageData ('.strlen($this->rawImageData).' bytes; magic="'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).'))', __FILE__, __LINE__); + if ($this->config_cache_source_enabled) { + $sourceFilename = ($sourceFilename ? $sourceFilename : md5($rawImageData)); + if (!is_dir($this->config_cache_source_directory)) { + $this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not a directory'); + } elseif (!@is_writable($this->config_cache_source_directory)) { + $this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not writable'); + } + $this->DebugMessage('setSourceData() attempting to save source image to "'.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).'"', __FILE__, __LINE__); + if ($fp = @fopen($this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename), 'wb')) { + fwrite($fp, $rawImageData); + fclose($fp); + } elseif (!$this->phpThumbDebug) { + $this->ErrorImage('setSourceData() failed to write to source cache ('.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).')'); + } + } + return true; + } + + // public: + public function setSourceImageResource($gdimg) { + //$this->resetObject(); + $this->gdimg_source = $gdimg; + return true; + } + + // public: + public function setParameter($param, $value) { + if ($param == 'src') { + $this->setSourceFilename($this->ResolveFilenameToAbsolute($value)); + } elseif (@is_array($this->$param)) { + if (is_array($value)) { + foreach ($value as $arraykey => $arrayvalue) { + array_push($this->$param, $arrayvalue); + } + } else { + array_push($this->$param, $value); + } + } else { + $this->$param = $value; + } + return true; + } + + // public: + public function getParameter($param) { + //if (property_exists('phpThumb', $param)) { + return $this->$param; + //} + //$this->DebugMessage('setParameter() attempting to get non-existant parameter "'.$param.'"', __FILE__, __LINE__); + //return false; + } + + + // public: + public function GenerateThumbnail() { + + $this->setOutputFormat(); + $this->phpThumbDebug('8a'); + $this->ResolveSource(); + $this->phpThumbDebug('8b'); + $this->SetCacheFilename(); + $this->phpThumbDebug('8c'); + $this->ExtractEXIFgetImageSize(); + $this->phpThumbDebug('8d'); + if ($this->useRawIMoutput) { + $this->DebugMessage('Skipping rest of GenerateThumbnail() because ($this->useRawIMoutput == true)', __FILE__, __LINE__); + return true; + } + $this->phpThumbDebug('8e'); + if (!$this->SourceImageToGD()) { + $this->DebugMessage('SourceImageToGD() failed', __FILE__, __LINE__); + return false; + } + $this->phpThumbDebug('8f'); + $this->ImageCropAuto(); + $this->phpThumbDebug('8h'); + $this->Rotate(); + $this->phpThumbDebug('8h'); + $this->CreateGDoutput(); + $this->phpThumbDebug('8i'); + + // default values, also applicable for far="C" + $destination_offset_x = round(($this->thumbnail_width - $this->thumbnail_image_width) / 2); + $destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2); + if (($this->far == 'L') || ($this->far == 'TL') || ($this->far == 'BL')) { + $destination_offset_x = 0; + } + if (($this->far == 'R') || ($this->far == 'TR') || ($this->far == 'BR')) { + $destination_offset_x = round($this->thumbnail_width - $this->thumbnail_image_width); + } + if (($this->far == 'T') || ($this->far == 'TL') || ($this->far == 'TR')) { + $destination_offset_y = 0; + } + if (($this->far == 'B') || ($this->far == 'BL') || ($this->far == 'BR')) { + $destination_offset_y = round($this->thumbnail_height - $this->thumbnail_image_height); + } + +// // copy/resize image to appropriate dimensions +// $borderThickness = 0; +// if (!empty($this->fltr)) { +// foreach ($this->fltr as $key => $value) { +// if (preg_match('#^bord\|([0-9]+)#', $value, $matches)) { +// $borderThickness = $matches[1]; +// break; +// } +// } +// } +// if ($borderThickness > 0) { +// //$this->DebugMessage('Skipping ImageResizeFunction() because BorderThickness="'.$borderThickness.'"', __FILE__, __LINE__); +// $this->thumbnail_image_height /= 2; +// } + $this->ImageResizeFunction( + $this->gdimg_output, + $this->gdimg_source, + $destination_offset_x, + $destination_offset_y, + $this->thumbnailCropX, + $this->thumbnailCropY, + $this->thumbnail_image_width, + $this->thumbnail_image_height, + $this->thumbnailCropW, + $this->thumbnailCropH + ); + + $this->DebugMessage('memory_get_usage() after copy-resize = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__); + imagedestroy($this->gdimg_source); + $this->DebugMessage('memory_get_usage() after imagedestroy = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__); + + $this->phpThumbDebug('8i'); + $this->AntiOffsiteLinking(); + $this->phpThumbDebug('8j'); + $this->ApplyFilters(); + $this->phpThumbDebug('8k'); + $this->AlphaChannelFlatten(); + $this->phpThumbDebug('8l'); + $this->MaxFileSize(); + $this->phpThumbDebug('8m'); + + $this->DebugMessage('GenerateThumbnail() completed successfully', __FILE__, __LINE__); + return true; + } + + + // public: + public function RenderOutput() { + if (!$this->useRawIMoutput && !(is_resource($this->gdimg_output) || (is_object($this->gdimg_source) && $this->gdimg_source instanceOf \GdImage))) { + $this->DebugMessage('RenderOutput() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__); + return false; + } + if (!$this->thumbnailFormat) { + $this->DebugMessage('RenderOutput() failed because $this->thumbnailFormat is empty', __FILE__, __LINE__); + return false; + } + if ($this->useRawIMoutput) { + $this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__); + $this->outputImageData = $this->IMresizedData; + return true; + } + + $builtin_formats = array(); + if (function_exists('imagetypes')) { + $imagetypes = imagetypes(); + $builtin_formats['wbmp'] = (bool) ($imagetypes & IMG_WBMP); + $builtin_formats['jpg'] = (bool) ($imagetypes & IMG_JPG); + $builtin_formats['gif'] = (bool) ($imagetypes & IMG_GIF); + $builtin_formats['png'] = (bool) ($imagetypes & IMG_PNG); + $builtin_formats['webp'] = (bool) ($imagetypes & IMG_WEBP); // PHP 7.0.10 + $builtin_formats['bmp'] = (bool) ($imagetypes & IMG_BMP); // PHP 7.2.0 + $builtin_formats['avif'] = (bool) ($imagetypes & IMG_AVIF); // PHP 8.1.0 + } + + $this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__); + imageinterlace($this->gdimg_output, (int) $this->config_output_interlace); + + $this->DebugMessage('RenderOutput() attempting image'.strtolower(@$this->thumbnailFormat).'($this->gdimg_output)', __FILE__, __LINE__); + ob_start(); + switch ($this->thumbnailFormat) { + case 'wbmp': + if (empty($builtin_formats['wbmp'])) { + $this->DebugMessage('GD does not have required built-in support for WBMP output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + imagewbmp($this->gdimg_output, null, $this->thumbnailQuality); + $this->outputImageData = ob_get_contents(); + break; + + case 'jpeg': + case 'jpg': // should be "jpeg" not "jpg" but just in case... + if (empty($builtin_formats['jpg'])) { + $this->DebugMessage('GD does not have required built-in support for JPEG output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + imagejpeg($this->gdimg_output, null, $this->thumbnailQuality); + $this->outputImageData = ob_get_contents(); + break; + + case 'png': + if (empty($builtin_formats['png'])) { + $this->DebugMessage('GD does not have required built-in support for PNG output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.1.2', '>=')) { + // https://github.com/JamesHeinrich/phpThumb/issues/24 + + /* http://php.net/manual/en/function.imagepng.php: + from php source (gd.h): + 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all, + :: 1 is FASTEST but produces larger files, 9 provides the best + :: compression (smallest files) but takes a long time to compress, and + :: -1 selects the default compiled into the zlib library. + Conclusion: Based on the Zlib manual (http://www.zlib.net/manual.html) the default compression level is set to 6. + */ + if (($this->thumbnailQuality >= -1) && ($this->thumbnailQuality <= 9)) { + $PNGquality = $this->thumbnailQuality; + } else { + $this->DebugMessage('Specified thumbnailQuality "'.$this->thumbnailQuality.'" is outside the accepted range (0-9, or -1). Using 6 as default value.', __FILE__, __LINE__); + $PNGquality = 6; + } + imagepng($this->gdimg_output, null, $PNGquality); + } else { + imagepng($this->gdimg_output); + } + $this->outputImageData = ob_get_contents(); + break; + + case 'gif': + if (empty($builtin_formats['gif'])) { + $this->DebugMessage('GD does not have required built-in support for GIF output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + imagegif($this->gdimg_output); + $this->outputImageData = ob_get_contents(); + break; + + case 'webp': + if (empty($builtin_formats['webp'])) { + $this->DebugMessage('GD does not have required built-in support for WebP output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + imagewebp($this->gdimg_output, null, $this->thumbnailQuality); + $this->outputImageData = ob_get_contents(); + break; + + case 'avif': + if (empty($builtin_formats['avif'])) { + $this->DebugMessage('GD does not have required built-in support for AVIF output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + imageavif($this->gdimg_output, null, $this->thumbnailQuality); + $this->outputImageData = ob_get_contents(); + break; + + case 'bmp': + if (!empty($builtin_formats['bmp'])) { + imagebmp($this->gdimg_output); + $this->outputImageData = ob_get_contents(); + break; + } + $this->DebugMessage('GD does not have required built-in support for BMP output', __FILE__, __LINE__); + if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + $phpthumb_bmp = new phpthumb_bmp(); + $this->outputImageData = $phpthumb_bmp->GD2BMPstring($this->gdimg_output); + unset($phpthumb_bmp); + break; + + case 'ico': + if (!@include_once __DIR__ .'/phpthumb.ico.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + $phpthumb_ico = new phpthumb_ico(); + $arrayOfOutputImages = array($this->gdimg_output); + $this->outputImageData = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages); + unset($phpthumb_ico); + break; + + default: + $this->DebugMessage('RenderOutput failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + ob_end_clean(); + if (!$this->outputImageData) { + $this->DebugMessage('RenderOutput() for "'.$this->thumbnailFormat.'" failed', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + $this->DebugMessage('RenderOutput() completing with $this->outputImageData = '.strlen($this->outputImageData).' bytes', __FILE__, __LINE__); + return true; + } + + + // public: + public function RenderToFile($filename) { + if (preg_match('#^[a-z0-9]+://#i', $filename)) { + $this->DebugMessage('RenderToFile() failed because $filename ('.$filename.') is a URL', __FILE__, __LINE__); + return false; + } + // render thumbnail to this file only, do not cache, do not output to browser + //$renderfilename = $this->ResolveFilenameToAbsolute(dirname($filename)).DIRECTORY_SEPARATOR.basename($filename); + $renderfilename = $filename; + if (($filename[0] != '/') && ($filename[0] != '\\') && ($filename[1] != ':')) { + $renderfilename = $this->ResolveFilenameToAbsolute($renderfilename); + } + if (!@is_writable(dirname($renderfilename))) { + $this->DebugMessage('RenderToFile() failed because "'.dirname($renderfilename).'/" is not writable', __FILE__, __LINE__); + return false; + } + if (@is_file($renderfilename) && !@is_writable($renderfilename)) { + $this->DebugMessage('RenderToFile() failed because "'.$renderfilename.'" is not writable', __FILE__, __LINE__); + return false; + } + + if ($this->RenderOutput()) { + if (file_put_contents($renderfilename, $this->outputImageData)) { + @chmod($renderfilename, $this->getParameter('config_file_create_mask')); + $this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__); + return true; + } + if (!@file_exists($renderfilename)) { + $this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] did not appear to fail, but the output image does not exist either...', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] failed', __FILE__, __LINE__); + } + return false; + } + + + // public: + public function OutputThumbnail() { + $this->purgeTempFiles(); + + if (!$this->useRawIMoutput && !(is_resource($this->gdimg_output) || (is_object($this->gdimg_source) && $this->gdimg_source instanceOf \GdImage))) { + $this->DebugMessage('OutputThumbnail() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__); + return false; + } + if (headers_sent()) { + return $this->ErrorImage('OutputThumbnail() failed - headers already sent'); + } + + $downloadfilename = phpthumb_functions::SanitizeFilename(is_string($this->sia) ? $this->sia : ($this->down ? $this->down : 'phpThumb_generated_thumbnail'.'.'.$this->thumbnailFormat)); + $this->DebugMessage('Content-Disposition header filename set to "'.$downloadfilename.'"', __FILE__, __LINE__); + if ($downloadfilename) { + header('Content-Disposition: '.($this->down ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"'); + } else { + $this->DebugMessage('failed to send Content-Disposition header because $downloadfilename is empty', __FILE__, __LINE__); + } + + if ($this->useRawIMoutput) { + + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat)); + echo $this->IMresizedData; + + } else { + + $this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__); + imageinterlace($this->gdimg_output, (int) $this->config_output_interlace); + switch ($this->thumbnailFormat) { + case 'gif': + case 'jpeg': + case 'png': + case 'webp': + case 'avif': + $ImageOutFunction = 'image'.$this->thumbnailFormat; + if (!function_exists($ImageOutFunction)) { + $this->DebugMessage($ImageOutFunction.' is not available', __FILE__, __LINE__); + return false; + } + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat)); + if ($this->thumbnailFormat == 'gif') { + @$ImageOutFunction($this->gdimg_output); + } else { + @$ImageOutFunction($this->gdimg_output, null, $this->thumbnailQuality); + } + break; + + case 'bmp': + if (function_exists('imagebmp')) { + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat)); + imagebmp($this->gdimg_output); + break; + } + if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__); + return false; + } + $phpthumb_bmp = new phpthumb_bmp(); + if (is_object($phpthumb_bmp)) { + $bmp_data = $phpthumb_bmp->GD2BMPstring($this->gdimg_output); + unset($phpthumb_bmp); + if (!$bmp_data) { + $this->DebugMessage('$phpthumb_bmp->GD2BMPstring() failed', __FILE__, __LINE__); + return false; + } + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat)); + echo $bmp_data; + } else { + $this->DebugMessage('new phpthumb_bmp() failed', __FILE__, __LINE__); + return false; + } + break; + + case 'ico': + if (!@include_once __DIR__ .'/phpthumb.ico.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__); + return false; + } + $phpthumb_ico = new phpthumb_ico(); + if (is_object($phpthumb_ico)) { + $arrayOfOutputImages = array($this->gdimg_output); + $ico_data = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages); + unset($phpthumb_ico); + if (!$ico_data) { + $this->DebugMessage('$phpthumb_ico->GD2ICOstring() failed', __FILE__, __LINE__); + return false; + } + header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat)); + echo $ico_data; + } else { + $this->DebugMessage('new phpthumb_ico() failed', __FILE__, __LINE__); + return false; + } + break; + + default: + $this->DebugMessage('OutputThumbnail failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__); + return false; + break; + } + + } + return true; + } + + + // public: + public function CleanUpCacheDirectory() { + $this->DebugMessage('CleanUpCacheDirectory() set to purge ('.(null === $this->config_cache_maxage ? 'NULL' : number_format($this->config_cache_maxage / 86400, 1)).' days; '.(null === $this->config_cache_maxsize ? 'NULL' : number_format($this->config_cache_maxsize / 1048576, 2)).' MB; '.(null === $this->config_cache_maxfiles ? 'NULL' : number_format($this->config_cache_maxfiles)).' files)', __FILE__, __LINE__); + + if (!is_writable($this->config_cache_directory)) { + $this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$this->config_cache_directory.'" is not writable', __FILE__, __LINE__); + return true; + } + + // cache status of cache directory for 1 hour to avoid hammering the filesystem functions + $phpThumbCacheStats_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheStats.txt'; + if (file_exists($phpThumbCacheStats_filename) && is_readable($phpThumbCacheStats_filename) && (filemtime($phpThumbCacheStats_filename) >= (time() - 3600))) { + $this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$phpThumbCacheStats_filename.'" is recently modified', __FILE__, __LINE__); + return true; + } + if (!@touch($phpThumbCacheStats_filename)) { + $this->DebugMessage('touch('.$phpThumbCacheStats_filename.') failed', __FILE__, __LINE__); + } + + $DeletedKeys = array(); + $AllFilesInCacheDirectory = array(); + if (($this->config_cache_maxage > 0) || ($this->config_cache_maxsize > 0) || ($this->config_cache_maxfiles > 0)) { + $CacheDirOldFilesAge = array(); + $CacheDirOldFilesSize = array(); + $AllFilesInCacheDirectory = phpthumb_functions::GetAllFilesInSubfolders($this->config_cache_directory); + foreach ($AllFilesInCacheDirectory as $fullfilename) { + if (preg_match('#'.preg_quote($this->config_cache_prefix).'#i', $fullfilename) && file_exists($fullfilename)) { + $CacheDirOldFilesAge[$fullfilename] = @fileatime($fullfilename); + if ($CacheDirOldFilesAge[$fullfilename] == 0) { + $CacheDirOldFilesAge[$fullfilename] = @filemtime($fullfilename); + } + $CacheDirOldFilesSize[$fullfilename] = @filesize($fullfilename); + } + } + if (empty($CacheDirOldFilesSize)) { + $this->DebugMessage('CleanUpCacheDirectory() skipped because $CacheDirOldFilesSize is empty (phpthumb_functions::GetAllFilesInSubfolders('.$this->config_cache_directory.') found no files)', __FILE__, __LINE__); + return true; + } + $DeletedKeys['zerobyte'] = array(); + foreach ($CacheDirOldFilesSize as $fullfilename => $filesize) { + // purge all zero-size files more than an hour old (to prevent trying to delete just-created and/or in-use files) + $cutofftime = time() - 3600; + if (($filesize == 0) && ($CacheDirOldFilesAge[$fullfilename] < $cutofftime)) { + $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__); + if (@unlink($fullfilename)) { + $DeletedKeys['zerobyte'][] = $fullfilename; + unset($CacheDirOldFilesSize[$fullfilename]); + unset($CacheDirOldFilesAge[$fullfilename]); + } + } + } + $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['zerobyte']).' zero-byte files', __FILE__, __LINE__); + asort($CacheDirOldFilesAge); + + if ($this->config_cache_maxfiles > 0) { + $TotalCachedFiles = count($CacheDirOldFilesAge); + $DeletedKeys['maxfiles'] = array(); + foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) { + if ($TotalCachedFiles > $this->config_cache_maxfiles) { + $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__); + if (@unlink($fullfilename)) { + $TotalCachedFiles--; + $DeletedKeys['maxfiles'][] = $fullfilename; + } + } else { + // there are few enough files to keep the rest + break; + } + } + $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxfiles']).' files based on (config_cache_maxfiles='.$this->config_cache_maxfiles.')', __FILE__, __LINE__); + foreach ($DeletedKeys['maxfiles'] as $fullfilename) { + unset($CacheDirOldFilesAge[$fullfilename]); + unset($CacheDirOldFilesSize[$fullfilename]); + } + } + + if ($this->config_cache_maxage > 0) { + $mindate = time() - $this->config_cache_maxage; + $DeletedKeys['maxage'] = array(); + foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) { + if ($filedate > 0) { + if ($filedate < $mindate) { + $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__); + if (@unlink($fullfilename)) { + $DeletedKeys['maxage'][] = $fullfilename; + } + } else { + // the rest of the files are new enough to keep + break; + } + } + } + $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxage']).' files based on (config_cache_maxage='.$this->config_cache_maxage.')', __FILE__, __LINE__); + foreach ($DeletedKeys['maxage'] as $fullfilename) { + unset($CacheDirOldFilesAge[$fullfilename]); + unset($CacheDirOldFilesSize[$fullfilename]); + } + } + + if ($this->config_cache_maxsize > 0) { + $TotalCachedFileSize = array_sum($CacheDirOldFilesSize); + $DeletedKeys['maxsize'] = array(); + foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) { + if ($TotalCachedFileSize > $this->config_cache_maxsize) { + $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__); + if (@unlink($fullfilename)) { + $TotalCachedFileSize -= $CacheDirOldFilesSize[$fullfilename]; + $DeletedKeys['maxsize'][] = $fullfilename; + } + } else { + // the total filesizes are small enough to keep the rest of the files + break; + } + } + $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxsize']).' files based on (config_cache_maxsize='.$this->config_cache_maxsize.')', __FILE__, __LINE__); + foreach ($DeletedKeys['maxsize'] as $fullfilename) { + unset($CacheDirOldFilesAge[$fullfilename]); + unset($CacheDirOldFilesSize[$fullfilename]); + } + } + + } else { + $this->DebugMessage('skipping CleanUpCacheDirectory() because config set to not use it', __FILE__, __LINE__); + } + $totalpurged = 0; + foreach ($DeletedKeys as $key => $value) { + $totalpurged += count($value); + } + $this->DebugMessage('CleanUpCacheDirectory() purged '.$totalpurged.' files (from '.count($AllFilesInCacheDirectory).') based on config settings', __FILE__, __LINE__); + if ($totalpurged > 0) { + $empty_dirs = array(); + foreach ($AllFilesInCacheDirectory as $fullfilename) { + if (is_dir($fullfilename)) { + $empty_dirs[$this->realPathSafe($fullfilename)] = 1; + } else { + unset($empty_dirs[$this->realPathSafe(dirname($fullfilename))]); + } + } + krsort($empty_dirs); + $totalpurgeddirs = 0; + foreach ($empty_dirs as $empty_dir => $dummy) { + if ($empty_dir == $this->config_cache_directory) { + // shouldn't happen, but just in case, don't let it delete actual cache directory + continue; + } elseif (@rmdir($empty_dir)) { + $totalpurgeddirs++; + } else { + $this->DebugMessage('failed to rmdir('.$empty_dir.')', __FILE__, __LINE__); + } + } + $this->DebugMessage('purged '.$totalpurgeddirs.' empty directories', __FILE__, __LINE__); + } + return true; + } + + ////////////////////////////////////////////////////////////////////// + + // private: re-initializator (call between rendering multiple images with one object) + public function resetObject() { + $class_vars = get_class_vars(get_class($this)); + foreach ($class_vars as $key => $value) { + // do not clobber debug or config info + if (!preg_match('#^(config_|debug|fatalerror)#i', $key)) { + $this->$key = $value; + } + } + $this->phpThumb(); // re-initialize some class variables + return true; + } + + ////////////////////////////////////////////////////////////////////// + + public function ResolveSource() { + if (is_resource($this->gdimg_source) || (is_object($this->gdimg_source) && $this->gdimg_source instanceOf \GdImage)) { + $this->DebugMessage('ResolveSource() exiting because is_resource($this->gdimg_source)', __FILE__, __LINE__); + return true; + } + if ($this->rawImageData) { + $this->sourceFilename = null; + $this->DebugMessage('ResolveSource() exiting because $this->rawImageData is set ('.number_format(strlen($this->rawImageData)).' bytes)', __FILE__, __LINE__); + return true; + } + if ($this->sourceFilename) { + $this->sourceFilename = $this->ResolveFilenameToAbsolute($this->sourceFilename); + $this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'"', __FILE__, __LINE__); + } elseif ($this->src) { + $this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src); + $this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'" from $this->src ('.$this->src.')', __FILE__, __LINE__); + } else { + return $this->ErrorImage('$this->sourceFilename and $this->src are both empty'); + } + if ($this->iswindows && ((substr($this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) { + // Windows \\share\filename.ext + } elseif (preg_match('#^[a-z0-9]+://#i', $this->sourceFilename, $protocol_matches)) { + if (preg_match('#^(f|ht)tps?\://#i', $this->sourceFilename)) { + // URL + if ($this->config_http_user_agent) { + ini_set('user_agent', $this->config_http_user_agent); + } + } else { + return $this->ErrorImage('only FTP and HTTP/HTTPS protocols are allowed, "'.$protocol_matches[1].'" is not'); + } + } elseif (!@file_exists($this->sourceFilename)) { + return $this->ErrorImage('"'.$this->sourceFilename.'" does not exist'); + } elseif (!@is_file($this->sourceFilename)) { + return $this->ErrorImage('"'.$this->sourceFilename.'" is not a file'); + } + return true; + } + + + public function setOutputFormat() { + static $alreadyCalled = false; + if ($this->thumbnailFormat && $alreadyCalled) { + return true; + } + $alreadyCalled = true; + + $AvailableImageOutputFormats = array(); + $AvailableImageOutputFormats[] = 'text'; + if (@is_readable( __DIR__ .'/phpthumb.ico.php')) { + $AvailableImageOutputFormats[] = 'ico'; + } + if (@is_readable( __DIR__ .'/phpthumb.bmp.php')) { + $AvailableImageOutputFormats[] = 'bmp'; + } + + $this->thumbnailFormat = 'ico'; + + // Set default output format based on what image types are available + if (function_exists('imagetypes')) { + $imagetypes = imagetypes(); + if ($imagetypes & IMG_WBMP) { + $this->thumbnailFormat = 'wbmp'; + $AvailableImageOutputFormats[] = 'wbmp'; + } + if ($imagetypes & IMG_GIF) { + $this->thumbnailFormat = 'gif'; + $AvailableImageOutputFormats[] = 'gif'; + } + if ($imagetypes & IMG_AVIF) { + $this->thumbnailFormat = 'avif'; + $AvailableImageOutputFormats[] = 'avif'; + } + if ($imagetypes & IMG_WEBP) { + $this->thumbnailFormat = 'webp'; + $AvailableImageOutputFormats[] = 'webp'; + } + if ($imagetypes & IMG_PNG) { + $this->thumbnailFormat = 'png'; + $AvailableImageOutputFormats[] = 'png'; + } + if ($imagetypes & IMG_JPG) { + $this->thumbnailFormat = 'jpeg'; + $AvailableImageOutputFormats[] = 'jpeg'; + } + } else { + $this->DebugMessage('imagetypes() does not exist - GD support might not be enabled?', __FILE__, __LINE__); + } + if ($this->ImageMagickVersion()) { + $IMformats = array('jpeg', 'png', 'gif', 'bmp', 'ico', 'wbmp', 'webp', 'avif'); + $this->DebugMessage('Addding ImageMagick formats to $AvailableImageOutputFormats ('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__); + foreach ($IMformats as $key => $format) { + $AvailableImageOutputFormats[] = $format; + } + } + $AvailableImageOutputFormats = array_unique($AvailableImageOutputFormats); + $this->DebugMessage('$AvailableImageOutputFormats = array('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__); + + $this->f = (!empty($this->f) ? $this->f : ''); + $this->f = preg_replace('#[^a-z]#', '', strtolower($this->f)); + if (strtolower($this->config_output_format) == 'jpg') { + $this->config_output_format = 'jpeg'; + } + if (strtolower($this->f) == 'jpg') { + $this->f = 'jpeg'; + } + if (phpthumb_functions::CaseInsensitiveInArray($this->config_output_format, $AvailableImageOutputFormats)) { + // set output format to config default if that format is available + $this->DebugMessage('$this->thumbnailFormat set to $this->config_output_format "'.strtolower($this->config_output_format).'"', __FILE__, __LINE__); + $this->thumbnailFormat = strtolower($this->config_output_format); + } elseif ($this->config_output_format) { + $this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->config_output_format ('.strtolower($this->config_output_format).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__); + } + if ($this->f && phpthumb_functions::CaseInsensitiveInArray($this->f, $AvailableImageOutputFormats) ) { + // override output format if $this->f is set and that format is available + $this->DebugMessage('$this->thumbnailFormat set to $this->f "'.strtolower($this->f).'"', __FILE__, __LINE__); + $this->thumbnailFormat = strtolower($this->f); + } elseif ($this->f) { + $this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->f ('.strtolower($this->f).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__); + } + + // for JPEG images, quality 1 (worst) to 99 (best) + // quality < 25 is nasty, with not much size savings - not recommended + // problems with 100 - invalid JPEG? + $this->thumbnailQuality = max(1, min(99, ($this->q ? (int) $this->q : 75))); + $this->DebugMessage('$this->thumbnailQuality set to "'.$this->thumbnailQuality.'"', __FILE__, __LINE__); + + return true; + } + + + public function setCacheDirectory() { + // resolve cache directory to absolute pathname + $this->DebugMessage('setCacheDirectory() starting with config_cache_directory = "'.$this->config_cache_directory.'"', __FILE__, __LINE__); + if ($this->config_cache_directory && ($this->config_cache_directory[0] == '.')) { + if (preg_match('#^(f|ht)tps?\://#i', $this->src)) { + if (!$this->config_cache_disable_warning) { + $this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') cannot be used for remote images. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php'); + } + } elseif ($this->src) { + // resolve relative cache directory to source image + $this->config_cache_directory = dirname($this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory; + } else { + // $this->new is probably set + } + } + if (substr($this->config_cache_directory, -1) == '/') { + $this->config_cache_directory = substr($this->config_cache_directory, 0, -1); + } + if ($this->iswindows) { + $this->config_cache_directory = str_replace('/', DIRECTORY_SEPARATOR, $this->config_cache_directory); + } + if ($this->config_cache_directory) { + $real_cache_path = $this->realPathSafe($this->config_cache_directory); + if (!$real_cache_path) { + $this->DebugMessage('$this->realPathSafe($this->config_cache_directory) failed for "'.$this->config_cache_directory.'"', __FILE__, __LINE__); + if (!is_dir($this->config_cache_directory)) { + $this->DebugMessage('!is_dir('.$this->config_cache_directory.')', __FILE__, __LINE__); + } + } + if ($real_cache_path) { + $this->DebugMessage('setting config_cache_directory to $this->realPathSafe('.$this->config_cache_directory.') = "'.$real_cache_path.'"', __FILE__, __LINE__); + $this->config_cache_directory = $real_cache_path; + } + } + if (!is_dir($this->config_cache_directory)) { + if (!$this->config_cache_disable_warning) { + $this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') does not exist. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php'); + } + $this->DebugMessage('$this->config_cache_directory ('.$this->config_cache_directory.') is not a directory', __FILE__, __LINE__); + $this->config_cache_directory = null; + } elseif (!@is_writable($this->config_cache_directory)) { + $this->DebugMessage('$this->config_cache_directory is not writable ('.$this->config_cache_directory.')', __FILE__, __LINE__); + } + + $this->InitializeTempDirSetting(); + if (!@is_dir($this->config_temp_directory) && !@is_writable($this->config_temp_directory) && @is_dir($this->config_cache_directory) && @is_writable($this->config_cache_directory)) { + $this->DebugMessage('setting $this->config_temp_directory = $this->config_cache_directory ('.$this->config_cache_directory.')', __FILE__, __LINE__); + $this->config_temp_directory = $this->config_cache_directory; + } + return true; + } + + /* Takes the array of path segments up to now, and the next segment (maybe a modifier: empty, . or ..) + Applies it, adding or removing from $segments as a result. Returns nothing. */ + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + public function applyPathSegment(&$segments, $segment) { + if ($segment == '.') { + return; // always remove + } + if ($segment == '') { + $test = array_pop($segments); + if (null === $test) { + $segments[] = $segment; // keep the first empty block + } elseif ($test == '') { + $test = array_pop($segments); + if (null === $test) { + $segments[] = $test; + $segments[] = $segment; // keep the second one too + } else { // put both back and ignore segment + $segments[] = $test; + $segments[] = $test; + } + } else { + $segments[] = $test; // ignore empty blocks + } + } else { + if ($segment == '..') { + $test = array_pop($segments); + if (null === $test) { + $segments[] = $segment; + } elseif ($test == '..') { + $segments[] = $test; + $segments[] = $segment; + } else { + if ($test == '') { + $segments[] = $test; + } // else nothing, remove both + } + } else { + $segments[] = $segment; + } + } + } + + /* Takes array of path components, normalizes it: removes empty slots and '.', collapses '..' and folder names. Returns array. */ + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + public function normalizePath($segments) { + $parts = array(); + foreach ($segments as $segment) { + $this->applyPathSegment($parts, $segment); + } + return $parts; + } + + /* True if the provided path points (without resolving symbolic links) into one of the allowed directories. */ + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + public function matchPath($path, $allowed_dirs) { + if (!empty($allowed_dirs)) { + foreach ($allowed_dirs as $one_dir) { + if (preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', $this->realPathSafe($one_dir))).'#', $path)) { + return true; + } + } + } + return false; + } + + /* True if the provided path points inside one of open_basedirs (or if open_basedirs are disabled) */ + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + public function isInOpenBasedir($path) { + static $open_basedirs = null; + if (null === $open_basedirs) { + $ini_text = ini_get('open_basedir'); + $this->DebugMessage('open_basedir: "'.$ini_text.'"', __FILE__, __LINE__); + $open_basedirs = array(); + if (strlen($ini_text) > 0) { + foreach (preg_split('#[;:]#', $ini_text) as $key => $value) { + $open_basedirs[$key] = $this->realPathSafe($value); + } + } + } + return (empty($open_basedirs) || $this->matchPath($path, $open_basedirs)); + } + + /* Resolves all symlinks in $path, checking that each continuous part ends in an allowed zone. Returns null, if any component leads outside of allowed zone. */ + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + public function resolvePath($path, $allowed_dirs) { + $this->DebugMessage('resolvePath: '.$path.' (allowed_dirs: '.print_r($allowed_dirs, true).')', __FILE__, __LINE__); + + // add base path to the top of the list + if (!$this->config_allow_src_above_docroot) { + array_unshift($allowed_dirs, $this->realPathSafe($this->config_document_root)); + } else { + if (!$this->config_allow_src_above_phpthumb) { + array_unshift($allowed_dirs, $this->realPathSafe( __DIR__ )); + } else { + // no checks are needed, offload the work to realpath and forget about it + $this->DebugMessage('resolvePath: checks disabled, returning '.$this->realPathSafe($path), __FILE__, __LINE__); + return $this->realPathSafe($path); + } + } + if ($path == '') { + return null; // save us trouble + } + + do { + $this->DebugMessage('resolvePath: iteration, path='.$path.', base path = '.$allowed_dirs[0], __FILE__, __LINE__); + + $parts = array(); + // do not use "cleaner" foreach version of this loop as later code relies on both $segments and $i + // http://support.silisoftware.com/phpBB3/viewtopic.php?t=964 + $segments = explode(DIRECTORY_SEPARATOR, $path); + for ($i = 0, $iMax = count($segments); $i < $iMax; $i++) { + $this->applyPathSegment($parts, $segments[$i]); + $thispart = implode(DIRECTORY_SEPARATOR, $parts); + if ($this->isInOpenBasedir($thispart)) { + if (is_link($thispart)) { + break; + } + } + } + + $this->DebugMessage('resolvePath: stop at component '.$i, __FILE__, __LINE__); + // test the part up to here + $path = implode(DIRECTORY_SEPARATOR, $parts); + $this->DebugMessage('resolvePath: stop at path='.$path, __FILE__, __LINE__); + if (!$this->matchPath($path, $allowed_dirs)) { + $this->DebugMessage('resolvePath: no match, returning null', __FILE__, __LINE__); + return null; + } + if ($i >= count($segments)) { // reached end + $this->DebugMessage('resolvePath: path parsed, over', __FILE__, __LINE__); + break; + } + // else it's symlink, rewrite path + $path = readlink($path); + $this->DebugMessage('resolvePath: symlink matched, target='.$path, __FILE__, __LINE__); + + /* + Replace base path with symlink target. + Assuming: + /www/img/external -> /external + This is allowed: + GET /www/img/external/../external/test/pic.jpg + This isn't: + GET /www/img/external/../www/img/pic.jpg + So there's only one base path which is the last symlink target, but any number of stable whitelisted paths. + */ + if ($this->config_auto_allow_symlinks) { + $allowed_dirs[0] = $path; + } + $path = $path.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, array_slice($segments,$i + 1)); + } while (true); + return $path; + } + + + public function realPathSafe($filename) { + // http://php.net/manual/en/function.realpath.php -- "Note: The running script must have executable permissions on all directories in the hierarchy, otherwise realpath() will return FALSE" + // realPathSafe() provides a reasonable facsimile of realpath() but does not resolve symbolic links, nor does it check that the file/path actually exists + if (!$this->config_disable_realpath) { + return realpath($filename); + } + + // http://stackoverflow.com/questions/21421569 + $newfilename = preg_replace('#[\\/]+#', DIRECTORY_SEPARATOR, $filename); + + if (phpthumb_functions::is_windows()) { + $isAlreadyAbsoluteFilename = preg_match('#^[A-Z]\\:#i', $newfilename); // C:\path\filename.ext + } else { + $isAlreadyAbsoluteFilename = ($newfilename[0] == DIRECTORY_SEPARATOR); // /path/filename.ext + } + if (!$isAlreadyAbsoluteFilename) { + // not already an absolute filename, prepend current directory + $newfilename = __DIR__ .DIRECTORY_SEPARATOR.$newfilename; + } + do { + $beforeloop = $newfilename; + + // Replace all sequences of more than one / with a single one [[ If you're working on a system that treats // at the start of a path as special, make sure you replace multiple / characters at the start with two of them. This is the only place where POSIX allows (but does not mandate) special handling for multiples, in all other cases, multiple / characters are equivalent to a single one.]] + $newfilename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'+#', DIRECTORY_SEPARATOR, $newfilename); + + // Replace all occurrences of /./ with / + $newfilename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'\\.'.preg_quote(DIRECTORY_SEPARATOR).'#', DIRECTORY_SEPARATOR, $newfilename); + + // Remove ./ if at the start + $newfilename = preg_replace('#^\\.'.preg_quote(DIRECTORY_SEPARATOR).'#', '', $newfilename); + + // Remove /. if at the end + $newfilename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'\\.$#', '', $newfilename); + + // Replace /anything/../ with / + $newfilename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'[^'.preg_quote(DIRECTORY_SEPARATOR).']+'.preg_quote(DIRECTORY_SEPARATOR).'\\.\\.'.preg_quote(DIRECTORY_SEPARATOR).'#', DIRECTORY_SEPARATOR, $newfilename); + + // Remove /anything/.. if at the end + $newfilename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'[^'.preg_quote(DIRECTORY_SEPARATOR).']+'.preg_quote(DIRECTORY_SEPARATOR).'\\.\\.$#', '', $newfilename); + + } while ($newfilename != $beforeloop); + return $newfilename; + } + + + public function ResolveFilenameToAbsolute($filename) { + if (empty($filename)) { + return false; + } + + if (preg_match('#^[a-z0-9]+\\:/{1,2}#i', $filename)) { + // eg: http://host/path/file.jpg (HTTP URL) + // eg: ftp://host/path/file.jpg (FTP URL) + // eg: data1:/path/file.jpg (Netware path) + + //$AbsoluteFilename = $filename; + return $filename; + + } elseif ($this->iswindows && isset($filename[1]) && ($filename[1] == ':')) { + + // absolute pathname (Windows) + $AbsoluteFilename = $filename; + + } elseif ($this->iswindows && ((substr($filename, 0, 2) == '//') || (substr($filename, 0, 2) == '\\\\'))) { + + // absolute pathname (Windows) + $AbsoluteFilename = $filename; + + } elseif ($filename[0] == '/') { + + if (@is_readable($filename) && !@is_readable($this->config_document_root.$filename)) { + + // absolute filename (*nix) + $AbsoluteFilename = $filename; + + } elseif (isset($filename[1]) && ($filename[1] == '~')) { + + // /~user/path + if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray($filename)) { + $AbsoluteFilename = $ApacheLookupURIarray['filename']; + } else { + $AbsoluteFilename = $this->realPathSafe($filename); + if (@is_readable($AbsoluteFilename)) { + $this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with $this->realPathSafe($filename)', __FILE__, __LINE__); + } elseif (is_dir(dirname($AbsoluteFilename))) { + $this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname($filename).'", but the correct directory ('.dirname($AbsoluteFilename).') seems to have been resolved with $this->realPathSafe(.)', __FILE__, __LINE__); + } else { + return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.$filename.'". This has been known to fail on Apache2 - try using the absolute filename for the source image (ex: "/home/user/httpdocs/image.jpg" instead of "/~user/image.jpg")'); + } + } + + } else { + + // relative filename (any OS) + if (preg_match('#^'.preg_quote($this->config_document_root).'#', $filename)) { + $AbsoluteFilename = $filename; + $this->DebugMessage('ResolveFilenameToAbsolute() NOT prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__); + } else { + $AbsoluteFilename = $this->config_document_root.$filename; + $this->DebugMessage('ResolveFilenameToAbsolute() prepending $this->config_document_root ('.$this->config_document_root.') to $filename ('.$filename.') resulting in ($AbsoluteFilename = "'.$AbsoluteFilename.'")', __FILE__, __LINE__); + } + + } + + } else { + + // relative to current directory (any OS) + $AbsoluteFilename = __DIR__ .DIRECTORY_SEPARATOR.preg_replace('#[/\\\\]#', DIRECTORY_SEPARATOR, $filename); + + if (substr(dirname(@$_SERVER['PHP_SELF']), 0, 2) == '/~') { + if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) { + $AbsoluteFilename = $ApacheLookupURIarray['filename'].DIRECTORY_SEPARATOR.$filename; + } else { + $AbsoluteFilename = $this->realPathSafe('.').DIRECTORY_SEPARATOR.$filename; + if (@is_readable($AbsoluteFilename)) { + $this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct filename ('.$AbsoluteFilename.') seems to have been resolved with $this->realPathSafe(.)/$filename', __FILE__, __LINE__); + } elseif (is_dir(dirname($AbsoluteFilename))) { + $this->DebugMessage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'", but the correct directory ('.dirname($AbsoluteFilename).') seems to have been resolved with $this->realPathSafe(.)', __FILE__, __LINE__); + } else { + return $this->ErrorImage('phpthumb_functions::ApacheLookupURIarray() failed for "'.dirname(@$_SERVER['PHP_SELF']).'". This has been known to fail on Apache2 - try using the absolute filename for the source image'); + } + } + } + + } + /* + // removed 2014-May-30: http://support.silisoftware.com/phpBB3/viewtopic.php?t=961 + if (is_link($AbsoluteFilename)) { + $this->DebugMessage('is_link()==true, changing "'.$AbsoluteFilename.'" to "'.readlink($AbsoluteFilename).'"', __FILE__, __LINE__); + $AbsoluteFilename = readlink($AbsoluteFilename); + } + if ($this->realPathSafe($AbsoluteFilename)) { + $AbsoluteFilename = $this->realPathSafe($AbsoluteFilename); + } + */ + if ($this->iswindows) { + $AbsoluteFilename = preg_replace('#^'.preg_quote($this->realPathSafe($this->config_document_root)).'#i', str_replace('\\', '\\\\', $this->realPathSafe($this->config_document_root)), $AbsoluteFilename); + $AbsoluteFilename = str_replace(DIRECTORY_SEPARATOR, '/', $AbsoluteFilename); + } + $resolvedAbsoluteFilename = $this->resolvePath($AbsoluteFilename, $this->config_additional_allowed_dirs); + if (!$this->config_allow_src_above_docroot && !preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', $this->realPathSafe($this->config_document_root))).'#', $resolvedAbsoluteFilename)) { + $this->DebugMessage('!$this->config_allow_src_above_docroot therefore setting "'.$AbsoluteFilename.'" (outside "'.$this->realPathSafe($this->config_document_root).'") to null', __FILE__, __LINE__); + return false; + } + if (!$this->config_allow_src_above_phpthumb && !preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', __DIR__ )).'#', $resolvedAbsoluteFilename)) { + $this->DebugMessage('!$this->config_allow_src_above_phpthumb therefore setting "'.$AbsoluteFilename.'" (outside "'. __DIR__ .'") to null', __FILE__, __LINE__); + return false; + } + return $resolvedAbsoluteFilename; + } + + + public function file_exists_ignoreopenbasedir($filename, $cached=true) { + static $open_basedirs = null; + static $file_exists_cache = array(); + if (!$cached || !isset($file_exists_cache[$filename])) { + if (is_null($open_basedirs)) { + $open_basedirs = preg_split('#[;:]#', ini_get('open_basedir')); + } + if (is_null($filename)) { // shouldn't happen, but https://github.com/JamesHeinrich/phpThumb/issues/188 + $file_exists_cache[$filename] = false; + } elseif (empty($open_basedirs) || in_array(dirname($filename), $open_basedirs)) { + $file_exists_cache[$filename] = file_exists($filename); + } elseif ($this->iswindows) { + $ls_filename = trim(phpthumb_functions::SafeExec('dir /b '.phpthumb_functions::escapeshellarg_replacement($filename))); + $file_exists_cache[$filename] = ($ls_filename == basename($filename)); // command dir /b return only filename without path + } else { + $ls_filename = trim(phpthumb_functions::SafeExec('ls '.phpthumb_functions::escapeshellarg_replacement($filename))); + $file_exists_cache[$filename] = ($ls_filename == $filename); + } + } + return $file_exists_cache[$filename]; + } + + + public function ImageMagickWhichConvert() { + static $WhichConvert = null; + if (null === $WhichConvert) { + if ($this->iswindows) { + $WhichConvert = false; + } else { + $IMwhichConvertCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMwhichConvert.txt'; + if (($cachedwhichconvertstring = @file_get_contents($IMwhichConvertCacheFilename)) !== false) { + $WhichConvert = $cachedwhichconvertstring; + } else { + $WhichConvert = trim(phpthumb_functions::SafeExec('which convert')); + @file_put_contents($IMwhichConvertCacheFilename, $WhichConvert); + @chmod($IMwhichConvertCacheFilename, $this->getParameter('config_file_create_mask')); + } + } + } + return $WhichConvert; + } + + + public function ImageMagickCommandlineBase() { + static $commandline = null; + if (null === $commandline) { + if ($this->issafemode) { + $commandline = ''; + return $commandline; + } + + $IMcommandlineBaseCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMcommandlineBase.txt'; + if (($commandline = @file_get_contents($IMcommandlineBaseCacheFilename)) !== false) { + return $commandline; + } + + $commandline = (null !== $this->config_imagemagick_path ? $this->config_imagemagick_path : ''); + + if ($this->config_imagemagick_path && ($this->config_imagemagick_path != $this->realPathSafe($this->config_imagemagick_path))) { + if (@is_executable($this->realPathSafe($this->config_imagemagick_path))) { + $this->DebugMessage('Changing $this->config_imagemagick_path ('.$this->config_imagemagick_path.') to $this->realPathSafe($this->config_imagemagick_path) ('.$this->realPathSafe($this->config_imagemagick_path).')', __FILE__, __LINE__); + $this->config_imagemagick_path = $this->realPathSafe($this->config_imagemagick_path); + } else { + $this->DebugMessage('Leaving $this->config_imagemagick_path as ('.$this->config_imagemagick_path.') because !is_execuatable($this->realPathSafe($this->config_imagemagick_path)) ('.$this->realPathSafe($this->config_imagemagick_path).')', __FILE__, __LINE__); + } + } + if (!empty($this->config_imagemagick_path)) { + $this->DebugMessage(' file_exists('.$this->config_imagemagick_path.') = '. (int) (@file_exists($this->config_imagemagick_path)), __FILE__, __LINE__); + $this->DebugMessage('file_exists_ignoreopenbasedir('.$this->config_imagemagick_path.') = '. (int) $this->file_exists_ignoreopenbasedir($this->config_imagemagick_path), __FILE__, __LINE__); + $this->DebugMessage(' is_file('.$this->config_imagemagick_path.') = '. (int) (@is_file($this->config_imagemagick_path)), __FILE__, __LINE__); + $this->DebugMessage(' is_executable('.$this->config_imagemagick_path.') = '. (int) (@is_executable($this->config_imagemagick_path)), __FILE__, __LINE__); + } + + if ($this->file_exists_ignoreopenbasedir($this->config_imagemagick_path)) { + + $this->DebugMessage('using ImageMagick path from $this->config_imagemagick_path ('.$this->config_imagemagick_path.')', __FILE__, __LINE__); + if ($this->iswindows) { + $commandline = ''; + $commandline .= substr($this->config_imagemagick_path, 0, 2); + $commandline .= ' && cd '.phpthumb_functions::escapeshellarg_replacement(str_replace('/', DIRECTORY_SEPARATOR, substr(dirname($this->config_imagemagick_path), 2))); + $commandline .= ' && '.phpthumb_functions::escapeshellarg_replacement(basename($this->config_imagemagick_path)); + } else { + $commandline = phpthumb_functions::escapeshellarg_replacement($this->config_imagemagick_path); + } + + } else { + + $which_convert = $this->ImageMagickWhichConvert(); + $IMversion = $this->ImageMagickVersion(); + + if ($which_convert && ($which_convert[0] == '/') && $this->file_exists_ignoreopenbasedir($which_convert)) { + + // `which convert` *should* return the path if "convert" exist, or nothing if it doesn't + // other things *may* get returned, like "sh: convert: not found" or "no convert in /usr/local/bin /usr/sbin /usr/bin /usr/ccs/bin" + // so only do this if the value returned exists as a file + $this->DebugMessage('using ImageMagick path from `which convert` ('.$which_convert.')', __FILE__, __LINE__); + $commandline = 'convert'; + + } elseif ($IMversion) { + + $this->DebugMessage('setting ImageMagick path to $this->config_imagemagick_path ('.$this->config_imagemagick_path.') ['.$IMversion.']', __FILE__, __LINE__); + $commandline = $this->config_imagemagick_path; + + } else { + + $this->DebugMessage('ImageMagickThumbnailToGD() aborting because cannot find convert in $this->config_imagemagick_path ('.$this->config_imagemagick_path.'), and `which convert` returned ('.$which_convert.')', __FILE__, __LINE__); + $commandline = ''; + + } + + } + + @file_put_contents($IMcommandlineBaseCacheFilename, $commandline); + @chmod($IMcommandlineBaseCacheFilename, $this->getParameter('config_file_create_mask')); + } + return $commandline; + } + + + public function ImageMagickVersion($returnRAW=false) { + static $versionstring = null; + if (null === $versionstring) { + $versionstring = array(0=>false, 1=>false); + + $IMversionCacheFilename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheIMversion.txt'; + if ($cachedversionstring = @file_get_contents($IMversionCacheFilename)) { + + $versionstring = explode("\n", $cachedversionstring, 2); + $versionstring[0] = ($versionstring[0] ? $versionstring[0] : false); // "false" is stored as an empty string in the cache file + $versionstring[1] = ($versionstring[1] ? $versionstring[1] : false); // "false" is stored as an empty string in the cache file + + } else { + + $commandline = $this->ImageMagickCommandlineBase(); + $commandline = (null !== $commandline ? $commandline : ''); + if ($commandline) { + $commandline .= ' --version'; + $this->DebugMessage('ImageMagick version checked with "'.$commandline.'"', __FILE__, __LINE__); + $versionstring[1] = trim(phpthumb_functions::SafeExec($commandline)); + if (preg_match('#^Version: [^\d]*([ 0-9\\.\\:Q/\\-]+)#i', $versionstring[1], $matches)) { + $versionstring[0] = trim($matches[1]); + } else { + $versionstring[0] = false; + $this->DebugMessage('ImageMagick did not return recognized version string ('.$versionstring[1].')', __FILE__, __LINE__); + } + $this->DebugMessage('ImageMagick convert --version says "'.@$matches[0].'"', __FILE__, __LINE__); + } + + @file_put_contents($IMversionCacheFilename, $versionstring[0]."\n".$versionstring[1]); + @chmod($IMversionCacheFilename, $this->getParameter('config_file_create_mask')); + + } + } + return $versionstring[ (int) $returnRAW ]; + } + + + public function ImageMagickSwitchAvailable($switchname) { + static $IMoptions = null; + if (null === $IMoptions) { + $IMoptions = array(); + $commandline = $this->ImageMagickCommandlineBase(); + if (null !== $commandline) { + $commandline .= ' -help'; + $IMhelp_lines = explode("\n", phpthumb_functions::SafeExec($commandline)); + foreach ($IMhelp_lines as $line) { + if (preg_match('#^[\\+\\-]([a-z\\-]+) #', trim($line), $matches)) { + $IMoptions[$matches[1]] = true; + } + } + } + } + if (is_array($switchname)) { + $allOK = true; + foreach ($switchname as $key => $value) { + if (!isset($IMoptions[$value])) { + $allOK = false; + break; + } + } + $this->DebugMessage('ImageMagickSwitchAvailable('.implode(';', $switchname).') = '. (int) $allOK .'', __FILE__, __LINE__); + } else { + $allOK = isset($IMoptions[$switchname]); + $this->DebugMessage('ImageMagickSwitchAvailable('.$switchname.') = '. (int) $allOK .'', __FILE__, __LINE__); + } + return $allOK; + } + + + public function ImageMagickFormatsList() { + static $IMformatsList = null; + if (null === $IMformatsList) { + $IMformatsList = ''; + $commandline = $this->ImageMagickCommandlineBase(); + if (!is_null($commandline)) { + $commandline = dirname($commandline).DIRECTORY_SEPARATOR.str_replace('convert', 'identify', basename($commandline)); + $commandline .= ' -list format'; + $IMformatsList = phpthumb_functions::SafeExec($commandline); + } + } + return $IMformatsList; + } + + + public function SourceDataToTempFile() { + if ($IMtempSourceFilename = $this->phpThumb_tempnam()) { + $IMtempSourceFilename = $this->realPathSafe($IMtempSourceFilename); + ob_start(); + $fp_tempfile = fopen($IMtempSourceFilename, 'wb'); + $tempfile_open_error = ob_get_contents(); + ob_end_clean(); + if ($fp_tempfile) { + fwrite($fp_tempfile, $this->rawImageData); + fclose($fp_tempfile); + @chmod($IMtempSourceFilename, $this->getParameter('config_file_create_mask')); + $this->sourceFilename = $IMtempSourceFilename; + $this->DebugMessage('ImageMagickThumbnailToGD() setting $this->sourceFilename to "'.$IMtempSourceFilename.'" from $this->rawImageData ('.strlen($this->rawImageData).' bytes)', __FILE__, __LINE__); + } else { + $this->DebugMessage('ImageMagickThumbnailToGD() FAILED setting $this->sourceFilename to "'.$IMtempSourceFilename.'" (failed to open for writing: "'.$tempfile_open_error.'")', __FILE__, __LINE__); + } + unset($tempfile_open_error, $IMtempSourceFilename); + return true; + } + $this->DebugMessage('SourceDataToTempFile() FAILED because $this->phpThumb_tempnam() failed', __FILE__, __LINE__); + return false; + } + + + public function ImageMagickThumbnailToGD() { + // http://www.imagemagick.org/script/command-line-options.php + + $this->useRawIMoutput = true; + if (phpthumb_functions::gd_version()) { + // if GD is not available, must use whatever ImageMagick can output + + // $CannotMagickParameters contains options that cannot be used with ImageMagick + $CannotMagickParameters = array('ica'); + foreach ($CannotMagickParameters as $parameter) { + if (isset($this->$parameter)) { + $this->DebugMessage('cannot process with ImageMagick because "'.$parameter.'" is set', __FILE__, __LINE__); + $this->useRawIMoutput = false; + return false; + } + } + + // $UnAllowedParameters contains options that can only be processed in GD, not ImageMagick + // note: 'fltr' *may* need to be processed by GD, but we'll check that in more detail below + $UnAllowedParameters = array('xto', 'ar', 'bg', 'bc'); + // 'ra' may be part of this list, if not a multiple of 90 degrees + foreach ($UnAllowedParameters as $parameter) { + if (isset($this->$parameter)) { + $this->DebugMessage('$this->useRawIMoutput=false because "'.$parameter.'" is set', __FILE__, __LINE__); + $this->useRawIMoutput = false; + break; + } + } + } + $this->DebugMessage('$this->useRawIMoutput='.($this->useRawIMoutput ? 'true' : 'false').' after checking $UnAllowedParameters', __FILE__, __LINE__); + $ImageCreateFunction = ''; + $outputFormat = $this->thumbnailFormat; + if (phpthumb_functions::gd_version()) { + if ($this->useRawIMoutput) { + switch ($this->thumbnailFormat) { + case 'gif': + $ImageCreateFunction = 'imagecreatefromgif'; + $this->is_alpha = true; + break; + case 'png': + $ImageCreateFunction = 'imagecreatefrompng'; + $this->is_alpha = true; + break; + case 'jpg': + case 'jpeg': + $ImageCreateFunction = 'imagecreatefromjpeg'; + break; + case 'webp': + $ImageCreateFunction = 'imagecreatefromwebp'; + $this->is_alpha = true; + break; + case 'avif': + $ImageCreateFunction = 'imagecreatefromavif'; + $this->is_alpha = true; + break; + default: + $this->DebugMessage('Forcing output to PNG because $this->thumbnailFormat ('.$this->thumbnailFormat.' is not a GD-supported format)', __FILE__, __LINE__); + $outputFormat = 'png'; + $ImageCreateFunction = 'imagecreatefrompng'; + $this->is_alpha = true; + $this->useRawIMoutput = false; + break; + } + if (!function_exists($ImageCreateFunction)) { + // ImageMagickThumbnailToGD() depends on imagecreatefrompng/imagecreatefromgif + //$this->DebugMessage('ImageMagickThumbnailToGD() aborting because '.@$ImageCreateFunction.'() is not available', __FILE__, __LINE__); + $this->useRawIMoutput = true; + //return false; + } + } else { + $outputFormat = 'png'; + $ImageCreateFunction = 'imagecreatefrompng'; + $this->is_alpha = true; + $this->useRawIMoutput = false; + } + } + + // http://freealter.org/doc_distrib/ImageMagick-5.1.1/www/convert.html + if (!$this->sourceFilename && $this->rawImageData) { + $this->SourceDataToTempFile(); + } + if (!$this->sourceFilename) { + $this->DebugMessage('ImageMagickThumbnailToGD() aborting because $this->sourceFilename is empty', __FILE__, __LINE__); + $this->useRawIMoutput = false; + return false; + } + if ($this->issafemode) { + $this->DebugMessage('ImageMagickThumbnailToGD() aborting because safe_mode is enabled', __FILE__, __LINE__); + $this->useRawIMoutput = false; + return false; + } +// TO BE FIXED +//if (true) { +// $this->DebugMessage('ImageMagickThumbnailToGD() aborting it is broken right now', __FILE__, __LINE__); +// $this->useRawIMoutput = false; +// return false; +//} + + $commandline = $this->ImageMagickCommandlineBase(); + if ($commandline) { + $commandline .= ' '.phpthumb_functions::escapeshellarg_replacement(preg_replace('#[/\\\\]#', DIRECTORY_SEPARATOR, $this->sourceFilename).(($outputFormat == 'gif') ? '' : '['. (int) $this->sfn .']')); // [0] means first frame of (GIF) animation, can be ignored + if ($IMtempfilename = $this->phpThumb_tempnam()) { + $IMtempfilename = $this->realPathSafe($IMtempfilename); + + $IMuseExplicitImageOutputDimensions = false; + if ($this->ImageMagickSwitchAvailable('thumbnail') && $this->config_imagemagick_use_thumbnail) { + $IMresizeParameter = 'thumbnail'; + } else { + $IMresizeParameter = 'resize'; + + // some (older? around 2002) versions of IM won't accept "-resize 100x" but require "-resize 100x100" + $commandline_test = $this->ImageMagickCommandlineBase().' logo: -resize 1x '.phpthumb_functions::escapeshellarg_replacement($IMtempfilename).' 2>&1'; + $IMresult_test = phpthumb_functions::SafeExec($commandline_test); + $IMuseExplicitImageOutputDimensions = preg_match('#image dimensions are zero#i', $IMresult_test); + $this->DebugMessage('IMuseExplicitImageOutputDimensions = '. (int) $IMuseExplicitImageOutputDimensions, __FILE__, __LINE__); + if ($fp_im_temp = @fopen($IMtempfilename, 'wb')) { + // erase temp image so ImageMagick logo doesn't get output if other processing fails + fclose($fp_im_temp); + @chmod($IMtempfilename, $this->getParameter('config_file_create_mask')); + } + } + + + ob_start(); + $getimagesize = getimagesize($this->sourceFilename); + $GetImageSizeError = ob_get_contents(); + ob_end_clean(); + if (is_array($getimagesize)) { + $this->DebugMessage('getimagesize('.$this->sourceFilename.') SUCCEEDED: '.print_r($getimagesize, true), __FILE__, __LINE__); + } else { + $this->DebugMessage('getimagesize('.$this->sourceFilename.') FAILED with error "'.$GetImageSizeError.'"', __FILE__, __LINE__); + } + if (null !== $this->dpi && $this->ImageMagickSwitchAvailable('density')) { + // for vector source formats only (WMF, PDF, etc) + if (is_array($getimagesize) && isset($getimagesize[2]) && ($getimagesize[2] == IMAGETYPE_PNG)) { + // explicitly exclude PNG from "-flatten" to make sure transparency is preserved + // https://github.com/JamesHeinrich/phpThumb/issues/65 + } else { + $commandline .= ' -flatten'; + $commandline .= ' -density '.phpthumb_functions::escapeshellarg_replacement($this->dpi); + } + } + if (is_array($getimagesize)) { + $this->DebugMessage('getimagesize('.$this->sourceFilename.') returned [w='.$getimagesize[0].';h='.$getimagesize[1].';f='.$getimagesize[2].']', __FILE__, __LINE__); + $this->source_width = $getimagesize[0]; + $this->source_height = $getimagesize[1]; + $this->DebugMessage('source dimensions set to '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__); + $this->SetOrientationDependantWidthHeight(); + + if (!preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat)) { + // not a transparency-capable format + $commandline .= ' -background '.phpthumb_functions::escapeshellarg_replacement('#'.($this->bg ? $this->bg : 'FFFFFF')); + if (!stristr($commandline, ' -flatten')) { + $commandline .= ' -flatten'; + } + } else { + if ($getimagesize[2] == IMAGETYPE_PNG && !$this->bg) { + $commandline .= ' -background none'; + } + } + if ($getimagesize[2] == IMAGETYPE_GIF) { + $commandline .= ' -coalesce'; // may be needed for animated GIFs + } + if ($this->source_width || $this->source_height) { + if ($this->zc) { + + $borderThickness = 0; + if (!empty($this->fltr)) { + foreach ($this->fltr as $key => $value) { + if (preg_match('#^bord\|([\d]+)#', $value, $matches)) { + $borderThickness = $matches[1]; + break; + } + } + } + $wAll = (int) max($this->w, $this->wp, $this->wl, $this->ws) - (2 * $borderThickness); + $hAll = (int) max($this->h, $this->hp, $this->hl, $this->hs) - (2 * $borderThickness); + $imAR = $this->source_width / $this->source_height; + $zcAR = (($wAll && $hAll) ? $wAll / $hAll : 1); + $side = phpthumb_functions::nonempty_min($this->source_width, $this->source_height, max($wAll, $hAll)); + $sideX = phpthumb_functions::nonempty_min($this->source_width, $wAll, round($hAll * $zcAR)); + $sideY = phpthumb_functions::nonempty_min( $this->source_height, $hAll, round($wAll / $zcAR)); + + $thumbnailH = round(max($sideY, ($sideY * $zcAR) / $imAR)); + if ($this->aoe == 1) { + $commandline .= ' -'.$IMresizeParameter.' "'.$wAll.'x'.$hAll.'^"'; + } else { + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($IMuseExplicitImageOutputDimensions ? $thumbnailH : '').'x'.$thumbnailH); + } + + switch (strtoupper($this->zc)) { + case 'T': + $commandline .= ' -gravity north'; + break; + case 'B': + $commandline .= ' -gravity south'; + break; + case 'L': + $commandline .= ' -gravity west'; + break; + case 'R': + $commandline .= ' -gravity east'; + break; + case 'TL': + $commandline .= ' -gravity northwest'; + break; + case 'TR': + $commandline .= ' -gravity northeast'; + break; + case 'BL': + $commandline .= ' -gravity southwest'; + break; + case 'BR': + $commandline .= ' -gravity southeast'; + break; + case '1': + case 'C': + default: + $commandline .= ' -gravity center'; + break; + } + + if (($wAll > 0) && ($hAll > 0)) { + $commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($wAll.'x'.$hAll.'+0+0'); + } else { + $commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($side.'x'.$side.'+0+0'); + } + if ($this->ImageMagickSwitchAvailable('repage')) { + $commandline .= ' +repage'; + } else { + $this->DebugMessage('Skipping "+repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__); + } + + } elseif ($this->sw || $this->sh || $this->sx || $this->sy) { + + $crop_param = ''; + $crop_param .= ($this->sw ? (($this->sw < 2) ? round($this->sw * $this->source_width) : $this->sw) : $this->source_width); + $crop_param .= 'x'.($this->sh ? (($this->sh < 2) ? round($this->sh * $this->source_height) : $this->sh) : $this->source_height); + $crop_param .= '+'.(($this->sx < 2) ? round($this->sx * $this->source_width) : $this->sx); + $crop_param .= '+'.(($this->sy < 2) ? round($this->sy * $this->source_height) : $this->sy); +// TO BE FIXED +// makes 1x1 output +// http://trainspotted.com/phpThumb/phpThumb.php?src=/content/CNR/47/CNR-4728-LD-L-20110723-898.jpg&w=100&h=100&far=1&f=png&fltr[]=lvl&sx=0.05&sy=0.25&sw=0.92&sh=0.42 +// '/usr/bin/convert' -density 150 -thumbnail 100x100 -contrast-stretch '0.1%' '/var/www/vhosts/trainspotted.com/httpdocs/content/CNR/47/CNR-4728-LD-L-20110723-898.jpg[0]' png:'/var/www/vhosts/trainspotted.com/httpdocs/phpThumb/_cache/pThumbIIUlvj' + $commandline .= ' -crop '.phpthumb_functions::escapeshellarg_replacement($crop_param); + + // this is broken for aoe=1, but unsure how to fix. Send advice to info@silisoftware.com + if ($this->w || $this->h) { + //if ($this->ImageMagickSwitchAvailable('repage')) { +if (false) { +// TO BE FIXED +// newer versions of ImageMagick require -repage + $commandline .= ' -repage'; + } else { + $this->DebugMessage('Skipping "-repage" because ImageMagick (v'.$this->ImageMagickVersion().') does not support it', __FILE__, __LINE__); + } + if ($IMuseExplicitImageOutputDimensions) { + if ($this->w && !$this->h) { + $this->h = ceil($this->w / ($this->source_width / $this->source_height)); + } elseif ($this->h && !$this->w) { + $this->w = ceil($this->h * ($this->source_width / $this->source_height)); + } + } + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h); + } + + } else { + + if ($this->iar && ((int) $this->w > 0) && ((int) $this->h > 0)) { + + list($nw, $nh) = phpthumb_functions::TranslateWHbyAngle($this->w, $this->h, $this->ra); + $nw = ((round($nw) != 0) ? round($nw) : ''); + $nh = ((round($nh) != 0) ? round($nh) : ''); + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($nw.'x'.$nh.'!'); + + } elseif ($this->far && ((int) $this->w > 0) && ((int) $this->h > 0)) { + + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(phpthumb_functions::nonempty_min($this->w, $getimagesize[0]).'x'.phpthumb_functions::nonempty_min($this->h, $getimagesize[1])); + $commandline .= ' -gravity center'; + if ($this->bg) { + $commandline .= ' -background ' . phpthumb_functions::escapeshellarg_replacement('#' . $this->bg); + } else { + $commandline .= ' -background none'; + } + $commandline .= ' -extent '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h); + + } else { + + $this->w = (($this->aoe && $this->w) ? $this->w : ($this->w ? phpthumb_functions::nonempty_min($this->w, $getimagesize[0]) : null)); + $this->h = (($this->aoe && $this->h) ? $this->h : ($this->h ? phpthumb_functions::nonempty_min($this->h, $getimagesize[1]) : null)); + if ($this->w || $this->h) { + if ($IMuseExplicitImageOutputDimensions) { + if ($this->w && !$this->h) { + $this->h = ceil($this->w / ($this->source_width / $this->source_height)); + } elseif ($this->h && !$this->w) { + $this->w = ceil($this->h * ($this->source_width / $this->source_height)); + } + } + list($nw, $nh) = phpthumb_functions::TranslateWHbyAngle($this->w, $this->h, $this->ra); + $nw = ((round($nw) != 0) ? round($nw) : ''); + $nh = ((round($nh) != 0) ? round($nh) : ''); + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($nw.'x'.$nh); + } + + } + } + } + + } else { + + $this->DebugMessage('getimagesize('.$this->sourceFilename.') failed', __FILE__, __LINE__); + if ($this->w || $this->h) { + $exactDimensionsBang = (($this->iar && ((int) $this->w > 0) && ((int) $this->h > 0)) ? '!' : ''); + if ($IMuseExplicitImageOutputDimensions) { + // unknown source aspect ratio, just put large number and hope IM figures it out + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($this->w ? $this->w : '9999').'x'.($this->h ? $this->h : '9999').$exactDimensionsBang); + } else { + $commandline .= ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement($this->w.'x'.$this->h.$exactDimensionsBang); + } + } + + } + + if ($this->ra) { + $this->ra = (int) $this->ra; + if ($this->ImageMagickSwitchAvailable('rotate')) { + if (!preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat) || phpthumb_functions::version_compare_replacement($this->ImageMagickVersion(), '6.3.7', '>=')) { + $this->DebugMessage('Using ImageMagick rotate', __FILE__, __LINE__); + $commandline .= ' -rotate '.phpthumb_functions::escapeshellarg_replacement($this->ra); + if (($this->ra % 90) != 0) { + if (preg_match('#('.implode('|', $this->AlphaCapableFormats).')#i', $outputFormat)) { + // alpha-capable format + $commandline .= ' -background rgba(255,255,255,0)'; + } else { + $commandline .= ' -background '.phpthumb_functions::escapeshellarg_replacement('#'.($this->bg ? $this->bg : 'FFFFFF')); + } + } + $this->ra = 0; + } else { + $this->DebugMessage('Not using ImageMagick rotate because alpha background buggy before v6.3.7', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('Not using ImageMagick rotate because not supported', __FILE__, __LINE__); + } + } + + $successfullyProcessedFilters = array(); + foreach ($this->fltr as $filterkey => $filtercommand) { + @list($command, $parameter) = explode('|', $filtercommand, 2); + switch ($command) { + case 'brit': + if ($this->ImageMagickSwitchAvailable('modulate')) { + $commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement((100 + (int) $parameter).',100,100'); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'cont': + if ($this->ImageMagickSwitchAvailable('contrast')) { + $contDiv10 = round((int) $parameter / 10); + if ($contDiv10 > 0) { + $contDiv10 = min($contDiv10, 100); + for ($i = 0; $i < $contDiv10; $i++) { + $commandline .= ' -contrast'; // increase contrast by 10% + } + } elseif ($contDiv10 < 0) { + $contDiv10 = max($contDiv10, -100); + for ($i = $contDiv10; $i < 0; $i++) { + $commandline .= ' +contrast'; // decrease contrast by 10% + } + } else { + // do nothing + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'ds': + if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) { + if ($parameter == 100) { + $commandline .= ' -colorspace GRAY'; + $commandline .= ' -modulate 100,0,100'; + } else { + $commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement('100,'.(100 - (int) $parameter).',100'); + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'sat': + if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) { + if ($parameter == -100) { + $commandline .= ' -colorspace GRAY'; + $commandline .= ' -modulate 100,0,100'; + } else { + $commandline .= ' -modulate '.phpthumb_functions::escapeshellarg_replacement('100,'.(100 + (int) $parameter).',100'); + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'gray': + if ($this->ImageMagickSwitchAvailable(array('colorspace', 'modulate'))) { + $commandline .= ' -colorspace GRAY'; + $commandline .= ' -modulate 100,0,100'; + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'clr': + if ($this->ImageMagickSwitchAvailable(array('fill', 'colorize'))) { + @list($amount, $color) = explode('|', $parameter); + $commandline .= ' -fill '.phpthumb_functions::escapeshellarg_replacement('#'.preg_replace('#[^0-9A-F]#i', '', $color)); + $commandline .= ' -colorize '.phpthumb_functions::escapeshellarg_replacement(min(max((int) $amount, 0), 100)); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'sep': + if ($this->ImageMagickSwitchAvailable('sepia-tone')) { + @list($amount, $color) = explode('|', $parameter); + $amount = ($amount ? $amount : 80); + if (!$color) { + $commandline .= ' -sepia-tone '.phpthumb_functions::escapeshellarg_replacement(min(max((int) $amount, 0), 100).'%'); + $successfullyProcessedFilters[] = $filterkey; + } + } + break; + + case 'gam': + @list($amount) = explode('|', $parameter); + $amount = min(max((float) $amount, 0.001), 10); + if (number_format($amount, 3) != '1.000') { + if ($this->ImageMagickSwitchAvailable('gamma')) { + $commandline .= ' -gamma '.phpthumb_functions::escapeshellarg_replacement($amount); + $successfullyProcessedFilters[] = $filterkey; + } + } + break; + + case 'neg': + if ($this->ImageMagickSwitchAvailable('negate')) { + $commandline .= ' -negate'; + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'th': + @list($amount) = explode('|', $parameter); + if ($this->ImageMagickSwitchAvailable(array('threshold', 'dither', 'monochrome'))) { + $commandline .= ' -threshold '.phpthumb_functions::escapeshellarg_replacement(round(min(max((int) $amount, 0), 255) / 2.55).'%'); + $commandline .= ' -dither'; + $commandline .= ' -monochrome'; + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'rcd': + if ($this->ImageMagickSwitchAvailable(array('colors', 'dither'))) { + @list($colors, $dither) = explode('|', $parameter); + $colors = ($colors ? (int) $colors : 256); + $dither = ((strlen($dither) > 0) ? (bool) $dither : true); + $commandline .= ' -colors '.phpthumb_functions::escapeshellarg_replacement(max($colors, 8)); // ImageMagick will otherwise fail with "cannot quantize to fewer than 8 colors" + $commandline .= ($dither ? ' -dither' : ' +dither'); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'flip': + if ($this->ImageMagickSwitchAvailable(array('flip', 'flop'))) { + if (strpos(strtolower($parameter), 'x') !== false) { + $commandline .= ' -flop'; + } + if (strpos(strtolower($parameter), 'y') !== false) { + $commandline .= ' -flip'; + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'edge': + if ($this->ImageMagickSwitchAvailable('edge')) { + $parameter = (!empty($parameter) ? $parameter : 2); + $commandline .= ' -edge '.phpthumb_functions::escapeshellarg_replacement(!empty($parameter) ? (int) $parameter : 1); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'emb': + if ($this->ImageMagickSwitchAvailable(array('emboss', 'negate'))) { + $parameter = (!empty($parameter) ? $parameter : 2); + $commandline .= ' -emboss '.phpthumb_functions::escapeshellarg_replacement((int) $parameter); + if ($parameter < 2) { + $commandline .= ' -negate'; // ImageMagick negates the image for some reason with '-emboss 1'; + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'lvl': + @list($band, $method, $threshold) = explode('|', $parameter); + $band = ($band ? preg_replace('#[^RGBA\\*]#', '', strtoupper($band)) : '*'); + $method = ((strlen($method) > 0) ? (int) $method : 2); + $threshold = ((strlen($threshold) > 0) ? min(max((float) $threshold, 0), 100) : 0.1); + + $band = preg_replace('#[^RGBA\\*]#', '', strtoupper($band)); + + if (($method > 1) && !$this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) { + // Because ImageMagick processing happens before PHP-GD filters, and because some + // clipping is involved in the "lvl" filter, if "lvl" happens before "wb" then the + // "wb" filter will have (almost) no effect. Therefore, if "wb" is enabled then + // force the "lvl" filter to be processed by GD, not ImageMagick. + foreach ($this->fltr as $fltr_key => $fltr_value) { + list($fltr_cmd) = explode('|', $fltr_value); + if ($fltr_cmd == 'wb') { + $this->DebugMessage('Setting "lvl" filter method to "0" (from "'.$method.'") because white-balance filter also enabled', __FILE__, __LINE__); + $method = 0; + } + } + } + + switch ($method) { + case 0: // internal RGB + case 1: // internal grayscale + break; + case 2: // ImageMagick "contrast-stretch" + if ($this->ImageMagickSwitchAvailable('contrast-stretch')) { + if ($band != '*') { + $commandline .= ' -channel '.phpthumb_functions::escapeshellarg_replacement(strtoupper($band)); + } + $threshold = preg_replace('#[^0-9\\.]#', '', $threshold); // should be unneccesary, but just to be double-sure + //$commandline .= ' -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); + $commandline .= ' -contrast-stretch \''.$threshold.'%\''; + if ($band != '*') { + $commandline .= ' +channel'; + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + case 3: // ImageMagick "normalize" + if ($this->ImageMagickSwitchAvailable('normalize')) { + if ($band != '*') { + $commandline .= ' -channel '.phpthumb_functions::escapeshellarg_replacement(strtoupper($band)); + } + $commandline .= ' -normalize'; + if ($band != '*') { + $commandline .= ' +channel'; + } + $successfullyProcessedFilters[] = $filterkey; + } + break; + default: + $this->DebugMessage('unsupported method ('.$method.') for "lvl" filter', __FILE__, __LINE__); + break; + } + if (isset($this->fltr[$filterkey]) && ($method > 1)) { + $this->fltr[$filterkey] = $command.'|'.$band.'|0|'.$threshold; + $this->DebugMessage('filter "lvl" remapped from method "'.$method.'" to method "0" because ImageMagick support is missing', __FILE__, __LINE__); + } + break; + + case 'wb': + if ($this->ImageMagickSwitchAvailable(array('channel', 'contrast-stretch'))) { + @list($threshold) = explode('|', $parameter); + $threshold = (!empty($threshold) ? min(max((float) $threshold, 0), 100) : 0.1); + $threshold = preg_replace('#[^0-9\\.]#', '', $threshold); // should be unneccesary, but just to be double-sure + //$commandline .= ' -channel R -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // doesn't work on Windows because most versions of PHP do not properly + //$commandline .= ' -channel G -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // escape special characters (such as %) and just replace them with spaces + //$commandline .= ' -channel B -contrast-stretch '.phpthumb_functions::escapeshellarg_replacement($threshold.'%'); // https://bugs.php.net/bug.php?id=43261 + $commandline .= ' -channel R -contrast-stretch \''.$threshold.'%\''; + $commandline .= ' -channel G -contrast-stretch \''.$threshold.'%\''; + $commandline .= ' -channel B -contrast-stretch \''.$threshold.'%\''; + $commandline .= ' +channel'; + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'blur': + if ($this->ImageMagickSwitchAvailable('blur')) { + @list($radius) = explode('|', $parameter); + $radius = (!empty($radius) ? min(max((int) $radius, 0), 25) : 1); + $commandline .= ' -blur '.phpthumb_functions::escapeshellarg_replacement($radius); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'gblr': + @list($radius) = explode('|', $parameter); + $radius = (!empty($radius) ? min(max((int) $radius, 0), 25) : 1); + // "-gaussian" changed to "-gaussian-blur" sometime around 2009 + if ($this->ImageMagickSwitchAvailable('gaussian-blur')) { + $commandline .= ' -gaussian-blur '.phpthumb_functions::escapeshellarg_replacement($radius); + $successfullyProcessedFilters[] = $filterkey; + } elseif ($this->ImageMagickSwitchAvailable('gaussian')) { + $commandline .= ' -gaussian '.phpthumb_functions::escapeshellarg_replacement($radius); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'usm': + if ($this->ImageMagickSwitchAvailable('unsharp')) { + @list($amount, $radius, $threshold) = explode('|', $parameter); + $amount = ($amount ? min(max((int) $amount, 0), 255) : 80); + $radius = ($radius ? min(max((int) $radius, 0), 10) : 0.5); + $threshold = ('' !== $threshold ? min(max((int) $threshold, 0), 50) : 3); + $commandline .= ' -unsharp '.phpthumb_functions::escapeshellarg_replacement(number_format(($radius * 2) - 1, 2, '.', '').'x1+'.number_format($amount / 100, 2, '.', '').'+'.number_format($threshold / 100, 2, '.', '')); + $successfullyProcessedFilters[] = $filterkey; + } + break; + + case 'bord': + if ($this->ImageMagickSwitchAvailable(array('border', 'bordercolor', 'thumbnail', 'crop'))) { + if (!$this->zc) { + @list($width, $rX, $rY, $color) = explode('|', $parameter); + $width = (int) $width; + $rX = (int) $rX; + $rY = (int) $rY; + if ($width && !$rX && !$rY) { + if (!phpthumb_functions::IsHexColor($color)) { + $color = ((!empty($this->bc) && phpthumb_functions::IsHexColor($this->bc)) ? $this->bc : '000000'); + } + $commandline .= ' -border '.phpthumb_functions::escapeshellarg_replacement((int) $width); + $commandline .= ' -bordercolor '.phpthumb_functions::escapeshellarg_replacement('#'.$color); + + if (preg_match('# \\-crop "([\d]+)x([\d]+)\\+0\\+0" #', $commandline, $matches)) { + $commandline = str_replace(' -crop "'.$matches[1].'x'.$matches[2].'+0+0" ', ' -crop '.phpthumb_functions::escapeshellarg_replacement(($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width)).'+0+0').' ', $commandline); + } elseif (preg_match('# \\-'.$IMresizeParameter.' "([0-9]+)x([0-9]+)" #', $commandline, $matches)) { + $commandline = str_replace(' -'.$IMresizeParameter.' "'.$matches[1].'x'.$matches[2].'" ', ' -'.$IMresizeParameter.' '.phpthumb_functions::escapeshellarg_replacement(($matches[1] - (2 * $width)).'x'.($matches[2] - (2 * $width))).' ', $commandline); + } + $successfullyProcessedFilters[] = $filterkey; + } + } + } + break; + + case 'crop': + break; + + case 'sblr': + break; + + case 'mean': + break; + + case 'smth': + break; + + case 'bvl': + break; + + case 'wmi': + break; + + case 'wmt': + break; + + case 'over': + break; + + case 'hist': + break; + + case 'fram': + break; + + case 'drop': + break; + + case 'mask': + break; + + case 'elip': + break; + + case 'ric': + break; + + case 'stc': + break; + + case 'size': + break; + + default: + $this->DebugMessage('Unknown $this->fltr['.$filterkey.'] ('.$filtercommand.') -- deleting filter command', __FILE__, __LINE__); + $successfullyProcessedFilters[] = $filterkey; + break; + } + if (!isset($this->fltr[$filterkey])) { + $this->DebugMessage('Processed $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__); + } else { + $this->DebugMessage('Skipping $this->fltr['.$filterkey.'] ('.$filtercommand.') with ImageMagick', __FILE__, __LINE__); + } + } + $this->DebugMessage('Remaining $this->fltr after ImageMagick: ('.$this->phpThumbDebugVarDump($this->fltr).')', __FILE__, __LINE__); + if (count($this->fltr) > 0) { + $this->useRawIMoutput = false; + } + + if (preg_match('#jpe?g#i', $outputFormat) && $this->q) { + if ($this->ImageMagickSwitchAvailable(array('quality', 'interlace'))) { + $commandline .= ' -quality '.phpthumb_functions::escapeshellarg_replacement($this->thumbnailQuality); + if ($this->config_output_interlace) { + // causes weird things with animated GIF... leave for JPEG only + $commandline .= ' -interlace line '; // Use Line or Plane to create an interlaced PNG or GIF or progressive JPEG image + } + } + } + $commandline .= ' '.$outputFormat.':'.phpthumb_functions::escapeshellarg_replacement($IMtempfilename); + if (!$this->iswindows) { + $commandline .= ' 2>&1'; + } + $this->DebugMessage('ImageMagick called as ('.$commandline.')', __FILE__, __LINE__); + $IMresult = phpthumb_functions::SafeExec($commandline); + clearstatcache(); + if (!@file_exists($IMtempfilename) || !@filesize($IMtempfilename)) { + $this->FatalError('ImageMagick failed with message ('.trim($IMresult).')'); + $this->DebugMessage('ImageMagick failed with message ('.trim($IMresult).')', __FILE__, __LINE__); + if ($this->iswindows && !$IMresult) { + $this->DebugMessage('Check to make sure that PHP has read+write permissions to "'.dirname($IMtempfilename).'"', __FILE__, __LINE__); + } + + } else { + + foreach ($successfullyProcessedFilters as $dummy => $filterkey) { + unset($this->fltr[$filterkey]); + } + $this->IMresizedData = file_get_contents($IMtempfilename); + $getimagesize_imresized = @getimagesize($IMtempfilename); + $this->DebugMessage('getimagesize('.$IMtempfilename.') returned [w='.$getimagesize_imresized[0].';h='.$getimagesize_imresized[1].';f='.$getimagesize_imresized[2].']', __FILE__, __LINE__); + if (($this->config_max_source_pixels > 0) && (($getimagesize_imresized[0] * $getimagesize_imresized[1]) > $this->config_max_source_pixels)) { + $this->DebugMessage('skipping ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() because IM output is too large ('.$getimagesize_imresized[0].'x'.$getimagesize_imresized[0].' = '.($getimagesize_imresized[0] * $getimagesize_imresized[1]).' > '.$this->config_max_source_pixels.')', __FILE__, __LINE__); + } elseif (function_exists(@$ImageCreateFunction) && ($this->gdimg_source = @$ImageCreateFunction($IMtempfilename))) { + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + $this->DebugMessage('ImageMagickThumbnailToGD::'.$ImageCreateFunction.'() succeeded, $this->gdimg_source is now ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__); + $this->DebugMessage('ImageMagickThumbnailToGD() returning $this->IMresizedData ('.strlen($this->IMresizedData).' bytes)', __FILE__, __LINE__); + } else { + $this->useRawIMoutput = true; + $this->DebugMessage('$this->useRawIMoutput set to TRUE because '.@$ImageCreateFunction.'('.$IMtempfilename.') failed', __FILE__, __LINE__); + } + if (file_exists($IMtempfilename)) { + $this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__); + @unlink($IMtempfilename); + } + return true; + + } + if (file_exists($IMtempfilename)) { + $this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__); + @unlink($IMtempfilename); + } + + } elseif ($this->issafemode) { + $this->DebugMessage('ImageMagickThumbnailToGD() aborting because PHP safe_mode is enabled and phpThumb_tempnam() failed', __FILE__, __LINE__); + $this->useRawIMoutput = false; + } else { + if (file_exists($IMtempfilename)) { + $this->DebugMessage('deleting "'.$IMtempfilename.'"', __FILE__, __LINE__); + @unlink($IMtempfilename); + } + $this->DebugMessage('ImageMagickThumbnailToGD() aborting, phpThumb_tempnam() failed', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('ImageMagickThumbnailToGD() aborting because ImageMagickCommandlineBase() failed', __FILE__, __LINE__); + } + $this->useRawIMoutput = false; + return false; + } + + + public function Rotate() { + if ($this->ra || $this->ar) { + if (!function_exists('imagerotate')) { + $this->DebugMessage('!function_exists(imagerotate)', __FILE__, __LINE__); + return false; + } + if (!include_once __DIR__ .'/phpthumb.filters.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__); + return false; + } + + $this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor); + if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) { + return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"'); + } + + $rotate_angle = 0; + if ($this->ra) { + + $rotate_angle = (float) $this->ra; + + } else { + + if ($this->ar == 'x') { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.2.0', '>=')) { + if ($this->sourceFilename) { + if (function_exists('exif_read_data')) { + if ($exif_data = @exif_read_data($this->sourceFilename, 'IFD0')) { + // http://sylvana.net/jpegcrop/exif_orientation.html + switch (@$exif_data['Orientation']) { + case 1: + $rotate_angle = 0; + break; + case 3: + $rotate_angle = 180; + break; + case 6: + $rotate_angle = 270; + break; + case 8: + $rotate_angle = 90; + break; + + default: + $this->DebugMessage('EXIF auto-rotate failed because unknown $exif_data[Orientation] "'.@$exif_data['Orientation'].'"', __FILE__, __LINE__); + return false; + break; + } + $this->DebugMessage('EXIF auto-rotate set to '.$rotate_angle.' degrees ($exif_data[Orientation] = "'.@$exif_data['Orientation'].'")', __FILE__, __LINE__); + } else { + $this->DebugMessage('failed: exif_read_data('.$this->sourceFilename.')', __FILE__, __LINE__); + return false; + } + } else { + $this->DebugMessage('!function_exists(exif_read_data)', __FILE__, __LINE__); + return false; + } + } else { + $this->DebugMessage('Cannot auto-rotate from EXIF data because $this->sourceFilename is empty', __FILE__, __LINE__); + return false; + } + } else { + $this->DebugMessage('Cannot auto-rotate from EXIF data because PHP is less than v4.2.0 ('. PHP_VERSION .')', __FILE__, __LINE__); + return false; + } + } elseif (($this->ar == 'l') && ($this->source_height > $this->source_width)) { + $rotate_angle = 270; + } elseif (($this->ar == 'L') && ($this->source_height > $this->source_width)) { + $rotate_angle = 90; + } elseif (($this->ar == 'p') && ($this->source_width > $this->source_height)) { + $rotate_angle = 90; + } elseif (($this->ar == 'P') && ($this->source_width > $this->source_height)) { + $rotate_angle = 270; + } + + } + if ($rotate_angle % 90) { + $this->is_alpha = true; + } + phpthumb_filters::ImprovedImageRotate($this->gdimg_source, $rotate_angle, $this->config_background_hexcolor, $this->bg, $this); + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + } + return true; + } + + + public function FixedAspectRatio() { + // optional fixed-dimension images (regardless of aspect ratio) + + if (!$this->far) { + // do nothing + return true; + } + + if (!$this->w || !$this->h) { + return false; + } + $this->thumbnail_width = $this->w; + $this->thumbnail_height = $this->h; + $this->is_alpha = true; + if ($this->thumbnail_image_width >= $this->thumbnail_width) { + + $aspectratio = $this->thumbnail_image_height / $this->thumbnail_image_width; + if ($this->w) { + $this->thumbnail_image_height = round($this->thumbnail_image_width * $aspectratio); + $this->thumbnail_height = ($this->h ? $this->h : $this->thumbnail_image_height); + } elseif ($this->thumbnail_image_height < $this->thumbnail_height) { + $this->thumbnail_image_height = $this->thumbnail_height; + $this->thumbnail_image_width = round($this->thumbnail_image_height / $aspectratio); + } + + } else { + + $aspectratio = $this->thumbnail_image_width / $this->thumbnail_image_height; + if ($this->h) { + $this->thumbnail_image_width = round($this->thumbnail_image_height * $aspectratio); + } elseif ($this->thumbnail_image_width < $this->thumbnail_width) { + $this->thumbnail_image_width = $this->thumbnail_width; + $this->thumbnail_image_height = round($this->thumbnail_image_width / $aspectratio); + } + + } + return true; + } + + + public function OffsiteDomainIsAllowed($hostname, $allowed_domains) { + static $domain_is_allowed = array(); + $hostname = strtolower($hostname); + if (!isset($domain_is_allowed[$hostname])) { + $domain_is_allowed[$hostname] = false; + foreach ($allowed_domains as $valid_domain) { + $starpos = strpos($valid_domain, '*'); + if ($starpos !== false) { + $valid_domain = substr($valid_domain, $starpos + 1); + if (preg_match('#'.preg_quote($valid_domain).'$#', $hostname)) { + $domain_is_allowed[$hostname] = true; + break; + } + } else { + if (strtolower($valid_domain) === $hostname) { + $domain_is_allowed[$hostname] = true; + break; + } + } + } + } + return $domain_is_allowed[$hostname]; + } + + + public function AntiOffsiteLinking() { + // Optional anti-offsite hijacking of the thumbnail script + $allow = true; + if ($allow && $this->config_nooffsitelink_enabled && (@$_SERVER['HTTP_REFERER'] || $this->config_nooffsitelink_require_refer)) { + $this->DebugMessage('AntiOffsiteLinking() checking $_SERVER[HTTP_REFERER] "'.@$_SERVER['HTTP_REFERER'].'"', __FILE__, __LINE__); + foreach ($this->config_nooffsitelink_valid_domains as $key => $valid_domain) { + // $_SERVER['HTTP_HOST'] contains the port number, so strip it out here to make default configuration work + list($clean_domain) = explode(':', $valid_domain); + $this->config_nooffsitelink_valid_domains[$key] = $clean_domain; + } + $parsed_url = phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']); + if (!$this->OffsiteDomainIsAllowed(@$parsed_url['host'], $this->config_nooffsitelink_valid_domains)) { + $allow = false; + //$this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__); + $this->ErrorImage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is NOT in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')'); + } else { + $this->DebugMessage('AntiOffsiteLinking() - "'.@$parsed_url['host'].'" is in $this->config_nooffsitelink_valid_domains ('.implode(';', $this->config_nooffsitelink_valid_domains).')', __FILE__, __LINE__); + } + } + + if ($allow && $this->config_nohotlink_enabled && preg_match('#^(f|ht)tps?\://#i', $this->src)) { + $parsed_url = phpthumb_functions::ParseURLbetter($this->src); + //if (!phpthumb_functions::CaseInsensitiveInArray(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) { + if (!$this->OffsiteDomainIsAllowed(@$parsed_url['host'], $this->config_nohotlink_valid_domains)) { + // This domain is not allowed + $allow = false; + $this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is NOT in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__); + } else { + $this->DebugMessage('AntiOffsiteLinking() - "'.$parsed_url['host'].'" is in $this->config_nohotlink_valid_domains ('.implode(';', $this->config_nohotlink_valid_domains).')', __FILE__, __LINE__); + } + } + + if ($allow) { + $this->DebugMessage('AntiOffsiteLinking() says this is allowed', __FILE__, __LINE__); + return true; + } + + if (!phpthumb_functions::IsHexColor($this->config_error_bgcolor)) { + return $this->ErrorImage('Invalid hex color string "'.$this->config_error_bgcolor.'" for $this->config_error_bgcolor'); + } + if (!phpthumb_functions::IsHexColor($this->config_error_textcolor)) { + return $this->ErrorImage('Invalid hex color string "'.$this->config_error_textcolor.'" for $this->config_error_textcolor'); + } + if ($this->config_nooffsitelink_erase_image) { + + return $this->ErrorImage($this->config_nooffsitelink_text_message, $this->thumbnail_width, $this->thumbnail_height); + + } else { + + $this->config_nooffsitelink_watermark_src = $this->ResolveFilenameToAbsolute($this->config_nooffsitelink_watermark_src); + if (is_file($this->config_nooffsitelink_watermark_src)) { + + if (!include_once __DIR__ .'/phpthumb.filters.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying watermark', __FILE__, __LINE__); + return false; + } + $watermark_img = $this->ImageCreateFromStringReplacement(file_get_contents($this->config_nooffsitelink_watermark_src)); + $phpthumbFilters = new phpthumb_filters(); + $phpthumbFilters->phpThumbObject = &$this; + $opacity = 50; + $margin = 5; + $phpthumbFilters->WatermarkOverlay($this->gdimg_output, $watermark_img, '*', $opacity, $margin); + imagedestroy($watermark_img); + unset($phpthumbFilters); + + } else { + + $nohotlink_text_array = explode("\n", wordwrap($this->config_nooffsitelink_text_message, floor($this->thumbnail_width / imagefontwidth($this->config_error_fontsize)), "\n")); + $nohotlink_text_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_error_textcolor); + + $topoffset = round(($this->thumbnail_height - (count($nohotlink_text_array) * imagefontheight($this->config_error_fontsize))) / 2); + + $rowcounter = 0; + $this->DebugMessage('AntiOffsiteLinking() writing '.count($nohotlink_text_array).' lines of text "'.$this->config_nooffsitelink_text_message.'" (in #'.$this->config_error_textcolor.') on top of image', __FILE__, __LINE__); + foreach ($nohotlink_text_array as $textline) { + $leftoffset = max(0, round(($this->thumbnail_width - (strlen($textline) * imagefontwidth($this->config_error_fontsize))) / 2)); + imagestring($this->gdimg_output, $this->config_error_fontsize, $leftoffset, $topoffset + ($rowcounter++ * imagefontheight($this->config_error_fontsize)), $textline, $nohotlink_text_color); + } + + } + + } + return true; + } + + + public function AlphaChannelFlatten() { + if (!$this->is_alpha) { + // image doesn't have alpha transparency, no need to flatten + $this->DebugMessage('skipping AlphaChannelFlatten() because !$this->is_alpha', __FILE__, __LINE__); + return false; + } + switch ($this->thumbnailFormat) { + case 'png': + case 'webp': + case 'avif': + case 'ico': + // image has alpha transparency, but output as PNG, WEBP, AVIF, ICO which can handle it + $this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'")', __FILE__, __LINE__); + return false; + break; + + case 'gif': + // image has alpha transparency, but output as GIF which can handle only single-color transparency + $CurrentImageColorTransparent = imagecolortransparent($this->gdimg_output); + if ($CurrentImageColorTransparent == -1) { + // no transparent color defined + + if (phpthumb_functions::gd_version() < 2.0) { + $this->DebugMessage('AlphaChannelFlatten() failed because GD version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + + if ($img_alpha_mixdown_dither = @imagecreatetruecolor(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) { + + $dither_color = array(); + for ($i = 0; $i <= 255; $i++) { + $dither_color[$i] = imagecolorallocate($img_alpha_mixdown_dither, $i, $i, $i); + } + + // scan through current truecolor image copy alpha channel to temp image as grayscale + for ($x = 0; $x < $this->thumbnail_width; $x++) { + for ($y = 0; $y < $this->thumbnail_height; $y++) { + $PixelColor = phpthumb_functions::GetPixelColor($this->gdimg_output, $x, $y); + imagesetpixel($img_alpha_mixdown_dither, $x, $y, $dither_color[ $PixelColor[ 'alpha'] * 2 ]); + } + } + + // dither alpha channel grayscale version down to 2 colors + imagetruecolortopalette($img_alpha_mixdown_dither, true, 2); + + // reduce color palette to 256-1 colors (leave one palette position for transparent color) + imagetruecolortopalette($this->gdimg_output, true, 255); + + // allocate a new color for transparent color index + $TransparentColor = imagecolorallocate($this->gdimg_output, 1, 254, 253); + imagecolortransparent($this->gdimg_output, $TransparentColor); + + // scan through alpha channel image and note pixels with >50% transparency + for ($x = 0; $x < $this->thumbnail_width; $x++) { + for ($y = 0; $y < $this->thumbnail_height; $y++) { + $AlphaChannelPixel = phpthumb_functions::GetPixelColor($img_alpha_mixdown_dither, $x, $y); + if ($AlphaChannelPixel['red'] > 127) { + imagesetpixel($this->gdimg_output, $x, $y, $TransparentColor); + } + } + } + imagedestroy($img_alpha_mixdown_dither); + + $this->DebugMessage('AlphaChannelFlatten() set image to 255+1 colors with transparency for GIF output', __FILE__, __LINE__); + return true; + + } else { + $this->DebugMessage('AlphaChannelFlatten() failed imagecreate('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).')', __FILE__, __LINE__); + return false; + } + + } else { + // a single transparent color already defined, leave as-is + $this->DebugMessage('skipping AlphaChannelFlatten() because ($this->thumbnailFormat == "'.$this->thumbnailFormat.'") and imagecolortransparent() returned "'.$CurrentImageColorTransparent.'"', __FILE__, __LINE__); + return true; + } + break; + } + $this->DebugMessage('continuing AlphaChannelFlatten() for output format "'.$this->thumbnailFormat.'"', __FILE__, __LINE__); + // image has alpha transparency, and is being output in a format that doesn't support it -- flatten + if ($gdimg_flatten_temp = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height)) { + + $this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor); + if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) { + return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"'); + } + $background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor); + imagefilledrectangle($gdimg_flatten_temp, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color); + imagecopy($gdimg_flatten_temp, $this->gdimg_output, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height); + + imagealphablending($this->gdimg_output, true); + imagesavealpha($this->gdimg_output, false); + imagecolortransparent($this->gdimg_output, -1); + imagecopy($this->gdimg_output, $gdimg_flatten_temp, 0, 0, 0, 0, $this->thumbnail_width, $this->thumbnail_height); + + imagedestroy($gdimg_flatten_temp); + return true; + + } else { + $this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__); + } + return false; + } + + + public function ApplyFilters() { + if ($this->fltr && is_array($this->fltr)) { + if (!include_once __DIR__ .'/phpthumb.filters.php' ) { + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.filters.php" which is required for applying filters ('.implode(';', $this->fltr).')', __FILE__, __LINE__); + return false; + } + $phpthumbFilters = new phpthumb_filters(); + $phpthumbFilters->phpThumbObject = &$this; + foreach ($this->fltr as $filtercommand) { + @list($command, $parameter) = explode('|', $filtercommand, 2); + $this->DebugMessage('Attempting to process filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__); + switch ($command) { + case 'brit': // Brightness + $phpthumbFilters->Brightness($this->gdimg_output, $parameter); + break; + + case 'cont': // Contrast + $phpthumbFilters->Contrast($this->gdimg_output, $parameter); + break; + + case 'ds': // Desaturation + $phpthumbFilters->Desaturate($this->gdimg_output, $parameter, ''); + break; + + case 'sat': // Saturation + $phpthumbFilters->Saturation($this->gdimg_output, $parameter, ''); + break; + + case 'gray': // Grayscale + $phpthumbFilters->Grayscale($this->gdimg_output); + break; + + case 'clr': // Colorize + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping Colorize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + break; + } + @list($amount, $color) = explode('|', $parameter, 2); + $phpthumbFilters->Colorize($this->gdimg_output, $amount, $color); + break; + + case 'sep': // Sepia + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping Sepia() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + break; + } + @list($amount, $color) = explode('|', $parameter, 2); + $phpthumbFilters->Sepia($this->gdimg_output, $amount, $color); + break; + + case 'gam': // Gamma correction + $phpthumbFilters->Gamma($this->gdimg_output, $parameter); + break; + + case 'neg': // Negative colors + $phpthumbFilters->Negative($this->gdimg_output); + break; + + case 'th': // Threshold + $phpthumbFilters->Threshold($this->gdimg_output, $parameter); + break; + + case 'rcd': // ReduceColorDepth + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping ReduceColorDepth() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + break; + } + @list($colors, $dither) = explode('|', $parameter, 2); + $colors = ($colors ? (int) $colors : 256); + $dither = ((strlen($dither) > 0) ? (bool) $dither : true); + $phpthumbFilters->ReduceColorDepth($this->gdimg_output, $colors, $dither); + break; + + case 'flip': // Flip + $phpthumbFilters->Flip($this->gdimg_output, strpos(strtolower($parameter), 'x') !== false, strpos(strtolower($parameter), 'y') !== false); + break; + + case 'edge': // EdgeDetect + $phpthumbFilters->EdgeDetect($this->gdimg_output); + break; + + case 'emb': // Emboss + $phpthumbFilters->Emboss($this->gdimg_output); + break; + + case 'bvl': // Bevel + @list($width, $color1, $color2) = explode('|', $parameter, 3); + $phpthumbFilters->Bevel($this->gdimg_output, $width, $color1, $color2); + break; + + case 'lvl': // autoLevels + @list($band, $method, $threshold) = explode('|', $parameter, 3); + $band = ($band ? preg_replace('#[^RGBA\\*]#', '', strtoupper($band)) : '*'); + $method = ((strlen($method) > 0) ? (int) $method : 2); + $threshold = ((strlen($threshold) > 0) ? (float) $threshold : 0.1); + + $phpthumbFilters->HistogramStretch($this->gdimg_output, $band, $method, $threshold); + break; + + case 'wb': // WhiteBalance + $phpthumbFilters->WhiteBalance($this->gdimg_output, $parameter); + break; + + case 'hist': // Histogram overlay + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping HistogramOverlay() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + break; + } + @list($bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y) = explode('|', $parameter, 8); + $bands = ($bands ? $bands : '*'); + $colors = ($colors ? $colors : ''); + $width = ($width ? $width : 0.25); + $height = ($height ? $height : 0.25); + $alignment = ($alignment ? $alignment : 'BR'); + $opacity = ($opacity ? $opacity : 50); + $margin_x = ($margin_x ? $margin_x : 5); + // $margin_y -- it wasn't forgotten, let the value always pass unchanged + $phpthumbFilters->HistogramOverlay($this->gdimg_output, $bands, $colors, $width, $height, $alignment, $opacity, $margin_x, $margin_y); + break; + + case 'fram': // Frame + @list($frame_width, $edge_width, $color_frame, $color1, $color2) = explode('|', $parameter, 5); + $phpthumbFilters->Frame($this->gdimg_output, $frame_width, $edge_width, $color_frame, $color1, $color2); + break; + + case 'drop': // DropShadow + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping DropShadow() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + $this->is_alpha = true; + @list($distance, $width, $color, $angle, $fade) = explode('|', $parameter, 5); + $phpthumbFilters->DropShadow($this->gdimg_output, $distance, $width, $color, $angle, $fade); + break; + + case 'mask': // Mask cropping + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping Mask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + @list($mask_filename, $invert) = explode('|', $parameter, 2); + $mask_filename = $this->ResolveFilenameToAbsolute($mask_filename); + if (@is_readable($mask_filename) && ($fp_mask = @fopen($mask_filename, 'rb'))) { + $MaskImageData = ''; + do { + $buffer = fread($fp_mask, 8192); + $MaskImageData .= $buffer; + } while (strlen($buffer) > 0); + fclose($fp_mask); + if ($gdimg_mask = $this->ImageCreateFromStringReplacement($MaskImageData)) { + if ($invert && phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + imagefilter($gdimg_mask, IMG_FILTER_NEGATE); + } + $this->is_alpha = true; + $phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output); + imagedestroy($gdimg_mask); + } else { + $this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$mask_filename.'"', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('Cannot open mask file "'.$mask_filename.'"', __FILE__, __LINE__); + } + break; + + case 'elip': // Ellipse cropping + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping Ellipse() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + $this->is_alpha = true; + $phpthumbFilters->Ellipse($this->gdimg_output); + break; + + case 'ric': // RoundedImageCorners + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping RoundedImageCorners() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + @list($radius_x, $radius_y) = explode('|', $parameter, 2); + if (($radius_x < 1) || ($radius_y < 1)) { + $this->DebugMessage('Skipping RoundedImageCorners('.$radius_x.', '.$radius_y.') because x/y radius is less than 1', __FILE__, __LINE__); + break; + } + $this->is_alpha = true; + $phpthumbFilters->RoundedImageCorners($this->gdimg_output, $radius_x, $radius_y); + break; + + case 'crop': // Crop + @list($left, $right, $top, $bottom) = explode('|', $parameter, 4); + $phpthumbFilters->Crop($this->gdimg_output, $left, $right, $top, $bottom); + break; + + case 'bord': // Border + @list($border_width, $radius_x, $radius_y, $hexcolor_border) = explode('|', $parameter, 4); + $this->is_alpha = true; + $phpthumbFilters->ImageBorder($this->gdimg_output, $border_width, $radius_x, $radius_y, $hexcolor_border); + break; + + case 'over': // Overlay + @list($filename, $underlay, $margin, $opacity) = explode('|', $parameter, 4); + $underlay = (bool) ($underlay ? $underlay : false); + $margin = ((strlen($margin) > 0) ? $margin : ($underlay ? 0.1 : 0.0)); + $opacity = ((strlen($opacity) > 0) ? $opacity : 100); + if (($margin > 0) && ($margin < 1)) { + $margin = min(0.499, $margin); + } elseif (($margin > -1) && ($margin < 0)) { + $margin = max(-0.499, $margin); + } + + $filename = $this->ResolveFilenameToAbsolute($filename); + if (@is_readable($filename) && ($fp_watermark = @fopen($filename, 'rb'))) { + $WatermarkImageData = ''; + do { + $buffer = fread($fp_watermark, 8192); + $WatermarkImageData .= $buffer; + } while (strlen($buffer) > 0); + fclose($fp_watermark); + if ($img_watermark = $this->ImageCreateFromStringReplacement($WatermarkImageData)) { + if (($margin > 0) && ($margin < 1)) { + $resized_x = max(1, imagesx($this->gdimg_output) - round(2 * (imagesx($this->gdimg_output) * $margin))); + $resized_y = max(1, imagesy($this->gdimg_output) - round(2 * (imagesy($this->gdimg_output) * $margin))); + } else { + $resized_x = max(1, imagesx($this->gdimg_output) - round(2 * $margin)); + $resized_y = max(1, imagesy($this->gdimg_output) - round(2 * $margin)); + } + + if ($underlay) { + + if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) { + imagealphablending($img_watermark_resized, false); + imagesavealpha($img_watermark_resized, true); + $this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark_resized), imagesy($img_watermark_resized), imagesx($img_watermark), imagesy($img_watermark)); + if ($img_source_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) { + imagealphablending($img_source_resized, false); + imagesavealpha($img_source_resized, true); + $this->ImageResizeFunction($img_source_resized, $this->gdimg_output, 0, 0, 0, 0, imagesx($img_source_resized), imagesy($img_source_resized), imagesx($this->gdimg_output), imagesy($this->gdimg_output)); + $phpthumbFilters->WatermarkOverlay($img_watermark_resized, $img_source_resized, 'C', $opacity, $margin); + imagecopy($this->gdimg_output, $img_watermark_resized, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output)); + } else { + $this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__); + } + imagedestroy($img_watermark_resized); + } else { + $this->DebugMessage('phpthumb_functions::ImageCreateFunction('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).')', __FILE__, __LINE__); + } + + } else { // overlay + + if ($img_watermark_resized = phpthumb_functions::ImageCreateFunction($resized_x, $resized_y)) { + imagealphablending($img_watermark_resized, false); + imagesavealpha($img_watermark_resized, true); + $this->ImageResizeFunction($img_watermark_resized, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark_resized), imagesy($img_watermark_resized), imagesx($img_watermark), imagesy($img_watermark)); + $phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark_resized, 'C', $opacity, $margin); + imagedestroy($img_watermark_resized); + } else { + $this->DebugMessage('phpthumb_functions::ImageCreateFunction('.$resized_x.', '.$resized_y.')', __FILE__, __LINE__); + } + + } + imagedestroy($img_watermark); + + } else { + $this->DebugMessage('ImageCreateFromStringReplacement() failed for "'.$filename.'"', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('Cannot open overlay file "'.$filename.'"', __FILE__, __LINE__); + } + break; + + case 'wmi': // WaterMarkImage + @list($filename, $alignment, $opacity, $margin['x'], $margin['y'], $rotate_angle) = explode('|', $parameter, 6); + // $margin can be pixel margin or percent margin if $alignment is text, or max width/height if $alignment is position like "50x75" + $alignment = ($alignment ? $alignment : 'BR'); + $opacity = ('' != $opacity ? (int) $opacity : 50); + $rotate_angle = ('' != $rotate_angle ? (int) $rotate_angle : 0); + if (!preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)$#i', $alignment, $matches)) { + $margins = array('x', 'y'); + foreach ($margins as $xy) { + $margin[$xy] = ('' !== $margin[ $xy ] ? $margin[ $xy] : 5); + if (($margin[$xy] > 0) && ($margin[$xy] < 1)) { + $margin[$xy] = min(0.499, $margin[$xy]); + } elseif (($margin[$xy] > -1) && ($margin[$xy] < 0)) { + $margin[$xy] = max(-0.499, $margin[$xy]); + } + } + } + + $filename = $this->ResolveFilenameToAbsolute($filename); + if (@is_readable($filename)) { + if ($img_watermark = $this->ImageCreateFromFilename($filename)) { + if ($rotate_angle !== 0) { + $phpthumbFilters->ImprovedImageRotate($img_watermark, $rotate_angle, 'FFFFFF', null, $this); + } + if (preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)$#i', $alignment, $matches)) { + $watermark_max_width = (int) ($margin[ 'x'] ? $margin[ 'x'] : imagesx($img_watermark)); + $watermark_max_height = (int) ($margin[ 'y'] ? $margin[ 'y'] : imagesy($img_watermark)); + $scale = phpthumb_functions::ScaleToFitInBox(imagesx($img_watermark), imagesy($img_watermark), $watermark_max_width, $watermark_max_height, true, true); + $this->DebugMessage('Scaling watermark by a factor of '.number_format($scale, 4), __FILE__, __LINE__); + if (($scale > 1) || ($scale < 1)) { + if ($img_watermark2 = phpthumb_functions::ImageCreateFunction($scale * imagesx($img_watermark), $scale * imagesy($img_watermark))) { + imagealphablending($img_watermark2, false); + imagesavealpha($img_watermark2, true); + $this->ImageResizeFunction($img_watermark2, $img_watermark, 0, 0, 0, 0, imagesx($img_watermark2), imagesy($img_watermark2), imagesx($img_watermark), imagesy($img_watermark)); + $img_watermark = $img_watermark2; + } else { + $this->DebugMessage('ImageCreateFunction('.($scale * imagesx($img_watermark)).', '.($scale * imagesx($img_watermark)).') failed', __FILE__, __LINE__); + } + } + $watermark_dest_x = round($matches[1] - (imagesx($img_watermark) / 2)); + $watermark_dest_y = round($matches[2] - (imagesy($img_watermark) / 2)); + $alignment = $watermark_dest_x.'x'.$watermark_dest_y; + } + $phpthumbFilters->WatermarkOverlay($this->gdimg_output, $img_watermark, $alignment, $opacity, $margin['x'], $margin['y']); + imagedestroy($img_watermark); + if (isset($img_watermark2) && (is_resource($img_watermark2) || (is_object($img_watermark2) && $img_watermark2 instanceOf \GdImage))) { + imagedestroy($img_watermark2); + } + } else { + $this->DebugMessage('ImageCreateFromFilename() failed for "'.$filename.'"', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('!is_readable('.$filename.')', __FILE__, __LINE__); + } + break; + + case 'wmt': // WaterMarkText + @list($text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend, $lineheight) = explode('|', $parameter, 12); + $text = ($text ? $text : ''); + $size = ($size ? $size : 3); + $alignment = ($alignment ? $alignment : 'BR'); + $hex_color = ($hex_color ? $hex_color : '000000'); + $ttffont = ($ttffont ? $ttffont : ''); + $opacity = ('' != $opacity ? $opacity : 50); + $margin = ('' != $margin ? $margin : 5); + $angle = ('' != $angle ? $angle : 0); + $bg_color = ($bg_color ? $bg_color : false); + $bg_opacity = ($bg_opacity ? $bg_opacity : 0); + $fillextend = ($fillextend ? $fillextend : ''); + $lineheight = ($lineheight ? $lineheight : 1.0); + + if (basename($ttffont) == $ttffont) { + $ttffont = $this->realPathSafe($this->config_ttf_directory.DIRECTORY_SEPARATOR.$ttffont); + } else { + $ttffont = $this->ResolveFilenameToAbsolute($ttffont); + } + $phpthumbFilters->WatermarkText($this->gdimg_output, $text, $size, $alignment, $hex_color, $ttffont, $opacity, $margin, $angle, $bg_color, $bg_opacity, $fillextend, $lineheight); + break; + + case 'blur': // Blur + @list($radius) = explode('|', $parameter, 1); + $radius = ($radius ? $radius : 1); + if (phpthumb_functions::gd_version() >= 2) { + $phpthumbFilters->Blur($this->gdimg_output, $radius); + } else { + $this->DebugMessage('Skipping Blur() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + } + break; + + case 'gblr': // Gaussian Blur + $phpthumbFilters->BlurGaussian($this->gdimg_output); + break; + + case 'sblr': // Selective Blur + $phpthumbFilters->BlurSelective($this->gdimg_output); + break; + + case 'mean': // MeanRemoval blur + $phpthumbFilters->MeanRemoval($this->gdimg_output); + break; + + case 'smth': // Smooth blur + $phpthumbFilters->Smooth($this->gdimg_output, $parameter); + break; + + case 'usm': // UnSharpMask sharpening + @list($amount, $radius, $threshold) = explode('|', $parameter, 3); + $amount = ($amount ? $amount : 80); + $radius = ($radius ? $radius : 0.5); + $threshold = ('' !== $threshold ? $threshold : 3); + if (phpthumb_functions::gd_version() >= 2.0) { + ob_start(); + if (!@include_once __DIR__ .'/phpthumb.unsharp.php' ) { + $include_error = ob_get_contents(); + if ($include_error) { + $this->DebugMessage('include_once("'. __DIR__ .'/phpthumb.unsharp.php") generated message: "'.$include_error.'"', __FILE__, __LINE__); + } + $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.unsharp.php" which is required for unsharp masking', __FILE__, __LINE__); + ob_end_clean(); + return false; + } + ob_end_clean(); + phpUnsharpMask::applyUnsharpMask($this->gdimg_output, $amount, $radius, $threshold); + } else { + $this->DebugMessage('Skipping unsharp mask because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + break; + + case 'size': // Resize + @list($newwidth, $newheight, $stretch) = explode('|', $parameter); + $newwidth = (!$newwidth ? imagesx($this->gdimg_output) : ((($newwidth > 0) && ($newwidth < 1)) ? round($newwidth * imagesx($this->gdimg_output)) : round($newwidth))); + $newheight = (!$newheight ? imagesy($this->gdimg_output) : ((($newheight > 0) && ($newheight < 1)) ? round($newheight * imagesy($this->gdimg_output)) : round($newheight))); + $stretch = ($stretch ? true : false); + if ($stretch) { + $scale_x = phpthumb_functions::ScaleToFitInBox(imagesx($this->gdimg_output), imagesx($this->gdimg_output), $newwidth, $newwidth, true, true); + $scale_y = phpthumb_functions::ScaleToFitInBox(imagesy($this->gdimg_output), imagesy($this->gdimg_output), $newheight, $newheight, true, true); + } else { + $scale_x = phpthumb_functions::ScaleToFitInBox(imagesx($this->gdimg_output), imagesy($this->gdimg_output), $newwidth, $newheight, true, true); + $scale_y = $scale_x; + } + $this->DebugMessage('Scaling watermark ('.($stretch ? 'with' : 'without').' stretch) by a factor of "'.number_format($scale_x, 4).' x '.number_format($scale_y, 4).'"', __FILE__, __LINE__); + if (($scale_x > 1) || ($scale_x < 1) || ($scale_y > 1) || ($scale_y < 1)) { + if ($img_temp = phpthumb_functions::ImageCreateFunction(imagesx($this->gdimg_output), imagesy($this->gdimg_output))) { + imagecopy($img_temp, $this->gdimg_output, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output)); + if ($this->gdimg_output = phpthumb_functions::ImageCreateFunction($scale_x * imagesx($img_temp), $scale_y * imagesy($img_temp))) { + imagealphablending($this->gdimg_output, false); + imagesavealpha($this->gdimg_output, true); + $this->ImageResizeFunction($this->gdimg_output, $img_temp, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output), imagesx($img_temp), imagesy($img_temp)); + } else { + $this->DebugMessage('ImageCreateFunction('.($scale_x * imagesx($img_temp)).', '.($scale_y * imagesy($img_temp)).') failed', __FILE__, __LINE__); + } + imagedestroy($img_temp); + } else { + $this->DebugMessage('ImageCreateFunction('.imagesx($this->gdimg_output).', '.imagesy($this->gdimg_output).') failed', __FILE__, __LINE__); + } + } + break; + + case 'rot': // ROTate + @list($angle, $bgcolor) = explode('|', $parameter, 2); + $phpthumbFilters->ImprovedImageRotate($this->gdimg_output, $angle, $bgcolor, null, $this); + break; + + case 'stc': // Source Transparent Color + @list($hexcolor, $min_limit, $max_limit) = explode('|', $parameter, 3); + if (!phpthumb_functions::IsHexColor($hexcolor)) { + $this->DebugMessage('Skipping SourceTransparentColor hex color is invalid ('.$hexcolor.')', __FILE__, __LINE__); + return false; + } + $min_limit = ('' !== $min_limit ? $min_limit : 5); + $max_limit = ('' !== $max_limit ? $max_limit : 10); + if ($gdimg_mask = $phpthumbFilters->SourceTransparentColorMask($this->gdimg_output, $hexcolor, $min_limit, $max_limit)) { + $this->is_alpha = true; + $phpthumbFilters->ApplyMask($gdimg_mask, $this->gdimg_output); + imagedestroy($gdimg_mask); + } else { + $this->DebugMessage('SourceTransparentColorMask() failed for "'.$hexcolor.','.$min_limit.','.$max_limit.'"', __FILE__, __LINE__); + } + break; + } + $this->DebugMessage('Finished processing filter command "'.$command.'('.$parameter.')"', __FILE__, __LINE__); + } + } + return true; + } + + + public function MaxFileSize() { + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping MaxFileSize() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + if ($this->maxb > 0) { + switch ($this->thumbnailFormat) { + case 'png': + case 'gif': + $imgRenderFunction = 'image'.$this->thumbnailFormat; + + ob_start(); + $imgRenderFunction($this->gdimg_output); + $imgdata = ob_get_contents(); + ob_end_clean(); + + if (strlen($imgdata) > $this->maxb) { + for ($i = 8; $i >= 1; $i--) { + $tempIMG = imagecreatetruecolor(imagesx($this->gdimg_output), imagesy($this->gdimg_output)); + imagecopy($tempIMG, $this->gdimg_output, 0, 0, 0, 0, imagesx($this->gdimg_output), imagesy($this->gdimg_output)); + imagetruecolortopalette($tempIMG, true, pow(2, $i)); + ob_start(); + $imgRenderFunction($tempIMG); + $imgdata = ob_get_contents(); + ob_end_clean(); + + if (strlen($imgdata) <= $this->maxb) { + imagetruecolortopalette($this->gdimg_output, true, pow(2, $i)); + break; + } + } + } + break; + + case 'jpeg': + ob_start(); + imagejpeg($this->gdimg_output); + $imgdata = ob_get_contents(); + ob_end_clean(); + + if (strlen($imgdata) > $this->maxb) { + for ($i = 3; $i < 20; $i++) { + $q = round(100 * (1 - log10($i / 2))); + ob_start(); + imagejpeg($this->gdimg_output, null, $q); + $imgdata = ob_get_contents(); + ob_end_clean(); + + $this->thumbnailQuality = $q; + if (strlen($imgdata) <= $this->maxb) { + break; + } + } + } + if (strlen($imgdata) > $this->maxb) { + return false; + } + break; + + default: + return false; + } + } + return true; + } + + + public function CalculateThumbnailDimensions() { + $this->DebugMessage('CalculateThumbnailDimensions() starting with [W,H,sx,sy,sw,sh] initially set to ['.$this->source_width.','.$this->source_height.','.$this->sx.','.$this->sy.','.$this->sw.','.$this->sh.']', __FILE__, __LINE__); +//echo $this->source_width.'x'.$this->source_height.'
        '; + $this->thumbnailCropX = ($this->sx ? (($this->sx >= 2) ? $this->sx : round($this->sx * $this->source_width)) : 0); +//echo $this->thumbnailCropX.'
        '; + $this->thumbnailCropY = ($this->sy ? (($this->sy >= 2) ? $this->sy : round($this->sy * $this->source_height)) : 0); +//echo $this->thumbnailCropY.'
        '; + $this->thumbnailCropW = ($this->sw ? (($this->sw >= 2) ? $this->sw : round($this->sw * $this->source_width)) : $this->source_width); +//echo $this->thumbnailCropW.'
        '; + $this->thumbnailCropH = ($this->sh ? (($this->sh >= 2) ? $this->sh : round($this->sh * $this->source_height)) : $this->source_height); +//echo $this->thumbnailCropH.'
        '; + + // limit source area to original image area + $this->thumbnailCropW = max(1, min($this->thumbnailCropW, $this->source_width - $this->thumbnailCropX)); + $this->thumbnailCropH = max(1, min($this->thumbnailCropH, $this->source_height - $this->thumbnailCropY)); + + $this->DebugMessage('CalculateThumbnailDimensions() starting with [x,y,w,h] initially set to ['.$this->thumbnailCropX.','.$this->thumbnailCropY.','.$this->thumbnailCropW.','.$this->thumbnailCropH.']', __FILE__, __LINE__); + + + if ($this->zc && $this->w && $this->h) { + // Zoom Crop + // retain proportional resizing we did above, but crop off larger dimension so smaller + // dimension fully fits available space + + $scaling_X = $this->source_width / $this->w; + $scaling_Y = $this->source_height / $this->h; + if ($scaling_X > $scaling_Y) { + // some of the width will need to be cropped + $allowable_width = $this->source_width / $scaling_X * $scaling_Y; + $this->thumbnailCropW = round($allowable_width); + $this->thumbnailCropX = round(($this->source_width - $allowable_width) / 2); + + } elseif ($scaling_Y > $scaling_X) { + // some of the height will need to be cropped + $allowable_height = $this->source_height / $scaling_Y * $scaling_X; + $this->thumbnailCropH = round($allowable_height); + $this->thumbnailCropY = round(($this->source_height - $allowable_height) / 2); + + } else { + // image fits perfectly, no cropping needed + } + $this->thumbnail_width = $this->w; + $this->thumbnail_height = $this->h; + $this->thumbnail_image_width = $this->thumbnail_width; + $this->thumbnail_image_height = $this->thumbnail_height; + + } elseif ($this->iar && $this->w && $this->h) { + + // Ignore Aspect Ratio + // stretch image to fit exactly 'w' x 'h' + $this->thumbnail_width = $this->w; + $this->thumbnail_height = $this->h; + $this->thumbnail_image_width = $this->thumbnail_width; + $this->thumbnail_image_height = $this->thumbnail_height; + + } else { + + $original_aspect_ratio = $this->thumbnailCropW / $this->thumbnailCropH; + if ($this->aoe) { + if ($this->w && $this->h) { + $maxwidth = min($this->w, $this->h * $original_aspect_ratio); + $maxheight = min($this->h, $this->w / $original_aspect_ratio); + } elseif ($this->w) { + $maxwidth = $this->w; + $maxheight = $this->w / $original_aspect_ratio; + } elseif ($this->h) { + $maxwidth = $this->h * $original_aspect_ratio; + $maxheight = $this->h; + } else { + $maxwidth = $this->thumbnailCropW; + $maxheight = $this->thumbnailCropH; + } + } else { + $maxwidth = phpthumb_functions::nonempty_min($this->w, $this->thumbnailCropW, $this->config_output_maxwidth); + $maxheight = phpthumb_functions::nonempty_min($this->h, $this->thumbnailCropH, $this->config_output_maxheight); +//echo $maxwidth.'x'.$maxheight.'
        '; + $maxwidth = min($maxwidth, $maxheight * $original_aspect_ratio); + $maxheight = min($maxheight, $maxwidth / $original_aspect_ratio); +//echo $maxwidth.'x'.$maxheight.'
        '; + } + + $this->thumbnail_image_width = $maxwidth; + $this->thumbnail_image_height = $maxheight; + $this->thumbnail_width = $maxwidth; + $this->thumbnail_height = $maxheight; + + $this->FixedAspectRatio(); + } + + $this->thumbnail_width = max(1, floor($this->thumbnail_width)); + $this->thumbnail_height = max(1, floor($this->thumbnail_height)); + return true; + } + + + public function CreateGDoutput() { + $this->CalculateThumbnailDimensions(); + + // create the GD image (either true-color or 256-color, depending on GD version) + $this->gdimg_output = phpthumb_functions::ImageCreateFunction($this->thumbnail_width, $this->thumbnail_height); + + // images that have transparency must have the background filled with the configured 'bg' color otherwise the transparent color will appear as black + imagesavealpha($this->gdimg_output, true); + if ($this->is_alpha && phpthumb_functions::gd_version() >= 2) { + + imagealphablending($this->gdimg_output, false); + $output_full_alpha = phpthumb_functions::ImageColorAllocateAlphaSafe($this->gdimg_output, 255, 255, 255, 127); + imagefilledrectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $output_full_alpha); + + } else { + + $current_transparent_color = imagecolortransparent($this->gdimg_source); + if ($this->bg || (@$current_transparent_color >= 0)) { + + $this->config_background_hexcolor = ($this->bg ? $this->bg : $this->config_background_hexcolor); + if (!phpthumb_functions::IsHexColor($this->config_background_hexcolor)) { + return $this->ErrorImage('Invalid hex color string "'.$this->config_background_hexcolor.'" for parameter "bg"'); + } + $background_color = phpthumb_functions::ImageHexColorAllocate($this->gdimg_output, $this->config_background_hexcolor); + imagefilledrectangle($this->gdimg_output, 0, 0, $this->thumbnail_width, $this->thumbnail_height, $background_color); + + } + + } + $this->DebugMessage('CreateGDoutput() returning canvas "'.$this->thumbnail_width.'x'.$this->thumbnail_height.'"', __FILE__, __LINE__); + return true; + } + + public function SetOrientationDependantWidthHeight() { + $this->DebugMessage('SetOrientationDependantWidthHeight() starting with "'.$this->source_width.'"x"'.$this->source_height.'"', __FILE__, __LINE__); + if ($this->source_height > $this->source_width) { + // portrait + $this->w = phpthumb_functions::OneOfThese($this->wp, $this->w, $this->ws, $this->wl); + $this->h = phpthumb_functions::OneOfThese($this->hp, $this->h, $this->hs, $this->hl); + } elseif ($this->source_height < $this->source_width) { + // landscape + $this->w = phpthumb_functions::OneOfThese($this->wl, $this->w, $this->ws, $this->wp); + $this->h = phpthumb_functions::OneOfThese($this->hl, $this->h, $this->hs, $this->hp); + } else { + // square + $this->w = phpthumb_functions::OneOfThese($this->ws, $this->w, $this->wl, $this->wp); + $this->h = phpthumb_functions::OneOfThese($this->hs, $this->h, $this->hl, $this->hp); + } + //$this->w = round($this->w ? $this->w : (($this->h && $this->source_height) ? $this->h * $this->source_width / $this->source_height : $this->w)); + //$this->h = round($this->h ? $this->h : (($this->w && $this->source_width) ? $this->w * $this->source_height / $this->source_width : $this->h)); + $this->DebugMessage('SetOrientationDependantWidthHeight() setting w="'. (int) $this->w .'", h="'. (int) $this->h .'"', __FILE__, __LINE__); + return true; + } + + public function ExtractEXIFgetImageSize() { + $this->DebugMessage('starting ExtractEXIFgetImageSize()', __FILE__, __LINE__); + + if (preg_match('#^http:#i', $this->src) && !$this->sourceFilename && $this->rawImageData) { + $this->SourceDataToTempFile(); + } + if (null === $this->getimagesizeinfo) { + if ($this->sourceFilename) { + if ($this->getimagesizeinfo = @getimagesize($this->sourceFilename)) { + $this->source_width = $this->getimagesizeinfo[0]; + $this->source_height = $this->getimagesizeinfo[1]; + $this->DebugMessage('getimagesize('.$this->sourceFilename.') says image is '.$this->source_width.'x'.$this->source_height, __FILE__, __LINE__); + } else { + $this->DebugMessage('getimagesize('.$this->sourceFilename.') failed', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('skipping getimagesize() because $this->sourceFilename is empty', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('skipping getimagesize() because !is_null($this->getimagesizeinfo)', __FILE__, __LINE__); + } + + if (is_resource($this->gdimg_source) || (is_object($this->gdimg_source) && $this->gdimg_source instanceOf \GdImage)) { + + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + + $this->SetOrientationDependantWidthHeight(); + + } elseif ($this->rawImageData && !$this->sourceFilename) { + + if ($this->SourceImageIsTooLarge($this->source_width, $this->source_height)) { + $this->DebugMessage('NOT bypassing EXIF and getimagesize sections because source image is too large for GD ('.$this->source_width.'x'.$this->source_width.'='.($this->source_width * $this->source_height * 5).'MB)', __FILE__, __LINE__); + } else { + $this->DebugMessage('bypassing EXIF and getimagesize sections because $this->rawImageData is set, and $this->sourceFilename is not set, and source image is not too large for GD ('.$this->source_width.'x'.$this->source_width.'='.($this->source_width * $this->source_height * 5).'MB)', __FILE__, __LINE__); + } + + } + + if (!empty($this->getimagesizeinfo)) { + // great + $this->getimagesizeinfo['filesize'] = @filesize($this->sourceFilename); + } elseif (!$this->rawImageData) { + $this->DebugMessage('getimagesize("'.$this->sourceFilename.'") failed', __FILE__, __LINE__); + } + + if ($this->config_prefer_imagemagick) { + if ($this->ImageMagickThumbnailToGD()) { + return true; + } + $this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__); + } + + if (isset($this->getimagesizeinfo[1])) { + $this->source_width = $this->getimagesizeinfo[0]; + $this->source_height = $this->getimagesizeinfo[1]; + } + + $this->SetOrientationDependantWidthHeight(); + + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.2.0', '>=') && function_exists('exif_read_data')) { + switch (@$this->getimagesizeinfo[2]) { + case IMAGETYPE_JPEG: + case IMAGETYPE_TIFF_II: + case IMAGETYPE_TIFF_MM: + $this->exif_raw_data = @exif_read_data($this->sourceFilename, 0, true); + break; + } + } + if (function_exists('exif_thumbnail') && (@$this->getimagesizeinfo[2] == IMAGETYPE_JPEG)) { + // Extract EXIF info from JPEGs + + $this->exif_thumbnail_width = ''; + $this->exif_thumbnail_height = ''; + $this->exif_thumbnail_type = ''; + + // The parameters width, height and imagetype are available since PHP v4.3.0 + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) { + + $this->exif_thumbnail_data = @exif_thumbnail($this->sourceFilename, $this->exif_thumbnail_width, $this->exif_thumbnail_height, $this->exif_thumbnail_type); + + } else { + + // older versions of exif_thumbnail output an error message but NOT return false on failure + ob_start(); + $this->exif_thumbnail_data = exif_thumbnail($this->sourceFilename); + $exit_thumbnail_error = ob_get_contents(); + ob_end_clean(); + if (!$exit_thumbnail_error && $this->exif_thumbnail_data) { + + if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) { + $this->exif_thumbnail_width = imagesx($gdimg_exif_temp); + $this->exif_thumbnail_height = imagesy($gdimg_exif_temp); + $this->exif_thumbnail_type = 2; // (2 == JPEG) before PHP v4.3.0 only JPEG format EXIF thumbnails are returned + unset($gdimg_exif_temp); + } else { + return $this->ErrorImage('Failed - $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data) in '.__FILE__.' on line '.__LINE__); + } + + } + + } + + } elseif (!function_exists('exif_thumbnail')) { + + $this->DebugMessage('exif_thumbnail() does not exist, cannot extract EXIF thumbnail', __FILE__, __LINE__); + + } + + $this->DebugMessage('EXIF thumbnail extraction: (size='.(!empty($this->exif_thumbnail_data) ? strlen((string) $this->exif_thumbnail_data) : 0).'; type="'.$this->exif_thumbnail_type.'"; '. (int) $this->exif_thumbnail_width .'x'. (int) $this->exif_thumbnail_height .')', __FILE__, __LINE__); + + // see if EXIF thumbnail can be used directly with no processing + if ($this->config_use_exif_thumbnail_for_speed && $this->exif_thumbnail_data) { + while (true) { + if (!$this->xto) { + $source_ar = $this->source_width / $this->source_height; + $exif_ar = $this->exif_thumbnail_width / $this->exif_thumbnail_height; + if (number_format($source_ar, 2) != number_format($exif_ar, 2)) { + $this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__); + break; + } + if ($this->w && ($this->w != $this->exif_thumbnail_width)) { + $this->DebugMessage('not using EXIF thumbnail because $this->w != $this->exif_thumbnail_width ('.$this->w.' != '.$this->exif_thumbnail_width.')', __FILE__, __LINE__); + break; + } + if ($this->h && ($this->h != $this->exif_thumbnail_height)) { + $this->DebugMessage('not using EXIF thumbnail because $this->h != $this->exif_thumbnail_height ('.$this->h.' != '.$this->exif_thumbnail_height.')', __FILE__, __LINE__); + break; + } + $CannotBeSetParameters = array('sx', 'sy', 'sh', 'sw', 'far', 'bg', 'bc', 'fltr', 'phpThumbDebug'); + foreach ($CannotBeSetParameters as $parameter) { + if ($this->$parameter) { + break 2; + } + } + } + + $this->DebugMessage('setting $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data)', __FILE__, __LINE__); + $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data); + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + return true; + } + } + + if (($this->config_max_source_pixels > 0) && (($this->source_width * $this->source_height) > $this->config_max_source_pixels)) { + + // Source image is larger than would fit in available PHP memory. + // If ImageMagick is installed, use it to generate the thumbnail. + // Else, if an EXIF thumbnail is available, use that as the source image. + // Otherwise, no choice but to fail with an error message + $this->DebugMessage('image is '.$this->source_width.'x'.$this->source_height.' and therefore contains more pixels ('.($this->source_width * $this->source_height).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__); + if (!$this->config_prefer_imagemagick && $this->ImageMagickThumbnailToGD()) { + // excellent, we have a thumbnailed source image + return true; + } + + } + return true; + } + + + public function SetCacheFilename() { + if (null !== $this->cache_filename) { + $this->DebugMessage('$this->cache_filename already set, skipping SetCacheFilename()', __FILE__, __LINE__); + return true; + } + if (null === $this->config_cache_directory) { + $this->setCacheDirectory(); + if (!$this->config_cache_directory) { + $this->DebugMessage('SetCacheFilename() failed because $this->config_cache_directory is empty', __FILE__, __LINE__); + return false; + } + } + $this->setOutputFormat(); + + if (!$this->sourceFilename && !$this->rawImageData && $this->src) { + $this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src); + } + + if ($this->config_cache_default_only_suffix && $this->sourceFilename) { + // simplified cache filenames: + // only use default parameters in phpThumb.config.php + // substitute source filename into * in $this->config_cache_default_only_suffix + // (eg: '*_thumb' becomes 'picture_thumb.jpg') + if (strpos($this->config_cache_default_only_suffix, '*') === false) { + $this->DebugMessage('aborting simplified caching filename because no * in "'.$this->config_cache_default_only_suffix.'"', __FILE__, __LINE__); + } else { + preg_match('#(.+)(\\.[a-z0-9]+)?$#i', basename($this->sourceFilename), $matches); + $this->cache_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.rawurlencode(str_replace('*', @$matches[1], $this->config_cache_default_only_suffix)).'.'.strtolower($this->thumbnailFormat); + return true; + } + } + + $this->cache_filename = ''; + if ($this->new) { + $broad_directory_name = strtolower(md5($this->new)); + $this->cache_filename .= '_new'.$broad_directory_name; + } elseif ($this->md5s) { + // source image MD5 hash provided + $this->DebugMessage('SetCacheFilename() _raw set from $this->md5s = "'.$this->md5s.'"', __FILE__, __LINE__); + $broad_directory_name = $this->md5s; + $this->cache_filename .= '_raw'.$this->md5s; + } elseif (!$this->src && $this->rawImageData) { + $this->DebugMessage('SetCacheFilename() _raw set from md5($this->rawImageData) = "'.md5($this->rawImageData).'"', __FILE__, __LINE__); + $broad_directory_name = strtolower(md5($this->rawImageData)); + $this->cache_filename .= '_raw'.$broad_directory_name; + } else { + $this->DebugMessage('SetCacheFilename() _src set from md5($this->sourceFilename) "'.$this->sourceFilename.'" = "'.md5($this->sourceFilename).'"', __FILE__, __LINE__); + $broad_directory_name = strtolower(md5($this->sourceFilename)); + $this->cache_filename .= '_src'.$broad_directory_name; + } + if (!empty($_SERVER['HTTP_REFERER']) && $this->config_nooffsitelink_enabled) { + $parsed_url1 = @phpthumb_functions::ParseURLbetter(@$_SERVER['HTTP_REFERER']); + $parsed_url2 = @phpthumb_functions::ParseURLbetter('http://'.@$_SERVER['HTTP_HOST']); + if (@$parsed_url1['host'] && @$parsed_url2['host'] && ($parsed_url1['host'] != $parsed_url2['host'])) { + // include "_offsite" only if nooffsitelink_enabled and if referrer doesn't match the domain of the current server + $this->cache_filename .= '_offsite'; + } + } + + $ParametersString = ''; + if ($this->fltr && is_array($this->fltr)) { + $ParametersString .= '_fltr'.implode('_fltr', $this->fltr); + } + $FilenameParameters1 = array('ar', 'bg', 'bc', 'far', 'sx', 'sy', 'sw', 'sh', 'zc'); + foreach ($FilenameParameters1 as $key) { + if ($this->$key) { + $ParametersString .= '_'.$key.$this->$key; + } + } + $FilenameParameters2 = array('h', 'w', 'wl', 'wp', 'ws', 'hp', 'hs', 'xto', 'ra', 'iar', 'aoe', 'maxb', 'sfn', 'dpi'); + foreach ($FilenameParameters2 as $key) { + if ($this->$key) { + $ParametersString .= '_'.$key. (int) $this->$key; + } + } + $FilenameParameters3 = array('ica'); + foreach ($FilenameParameters3 as $key) { + if ($this->$key) { + $ParametersString .= '_'.$key.substr(md5($this->$key), 0, 4); + } + } + if ($this->thumbnailFormat == 'jpeg') { + // only JPEG output has variable quality option + $ParametersString .= '_q'. (int) $this->thumbnailQuality; + } + $this->DebugMessage('SetCacheFilename() _par set from md5('.$ParametersString.')', __FILE__, __LINE__); + $this->cache_filename .= '_par'.strtolower(md5($ParametersString)); + + if ($this->md5s) { + // source image MD5 hash provided + // do not source image modification date -- + // cached image will be used even if file was modified or removed + } elseif (!$this->config_cache_source_filemtime_ignore_remote && preg_match('#^(f|ht)tps?\://#i', $this->src)) { + $this->cache_filename .= '_dat'. (int) phpthumb_functions::filedate_remote($this->src); + } elseif (!$this->config_cache_source_filemtime_ignore_local && $this->src && !$this->rawImageData) { + $this->cache_filename .= '_dat'. (int) (@filemtime($this->sourceFilename)); + } + + $this->cache_filename .= '.'.strtolower($this->thumbnailFormat); + $broad_directories = ''; + for ($i = 0; $i < $this->config_cache_directory_depth; $i++) { + $broad_directories .= DIRECTORY_SEPARATOR.substr($broad_directory_name, 0, $i + 1); + } + + $this->cache_filename = $this->config_cache_directory.$broad_directories.DIRECTORY_SEPARATOR.$this->config_cache_prefix.rawurlencode($this->cache_filename); + return true; + } + + + public function SourceImageIsTooLarge($width, $height) { + if (!$this->config_max_source_pixels) { + return false; + } + if ($this->php_memory_limit && function_exists('memory_get_usage')) { + $available_memory = $this->php_memory_limit - memory_get_usage(); + return (bool) (($width * $height * 5) > $available_memory); + } + return (bool) (($width * $height) > $this->config_max_source_pixels); + } + + public function ImageCreateFromFilename($filename) { + // try to create GD image source directly via GD, if possible, + // rather than buffering to memory and creating with imagecreatefromstring + $ImageCreateWasAttempted = false; + $gd_image = false; + + $this->DebugMessage('starting ImageCreateFromFilename('.$filename.')', __FILE__, __LINE__); + if ($filename && ($getimagesizeinfo = @getimagesize($filename))) { + if (!$this->SourceImageIsTooLarge($getimagesizeinfo[0], $getimagesizeinfo[1])) { + $ImageCreateFromFunction = array( + IMAGETYPE_GIF => 'imagecreatefromgif', + IMAGETYPE_JPEG => 'imagecreatefromjpeg', + IMAGETYPE_PNG => 'imagecreatefrompng', + IMAGETYPE_WBMP => 'imagecreatefromwbmp', + IMAGETYPE_WEBP => 'imagecreatefromwebp', + IMAGETYPE_AVIF => 'imagecreatefromavif', + ); + $this->DebugMessage('ImageCreateFromFilename found ($getimagesizeinfo[2]=='.@$getimagesizeinfo[2].')', __FILE__, __LINE__); + switch (@$getimagesizeinfo[2]) { + case IMAGETYPE_GIF: + case IMAGETYPE_JPEG: + case IMAGETYPE_PNG: + case IMAGETYPE_WBMP: + case IMAGETYPE_WEBP: + case IMAGETYPE_AVIF: + $ImageCreateFromFunctionName = $ImageCreateFromFunction[$getimagesizeinfo[2]]; + if (function_exists($ImageCreateFromFunctionName)) { + $this->DebugMessage('Calling '.$ImageCreateFromFunctionName.'('.$filename.')', __FILE__, __LINE__); + $ImageCreateWasAttempted = true; + $gd_image = $ImageCreateFromFunctionName($filename); + } else { + $this->DebugMessage('NOT calling '.$ImageCreateFromFunctionName.'('.$filename.') because !function_exists('.$ImageCreateFromFunctionName.')', __FILE__, __LINE__); + } + break; + + case IMAGETYPE_SWF: + case IMAGETYPE_PSD: + case IMAGETYPE_BMP: + case IMAGETYPE_TIFF_II: + case IMAGETYPE_TIFF_MM: + case IMAGETYPE_JPC: + case IMAGETYPE_JP2: + case IMAGETYPE_JPX: + case IMAGETYPE_JB2: + case IMAGETYPE_SWC: + case IMAGETYPE_IFF: + case IMAGETYPE_XBM: + case IMAGETYPE_ICO: + $this->DebugMessage('No built-in image creation function for image type "'.@$getimagesizeinfo[2].'" ($getimagesizeinfo[2])', __FILE__, __LINE__); + break; + + default: + $this->DebugMessage('Unknown value for $getimagesizeinfo[2]: "'.@$getimagesizeinfo[2].'"', __FILE__, __LINE__); + break; + } + } else { + $this->DebugMessage('image is '.$getimagesizeinfo[0].'x'.$getimagesizeinfo[1].' and therefore contains more pixels ('.($getimagesizeinfo[0] * $getimagesizeinfo[1]).') than $this->config_max_source_pixels setting ('.$this->config_max_source_pixels.')', __FILE__, __LINE__); + return false; + } + } else { + $this->DebugMessage('empty $filename or getimagesize('.$filename.') failed', __FILE__, __LINE__); + } + + if (!$gd_image) { + // cannot create from filename, attempt to create source image with imagecreatefromstring, if possible + if ($ImageCreateWasAttempted) { + $this->DebugMessage($ImageCreateFromFunctionName.'() was attempted but FAILED', __FILE__, __LINE__); + } + $this->DebugMessage('Populating $rawimagedata', __FILE__, __LINE__); + $rawimagedata = ''; + if ($fp = @fopen($filename, 'rb')) { + $filesize = filesize($filename); + $blocksize = 8192; + $blockreads = ceil($filesize / $blocksize); + for ($i = 0; $i < $blockreads; $i++) { + $rawimagedata .= fread($fp, $blocksize); + } + fclose($fp); + } else { + $this->DebugMessage('cannot fopen('.$filename.')', __FILE__, __LINE__); + } + if ($rawimagedata) { + $this->DebugMessage('attempting ImageCreateFromStringReplacement($rawimagedata ('.strlen($rawimagedata).' bytes), true)', __FILE__, __LINE__); + $gd_image = $this->ImageCreateFromStringReplacement($rawimagedata, true); + } + } + return $gd_image; + } + + public function SourceImageToGD() { + if (is_resource($this->gdimg_source) || (is_object($this->gdimg_source) && $this->gdimg_source instanceOf \GdImage)) { + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + $this->DebugMessage('skipping SourceImageToGD() because $this->gdimg_source is already a resource ('.$this->source_width.'x'.$this->source_height.')', __FILE__, __LINE__); + return true; + } + $this->DebugMessage('starting SourceImageToGD()', __FILE__, __LINE__); + + if ($this->config_prefer_imagemagick) { + if (empty($this->sourceFilename) && !empty($this->rawImageData)) { + $this->DebugMessage('Copying raw image data to temp file and trying again with ImageMagick', __FILE__, __LINE__); + if ($tempnam = $this->phpThumb_tempnam()) { + if (file_put_contents($tempnam, $this->rawImageData)) { + $this->sourceFilename = $tempnam; + if ($this->ImageMagickThumbnailToGD()) { + // excellent, we have a thumbnailed source image + $this->DebugMessage('ImageMagickThumbnailToGD() succeeded', __FILE__, __LINE__); + } else { + $this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__); + } + @chmod($tempnam, $this->getParameter('config_file_create_mask')); + } else { + $this->DebugMessage('failed to put $this->rawImageData into temp file "'.$tempnam.'"', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('failed to generate temp file name', __FILE__, __LINE__); + } + } + } + if (!$this->gdimg_source && $this->rawImageData) { + + if ($this->SourceImageIsTooLarge($this->source_width, $this->source_height)) { + $memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0); + return $this->ErrorImage('Source image is too large ('.$this->source_width.'x'.$this->source_height.' = '.number_format($this->source_width * $this->source_height / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->source_width * $this->source_height)) / 1048576).'M).'); + } + if ($this->md5s && ($this->md5s != md5($this->rawImageData))) { + return $this->ErrorImage('$this->md5s != md5($this->rawImageData)'."\n".'"'.$this->md5s.'" != '."\n".'"'.md5($this->rawImageData).'"'); + } + //if ($this->issafemode) { + // return $this->ErrorImage('Cannot generate thumbnails from raw image data when PHP SAFE_MODE enabled'); + //} + $this->gdimg_source = $this->ImageCreateFromStringReplacement($this->rawImageData); + if (!$this->gdimg_source) { + if (substr($this->rawImageData, 0, 2) === 'BM') { + $this->getimagesizeinfo[2] = 6; // BMP + } elseif (substr($this->rawImageData, 0, 4) === 'II'."\x2A\x00") { + $this->getimagesizeinfo[2] = 7; // TIFF (littlendian) + } elseif (substr($this->rawImageData, 0, 4) === 'MM'."\x00\x2A") { + $this->getimagesizeinfo[2] = 8; // TIFF (bigendian) + } + $this->DebugMessage('SourceImageToGD.ImageCreateFromStringReplacement() failed with unknown image type "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).')', __FILE__, __LINE__); +// return $this->ErrorImage('Unknown image type identified by "'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).') in SourceImageToGD()['.__LINE__.']'); + } + + } elseif (!$this->gdimg_source && $this->sourceFilename) { + + if ($this->md5s && ($this->md5s != phpthumb_functions::md5_file_safe($this->sourceFilename))) { + return $this->ErrorImage('$this->md5s != md5(sourceFilename)'."\n".'"'.$this->md5s.'" != '."\n".'"'.phpthumb_functions::md5_file_safe($this->sourceFilename).'"'); + } + switch (@$this->getimagesizeinfo[2]) { + case 1: + case 3: + // GIF or PNG input file may have transparency + $this->is_alpha = true; + break; + } + if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) { + $this->gdimg_source = $this->ImageCreateFromFilename($this->sourceFilename); + } + + } + + while (true) { + if ($this->gdimg_source) { + $this->DebugMessage('Not using EXIF thumbnail data because $this->gdimg_source is already set', __FILE__, __LINE__); + break; + } + if (!$this->exif_thumbnail_data) { + $this->DebugMessage('Not using EXIF thumbnail data because $this->exif_thumbnail_data is empty', __FILE__, __LINE__); + break; + } + if (ini_get('safe_mode')) { + if (!$this->SourceImageIsTooLarge($this->source_width, $this->source_height)) { + $this->DebugMessage('Using EXIF thumbnail data because source image too large and safe_mode enabled', __FILE__, __LINE__); + $this->aoe = true; + } else { + break; + } + } else { + if (!$this->config_use_exif_thumbnail_for_speed) { + $this->DebugMessage('Not using EXIF thumbnail data because $this->config_use_exif_thumbnail_for_speed is FALSE', __FILE__, __LINE__); + break; + } + if (($this->thumbnailCropX != 0) || ($this->thumbnailCropY != 0)) { + $this->DebugMessage('Not using EXIF thumbnail data because source cropping is enabled ('.$this->thumbnailCropX.','.$this->thumbnailCropY.')', __FILE__, __LINE__); + break; + } + if (($this->w > $this->exif_thumbnail_width) || ($this->h > $this->exif_thumbnail_height)) { + $this->DebugMessage('Not using EXIF thumbnail data because EXIF thumbnail is too small ('.$this->exif_thumbnail_width.'x'.$this->exif_thumbnail_height.' vs '.$this->w.'x'.$this->h.')', __FILE__, __LINE__); + break; + } + $source_ar = $this->source_width / $this->source_height; + $exif_ar = $this->exif_thumbnail_width / $this->exif_thumbnail_height; + if (number_format($source_ar, 2) != number_format($exif_ar, 2)) { + $this->DebugMessage('not using EXIF thumbnail because $source_ar != $exif_ar ('.$source_ar.' != '.$exif_ar.')', __FILE__, __LINE__); + break; + } + } + + // EXIF thumbnail exists, and is equal to or larger than destination thumbnail, and will be use as source image + $this->DebugMessage('Trying to use EXIF thumbnail as source image', __FILE__, __LINE__); + + if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) { + + $this->DebugMessage('Successfully using EXIF thumbnail as source image', __FILE__, __LINE__); + $this->gdimg_source = $gdimg_exif_temp; + $this->source_width = $this->exif_thumbnail_width; + $this->source_height = $this->exif_thumbnail_height; + $this->thumbnailCropW = $this->source_width; + $this->thumbnailCropH = $this->source_height; + return true; + + } else { + $this->DebugMessage('$this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false) failed', __FILE__, __LINE__); + } + + break; + } + + if (!$this->gdimg_source) { + $this->DebugMessage('$this->gdimg_source is still empty', __FILE__, __LINE__); + + $this->DebugMessage('ImageMagickThumbnailToGD() failed', __FILE__, __LINE__); + + $imageHeader = ''; + $gd_info = gd_info(); + $GDreadSupport = false; + switch (@$this->getimagesizeinfo[2]) { + case 1: + $imageHeader = 'Content-Type: image/gif'; + $GDreadSupport = (bool) @$gd_info['GIF Read Support']; + break; + case 2: + $imageHeader = 'Content-Type: image/jpeg'; + $GDreadSupport = (bool) @$gd_info['JPG Support']; + break; + case 3: + $imageHeader = 'Content-Type: image/png'; + $GDreadSupport = (bool) @$gd_info['PNG Support']; + break; + } + if ($imageHeader) { + // cannot create image for whatever reason (maybe imagecreatefromjpeg et al are not available?) + // and ImageMagick is not available either, no choice but to output original (not resized/modified) data and exit + if ($this->config_error_die_on_source_failure) { + $errormessages = array(); + $errormessages[] = 'All attempts to create GD image source failed.'; + if ($this->fatalerror) { + $errormessages[] = $this->fatalerror; + } + if ($this->issafemode) { + $errormessages[] = 'Safe Mode enabled, therefore ImageMagick is unavailable. (disable Safe Mode if possible)'; + } elseif (!$this->ImageMagickVersion()) { + $errormessages[] = 'ImageMagick is not installed (it is highly recommended that you install it).'; + } + if ($this->SourceImageIsTooLarge($this->getimagesizeinfo[0], $this->getimagesizeinfo[1])) { + $memory_get_usage = (function_exists('memory_get_usage') ? memory_get_usage() : 0); + $errormessages[] = 'Source image is too large ('.$this->getimagesizeinfo[0].'x'.$this->getimagesizeinfo[1].' = '.number_format($this->getimagesizeinfo[0] * $this->getimagesizeinfo[1] / 1000000, 1).'Mpx, max='.number_format($this->config_max_source_pixels / 1000000, 1).'Mpx) for GD creation (either install ImageMagick or increase PHP memory_limit to at least '.ceil(($memory_get_usage + (5 * $this->getimagesizeinfo[0] * $this->getimagesizeinfo[1])) / 1048576).'M).'; + } elseif (!$GDreadSupport) { + $errormessages[] = 'GD does not have read support for "'.$imageHeader.'".'; + } else { + $errormessages[] = 'Source image probably corrupt.'; + } + $this->ErrorImage(implode("\n", $errormessages)); + + } else { + $this->DebugMessage('All attempts to create GD image source failed ('.(ini_get('safe_mode') ? 'Safe Mode enabled, ImageMagick unavailable and source image probably too large for GD': ($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"')).'), cannot generate thumbnail'); + //$this->DebugMessage('All attempts to create GD image source failed ('.($GDreadSupport ? 'source image probably corrupt' : 'GD does not have read support for "'.$imageHeader.'"').'), outputing raw image', __FILE__, __LINE__); + //if (!$this->phpThumbDebug) { + // header($imageHeader); + // echo $this->rawImageData; + // exit; + //} + return false; + } + } + + //switch (substr($this->rawImageData, 0, 2)) { + // case 'BM': + switch (@$this->getimagesizeinfo[2]) { + case 6: + ob_start(); + if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) { + ob_end_clean(); + return $this->ErrorImage('include_once('. __DIR__ .'/phpthumb.bmp.php) failed'); + } + ob_end_clean(); + if ($fp = @fopen($this->sourceFilename, 'rb')) { + $this->rawImageData = ''; + while (!feof($fp)) { + $this->rawImageData .= fread($fp, 32768); + } + fclose($fp); + } + $phpthumb_bmp = new phpthumb_bmp(); + $this->gdimg_source = $phpthumb_bmp->phpthumb_bmp2gd($this->rawImageData, phpthumb_functions::gd_version() >= 2.0); + unset($phpthumb_bmp); + if ($this->gdimg_source) { + $this->DebugMessage('$phpthumb_bmp->phpthumb_bmp2gd() succeeded', __FILE__, __LINE__); + } else { + return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on BMP source conversion' : 'phpthumb_bmp2gd() failed'); + } + break; + //} + //switch (substr($this->rawImageData, 0, 4)) { + // case 'II'."\x2A\x00": + // case 'MM'."\x00\x2A": + case 7: + case 8: + return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on TIFF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support TIFF source images without it'); + break; + + //case "\xD7\xCD\xC6\x9A": + // return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it'); + // break; + } + + if (!$this->gdimg_source) { + if ($this->rawImageData) { + $HeaderFourBytes = substr($this->rawImageData, 0, 4); + } elseif ($this->sourceFilename) { + if ($fp = @fopen($this->sourceFilename, 'rb')) { + $HeaderFourBytes = fread($fp, 4); + fclose($fp); + } else { + return $this->ErrorImage('failed to open "'.$this->sourceFilename.'" SourceImageToGD() ['.__LINE__.']'); + } + } else { + return $this->ErrorImage('Unable to create image, neither filename nor image data suppplied in SourceImageToGD() ['.__LINE__.']'); + } + if (!$this->ImageMagickVersion() && !phpthumb_functions::gd_version()) { + return $this->ErrorImage('Neither GD nor ImageMagick seem to be installed on this server. At least one (preferably GD), or better both, MUST be installed for phpThumb to work.'); + } elseif ($HeaderFourBytes == "\xD7\xCD\xC6\x9A") { // WMF + return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick failed on WMF source conversion' : 'ImageMagick is unavailable and phpThumb() does not support WMF source images without it'); + } elseif ($HeaderFourBytes == '%PDF') { // "%PDF" + return $this->ErrorImage($this->ImageMagickVersion() ? 'ImageMagick and GhostScript are both required for PDF source images; GhostScript may not be properly configured' : 'ImageMagick and/or GhostScript are unavailable and phpThumb() does not support PDF source images without them'); + } elseif (substr($HeaderFourBytes, 0, 3) == "\xFF\xD8\xFF") { // JPEG + return $this->ErrorImage('Image (JPEG) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting'); + } elseif ($HeaderFourBytes == '%PNG') { // "%PNG" + return $this->ErrorImage('Image (PNG) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting'); + } elseif (substr($HeaderFourBytes, 0, 3) == 'GIF') { // GIF + return $this->ErrorImage('Image (GIF) is too large for PHP-GD memory_limit, please install ImageMagick or increase php.ini memory_limit setting'); + } + return $this->ErrorImage('Unknown image type identified by "'.$HeaderFourBytes.'" ('.phpthumb_functions::HexCharDisplay($HeaderFourBytes).') in SourceImageToGD() ['.__LINE__.']'); + } + } + + if (!$this->gdimg_source) { + if ($gdimg_exif_temp = $this->ImageCreateFromStringReplacement($this->exif_thumbnail_data, false)) { + $this->DebugMessage('All other attempts failed, but successfully using EXIF thumbnail as source image', __FILE__, __LINE__); + $this->gdimg_source = $gdimg_exif_temp; + // override allow-enlarging setting if EXIF thumbnail is the only source available + // otherwise thumbnails larger than the EXIF thumbnail will be created at EXIF size + $this->aoe = true; + return true; + } + return false; + } + + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + return true; + } + + private function ImageCropAuto() { + // ImageCropAuto + if (!is_null($this->ica)) { + $this->DebugMessage('ImageCropAuto('.$this->ica.') starting', __FILE__, __LINE__); + if (function_exists('imagecropauto')) { // (PHP 5 >= 5.5.0, PHP 7) + // https://www.php.net/manual/en/function.imagecropauto.php + // 0 = IMG_CROP_DEFAULT + // 1 = IMG_CROP_TRANSPARENT + // 2 = IMG_CROP_BLACK + // 3 = IMG_CROP_WHITE + // 4 = IMG_CROP_SIDES + // 5 = IMG_CROP_THRESHOLD + if (preg_match('#^(([0-4])|(5)\\|(0?\\.?[0-9]+)\\|([0-9A-F]{6}))$#i', $this->ica, $matches)) { + @list($dummy, $dummy, $ica_mode1, $ica_mode2, $ica_threshold, $ica_color) = $matches; + if ($ica_mode2) { + $param_color = hexdec($ica_color); + if (!imageistruecolor($this->gdimg_source)) { + $param_color = imagecolorclosest($this->gdimg_source, hexdec(substr($ica_color, 0, 2)), hexdec(substr($ica_color, 2, 2)), hexdec(substr($ica_color, 4, 2))); + } + $cropped = imagecropauto($this->gdimg_source, intval($ica_mode2), floatval($ica_threshold), $param_color); + } else { + $cropped = imagecropauto($this->gdimg_source, intval($ica_mode1)); + } + if ($cropped !== false) { // in case a new image resource was returned + $this->DebugMessage('ImageCropAuto changing source image size from '.imagesx($this->gdimg_source).'x'.imagesy($this->gdimg_source).' to '.imagesx($cropped).'x'.imagesy($cropped), __FILE__, __LINE__); + imagedestroy($this->gdimg_source); // we destroy the original image + $this->gdimg_source = $cropped; // and assign the cropped image to $im + $this->source_width = imagesx($this->gdimg_source); + $this->source_height = imagesy($this->gdimg_source); + } else { + $this->DebugMessage('imagecropauto failed', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('invalid "ica" parameter syntax, ignoring', __FILE__, __LINE__); + } + } else { + $this->DebugMessage('!function_exists(imagecropauto), ignoring "ica" parameter', __FILE__, __LINE__); + } + } + return true; + } + + public function phpThumbDebugVarDump($var) { + if (null === $var) { + return 'NULL'; + } elseif (is_bool($var)) { + return ($var ? 'TRUE' : 'FALSE'); + } elseif (is_string($var)) { + return 'string('.strlen($var).')'.str_repeat(' ', max(0, 3 - strlen(strlen($var)))).' "'.$var.'"'; + } elseif (is_int($var)) { + return 'integer '.$var; + } elseif (is_float($var)) { + return 'float '.$var; + } elseif (is_array($var)) { + ob_start(); + var_dump($var); + $vardumpoutput = ob_get_contents(); + ob_end_clean(); + return strtr($vardumpoutput, "\n\r\t", ' '); + } + return gettype($var); + } + + public function phpThumbDebug($level='') { + if ($level && ($this->phpThumbDebug !== $level)) { + return true; + } + if ($this->config_disable_debug) { + return $this->ErrorImage('phpThumbDebug disabled'); + } + + $FunctionsExistance = array('exif_thumbnail', 'gd_info', 'image_type_to_mime_type', 'getimagesize', 'imagecopyresampled', 'imagecopyresized', 'imagecreate', 'imagecreatefromstring', 'imagecreatetruecolor', 'imageistruecolor', 'imagerotate', 'imagetypes', 'version_compare', 'imagecreatefromgif', 'imagecreatefromjpeg', 'imagecreatefrompng', 'imagecreatefromwbmp', 'imagecreatefromxbm', 'imagecreatefromxpm', 'imagecreatefromstring', 'imagecreatefromgd', 'imagecreatefromgd2', 'imagecreatefromgd2part', 'imagejpeg', 'imagegif', 'imagepng', 'imagewbmp'); + $ParameterNames = array('src', 'new', 'w', 'h', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'far', 'bg', 'bc', 'zc', 'ica', 'file', 'goto', 'err', 'xto', 'ra', 'ar', 'aoe', 'iar', 'maxb'); + $ConfigVariableNames = array('document_root', 'temp_directory', 'output_format', 'output_maxwidth', 'output_maxheight', 'error_message_image_default', 'error_bgcolor', 'error_textcolor', 'error_fontsize', 'error_die_on_error', 'error_silent_die_on_error', 'error_die_on_source_failure', 'nohotlink_enabled', 'nohotlink_valid_domains', 'nohotlink_erase_image', 'nohotlink_text_message', 'nooffsitelink_enabled', 'nooffsitelink_valid_domains', 'nooffsitelink_require_refer', 'nooffsitelink_erase_image', 'nooffsitelink_text_message', 'high_security_enabled', 'allow_src_above_docroot', 'allow_src_above_phpthumb', 'max_source_pixels', 'use_exif_thumbnail_for_speed', 'border_hexcolor', 'background_hexcolor', 'ttf_directory', 'disable_pathinfo_parsing', 'disable_imagecopyresampled'); + $OtherVariableNames = array('phpThumbDebug', 'thumbnailQuality', 'thumbnailFormat', 'gdimg_output', 'gdimg_source', 'sourceFilename', 'source_width', 'source_height', 'thumbnailCropX', 'thumbnailCropY', 'thumbnailCropW', 'thumbnailCropH', 'exif_thumbnail_width', 'exif_thumbnail_height', 'exif_thumbnail_type', 'thumbnail_width', 'thumbnail_height', 'thumbnail_image_width', 'thumbnail_image_height'); + + $DebugOutput = array(); + $DebugOutput[] = 'phpThumb() version = '.$this->phpthumb_version; + $DebugOutput[] = 'phpversion() = '.@PHP_VERSION; + $DebugOutput[] = 'PHP_OS = '.PHP_OS; + $DebugOutput[] = '$_SERVER[SERVER_SOFTWARE] = '.@$_SERVER['SERVER_SOFTWARE']; + $DebugOutput[] = '__FILE__ = '.__FILE__; + $DebugOutput[] = 'realpath(.) = '.@realpath('.'); + $DebugOutput[] = '$_SERVER[PHP_SELF] = '.@$_SERVER['PHP_SELF']; + $DebugOutput[] = '$_SERVER[HOST_NAME] = '.@$_SERVER['HOST_NAME']; + $DebugOutput[] = '$_SERVER[HTTP_REFERER] = '.@$_SERVER['HTTP_REFERER']; + $DebugOutput[] = '$_SERVER[QUERY_STRING] = '.@$_SERVER['QUERY_STRING']; + $DebugOutput[] = '$_SERVER[PATH_INFO] = '.@$_SERVER['PATH_INFO']; + $DebugOutput[] = '$_SERVER[DOCUMENT_ROOT] = '.@$_SERVER['DOCUMENT_ROOT']; + $DebugOutput[] = 'getenv(DOCUMENT_ROOT) = '.@getenv('DOCUMENT_ROOT'); + $DebugOutput[] = ''; + + $DebugOutput[] = 'get_magic_quotes_gpc() = '.(function_exists('get_magic_quotes_gpc') ? $this->phpThumbDebugVarDump(@get_magic_quotes_gpc()) : false); + $DebugOutput[] = 'get_magic_quotes_runtime() = '.(function_exists('get_magic_quotes_runtime') ? $this->phpThumbDebugVarDump(@get_magic_quotes_runtime()) : false); + $DebugOutput[] = 'error_reporting() = '.$this->phpThumbDebugVarDump(error_reporting()); + $DebugOutput[] = 'ini_get(error_reporting) = '.$this->phpThumbDebugVarDump(@ini_get('error_reporting')); + $DebugOutput[] = 'ini_get(display_errors) = '.$this->phpThumbDebugVarDump(@ini_get('display_errors')); + $DebugOutput[] = 'ini_get(allow_url_fopen) = '.$this->phpThumbDebugVarDump(@ini_get('allow_url_fopen')); + $DebugOutput[] = 'ini_get(disable_functions) = '.$this->phpThumbDebugVarDump(@ini_get('disable_functions')); + $DebugOutput[] = 'get_cfg_var(disable_functions) = '.$this->phpThumbDebugVarDump(@get_cfg_var('disable_functions')); + $DebugOutput[] = 'ini_get(safe_mode) = '.$this->phpThumbDebugVarDump(@ini_get('safe_mode')); + $DebugOutput[] = 'ini_get(open_basedir) = '.$this->phpThumbDebugVarDump(@ini_get('open_basedir')); + $DebugOutput[] = 'ini_get(max_execution_time) = '.$this->phpThumbDebugVarDump(@ini_get('max_execution_time')); + $DebugOutput[] = 'ini_get(memory_limit) = '.$this->phpThumbDebugVarDump(@ini_get('memory_limit')); + $DebugOutput[] = 'get_cfg_var(memory_limit) = '.$this->phpThumbDebugVarDump(@get_cfg_var('memory_limit')); + $DebugOutput[] = 'memory_get_usage() = '.(function_exists('memory_get_usage') ? $this->phpThumbDebugVarDump(@memory_get_usage()) : 'n/a'); + $DebugOutput[] = ''; + + $DebugOutput[] = '$this->config_prefer_imagemagick = '.$this->phpThumbDebugVarDump($this->config_prefer_imagemagick); + $DebugOutput[] = '$this->config_imagemagick_path = '.$this->phpThumbDebugVarDump($this->config_imagemagick_path); + $DebugOutput[] = '$this->ImageMagickWhichConvert() = '.$this->ImageMagickWhichConvert(); + $IMpathUsed = ($this->config_imagemagick_path ? $this->config_imagemagick_path : $this->ImageMagickWhichConvert()); + $DebugOutput[] = '[actual ImageMagick path used] = '.$this->phpThumbDebugVarDump($IMpathUsed); + $DebugOutput[] = 'file_exists([actual ImageMagick path used]) = '.$this->phpThumbDebugVarDump(@file_exists($IMpathUsed)); + $DebugOutput[] = 'ImageMagickVersion(false) = '.$this->ImageMagickVersion(false); + $DebugOutput[] = 'ImageMagickVersion(true) = '.$this->ImageMagickVersion(true); + $DebugOutput[] = ''; + + $DebugOutput[] = '$this->config_cache_directory = '.$this->phpThumbDebugVarDump($this->config_cache_directory); + $DebugOutput[] = '$this->config_cache_directory_depth = '.$this->phpThumbDebugVarDump($this->config_cache_directory_depth); + $DebugOutput[] = '$this->config_cache_disable_warning = '.$this->phpThumbDebugVarDump($this->config_cache_disable_warning); + $DebugOutput[] = '$this->config_cache_maxage = '.$this->phpThumbDebugVarDump($this->config_cache_maxage); + $DebugOutput[] = '$this->config_cache_maxsize = '.$this->phpThumbDebugVarDump($this->config_cache_maxsize); + $DebugOutput[] = '$this->config_cache_maxfiles = '.$this->phpThumbDebugVarDump($this->config_cache_maxfiles); + $DebugOutput[] = '$this->config_cache_force_passthru = '.$this->phpThumbDebugVarDump($this->config_cache_force_passthru); + $DebugOutput[] = '$this->cache_filename = '.$this->phpThumbDebugVarDump($this->cache_filename); + $DebugOutput[] = 'is_readable($this->config_cache_directory) = '.$this->phpThumbDebugVarDump(@is_readable($this->config_cache_directory)); + $DebugOutput[] = 'is_writable($this->config_cache_directory) = '.$this->phpThumbDebugVarDump(@is_writable($this->config_cache_directory)); + $DebugOutput[] = 'is_readable($this->cache_filename) = '.$this->phpThumbDebugVarDump(@is_readable($this->cache_filename)); + $DebugOutput[] = 'is_writable($this->cache_filename) = '.(@file_exists($this->cache_filename) ? $this->phpThumbDebugVarDump(@is_writable($this->cache_filename)) : 'n/a'); + $DebugOutput[] = ''; + + foreach ($ConfigVariableNames as $varname) { + $varname = 'config_'.$varname; + $value = $this->$varname; + $DebugOutput[] = '$this->'.str_pad($varname, 37, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + $DebugOutput[] = ''; + foreach ($OtherVariableNames as $varname) { + $value = $this->$varname; + $DebugOutput[] = '$this->'.str_pad($varname, 27, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + $DebugOutput[] = 'strlen($this->rawImageData) = '.(!empty($this->rawImageData) ? strlen($this->rawImageData) : ''); + $DebugOutput[] = 'strlen($this->exif_thumbnail_data) = '.(!empty($this->exif_thumbnail_data) ? strlen($this->exif_thumbnail_data) : ''); + $DebugOutput[] = ''; + + foreach ($ParameterNames as $varname) { + $value = $this->$varname; + $DebugOutput[] = '$this->'.str_pad($varname, 4, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + $DebugOutput[] = ''; + + foreach ($FunctionsExistance as $functionname) { + $DebugOutput[] = 'builtin_function_exists('.$functionname.')'.str_repeat(' ', 23 - strlen($functionname)).' = '.$this->phpThumbDebugVarDump(phpthumb_functions::builtin_function_exists($functionname)); + } + $DebugOutput[] = ''; + + $gd_info = gd_info(); + foreach ($gd_info as $key => $value) { + $DebugOutput[] = 'gd_info.'.str_pad($key, 34, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + $DebugOutput[] = ''; + + $exif_info = phpthumb_functions::exif_info(); + foreach ($exif_info as $key => $value) { + $DebugOutput[] = 'exif_info.'.str_pad($key, 26, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + $DebugOutput[] = ''; + + if ($ApacheLookupURIarray = phpthumb_functions::ApacheLookupURIarray(dirname(@$_SERVER['PHP_SELF']))) { + foreach ($ApacheLookupURIarray as $key => $value) { + $DebugOutput[] = 'ApacheLookupURIarray.'.str_pad($key, 15, ' ', STR_PAD_RIGHT).' = '.$this->phpThumbDebugVarDump($value); + } + } else { + $DebugOutput[] = 'ApacheLookupURIarray() -- FAILED'; + } + $DebugOutput[] = ''; + + if (isset($_GET) && is_array($_GET)) { + foreach ($_GET as $key => $value) { + $DebugOutput[] = '$_GET['.$key.']'.str_repeat(' ', 30 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value); + } + } + if (isset($_POST) && is_array($_POST)) { + foreach ($_POST as $key => $value) { + $DebugOutput[] = '$_POST['.$key.']'.str_repeat(' ', 29 - strlen($key)).'= '.$this->phpThumbDebugVarDump($value); + } + } + $DebugOutput[] = ''; + + $DebugOutput[] = '$this->debugmessages:'; + foreach ($this->debugmessages as $errorstring) { + $DebugOutput[] = ' * '.$errorstring; + } + $DebugOutput[] = ''; + + $DebugOutput[] = '$this->debugtiming:'; + foreach ($this->debugtiming as $timestamp => $timingstring) { + $DebugOutput[] = ' * '.$timestamp.' '.$timingstring; + } + $DebugOutput[] = ' * Total processing time: '.number_format(max(array_keys($this->debugtiming)) - min(array_keys($this->debugtiming)), 6); + + $this->f = (isset($_GET['f']) ? $_GET['f'] : $this->f); // debug modes 0-2 don't recognize text mode otherwise + return $this->ErrorImage(implode("\n", $DebugOutput), 700, 500, true); + } + + public function FatalError($text) { + if (null === $this->fatalerror) { + $this->fatalerror = $text; + } + return true; + } + + public function ErrorImage($text, $width=0, $height=0, $forcedisplay=false) { + $width = ($width ? $width : $this->config_error_image_width); + $height = ($height ? $height : $this->config_error_image_height); + + $text = 'phpThumb() v'.$this->phpthumb_version."\n".'http://phpthumb.sourceforge.net'."\n\n".($this->config_disable_debug ? 'Error messages disabled.'."\n\n".'edit phpThumb.config.php and (temporarily) set'."\n".'$PHPTHUMB_CONFIG[\'disable_debug\'] = false;'."\n".'to view the details of this error' : $text); + + $this->FatalError($text); + $this->DebugMessage($text, __FILE__, __LINE__); + $this->purgeTempFiles(); + if ($this->config_error_silent_die_on_error) { + exit; + } + if ($this->phpThumbDebug && !$forcedisplay) { + return false; + } + if (!$this->config_error_die_on_error && !$forcedisplay) { + return false; + } + if ($this->err || $this->config_error_message_image_default) { + // Show generic custom error image instead of error message + // for use on production sites where you don't want debug messages + if (($this->err == 'showerror') || $this->phpThumbDebug) { + // fall through and actually show error message even if default error image is set + } else { + header('Location: '.($this->err ? $this->err : $this->config_error_message_image_default)); + exit; + } + } + $this->setOutputFormat(); + if (!$this->thumbnailFormat || !$this->config_disable_debug || (phpthumb_functions::gd_version() < 1)) { + $this->thumbnailFormat = 'text'; + } + if (@$this->thumbnailFormat == 'text') { + // bypass all GD functions and output text error message + if (!headers_sent()) { + header('Content-type: text/plain'); + echo $text; + } else { + echo '
        '.htmlspecialchars($text).'
        '; + } + exit; + } + + $FontWidth = imagefontwidth($this->config_error_fontsize); + $FontHeight = imagefontheight($this->config_error_fontsize); + + $LinesOfText = explode("\n", @wordwrap($text, floor($width / $FontWidth), "\n", true)); + $height = max($height, count($LinesOfText) * $FontHeight); + + $headers_file = ''; + $headers_line = ''; + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.0', '>=') && headers_sent($headers_file, $headers_line)) { + + echo "\n".'**Headers already sent in file "'.$headers_file.'" on line "'.$headers_line.'", dumping error message as text:**
        '."\n\n".$text."\n".'
        '; + + } elseif (headers_sent()) { + + echo "\n".'**Headers already sent, dumping error message as text:**
        '."\n\n".$text."\n".'
        '; + + } elseif ($gdimg_error = imagecreate($width, $height)) { + + $background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_bgcolor, true); + $text_color = phpthumb_functions::ImageHexColorAllocate($gdimg_error, $this->config_error_textcolor, true); + imagefilledrectangle($gdimg_error, 0, 0, $width, $height, $background_color); + $lineYoffset = 0; + foreach ($LinesOfText as $line) { + imagestring($gdimg_error, $this->config_error_fontsize, 2, $lineYoffset, $line, $text_color); + $lineYoffset += $FontHeight; + } + if (function_exists('imagetypes')) { + $imagetypes = imagetypes(); + if ($imagetypes & IMG_PNG) { + header('Content-Type: image/png'); + imagepng($gdimg_error); + } elseif ($imagetypes & IMG_GIF) { + header('Content-Type: image/gif'); + imagegif($gdimg_error); + } elseif ($imagetypes & IMG_JPG) { + header('Content-Type: image/jpeg'); + imagejpeg($gdimg_error); + } elseif ($imagetypes & IMG_WBMP) { + header('Content-Type: image/vnd.wap.wbmp'); + imagewbmp($gdimg_error); + } + } + imagedestroy($gdimg_error); + + } + if (!headers_sent()) { + echo "\n".'**Failed to send graphical error image, dumping error message as text:**
        '."\n\n".$text; + } + exit; + } + + public function ImageCreateFromStringReplacement(&$RawImageData, $DieOnErrors=false) { + // there are serious bugs in the non-bundled versions of GD which may cause + // PHP to segfault when calling imagecreatefromstring() - avoid if at all possible + // when not using a bundled version of GD2 + if (!phpthumb_functions::gd_version()) { + if ($DieOnErrors) { + if (!headers_sent()) { + // base64-encoded error image in GIF format + $ERROR_NOGD = 'R0lGODlhIAAgALMAAAAAABQUFCQkJDY2NkZGRldXV2ZmZnJycoaGhpSUlKWlpbe3t8XFxdXV1eTk5P7+/iwAAAAAIAAgAAAE/vDJSau9WILtTAACUinDNijZtAHfCojS4W5H+qxD8xibIDE9h0OwWaRWDIljJSkUJYsN4bihMB8th3IToAKs1VtYM75cyV8sZ8vygtOE5yMKmGbO4jRdICQCjHdlZzwzNW4qZSQmKDaNjhUMBX4BBAlmMywFSRWEmAI6b5gAlhNxokGhooAIK5o/pi9vEw4Lfj4OLTAUpj6IabMtCwlSFw0DCKBoFqwAB04AjI54PyZ+yY3TD0ss2YcVmN/gvpcu4TOyFivWqYJlbAHPpOntvxNAACcmGHjZzAZqzSzcq5fNjxFmAFw9iFRunD1epU6tsIPmFCAJnWYE0FURk7wJDA0MTKpEzoWAAskiAAA7'; + header('Content-Type: image/gif'); + echo base64_decode($ERROR_NOGD); + } else { + echo '*** ERROR: No PHP-GD support available ***'; + } + exit; + } else { + $this->DebugMessage('ImageCreateFromStringReplacement() failed: gd_version says "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + } + if (phpthumb_functions::gd_is_bundled()) { + $this->DebugMessage('ImageCreateFromStringReplacement() calling built-in imagecreatefromstring()', __FILE__, __LINE__); + return @imagecreatefromstring($RawImageData); + } + if ($this->issafemode) { + $this->DebugMessage('ImageCreateFromStringReplacement() failed: cannot create temp file in SAFE_MODE', __FILE__, __LINE__); + return false; + } + + switch (substr($RawImageData, 0, 3)) { + case 'GIF': + $ICFSreplacementFunctionName = 'imagecreatefromgif'; + break; + case "\xFF\xD8\xFF": + $ICFSreplacementFunctionName = 'imagecreatefromjpeg'; + break; + case "\x89".'PN': + $ICFSreplacementFunctionName = 'imagecreatefrompng'; + break; + default: + $this->DebugMessage('ImageCreateFromStringReplacement() failed: unknown fileformat signature "'.phpthumb_functions::HexCharDisplay(substr($RawImageData, 0, 3)).'"', __FILE__, __LINE__); + return false; + break; + } + $ErrorMessage = ''; + if ($tempnam = $this->phpThumb_tempnam()) { + if ($fp_tempnam = @fopen($tempnam, 'wb')) { + fwrite($fp_tempnam, $RawImageData); + fclose($fp_tempnam); + @chmod($tempnam, $this->getParameter('config_file_create_mask')); + if (($ICFSreplacementFunctionName == 'imagecreatefromgif') && !function_exists($ICFSreplacementFunctionName)) { + + // Need to create from GIF file, but imagecreatefromgif does not exist + ob_start(); + if (!@include_once __DIR__ .'/phpthumb.gif.php' ) { + $ErrorMessage = 'Failed to include required file "'. __DIR__ .'/phpthumb.gif.php" in '.__FILE__.' on line '.__LINE__; + $this->DebugMessage($ErrorMessage, __FILE__, __LINE__); + } + ob_end_clean(); + // gif_loadFileToGDimageResource() cannot read from raw data, write to file first + if ($tempfilename = $this->phpThumb_tempnam()) { + if ($fp_tempfile = @fopen($tempfilename, 'wb')) { + fwrite($fp_tempfile, $RawImageData); + fclose($fp_tempfile); + $gdimg_source = gif_loadFileToGDimageResource($tempfilename); + $this->DebugMessage('gif_loadFileToGDimageResource('.$tempfilename.') completed', __FILE__, __LINE__); + $this->DebugMessage('deleting "'.$tempfilename.'"', __FILE__, __LINE__); + unlink($tempfilename); + return $gdimg_source; + } else { + $ErrorMessage = 'Failed to open tempfile in '.__FILE__.' on line '.__LINE__; + $this->DebugMessage($ErrorMessage, __FILE__, __LINE__); + } + } else { + $ErrorMessage = 'Failed to open generate tempfile name in '.__FILE__.' on line '.__LINE__; + $this->DebugMessage($ErrorMessage, __FILE__, __LINE__); + } + + } elseif (function_exists($ICFSreplacementFunctionName) && ($gdimg_source = @$ICFSreplacementFunctionName($tempnam))) { + + // great + $this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') succeeded', __FILE__, __LINE__); + $this->DebugMessage('deleting "'.$tempnam.'"', __FILE__, __LINE__); + unlink($tempnam); + return $gdimg_source; + + } else { + + // GD functions not available, or failed to create image + $this->DebugMessage($ICFSreplacementFunctionName.'('.$tempnam.') '.(function_exists($ICFSreplacementFunctionName) ? 'failed' : 'does not exist'), __FILE__, __LINE__); + if (isset($_GET['phpThumbDebug'])) { + $this->phpThumbDebug(); + } + + } + } else { + $ErrorMessage = 'Failed to fopen('.$tempnam.', "wb") in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php'; + if ($this->issafemode) { + $ErrorMessage = 'ImageCreateFromStringReplacement() failed in '.__FILE__.' on line '.__LINE__.': cannot create temp file in SAFE_MODE'; + } + $this->DebugMessage($ErrorMessage, __FILE__, __LINE__); + } + $this->DebugMessage('deleting "'.$tempnam.'"', __FILE__, __LINE__); + @unlink($tempnam); + } else { + $ErrorMessage = 'Failed to generate phpThumb_tempnam() in '.__FILE__.' on line '.__LINE__."\n".'You may need to set $PHPTHUMB_CONFIG[temp_directory] in phpThumb.config.php'; + if ($this->issafemode) { + $ErrorMessage = 'ImageCreateFromStringReplacement() failed in '.__FILE__.' on line '.__LINE__.': cannot create temp file in SAFE_MODE'; + } + } + if ($DieOnErrors && $ErrorMessage) { + return $this->ErrorImage($ErrorMessage); + } + return false; + } + + public function ImageResizeFunction(&$dst_im, &$src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) { + $dstX = (int) round($dstX); + $dstY = (int) round($dstY); + $srcX = (int) round($srcX); + $srcY = (int) round($srcY); + $dstW = (int) round($dstW); + $dstH = (int) round($dstH); + $srcW = (int) round($srcW); + $srcH = (int) round($srcH); + + $this->DebugMessage('ImageResizeFunction($o, $s, '.$dstX.', '.$dstY.', '.$srcX.', '.$srcY.', '.$dstW.', '.$dstH.', '.$srcW.', '.$srcH.')', __FILE__, __LINE__); + if (($dstW == $srcW) && ($dstH == $srcH)) { + return imagecopy($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH); + } + if (phpthumb_functions::gd_version() >= 2.0) { + if ($this->config_disable_imagecopyresampled) { + return phpthumb_functions::ImageCopyResampleBicubic($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH); + } + return imagecopyresampled($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH); + } + return imagecopyresized($dst_im, $src_im, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH); + } + + public function InitializeTempDirSetting() { + $this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe((function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : ''))); // sys_get_temp_dir added in PHP v5.2.1 + $this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(ini_get('upload_tmp_dir'))); + $this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(getenv('TMPDIR'))); + $this->config_temp_directory = ($this->config_temp_directory ? $this->config_temp_directory : $this->realPathSafe(getenv('TMP'))); + return true; + } + + public function phpThumb_tempnam() { + $this->InitializeTempDirSetting(); + $tempnam = $this->realPathSafe(tempnam($this->config_temp_directory, 'pThumb')); + $this->tempFilesToDelete[$tempnam] = $tempnam; + $this->DebugMessage('phpThumb_tempnam() returning "'.$tempnam.'"', __FILE__, __LINE__); + return $tempnam; + } + + public function DebugMessage($message, $file='', $line='') { + $this->debugmessages[] = $message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : ''); + return true; + } + + public function DebugTimingMessage($message, $file='', $line='', $timestamp=0) { + if (!$timestamp) { + $timestamp = array_sum(explode(' ', microtime())); + } + $this->debugtiming[number_format($timestamp, 6, '.', '')] = ': '.$message.($file ? ' in file "'.(basename($file) ? basename($file) : $file).'"' : '').($line ? ' on line '.$line : ''); + return true; + } + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.filters.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.filters.php new file mode 100644 index 0000000..a18b739 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.filters.php @@ -0,0 +1,1543 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpthumb.filters.php - image processing filter functions // +// /// +////////////////////////////////////////////////////////////// + +class phpthumb_filters { + + /** + * @var phpthumb + */ + + public $phpThumbObject = null; + + + public function DebugMessage($message, $file='', $line='') { + if (is_object($this->phpThumbObject)) { + return $this->phpThumbObject->DebugMessage($message, $file, $line); + } + return false; + } + + + public function ApplyMask(&$gdimg_mask, &$gdimg_image) { + if (phpthumb_functions::gd_version() < 2) { + $this->DebugMessage('Skipping ApplyMask() because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + return false; + } + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '4.3.2', '>=')) { + + $this->DebugMessage('Using alpha ApplyMask() technique', __FILE__, __LINE__); + if ($gdimg_mask_resized = phpthumb_functions::ImageCreateFunction(imagesx($gdimg_image), imagesy($gdimg_image))) { + + imagecopyresampled($gdimg_mask_resized, $gdimg_mask, 0, 0, 0, 0, imagesx($gdimg_image), imagesy($gdimg_image), imagesx($gdimg_mask), imagesy($gdimg_mask)); + if ($gdimg_mask_blendtemp = phpthumb_functions::ImageCreateFunction(imagesx($gdimg_image), imagesy($gdimg_image))) { + + $color_background = imagecolorallocate($gdimg_mask_blendtemp, 0, 0, 0); + imagefilledrectangle($gdimg_mask_blendtemp, 0, 0, imagesx($gdimg_mask_blendtemp), imagesy($gdimg_mask_blendtemp), $color_background); + imagealphablending($gdimg_mask_blendtemp, false); + imagesavealpha($gdimg_mask_blendtemp, true); + for ($x = 0, $xMax = imagesx($gdimg_image); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg_image); $y < $yMax; $y++) { + //$RealPixel = phpthumb_functions::GetPixelColor($gdimg_mask_blendtemp, $x, $y); + $RealPixel = phpthumb_functions::GetPixelColor($gdimg_image, $x, $y); + $MaskPixel = phpthumb_functions::GrayscalePixel(phpthumb_functions::GetPixelColor($gdimg_mask_resized, $x, $y)); + $MaskAlpha = 127 - (floor($MaskPixel['red'] / 2) * (1 - ($RealPixel['alpha'] / 127))); + $newcolor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_mask_blendtemp, $RealPixel['red'], $RealPixel['green'], $RealPixel['blue'], $MaskAlpha); + imagesetpixel($gdimg_mask_blendtemp, $x, $y, $newcolor); + } + } + imagealphablending($gdimg_image, false); + imagesavealpha($gdimg_image, true); + imagecopy($gdimg_image, $gdimg_mask_blendtemp, 0, 0, 0, 0, imagesx($gdimg_mask_blendtemp), imagesy($gdimg_mask_blendtemp)); + imagedestroy($gdimg_mask_blendtemp); + + } else { + $this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__); + } + imagedestroy($gdimg_mask_resized); + + } else { + $this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__); + } + + } else { + // alpha merging requires PHP v4.3.2+ + $this->DebugMessage('Skipping ApplyMask() technique because PHP is v"'. PHP_VERSION .'"', __FILE__, __LINE__); + } + return true; + } + + + public function Bevel(&$gdimg, $width, $hexcolor1, $hexcolor2) { + $width = ($width ? $width : 5); + $hexcolor1 = ($hexcolor1 ? $hexcolor1 : 'FFFFFF'); + $hexcolor2 = ($hexcolor2 ? $hexcolor2 : '000000'); + + imagealphablending($gdimg, true); + for ($i = 0; $i < $width; $i++) { + $alpha = round(($i / $width) * 127); + $color1 = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1, false, $alpha); + $color2 = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2, false, $alpha); + + imageline($gdimg, $i, $i + 1, $i, imagesy($gdimg) - $i - 1, $color1); // left + imageline($gdimg, $i, $i , imagesx($gdimg) - $i, $i , $color1); // top + imageline($gdimg, imagesx($gdimg) - $i, imagesy($gdimg) - $i - 1, imagesx($gdimg) - $i, $i + 1, $color2); // right + imageline($gdimg, imagesx($gdimg) - $i, imagesy($gdimg) - $i , $i, imagesy($gdimg) - $i , $color2); // bottom + } + return true; + } + + + public function Blur(&$gdimg, $radius=0.5) { + // Taken from Torstein Hønsi's phpUnsharpMask (see phpthumb.unsharp.php) + + $radius = round(max(0, min($radius, 50)) * 2); + if (!$radius) { + return false; + } + + $w = imagesx($gdimg); + $h = imagesy($gdimg); + if ($imgBlur = imagecreatetruecolor($w, $h)) { + // Gaussian blur matrix: + // 1 2 1 + // 2 4 2 + // 1 2 1 + + // Move copies of the image around one pixel at the time and merge them with weight + // according to the matrix. The same matrix is simply repeated for higher radii. + for ($i = 0; $i < $radius; $i++) { + imagecopy ($imgBlur, $gdimg, 0, 0, 1, 1, $w - 1, $h - 1); // up left + imagecopymerge($imgBlur, $gdimg, 1, 1, 0, 0, $w, $h, 50.00000); // down right + imagecopymerge($imgBlur, $gdimg, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left + imagecopymerge($imgBlur, $gdimg, 1, 0, 0, 1, $w, $h - 1, 25.00000); // up right + imagecopymerge($imgBlur, $gdimg, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left + imagecopymerge($imgBlur, $gdimg, 1, 0, 0, 0, $w, $h, 25.00000); // right + imagecopymerge($imgBlur, $gdimg, 0, 0, 0, 1, $w, $h - 1, 20.00000); // up + imagecopymerge($imgBlur, $gdimg, 0, 1, 0, 0, $w, $h, 16.666667); // down + imagecopymerge($imgBlur, $gdimg, 0, 0, 0, 0, $w, $h, 50.000000); // center + imagecopy ($gdimg, $imgBlur, 0, 0, 0, 0, $w, $h); + } + return true; + } + return false; + } + + + public function BlurGaussian(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_GAUSSIAN_BLUR)', __FILE__, __LINE__); + // fall through and try it the hard way + } + $this->DebugMessage('FAILED: phpthumb_filters::BlurGaussian($gdimg) [using phpthumb_filters::Blur() instead]', __FILE__, __LINE__); + return $this->Blur($gdimg, 0.5); + } + + + public function BlurSelective(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_SELECTIVE_BLUR)', __FILE__, __LINE__); + // fall through and try it the hard way + } + // currently not implemented "the hard way" + $this->DebugMessage('FAILED: phpthumb_filters::BlurSelective($gdimg) [function not implemented]', __FILE__, __LINE__); + return false; + } + + + public function Brightness(&$gdimg, $amount=0) { + if ($amount == 0) { + return true; + } + $amount = max(-255, min(255, $amount)); + + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_BRIGHTNESS, $amount)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_BRIGHTNESS, '.$amount.')', __FILE__, __LINE__); + // fall through and try it the hard way + } + + $scaling = (255 - abs($amount)) / 255; + $baseamount = (($amount > 0) ? $amount : 0); + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $NewPixel = array(); + foreach ($OriginalPixel as $key => $value) { + $NewPixel[$key] = round($baseamount + ($OriginalPixel[$key] * $scaling)); + } + $newColor = imagecolorallocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function Contrast(&$gdimg, $amount=0) { + if ($amount == 0) { + return true; + } + $amount = max(-255, min(255, $amount)); + + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + // imagefilter(IMG_FILTER_CONTRAST) has range +100 to -100 (positive numbers make it darker!) + $amount = ($amount / 255) * -100; + if (imagefilter($gdimg, IMG_FILTER_CONTRAST, $amount)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_CONTRAST, '.$amount.')', __FILE__, __LINE__); + // fall through and try it the hard way + } + + if ($amount > 0) { + $scaling = 1 + ($amount / 255); + } else { + $scaling = (255 - abs($amount)) / 255; + } + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $NewPixel = array(); + foreach ($OriginalPixel as $key => $value) { + $NewPixel[$key] = min(255, max(0, round($OriginalPixel[$key] * $scaling))); + } + $newColor = imagecolorallocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function Colorize(&$gdimg, $amount, $targetColor) { + $amount = (is_numeric($amount) ? $amount : 25); + $amountPct = $amount / 100; + $targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'gray'); + + if ($amount == 0) { + return true; + } + + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if ($targetColor == 'gray') { + $targetColor = '808080'; + } + $r = round($amountPct * hexdec(substr($targetColor, 0, 2))); + $g = round($amountPct * hexdec(substr($targetColor, 2, 2))); + $b = round($amountPct * hexdec(substr($targetColor, 4, 2))); + if (imagefilter($gdimg, IMG_FILTER_COLORIZE, $r, $g, $b)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_COLORIZE)', __FILE__, __LINE__); + // fall through and try it the hard way + } + + // overridden below for grayscale + $TargetPixel = array(); + if ($targetColor != 'gray') { + $TargetPixel['red'] = hexdec(substr($targetColor, 0, 2)); + $TargetPixel['green'] = hexdec(substr($targetColor, 2, 2)); + $TargetPixel['blue'] = hexdec(substr($targetColor, 4, 2)); + } + + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + if ($targetColor == 'gray') { + $TargetPixel = phpthumb_functions::GrayscalePixel($OriginalPixel); + } + $NewPixel = array(); + foreach ($TargetPixel as $key => $value) { + $NewPixel[$key] = round(max(0, min(255, ($OriginalPixel[$key] * ((100 - $amount) / 100)) + ($TargetPixel[$key] * $amountPct)))); + } + //$newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']); + $newColor = imagecolorallocate($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function Crop(&$gdimg, $left=0, $right=0, $top=0, $bottom=0) { + if (!$left && !$right && !$top && !$bottom) { + return true; + } + $oldW = imagesx($gdimg); + $oldH = imagesy($gdimg); + if (($left > 0) && ($left < 1)) { $left = round($left * $oldW); } + if (($right > 0) && ($right < 1)) { $right = round($right * $oldW); } + if (($top > 0) && ($top < 1)) { $top = round($top * $oldH); } + if (($bottom > 0) && ($bottom < 1)) { $bottom = round($bottom * $oldH); } + $right = min($oldW - $left - 1, $right); + $bottom = min($oldH - $top - 1, $bottom); + $newW = $oldW - $left - $right; + $newH = $oldH - $top - $bottom; + + if ($imgCropped = imagecreatetruecolor($newW, $newH)) { + imagecopy($imgCropped, $gdimg, 0, 0, $left, $top, $newW, $newH); + if ($gdimg = imagecreatetruecolor($newW, $newH)) { + imagecopy($gdimg, $imgCropped, 0, 0, 0, 0, $newW, $newH); + imagedestroy($imgCropped); + return true; + } + imagedestroy($imgCropped); + } + return false; + } + + + public function Desaturate(&$gdimg, $amount, $color='') { + if ($amount == 0) { + return true; + } + return $this->Colorize($gdimg, $amount, (phpthumb_functions::IsHexColor($color) ? $color : 'gray')); + } + + + public function DropShadow(&$gdimg, $distance, $width, $hexcolor, $angle, $alpha) { + if (phpthumb_functions::gd_version() < 2) { + return false; + } + $distance = ($distance ? $distance : 10); + $width = ($width ? $width : 10); + $hexcolor = ($hexcolor ? $hexcolor : '000000'); + $angle = ($angle ? $angle : 225) % 360; + $alpha = max(0, min(100, ($alpha ? $alpha : 100))); + + if ($alpha <= 0) { + // invisible shadow, nothing to do + return true; + } + if ($distance <= 0) { + // shadow completely obscured by source image, nothing to do + return true; + } + + //$width_shadow = cos(deg2rad($angle)) * ($distance + $width); + //$height_shadow = sin(deg2rad($angle)) * ($distance + $width); + //$scaling = min(imagesx($gdimg) / (imagesx($gdimg) + abs($width_shadow)), imagesy($gdimg) / (imagesy($gdimg) + abs($height_shadow))); + + $Offset = array(); + for ($i = 0; $i < $width; $i++) { + $WidthAlpha[$i] = (abs(($width / 2) - $i) / $width); + $Offset['x'] = cos(deg2rad($angle)) * ($distance + $i); + $Offset['y'] = sin(deg2rad($angle)) * ($distance + $i); + } + + $tempImageWidth = imagesx($gdimg) + abs($Offset['x']); + $tempImageHeight = imagesy($gdimg) + abs($Offset['y']); + + if ($gdimg_dropshadow_temp = phpthumb_functions::ImageCreateFunction($tempImageWidth, $tempImageHeight)) { + + imagealphablending($gdimg_dropshadow_temp, false); + imagesavealpha($gdimg_dropshadow_temp, true); + $transparent1 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, 0, 0, 0, 127); + imagefill($gdimg_dropshadow_temp, 0, 0, $transparent1); + + $PixelMap = array(); + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + $PixelMap[$x][$y] = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + } + } + for ($x = 0; $x < $tempImageWidth; $x++) { + for ($y = 0; $y < $tempImageHeight; $y++) { + //for ($i = 0; $i < $width; $i++) { + for ($i = 0; $i < 1; $i++) { + if (!isset($PixelMap[$x][$y]['alpha']) || ($PixelMap[$x][$y]['alpha'] > 0)) { + if (isset($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']) && ($PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha'] < 127)) { + $thisColor = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor, false, $PixelMap[$x + $Offset['x']][$y + $Offset['y']]['alpha']); + imagesetpixel($gdimg_dropshadow_temp, $x, $y, $thisColor); + } + } + } + } + } + + imagealphablending($gdimg_dropshadow_temp, true); + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + if ($PixelMap[$x][$y]['alpha'] < 127) { + $thisColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_dropshadow_temp, $PixelMap[$x][$y]['red'], $PixelMap[$x][$y]['green'], $PixelMap[$x][$y]['blue'], $PixelMap[$x][$y]['alpha']); + imagesetpixel($gdimg_dropshadow_temp, $x, $y, $thisColor); + } + } + } + + imagesavealpha($gdimg, true); + imagealphablending($gdimg, false); + //$this->is_alpha = true; + $transparent2 = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0, 0, 0, 127); + imagefilledrectangle($gdimg, 0, 0, imagesx($gdimg), imagesy($gdimg), $transparent2); + imagecopyresampled($gdimg, $gdimg_dropshadow_temp, 0, 0, 0, 0, imagesx($gdimg), imagesy($gdimg), imagesx($gdimg_dropshadow_temp), imagesy($gdimg_dropshadow_temp)); + + imagedestroy($gdimg_dropshadow_temp); + } + return true; + } + + + public function EdgeDetect(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_EDGEDETECT)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_EDGEDETECT)', __FILE__, __LINE__); + // fall through and try it the hard way + } + // currently not implemented "the hard way" + $this->DebugMessage('FAILED: phpthumb_filters::EdgeDetect($gdimg) [function not implemented]', __FILE__, __LINE__); + return false; + } + + + public function Ellipse($gdimg) { + if (phpthumb_functions::gd_version() < 2) { + return false; + } + // generate mask at twice desired resolution and downsample afterwards for easy antialiasing + if ($gdimg_ellipsemask_double = phpthumb_functions::ImageCreateFunction(imagesx($gdimg) * 2, imagesy($gdimg) * 2)) { + if ($gdimg_ellipsemask = phpthumb_functions::ImageCreateFunction(imagesx($gdimg), imagesy($gdimg))) { + + $color_transparent = imagecolorallocate($gdimg_ellipsemask_double, 255, 255, 255); + imagefilledellipse($gdimg_ellipsemask_double, imagesx($gdimg), imagesy($gdimg), (imagesx($gdimg) - 1) * 2, (imagesy($gdimg) - 1) * 2, $color_transparent); + imagecopyresampled($gdimg_ellipsemask, $gdimg_ellipsemask_double, 0, 0, 0, 0, imagesx($gdimg), imagesy($gdimg), imagesx($gdimg) * 2, imagesy($gdimg) * 2); + + $this->ApplyMask($gdimg_ellipsemask, $gdimg); + imagedestroy($gdimg_ellipsemask); + return true; + + } else { + $this->DebugMessage('$gdimg_ellipsemask = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__); + } + imagedestroy($gdimg_ellipsemask_double); + } else { + $this->DebugMessage('$gdimg_ellipsemask_double = phpthumb_functions::ImageCreateFunction() failed', __FILE__, __LINE__); + } + return false; + } + + + public function Emboss(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_EMBOSS)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_EMBOSS)', __FILE__, __LINE__); + // fall through and try it the hard way + } + // currently not implemented "the hard way" + $this->DebugMessage('FAILED: phpthumb_filters::Emboss($gdimg) [function not implemented]', __FILE__, __LINE__); + return false; + } + + + public function Flip(&$gdimg, $x=false, $y=false) { + if (!$x && !$y) { + return false; + } + if ($tempImage = phpthumb_functions::ImageCreateFunction(imagesx($gdimg), imagesy($gdimg))) { + if ($x) { + imagecopy($tempImage, $gdimg, 0, 0, 0, 0, imagesx($gdimg), imagesy($gdimg)); + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + imagecopy($gdimg, $tempImage, imagesx($gdimg) - 1 - $x, 0, $x, 0, 1, imagesy($gdimg)); + } + } + if ($y) { + imagecopy($tempImage, $gdimg, 0, 0, 0, 0, imagesx($gdimg), imagesy($gdimg)); + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + imagecopy($gdimg, $tempImage, 0, imagesy($gdimg) - 1 - $y, 0, $y, imagesx($gdimg), 1); + } + } + imagedestroy($tempImage); + } + return true; + } + + + public function Frame(&$gdimg, $frame_width, $edge_width, $hexcolor_frame, $hexcolor1, $hexcolor2) { + $frame_width = ($frame_width ? $frame_width : 5); + $edge_width = ($edge_width ? $edge_width : 1); + $hexcolor_frame = ($hexcolor_frame ? $hexcolor_frame : 'CCCCCC'); + $hexcolor1 = ($hexcolor1 ? $hexcolor1 : 'FFFFFF'); + $hexcolor2 = ($hexcolor2 ? $hexcolor2 : '000000'); + + $color_frame = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor_frame); + $color1 = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor1); + $color2 = phpthumb_functions::ImageHexColorAllocate($gdimg, $hexcolor2); + for ($i = 0; $i < $edge_width; $i++) { + // outer bevel + imageline($gdimg, $i, $i, $i, imagesy($gdimg) - $i, $color1); // left + imageline($gdimg, $i, $i, imagesx($gdimg) - $i, $i, $color1); // top + imageline($gdimg, imagesx($gdimg) - $i, imagesy($gdimg) - $i, imagesx($gdimg) - $i, $i, $color2); // right + imageline($gdimg, imagesx($gdimg) - $i, imagesy($gdimg) - $i, $i, imagesy($gdimg) - $i, $color2); // bottom + } + for ($i = 0; $i < $frame_width; $i++) { + // actual frame + imagerectangle($gdimg, $edge_width + $i, $edge_width + $i, imagesx($gdimg) - $edge_width - $i, imagesy($gdimg) - $edge_width - $i, $color_frame); + } + for ($i = 0; $i < $edge_width; $i++) { + // inner bevel + imageline($gdimg, $frame_width + $edge_width + $i, $frame_width + $edge_width + $i, $frame_width + $edge_width + $i, imagesy($gdimg) - $frame_width - $edge_width - $i, $color2); // left + imageline($gdimg, $frame_width + $edge_width + $i, $frame_width + $edge_width + $i, imagesx($gdimg) - $frame_width - $edge_width - $i, $frame_width + $edge_width + $i, $color2); // top + imageline($gdimg, imagesx($gdimg) - $frame_width - $edge_width - $i, imagesy($gdimg) - $frame_width - $edge_width - $i, imagesx($gdimg) - $frame_width - $edge_width - $i, $frame_width + $edge_width + $i, $color1); // right + imageline($gdimg, imagesx($gdimg) - $frame_width - $edge_width - $i, imagesy($gdimg) - $frame_width - $edge_width - $i, $frame_width + $edge_width + $i, imagesy($gdimg) - $frame_width - $edge_width - $i, $color1); // bottom + } + return true; + } + + + public function Gamma(&$gdimg, $amount) { + if (number_format($amount, 4) == '1.0000') { + return true; + } + return imagegammacorrect($gdimg, 1.0, $amount); + } + + + public function Grayscale(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_GRAYSCALE)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_GRAYSCALE)', __FILE__, __LINE__); + // fall through and try it the hard way + } + return $this->Colorize($gdimg, 100, 'gray'); + } + + + public function HistogramAnalysis(&$gdimg, $calculateGray=false) { + $ImageSX = imagesx($gdimg); + $ImageSY = imagesy($gdimg); + $Analysis = array(); + for ($x = 0; $x < $ImageSX; $x++) { + for ($y = 0; $y < $ImageSY; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + @$Analysis['red'][$OriginalPixel['red']]++; + @$Analysis['green'][$OriginalPixel['green']]++; + @$Analysis['blue'][$OriginalPixel['blue']]++; + @$Analysis['alpha'][$OriginalPixel['alpha']]++; + if ($calculateGray) { + $GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel); + @$Analysis['gray'][$GrayPixel['red']]++; + } + } + } + $keys = array('red', 'green', 'blue', 'alpha'); + if ($calculateGray) { + $keys[] = 'gray'; + } + foreach ($keys as $dummy => $key) { + ksort($Analysis[$key]); + } + return $Analysis; + } + + + public function HistogramStretch(&$gdimg, $band='*', $method=0, $threshold=0.1) { + // equivalent of "Auto Contrast" in Adobe Photoshop + // method 0 stretches according to RGB colors. Gives a more conservative stretch. + // method 1 band stretches according to grayscale which is color-biased (59% green, 30% red, 11% blue). May give a punchier / more aggressive stretch, possibly appearing over-saturated + $Analysis = $this->HistogramAnalysis($gdimg, true); + $keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=> ($method == 0) ? 'all' : 'gray' ); + $band = $band[ 0 ]; + if (!isset($keys[$band])) { + return false; + } + $key = $keys[$band]; + + // If the absolute brightest and darkest pixels are used then one random + // pixel in the image could throw off the whole system. Instead, count up/down + // from the limit and allow (default = 0.1%) of brightest/darkest + // pixels to be clipped to min/max + $threshold = (float) $threshold / 100; + $clip_threshold = imagesx($gdimg) * imagesx($gdimg) * $threshold; + + $countsum = 0; + $range_min = 0; + for ($i = 0; $i <= 255; $i++) { + if ($method == 0) { + $countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]); + } else { + $countsum += @$Analysis[$key][$i]; + } + if ($countsum >= $clip_threshold) { + $range_min = $i - 1; + break; + } + } + $range_min = max($range_min, 0); + + $countsum = 0; + $range_max = 255; + for ($i = 255; $i >= 0; $i--) { + if ($method == 0) { + $countsum = max(@$Analysis['red'][$i], @$Analysis['green'][$i], @$Analysis['blue'][$i]); + } else { + $countsum += @$Analysis[$key][$i]; + } + if ($countsum >= $clip_threshold) { + $range_max = $i + 1; + break; + } + } + $range_max = min($range_max, 255); + + $range_scale = (($range_max == $range_min) ? 1 : (255 / ($range_max - $range_min))); + if (($range_min == 0) && ($range_max == 255)) { + // no adjustment necessary - don't waste CPU time! + return true; + } + + $ImageSX = imagesx($gdimg); + $ImageSY = imagesy($gdimg); + for ($x = 0; $x < $ImageSX; $x++) { + for ($y = 0; $y < $ImageSY; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + if ($band == '*') { + $new['red'] = min(255, max(0, ($OriginalPixel['red'] - $range_min) * $range_scale)); + $new['green'] = min(255, max(0, ($OriginalPixel['green'] - $range_min) * $range_scale)); + $new['blue'] = min(255, max(0, ($OriginalPixel['blue'] - $range_min) * $range_scale)); + $new['alpha'] = min(255, max(0, ($OriginalPixel['alpha'] - $range_min) * $range_scale)); + } else { + $new = $OriginalPixel; + $new[$key] = min(255, max(0, ($OriginalPixel[$key] - $range_min) * $range_scale)); + } + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $new['red'], $new['green'], $new['blue'], $new['alpha']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + + return true; + } + + + public function HistogramOverlay(&$gdimg, $bands='*', $colors='', $width=0.25, $height=0.25, $alignment='BR', $opacity=50, $margin_x=5, $margin_y=null) { + $margin_y = (null === $margin_y ? $margin_x : $margin_y); + + $Analysis = $this->HistogramAnalysis($gdimg, true); + $histW = round(($width > 1) ? min($width, imagesx($gdimg)) : imagesx($gdimg) * $width); + $histH = round(($width > 1) ? min($width, imagesx($gdimg)) : imagesx($gdimg) * $width); + if ($gdHist = imagecreatetruecolor($histW, $histH)) { + $color_back = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHist, 0, 0, 0, 127); + imagefilledrectangle($gdHist, 0, 0, $histW, $histH, $color_back); + imagealphablending($gdHist, false); + imagesavealpha($gdHist, true); + + $HistogramTempWidth = 256; + $HistogramTempHeight = 100; + if ($gdHistTemp = imagecreatetruecolor($HistogramTempWidth, $HistogramTempHeight)) { + $color_back_temp = phpthumb_functions::ImageColorAllocateAlphaSafe($gdHistTemp, 255, 0, 255, 127); + imagealphablending($gdHistTemp, false); + imagesavealpha($gdHistTemp, true); + imagefilledrectangle($gdHistTemp, 0, 0, imagesx($gdHistTemp), imagesy($gdHistTemp), $color_back_temp); + + $DefaultColors = array('r'=>'FF0000', 'g'=>'00FF00', 'b'=>'0000FF', 'a'=>'999999', '*'=>'FFFFFF'); + $Colors = explode(';', $colors); + $BandsToGraph = array_unique(preg_split('##', $bands)); + $keys = array('r'=>'red', 'g'=>'green', 'b'=>'blue', 'a'=>'alpha', '*'=>'gray'); + foreach ($BandsToGraph as $key => $band) { + if (!isset($keys[$band])) { + continue; + } + $PeakValue = max($Analysis[$keys[$band]]); + $thisColor = phpthumb_functions::ImageHexColorAllocate($gdHistTemp, phpthumb_functions::IsHexColor(@$Colors[$key]) ? $Colors[$key] : $DefaultColors[$band]); + for ($x = 0; $x < $HistogramTempWidth; $x++) { + imageline($gdHistTemp, $x, $HistogramTempHeight - 1, $x, $HistogramTempHeight - 1 - round(@$Analysis[$keys[$band]][$x] / $PeakValue * $HistogramTempHeight), $thisColor); + } + imageline($gdHistTemp, 0, $HistogramTempHeight - 1, $HistogramTempWidth - 1, $HistogramTempHeight - 1, $thisColor); + imageline($gdHistTemp, 0, $HistogramTempHeight - 2, $HistogramTempWidth - 1, $HistogramTempHeight - 2, $thisColor); + } + imagecopyresampled($gdHist, $gdHistTemp, 0, 0, 0, 0, imagesx($gdHist), imagesy($gdHist), imagesx($gdHistTemp), imagesy($gdHistTemp)); + imagedestroy($gdHistTemp); + } else { + return false; + } + + $this->WatermarkOverlay($gdimg, $gdHist, $alignment, $opacity, $margin_x, $margin_y); + imagedestroy($gdHist); + return true; + } + return false; + } + + + public function ImageBorder(&$gdimg, $border_width, $radius_x, $radius_y, $hexcolor_border) { + $border_width = ($border_width ? $border_width : 1); + $radius_x = ($radius_x ? $radius_x : 0); + $radius_y = ($radius_y ? $radius_y : 0); + + $output_width = imagesx($gdimg); + $output_height = imagesy($gdimg); + + list($new_width, $new_height) = phpthumb_functions::ProportionalResize($output_width, $output_height, $output_width - max($border_width * 2, $radius_x), $output_height - max($border_width * 2, $radius_y)); + $offset_x = ($radius_x ? $output_width - $new_width - $radius_x : 0); + + if ($gd_border_canvas = phpthumb_functions::ImageCreateFunction($output_width, $output_height)) { + + imagesavealpha($gd_border_canvas, true); + imagealphablending($gd_border_canvas, false); + $color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gd_border_canvas, 255, 255, 255, 127); + imagefilledrectangle($gd_border_canvas, 0, 0, $output_width, $output_height, $color_background); + + $color_border = phpthumb_functions::ImageHexColorAllocate($gd_border_canvas, (phpthumb_functions::IsHexColor($hexcolor_border) ? $hexcolor_border : '000000')); + + for ($i = 0; $i < $border_width; $i++) { + imageline($gd_border_canvas, floor($offset_x / 2) + $radius_x, $i, $output_width - $radius_x - ceil($offset_x / 2), $i, $color_border); // top + imageline($gd_border_canvas, floor($offset_x / 2) + $radius_x, $output_height - 1 - $i, $output_width - $radius_x - ceil($offset_x / 2), $output_height - 1 - $i, $color_border); // bottom + imageline($gd_border_canvas, floor($offset_x / 2) + $i, $radius_y, floor($offset_x / 2) + $i, $output_height - $radius_y, $color_border); // left + imageline($gd_border_canvas, $output_width - 1 - $i - ceil($offset_x / 2), $radius_y, $output_width - 1 - $i - ceil($offset_x / 2), $output_height - $radius_y, $color_border); // right + } + + if ($radius_x && $radius_y) { + + // PHP bug: imagearc() with thicknesses > 1 give bad/undesirable/unpredicatable results + // Solution: Draw multiple 1px arcs side-by-side. + + // Problem: parallel arcs give strange/ugly antialiasing problems + // Solution: draw non-parallel arcs, from one side of the line thickness at the start angle + // to the opposite edge of the line thickness at the terminating angle + for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) { + imagearc($gd_border_canvas, floor($offset_x / 2) + 1 + $radius_x, $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left + imagearc($gd_border_canvas, $output_width - $radius_x - 1 - ceil($offset_x / 2), $thickness_offset - 1 + $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right + imagearc($gd_border_canvas, $output_width - $radius_x - 1 - ceil($offset_x / 2), $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2, 0, 90, $color_border); // bottom-right + imagearc($gd_border_canvas, floor($offset_x / 2) + 1 + $radius_x, $output_height - $thickness_offset - $radius_y, $radius_x * 2, $radius_y * 2, 90, 180, $color_border); // bottom-left + } + if ($border_width > 1) { + for ($thickness_offset = 0; $thickness_offset < $border_width; $thickness_offset++) { + imagearc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x, $radius_y, $radius_x * 2, $radius_y * 2, 180, 270, $color_border); // top-left + imagearc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2), $radius_y, $radius_x * 2, $radius_y * 2, 270, 360, $color_border); // top-right + imagearc($gd_border_canvas, $output_width - $thickness_offset - $radius_x - 1 - ceil($offset_x / 2), $output_height - $radius_y, $radius_x * 2, $radius_y * 2, 0, 90, $color_border); // bottom-right + imagearc($gd_border_canvas, floor($offset_x / 2) + $thickness_offset + $radius_x, $output_height - $radius_y, $radius_x * 2, $radius_y * 2, 90, 180, $color_border); // bottom-left + } + } + + } + $this->phpThumbObject->ImageResizeFunction($gd_border_canvas, $gdimg, floor(($output_width - $new_width) / 2), round(($output_height - $new_height) / 2), 0, 0, $new_width, $new_height, $output_width, $output_height); + + imagedestroy($gdimg); + $gdimg = phpthumb_functions::ImageCreateFunction($output_width, $output_height); + imagesavealpha($gdimg, true); + imagealphablending($gdimg, false); + $gdimg_color_background = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 255, 255, 255, 127); + imagefilledrectangle($gdimg, 0, 0, $output_width, $output_height, $gdimg_color_background); + + imagecopy($gdimg, $gd_border_canvas, 0, 0, 0, 0, $output_width, $output_height); + imagedestroy($gd_border_canvas); + return true; + + + } else { + $this->DebugMessage('FAILED: $gd_border_canvas = phpthumb_functions::ImageCreateFunction('.$output_width.', '.$output_height.')', __FILE__, __LINE__); + } + return false; + } + + + public static function ImprovedImageRotate(&$gdimg_source, $rotate_angle, $config_background_hexcolor, $bg, &$phpThumbObject) { + while ($rotate_angle < 0) { + $rotate_angle += 360; + } + $rotate_angle %= 360; + if ($rotate_angle != 0) { + + $background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_source, $config_background_hexcolor); + + if ((phpthumb_functions::gd_version() >= 2) && !$bg && ($rotate_angle % 90)) { + + //$this->DebugMessage('Using alpha rotate', __FILE__, __LINE__); + if ($gdimg_rotate_mask = phpthumb_functions::ImageCreateFunction(imagesx($gdimg_source), imagesy($gdimg_source))) { + + $color_mask = array(); + for ($i = 0; $i <= 255; $i++) { + $color_mask[$i] = imagecolorallocate($gdimg_rotate_mask, $i, $i, $i); + } + imagefilledrectangle($gdimg_rotate_mask, 0, 0, imagesx($gdimg_rotate_mask), imagesy($gdimg_rotate_mask), $color_mask[255]); + $imageX = imagesx($gdimg_source); + $imageY = imagesy($gdimg_source); + for ($x = 0; $x < $imageX; $x++) { + for ($y = 0; $y < $imageY; $y++) { + $pixelcolor = phpthumb_functions::GetPixelColor($gdimg_source, $x, $y); + imagesetpixel($gdimg_rotate_mask, $x, $y, $color_mask[255 - round($pixelcolor['alpha'] * 255 / 127)]); + } + } + $gdimg_rotate_mask = imagerotate($gdimg_rotate_mask, $rotate_angle, $color_mask[0]); + $gdimg_source = imagerotate($gdimg_source, $rotate_angle, $background_color); + + imagealphablending($gdimg_source, false); + imagesavealpha($gdimg_source, true); + //$this->is_alpha = true; + $phpThumbFilters = new self(); + //$phpThumbFilters->phpThumbObject = $this; + $phpThumbFilters->phpThumbObject = $phpThumbObject; + $phpThumbFilters->ApplyMask($gdimg_rotate_mask, $gdimg_source); + + imagedestroy($gdimg_rotate_mask); + + } else { + //$this->DebugMessage('ImageCreateFunction() failed', __FILE__, __LINE__); + } + + } else { + + if (phpthumb_functions::gd_version() < 2) { + //$this->DebugMessage('Using non-alpha rotate because gd_version is "'.phpthumb_functions::gd_version().'"', __FILE__, __LINE__); + } elseif ($bg) { + //$this->DebugMessage('Using non-alpha rotate because $this->bg is "'.$bg.'"', __FILE__, __LINE__); + } elseif ($rotate_angle % 90) { + //$this->DebugMessage('Using non-alpha rotate because ($rotate_angle % 90) = "'.($rotate_angle % 90).'"', __FILE__, __LINE__); + } else { + //$this->DebugMessage('Using non-alpha rotate because $this->thumbnailFormat is "'.$this->thumbnailFormat.'"', __FILE__, __LINE__); + } + + if (imagecolortransparent($gdimg_source) >= 0) { + // imagerotate() forgets all about an image's transparency and sets the transparent color to black + // To compensate, flood-fill the transparent color of the source image with the specified background color first + // then rotate and the colors should match + + if (!function_exists('imageistruecolor') || !imageistruecolor($gdimg_source)) { + // convert paletted image to true-color before rotating to prevent nasty aliasing artifacts + + //$this->source_width = imagesx($gdimg_source); + //$this->source_height = imagesy($gdimg_source); + $gdimg_newsrc = phpthumb_functions::ImageCreateFunction(imagesx($gdimg_source), imagesy($gdimg_source)); + $background_color = phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor); + imagefilledrectangle($gdimg_newsrc, 0, 0, imagesx($gdimg_source), imagesy($gdimg_source), phpthumb_functions::ImageHexColorAllocate($gdimg_newsrc, $config_background_hexcolor)); + imagecopy($gdimg_newsrc, $gdimg_source, 0, 0, 0, 0, imagesx($gdimg_source), imagesy($gdimg_source)); + imagedestroy($gdimg_source); + unset($gdimg_source); + $gdimg_source = $gdimg_newsrc; + unset($gdimg_newsrc); + + } else { + + imagecolorset( + $gdimg_source, + imagecolortransparent($gdimg_source), + hexdec(substr($config_background_hexcolor, 0, 2)), + hexdec(substr($config_background_hexcolor, 2, 2)), + hexdec(substr($config_background_hexcolor, 4, 2))); + + imagecolortransparent($gdimg_source, -1); + + } + } + + $gdimg_source = imagerotate($gdimg_source, $rotate_angle, $background_color); + + } + } + return true; + } + + + public function MeanRemoval(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_MEAN_REMOVAL)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_MEAN_REMOVAL)', __FILE__, __LINE__); + // fall through and try it the hard way + } + // currently not implemented "the hard way" + $this->DebugMessage('FAILED: phpthumb_filters::MeanRemoval($gdimg) [function not implemented]', __FILE__, __LINE__); + return false; + } + + + public function Negative(&$gdimg) { + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_NEGATE)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_NEGATE)', __FILE__, __LINE__); + // fall through and try it the hard way + } + $ImageSX = imagesx($gdimg); + $ImageSY = imagesy($gdimg); + for ($x = 0; $x < $ImageSX; $x++) { + for ($y = 0; $y < $ImageSY; $y++) { + $currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, ~$currentPixel[ 'red'] & 0xFF, ~$currentPixel[ 'green'] & 0xFF, ~$currentPixel[ 'blue'] & 0xFF, $currentPixel[ 'alpha']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function RoundedImageCorners(&$gdimg, $radius_x, $radius_y) { + // generate mask at twice desired resolution and downsample afterwards for easy antialiasing + // mask is generated as a white double-size ellipse on a triple-size black background and copy-paste-resampled + // onto a correct-size mask image as 4 corners due to errors when the entire mask is resampled at once (gray edges) + if ($gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction($radius_x * 6, $radius_y * 6)) { + if ($gdimg_cornermask = phpthumb_functions::ImageCreateFunction(imagesx($gdimg), imagesy($gdimg))) { + + $color_transparent = imagecolorallocate($gdimg_cornermask_triple, 255, 255, 255); + imagefilledellipse($gdimg_cornermask_triple, $radius_x * 3, $radius_y * 3, $radius_x * 4, $radius_y * 4, $color_transparent); + + imagefilledrectangle($gdimg_cornermask, 0, 0, imagesx($gdimg), imagesy($gdimg), $color_transparent); + + imagecopyresampled($gdimg_cornermask, $gdimg_cornermask_triple, 0, 0, $radius_x, $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2); + imagecopyresampled($gdimg_cornermask, $gdimg_cornermask_triple, 0, imagesy($gdimg) - $radius_y, $radius_x, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2); + imagecopyresampled($gdimg_cornermask, $gdimg_cornermask_triple, imagesx($gdimg) - $radius_x, imagesy($gdimg) - $radius_y, $radius_x * 3, $radius_y * 3, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2); + imagecopyresampled($gdimg_cornermask, $gdimg_cornermask_triple, imagesx($gdimg) - $radius_x, 0, $radius_x * 3, $radius_y, $radius_x, $radius_y, $radius_x * 2, $radius_y * 2); + + $this->ApplyMask($gdimg_cornermask, $gdimg); + imagedestroy($gdimg_cornermask); + $this->DebugMessage('RoundedImageCorners('.$radius_x.', '.$radius_y.') succeeded', __FILE__, __LINE__); + return true; + + } else { + $this->DebugMessage('FAILED: $gdimg_cornermask = phpthumb_functions::ImageCreateFunction('.imagesx($gdimg).', '.imagesy($gdimg).')', __FILE__, __LINE__); + } + imagedestroy($gdimg_cornermask_triple); + + } else { + $this->DebugMessage('FAILED: $gdimg_cornermask_triple = phpthumb_functions::ImageCreateFunction('.($radius_x * 6).', '.($radius_y * 6).')', __FILE__, __LINE__); + } + return false; + } + + + public function Saturation(&$gdimg, $amount, $color='') { + if ($amount == 0) { + return true; + } elseif ($amount > 0) { + $amount = 0 - $amount; + } else { + $amount = abs($amount); + } + return $this->Desaturate($gdimg, $amount, $color); + } + + + public function Sepia(&$gdimg, $amount, $targetColor) { + $amount = (is_numeric($amount) ? max(0, min(100, $amount)) : 50); + $amountPct = $amount / 100; + $targetColor = (phpthumb_functions::IsHexColor($targetColor) ? $targetColor : 'A28065'); + + if ($amount == 0) { + return true; + } + + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_GRAYSCALE)) { + + $r = round($amountPct * hexdec(substr($targetColor, 0, 2))); + $g = round($amountPct * hexdec(substr($targetColor, 2, 2))); + $b = round($amountPct * hexdec(substr($targetColor, 4, 2))); + if (imagefilter($gdimg, IMG_FILTER_COLORIZE, $r, $g, $b)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_COLORIZE)', __FILE__, __LINE__); + // fall through and try it the hard way + + } else { + + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_GRAYSCALE)', __FILE__, __LINE__); + // fall through and try it the hard way + + } + } + + $TargetPixel['red'] = hexdec(substr($targetColor, 0, 2)); + $TargetPixel['green'] = hexdec(substr($targetColor, 2, 2)); + $TargetPixel['blue'] = hexdec(substr($targetColor, 4, 2)); + + $ImageSX = imagesx($gdimg); + $ImageSY = imagesy($gdimg); + for ($x = 0; $x < $ImageSX; $x++) { + for ($y = 0; $y < $ImageSY; $y++) { + $OriginalPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $GrayPixel = phpthumb_functions::GrayscalePixel($OriginalPixel); + + // http://www.gimpguru.org/Tutorials/SepiaToning/ + // "In the traditional sepia toning process, the tinting occurs most in + // the mid-tones: the lighter and darker areas appear to be closer to B&W." + $SepiaAmount = ((128 - abs($GrayPixel['red'] - 128)) / 128) * $amountPct; + + $NewPixel = array(); + foreach ($TargetPixel as $key => $value) { + $NewPixel[$key] = round(max(0, min(255, $GrayPixel[$key] * (1 - $SepiaAmount) + ($TargetPixel[$key] * $SepiaAmount)))); + } + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, $NewPixel['red'], $NewPixel['green'], $NewPixel['blue'], $OriginalPixel['alpha']); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function Smooth(&$gdimg, $amount=6) { + $amount = min(25, max(0, $amount)); + if ($amount == 0) { + return true; + } + if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.0.0', '>=') && phpthumb_functions::gd_is_bundled()) { + if (imagefilter($gdimg, IMG_FILTER_SMOOTH, $amount)) { + return true; + } + $this->DebugMessage('FAILED: imagefilter($gdimg, IMG_FILTER_SMOOTH, '.$amount.')', __FILE__, __LINE__); + // fall through and try it the hard way + } + // currently not implemented "the hard way" + $this->DebugMessage('FAILED: phpthumb_filters::Smooth($gdimg, '.$amount.') [function not implemented]', __FILE__, __LINE__); + return false; + } + + + public function SourceTransparentColorMask(&$gdimg, $hexcolor, $min_limit=5, $max_limit=10) { + $width = imagesx($gdimg); + $height = imagesy($gdimg); + if ($gdimg_mask = imagecreatetruecolor($width, $height)) { + $R = hexdec(substr($hexcolor, 0, 2)); + $G = hexdec(substr($hexcolor, 2, 2)); + $B = hexdec(substr($hexcolor, 4, 2)); + $targetPixel = array('red'=>$R, 'green'=>$G, 'blue'=>$B); + $cutoffRange = $max_limit - $min_limit; + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $colorDiff = phpthumb_functions::PixelColorDifferencePercent($currentPixel, $targetPixel); + $grayLevel = min($cutoffRange, max(0, -$min_limit + $colorDiff)) * (255 / max(1, $cutoffRange)); + $newColor = imagecolorallocate($gdimg_mask, $grayLevel, $grayLevel, $grayLevel); + imagesetpixel($gdimg_mask, $x, $y, $newColor); + } + } + return $gdimg_mask; + } + return false; + } + + + public function Threshold(&$gdimg, $cutoff) { + $width = imagesx($gdimg); + $height = imagesy($gdimg); + $cutoff = min(255, max(0, ($cutoff ? $cutoff : 128))); + for ($x = 0; $x < $width; $x++) { + for ($y = 0; $y < $height; $y++) { + $currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $grayPixel = phpthumb_functions::GrayscalePixel($currentPixel); + if ($grayPixel['red'] < $cutoff) { + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0x00, 0x00, 0x00, $currentPixel['alpha']); + } else { + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg, 0xFF, 0xFF, 0xFF, $currentPixel['alpha']); + } + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function ImageTrueColorToPalette2(&$image, $dither, $ncolors) { + // http://www.php.net/manual/en/function.imagetruecolortopalette.php + // zmorris at zsculpt dot com (17-Aug-2004 06:58) + $width = imagesx($image); + $height = imagesy($image); + $image_copy = imagecreatetruecolor($width, $height); + //imagecopymerge($image_copy, $image, 0, 0, 0, 0, $width, $height, 100); + imagecopy($image_copy, $image, 0, 0, 0, 0, $width, $height); + imagetruecolortopalette($image, $dither, $ncolors); + imagecolormatch($image_copy, $image); + imagedestroy($image_copy); + return true; + } + + public function ReduceColorDepth(&$gdimg, $colors=256, $dither=true) { + $colors = max(min($colors, 256), 2); + // imagetruecolortopalette usually makes ugly colors, the replacement is a bit better + //imagetruecolortopalette($gdimg, $dither, $colors); + $this->ImageTrueColorToPalette2($gdimg, $dither, $colors); + return true; + } + + + public function WhiteBalance(&$gdimg, $targetColor='') { + if (phpthumb_functions::IsHexColor($targetColor)) { + $targetPixel = array( + 'red' => hexdec(substr($targetColor, 0, 2)), + 'green' => hexdec(substr($targetColor, 2, 2)), + 'blue' => hexdec(substr($targetColor, 4, 2)) + ); + } else { + $Analysis = $this->HistogramAnalysis($gdimg, false); + $targetPixel = array( + 'red' => max(array_keys($Analysis['red'])), + 'green' => max(array_keys($Analysis['green'])), + 'blue' => max(array_keys($Analysis['blue'])) + ); + } + $grayValue = phpthumb_functions::GrayscaleValue($targetPixel['red'], $targetPixel['green'], $targetPixel['blue']); + $scaleR = $grayValue / $targetPixel['red']; + $scaleG = $grayValue / $targetPixel['green']; + $scaleB = $grayValue / $targetPixel['blue']; + + for ($x = 0, $xMax = imagesx($gdimg); $x < $xMax; $x++) { + for ($y = 0, $yMax = imagesy($gdimg); $y < $yMax; $y++) { + $currentPixel = phpthumb_functions::GetPixelColor($gdimg, $x, $y); + $newColor = phpthumb_functions::ImageColorAllocateAlphaSafe( + $gdimg, + max(0, min(255, round($currentPixel['red'] * $scaleR))), + max(0, min(255, round($currentPixel['green'] * $scaleG))), + max(0, min(255, round($currentPixel['blue'] * $scaleB))), + $currentPixel['alpha'] + ); + imagesetpixel($gdimg, $x, $y, $newColor); + } + } + return true; + } + + + public function WatermarkText(&$gdimg, $text, $size, $alignment, $hex_color='000000', $ttffont='', $opacity=100, $margin=5, $angle=0, $bg_color=false, $bg_opacity=0, $fillextend='', $lineheight=1.0) { + // text watermark requested + if (!$text) { + return false; + } + imagealphablending($gdimg, true); + + if (preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)(@[LCR])?$#i', $alignment, $matches)) { + $originOffsetX = (int) $matches[ 1]; + $originOffsetY = (int) $matches[ 2]; + $alignment = (@$matches[4] ? $matches[4] : 'L'); + $margin = 0; + } else { + $originOffsetX = 0; + $originOffsetY = 0; + } + $lineheight = min(100.0, max(0.01, (float) $lineheight)); + + $metaTextArray = array( + '^Fb' => $this->phpThumbObject->getimagesizeinfo['filesize'], + '^Fk' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1024), + '^Fm' => round($this->phpThumbObject->getimagesizeinfo['filesize'] / 1048576), + '^X' => $this->phpThumbObject->getimagesizeinfo[0], + '^Y' => $this->phpThumbObject->getimagesizeinfo[1], + '^x' => imagesx($gdimg), + '^y' => imagesy($gdimg), + '^^' => '^', + ); + $text = strtr($text, $metaTextArray); + + $text = str_replace(array( + "\r\n", + "\r" + ), "\n", $text); + $textlines = explode("\n", $text); + $this->DebugMessage('Processing '.count($textlines).' lines of text', __FILE__, __LINE__); + + if (@is_readable($ttffont) && is_file($ttffont)) { + + $opacity = 100 - (int) max(min($opacity, 100), 0); + $letter_color_text = phpthumb_functions::ImageHexColorAllocate($gdimg, $hex_color, false, $opacity * 1.27); + + $this->DebugMessage('Using TTF font "'.$ttffont.'"', __FILE__, __LINE__); + + $TTFbox = imagettfbbox($size, $angle, $ttffont, $text); + + $min_x = min($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]); + $max_x = max($TTFbox[0], $TTFbox[2], $TTFbox[4], $TTFbox[6]); + //$text_width = round($max_x - $min_x + ($size * 0.5)); + $text_width = round($max_x - $min_x); + + $min_y = min($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]); + $max_y = max($TTFbox[1], $TTFbox[3], $TTFbox[5], $TTFbox[7]); + //$text_height = round($max_y - $min_y + ($size * 0.5)); + $text_height = round($max_y - $min_y); + + $TTFboxChar = imagettfbbox($size, $angle, $ttffont, 'jH'); + $char_min_y = min($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]); + $char_max_y = max($TTFboxChar[1], $TTFboxChar[3], $TTFboxChar[5], $TTFboxChar[7]); + $char_height = round($char_max_y - $char_min_y); + + if ($alignment == '*') { + + $text_origin_y = $char_height + $margin; + while (($text_origin_y - $text_height) < imagesy($gdimg)) { + $text_origin_x = $margin; + while ($text_origin_x < imagesx($gdimg)) { + imagettftext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text); + $text_origin_x += ($text_width + $margin); + } + $text_origin_y += ($text_height + $margin) * $lineheight; + } + + } else { + + // this block for background color only + + $text_origin_x = 0; + $text_origin_y = 0; + switch ($alignment) { + case '*': + // handled separately + break; + + case 'T': + $text_origin_x = ($originOffsetX ? $originOffsetX - round($text_width / 2) : round((imagesx($gdimg) - $text_width) / 2)); + $text_origin_y = $char_height + $margin + $originOffsetY; + break; + + case 'B': + $text_origin_x = ($originOffsetX ? $originOffsetX - round($text_width / 2) : round((imagesx($gdimg) - $text_width) / 2)); + $text_origin_y = imagesy($gdimg) + $TTFbox[1] - $margin + $originOffsetY; + break; + + case 'L': + $text_origin_x = $margin + $originOffsetX; + $text_origin_y = ($originOffsetY ? $originOffsetY : round((imagesy($gdimg) - $text_height) / 2) + $char_height); + break; + + case 'R': + $text_origin_x = ($originOffsetX ? $originOffsetX - $text_width : imagesx($gdimg) - $text_width + $TTFbox[0] - $min_x + round($size * 0.25) - $margin); + $text_origin_y = ($originOffsetY ? $originOffsetY : round((imagesy($gdimg) - $text_height) / 2) + $char_height); + break; + + case 'C': + $text_origin_x = ($originOffsetX ? $originOffsetX - round($text_width / 2) : round((imagesx($gdimg) - $text_width) / 2)); + $text_origin_y = ($originOffsetY ? $originOffsetY : round((imagesy($gdimg) - $text_height) / 2) + $char_height); + break; + + case 'TL': + $text_origin_x = $margin + $originOffsetX; + $text_origin_y = $char_height + $margin + $originOffsetY; + break; + + case 'TR': + $text_origin_x = ($originOffsetX ? $originOffsetX - $text_width : imagesx($gdimg) - $text_width + $TTFbox[0] - $min_x + round($size * 0.25) - $margin); + $text_origin_y = $char_height + $margin + $originOffsetY; + break; + + case 'BL': + $text_origin_x = $margin + $originOffsetX; + $text_origin_y = imagesy($gdimg) + $TTFbox[1] - $margin + $originOffsetY; + break; + + case 'BR': + default: + $text_origin_x = ($originOffsetX ? $originOffsetX - $text_width : imagesx($gdimg) - $text_width + $TTFbox[0] - $min_x + round($size * 0.25) - $margin); + $text_origin_y = imagesy($gdimg) + $TTFbox[1] - $margin + $originOffsetY; + break; + } + + if (phpthumb_functions::IsHexColor($bg_color)) { + $text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100)); + $text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, $bg_color, false, $text_background_alpha); + } else { + $text_color_background = phpthumb_functions::ImageHexColorAllocate($gdimg, 'FFFFFF', false, 127); + } + $x1 = $text_origin_x + $min_x; + $y1 = $text_origin_y + $TTFbox[1]; + $x2 = $text_origin_x + $min_x + $text_width; + $y2 = $text_origin_y + $TTFbox[1] - $text_height; + $x_TL = false !== stripos($fillextend, 'x') ? 0 : min($x1, $x2); + $y_TL = false !== stripos($fillextend, 'y') ? 0 : min($y1, $y2); + $x_BR = false !== stripos($fillextend, 'x') ? imagesx($gdimg) : max($x1, $x2); + $y_BR = false !== stripos($fillextend, 'y') ? imagesy($gdimg) : max($y1, $y2); + $this->DebugMessage('WatermarkText() calling imagefilledrectangle($gdimg, '.$x_TL.', '.$y_TL.', '.$x_BR.', '.$y_BR.', $text_color_background)', __FILE__, __LINE__); + imagefilledrectangle($gdimg, $x_TL, $y_TL, $x_BR, $y_BR, $text_color_background); + + // end block for background color only + + + $y_offset = 0; + foreach ($textlines as $dummy => $line) { + + $TTFboxLine = imagettfbbox($size, $angle, $ttffont, $line); + $min_x_line = min($TTFboxLine[0], $TTFboxLine[2], $TTFboxLine[4], $TTFboxLine[6]); + $max_x_line = max($TTFboxLine[0], $TTFboxLine[2], $TTFboxLine[4], $TTFboxLine[6]); + $text_width_line = round($max_x_line - $min_x_line); + + switch ($alignment) { + // $text_origin_y set above, just re-set $text_origin_x here as needed + + case 'L': + case 'TL': + case 'BL': + // no change necessary + break; + + case 'C': + case 'T': + case 'B': + $text_origin_x = ($originOffsetX ? $originOffsetX - round($text_width_line / 2) : round((imagesx($gdimg) - $text_width_line) / 2)); + break; + + case 'R': + case 'TR': + case 'BR': + $text_origin_x = ($originOffsetX ? $originOffsetX - $text_width_line : imagesx($gdimg) - $text_width_line + $TTFbox[0] - $min_x + round($size * 0.25) - $margin); + break; + } + + //imagettftext($gdimg, $size, $angle, $text_origin_x, $text_origin_y, $letter_color_text, $ttffont, $text); + $this->DebugMessage('WatermarkText() calling imagettftext($gdimg, '.$size.', '.$angle.', '.$text_origin_x.', '.($text_origin_y + $y_offset).', $letter_color_text, '.$ttffont.', '.$line.')', __FILE__, __LINE__); + imagettftext($gdimg, $size, $angle, $text_origin_x, $text_origin_y + $y_offset, $letter_color_text, $ttffont, $line); + + $y_offset += $char_height * $lineheight; + } + + } + return true; + + } else { + + $size = min(5, max(1, $size)); + $this->DebugMessage('Using built-in font (size='.$size.') for text watermark'.($ttffont ? ' because $ttffont !is_readable('.$ttffont.')' : ''), __FILE__, __LINE__); + + $text_width = 0; + $text_height = 0; + foreach ($textlines as $dummy => $line) { + $text_width = max($text_width, imagefontwidth($size) * strlen($line)); + $text_height += imagefontheight($size); + } + if ($img_watermark = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) { + imagealphablending($img_watermark, false); + if (phpthumb_functions::IsHexColor($bg_color)) { + $text_background_alpha = round(127 * ((100 - min(max(0, $bg_opacity), 100)) / 100)); + $text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, $bg_color, false, $text_background_alpha); + } else { + $text_color_background = phpthumb_functions::ImageHexColorAllocate($img_watermark, 'FFFFFF', false, 127); + } + $this->DebugMessage('WatermarkText() calling imagefilledrectangle($img_watermark, 0, 0, '.imagesx($img_watermark).', '.imagesy($img_watermark).', $text_color_background)', __FILE__, __LINE__); + imagefilledrectangle($img_watermark, 0, 0, imagesx($img_watermark), imagesy($img_watermark), $text_color_background); + + $img_watermark_mask = false; + $mask_color_background = false; + $mask_color_watermark = false; + if ($angle && function_exists('imagerotate')) { + // using $img_watermark_mask is pointless if imagerotate function isn't available + if ($img_watermark_mask = phpthumb_functions::ImageCreateFunction($text_width, $text_height)) { + $mask_color_background = imagecolorallocate($img_watermark_mask, 0, 0, 0); + imagealphablending($img_watermark_mask, false); + imagefilledrectangle($img_watermark_mask, 0, 0, imagesx($img_watermark_mask), imagesy($img_watermark_mask), $mask_color_background); + $mask_color_watermark = imagecolorallocate($img_watermark_mask, 255, 255, 255); + } + } + + $text_color_watermark = phpthumb_functions::ImageHexColorAllocate($img_watermark, $hex_color); + $x_offset = 0; + foreach ($textlines as $key => $line) { + switch ($alignment) { + case 'C': + $x_offset = round(($text_width - (imagefontwidth($size) * strlen($line))) / 2); + $originOffsetX = (imagesx($gdimg) - imagesx($img_watermark)) / 2; + $originOffsetY = (imagesy($gdimg) - imagesy($img_watermark)) / 2; + break; + + case 'T': + $x_offset = round(($text_width - (imagefontwidth($size) * strlen($line))) / 2); + $originOffsetX = (imagesx($gdimg) - imagesx($img_watermark)) / 2; + $originOffsetY = $margin; + break; + + case 'B': + $x_offset = round(($text_width - (imagefontwidth($size) * strlen($line))) / 2); + $originOffsetX = (imagesx($gdimg) - imagesx($img_watermark)) / 2; + $originOffsetY = imagesy($gdimg) - imagesy($img_watermark) - $margin; + break; + + case 'L': + $x_offset = 0; + $originOffsetX = $margin; + $originOffsetY = (imagesy($gdimg) - imagesy($img_watermark)) / 2; + break; + + case 'TL': + $x_offset = 0; + $originOffsetX = $margin; + $originOffsetY = $margin; + break; + + case 'BL': + $x_offset = 0; + $originOffsetX = $margin; + $originOffsetY = imagesy($gdimg) - imagesy($img_watermark) - $margin; + break; + + case 'R': + $x_offset = $text_width - (imagefontwidth($size) * strlen($line)); + $originOffsetX = imagesx($gdimg) - imagesx($img_watermark) - $margin; + $originOffsetY = (imagesy($gdimg) - imagesy($img_watermark)) / 2; + break; + + case 'TR': + $x_offset = $text_width - (imagefontwidth($size) * strlen($line)); + $originOffsetX = imagesx($gdimg) - imagesx($img_watermark) - $margin; + $originOffsetY = $margin; + break; + + case 'BR': + default: + if (!empty($originOffsetX) || !empty($originOffsetY)) { + // absolute pixel positioning + } else { + $x_offset = $text_width - (imagefontwidth($size) * strlen($line)); + $originOffsetX = imagesx($gdimg) - imagesx($img_watermark) - $margin; + $originOffsetY = imagesy($gdimg) - imagesy($img_watermark) - $margin; + } + break; + } + $this->DebugMessage('WatermarkText() calling imagestring($img_watermark, '.$size.', '.$x_offset.', '.($key * imagefontheight($size)).', '.$line.', $text_color_watermark)', __FILE__, __LINE__); + imagestring($img_watermark, $size, $x_offset, $key * imagefontheight($size), $line, $text_color_watermark); + if ($angle && $img_watermark_mask) { + $this->DebugMessage('WatermarkText() calling imagestring($img_watermark_mask, '.$size.', '.$x_offset.', '.($key * imagefontheight($size) * $lineheight).', '.$text.', $mask_color_watermark)', __FILE__, __LINE__); + imagestring($img_watermark_mask, $size, $x_offset, $key * imagefontheight($size) * $lineheight, $text, $mask_color_watermark); + } + } + if ($angle && $img_watermark_mask) { + $img_watermark = imagerotate($img_watermark, $angle, $text_color_background); + $img_watermark_mask = imagerotate($img_watermark_mask, $angle, $mask_color_background); + $this->ApplyMask($img_watermark_mask, $img_watermark); + } + //phpthumb_filters::WatermarkOverlay($gdimg, $img_watermark, $alignment, $opacity, $margin); + $this->DebugMessage('WatermarkText() calling phpthumb_filters::WatermarkOverlay($gdimg, $img_watermark, '.($originOffsetX.'x'.$originOffsetY).', '.$opacity.', 0)', __FILE__, __LINE__); + $this->WatermarkOverlay($gdimg, $img_watermark, $originOffsetX.'x'.$originOffsetY, $opacity, 0); + imagedestroy($img_watermark); + return true; + } + + } + return false; + } + + + public function WatermarkOverlay(&$gdimg_dest, &$img_watermark, $alignment='*', $opacity=50, $margin_x=5, $margin_y=null) { + + if ((is_resource($gdimg_dest) || (is_object($gdimg_dest) && $gdimg_dest instanceOf \GdImage)) && (is_resource($img_watermark) || (is_object($img_watermark) && $img_watermark instanceOf \GdImage))) { + $img_source_width = imagesx($gdimg_dest); + $img_source_height = imagesy($gdimg_dest); + $watermark_source_width = imagesx($img_watermark); + $watermark_source_height = imagesy($img_watermark); + $watermark_opacity_percent = max(0, min(100, $opacity)); + $margin_y = (null === $margin_y ? $margin_x : $margin_y); + $watermark_margin_x = ((($margin_x > 0) && ($margin_x < 1)) ? round((1 - $margin_x) * $img_source_width) : $margin_x); + $watermark_margin_y = ((($margin_y > 0) && ($margin_y < 1)) ? round((1 - $margin_y) * $img_source_height) : $margin_y); + $watermark_destination_x = 0; + $watermark_destination_y = 0; + if (preg_match('#^([0-9\\.\\-]*)x([0-9\\.\\-]*)$#i', $alignment, $matches)) { + $watermark_destination_x = (int) $matches[ 1]; + $watermark_destination_y = (int) $matches[ 2]; + } else { + switch ($alignment) { + case '*': + if ($gdimg_tiledwatermark = phpthumb_functions::ImageCreateFunction($img_source_width, $img_source_height)) { + + imagealphablending($gdimg_tiledwatermark, false); + imagesavealpha($gdimg_tiledwatermark, true); + $text_color_transparent = phpthumb_functions::ImageColorAllocateAlphaSafe($gdimg_tiledwatermark, 255, 0, 255, 127); + imagefill($gdimg_tiledwatermark, 0, 0, $text_color_transparent); + + // set the tiled image transparent color to whatever the untiled image transparency index is + // imagecolortransparent($gdimg_tiledwatermark, imagecolortransparent($img_watermark)); + + // a "cleaner" way of doing it, but can't handle the margin feature :( + // imagesettile($gdimg_tiledwatermark, $img_watermark); + // imagefill($gdimg_tiledwatermark, 0, 0, IMG_COLOR_TILED); + // break; + + // imagefill($gdimg_tiledwatermark, 0, 0, imagecolortransparent($gdimg_tiledwatermark)); + // tile the image as many times as can fit + for ($x = $watermark_margin_x; $x < ($img_source_width + $watermark_source_width); $x += ($watermark_source_width + $watermark_margin_x)) { + for ($y = $watermark_margin_y; $y < ($img_source_height + $watermark_source_height); $y += ($watermark_source_height + $watermark_margin_y)) { + imagecopy( + $gdimg_tiledwatermark, + $img_watermark, + $x, + $y, + 0, + 0, + min($watermark_source_width, $img_source_width - $x - $watermark_margin_x), + min($watermark_source_height, $img_source_height - $y - $watermark_margin_y) + ); + } + } + + $watermark_source_width = imagesx($gdimg_tiledwatermark); + $watermark_source_height = imagesy($gdimg_tiledwatermark); + $watermark_destination_x = 0; + $watermark_destination_y = 0; + + imagedestroy($img_watermark); + $img_watermark = $gdimg_tiledwatermark; + } + break; + + case 'T': + $watermark_destination_x = round((($img_source_width / 2) - ($watermark_source_width / 2)) + $watermark_margin_x); + $watermark_destination_y = $watermark_margin_y; + break; + + case 'B': + $watermark_destination_x = round((($img_source_width / 2) - ($watermark_source_width / 2)) + $watermark_margin_x); + $watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y; + break; + + case 'L': + $watermark_destination_x = $watermark_margin_x; + $watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y); + break; + + case 'R': + $watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x; + $watermark_destination_y = round((($img_source_height / 2) - ($watermark_source_height / 2)) + $watermark_margin_y); + break; + + case 'C': + $watermark_destination_x = round(($img_source_width / 2) - ($watermark_source_width / 2)); + $watermark_destination_y = round(($img_source_height / 2) - ($watermark_source_height / 2)); + break; + + case 'TL': + $watermark_destination_x = $watermark_margin_x; + $watermark_destination_y = $watermark_margin_y; + break; + + case 'TR': + $watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x; + $watermark_destination_y = $watermark_margin_y; + break; + + case 'BL': + $watermark_destination_x = $watermark_margin_x; + $watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y; + break; + + case 'BR': + default: + $watermark_destination_x = $img_source_width - $watermark_source_width - $watermark_margin_x; + $watermark_destination_y = $img_source_height - $watermark_source_height - $watermark_margin_y; + break; + } + } + imagealphablending($gdimg_dest, false); + imagesavealpha($gdimg_dest, true); + imagesavealpha($img_watermark, true); + phpthumb_functions::ImageCopyRespectAlpha($gdimg_dest, $img_watermark, $watermark_destination_x, $watermark_destination_y, 0, 0, $watermark_source_width, $watermark_source_height, $watermark_opacity_percent); + + return true; + } + return false; + } + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.functions.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.functions.php new file mode 100644 index 0000000..74b6bab --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.functions.php @@ -0,0 +1,1095 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpthumb.functions.php - general support functions // +// /// +////////////////////////////////////////////////////////////// + +class phpthumb_functions { + + public static function is_windows() { + return (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'); + } + + public static function user_function_exists($functionname) { + if (function_exists('get_defined_functions')) { + static $get_defined_functions = array(); + if (empty($get_defined_functions)) { + $get_defined_functions = get_defined_functions(); + } + return in_array(strtolower($functionname), $get_defined_functions['user']); + } + return function_exists($functionname); + } + + + public static function builtin_function_exists($functionname) { + if (function_exists('get_defined_functions')) { + static $get_defined_functions = array(); + if (empty($get_defined_functions)) { + $get_defined_functions = get_defined_functions(); + } + return in_array(strtolower($functionname), $get_defined_functions['internal']); + } + return function_exists($functionname); + } + + + public static function version_compare_replacement_sub($version1, $version2, $operator='') { + // If you specify the third optional operator argument, you can test for a particular relationship. + // The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively. + // Using this argument, the function will return 1 if the relationship is the one specified by the operator, 0 otherwise. + + // If a part contains special version strings these are handled in the following order: + // (any string not found in this list) < (dev) < (alpha = a) < (beta = b) < (RC = rc) < (#) < (pl = p) + static $versiontype_lookup = array(); + if (empty($versiontype_lookup)) { + $versiontype_lookup['dev'] = 10001; + $versiontype_lookup['a'] = 10002; + $versiontype_lookup['alpha'] = 10002; + $versiontype_lookup['b'] = 10003; + $versiontype_lookup['beta'] = 10003; + $versiontype_lookup['RC'] = 10004; + $versiontype_lookup['rc'] = 10004; + $versiontype_lookup['#'] = 10005; + $versiontype_lookup['pl'] = 10006; + $versiontype_lookup['p'] = 10006; + } + $version1 = (isset($versiontype_lookup[$version1]) ? $versiontype_lookup[$version1] : $version1); + $version2 = (isset($versiontype_lookup[$version2]) ? $versiontype_lookup[$version2] : $version2); + + switch ($operator) { + case '<': + case 'lt': + return (int) ($version1 < $version2); + break; + case '<=': + case 'le': + return (int) ($version1 <= $version2); + break; + case '>': + case 'gt': + return (int) ($version1 > $version2); + break; + case '>=': + case 'ge': + return (int) ($version1 >= $version2); + break; + case '==': + case '=': + case 'eq': + return (int) ($version1 == $version2); + break; + case '!=': + case '<>': + case 'ne': + return (int) ($version1 != $version2); + break; + } + if ($version1 == $version2) { + return 0; + } elseif ($version1 < $version2) { + return -1; + } + return 1; + } + + + public static function version_compare_replacement($version1, $version2, $operator='') { + if (function_exists('version_compare')) { + // built into PHP v4.1.0+ + return version_compare($version1, $version2, $operator); + } + + // The function first replaces _, - and + with a dot . in the version strings + $version1 = strtr($version1, '_-+', '...'); + $version2 = strtr($version2, '_-+', '...'); + + // and also inserts dots . before and after any non number so that for example '4.3.2RC1' becomes '4.3.2.RC.1'. + // Then it splits the results like if you were using explode('.',$ver). Then it compares the parts starting from left to right. + $version1 = preg_replace('#([\d]+)([A-Z]+)([\d]+)#i', '$1.$2.$3', $version1); + $version2 = preg_replace('#([\d]+)([A-Z]+)([\d]+)#i', '$1.$2.$3', $version2); + + $parts1 = explode('.', $version1); + $parts2 = explode('.', $version1); + $parts_count = max(count($parts1), count($parts2)); + for ($i = 0; $i < $parts_count; $i++) { + $comparison = self::version_compare_replacement_sub($version1, $version2, $operator); + if ($comparison != 0) { + return $comparison; + } + } + return 0; + } + + public static function escapeshellarg_replacement($arg) { + if (function_exists('escapeshellarg') && !self::FunctionIsDisabled('escapeshellarg')) { + return escapeshellarg($arg); + } + return '\''.str_replace('\'', '\\\'', $arg).'\''; + } + + public static function phpinfo_array() { + static $phpinfo_array = array(); + if (empty($phpinfo_array)) { + ob_start(); + phpinfo(); + $phpinfo = ob_get_contents(); + ob_end_clean(); + $phpinfo_array = explode("\n", $phpinfo); + } + return $phpinfo_array; + } + + + public static function exif_info() { + static $exif_info = array(); + if (empty($exif_info)) { + // based on code by johnschaefer at gmx dot de + // from PHP help on gd_info() + $exif_info = array( + 'EXIF Support' => '', + 'EXIF Version' => '', + 'Supported EXIF Version' => '', + 'Supported filetypes' => '' + ); + $phpinfo_array = self::phpinfo_array(); + foreach ($phpinfo_array as $line) { + $line = trim(strip_tags($line)); + foreach ($exif_info as $key => $value) { + if (strpos($line, $key) === 0) { + $newvalue = trim(str_replace($key, '', $line)); + $exif_info[$key] = $newvalue; + } + } + } + } + return $exif_info; + } + + + public static function ImageTypeToMIMEtype($imagetype) { + if (function_exists('image_type_to_mime_type') && ($imagetype >= 1) && ($imagetype <= 18)) { + // PHP v4.3.0+ + return image_type_to_mime_type($imagetype); + } + static $image_type_to_mime_type = array( + 1 => 'image/gif', // IMAGETYPE_GIF + 2 => 'image/jpeg', // IMAGETYPE_JPEG + 3 => 'image/png', // IMAGETYPE_PNG + 4 => 'application/x-shockwave-flash', // IMAGETYPE_SWF + 5 => 'image/psd', // IMAGETYPE_PSD + 6 => 'image/bmp', // IMAGETYPE_BMP + 7 => 'image/tiff', // IMAGETYPE_TIFF_II (intel byte order) + 8 => 'image/tiff', // IMAGETYPE_TIFF_MM (motorola byte order) + 9 => 'application/octet-stream', // IMAGETYPE_JPC + 10 => 'image/jp2', // IMAGETYPE_JP2 + 11 => 'application/octet-stream', // IMAGETYPE_JPX + 12 => 'application/octet-stream', // IMAGETYPE_JB2 + 13 => 'application/x-shockwave-flash', // IMAGETYPE_SWC + 14 => 'image/iff', // IMAGETYPE_IFF + 15 => 'image/vnd.wap.wbmp', // IMAGETYPE_WBMP + 16 => 'image/xbm', // IMAGETYPE_XBM + 17 => 'image/x-icon', // IMAGETYPE_ICO + 18 => 'image/webp', // IMAGETYPE_WEBP + + 'gif' => 'image/gif', // IMAGETYPE_GIF + 'jpg' => 'image/jpeg', // IMAGETYPE_JPEG + 'jpeg' => 'image/jpeg', // IMAGETYPE_JPEG + 'png' => 'image/png', // IMAGETYPE_PNG + 'bmp' => 'image/bmp', // IMAGETYPE_BMP + 'ico' => 'image/x-icon', // IMAGETYPE_ICO + 'webp' => 'image/webp', // IMAGETYPE_WEBP + ); + + return (isset($image_type_to_mime_type[$imagetype]) ? $image_type_to_mime_type[$imagetype] : false); + } + + + public static function TranslateWHbyAngle($width, $height, $angle) { + if (($angle % 180) == 0) { + return array($width, $height); + } + $newwidth = (abs(sin(deg2rad($angle))) * $height) + (abs(cos(deg2rad($angle))) * $width); + $newheight = (abs(sin(deg2rad($angle))) * $width) + (abs(cos(deg2rad($angle))) * $height); + return array($newwidth, $newheight); + } + + public static function HexCharDisplay($string) { + $len = strlen($string); + $output = ''; + for ($i = 0; $i < $len; $i++) { + $output .= ' 0x'.str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT); + } + return $output; + } + + + public static function IsHexColor($HexColorString) { + return preg_match('#^[0-9A-F]{6}$#i', $HexColorString); + } + + + public static function ImageColorAllocateAlphaSafe(&$gdimg_hexcolorallocate, $R, $G, $B, $alpha=false) { + if (self::version_compare_replacement(PHP_VERSION, '4.3.2', '>=') && ($alpha !== false)) { + return imagecolorallocatealpha($gdimg_hexcolorallocate, $R, $G, $B, (int) $alpha); + } else { + return imagecolorallocate($gdimg_hexcolorallocate, $R, $G, $B); + } + } + + public static function ImageHexColorAllocate(&$gdimg_hexcolorallocate, $HexColorString, $dieOnInvalid=false, $alpha=false) { + if (!is_resource($gdimg_hexcolorallocate) && !(is_object($gdimg_hexcolorallocate) && $gdimg_hexcolorallocate instanceOf \GdImage)) { + die('$gdimg_hexcolorallocate is not a GD resource in ImageHexColorAllocate()'); + } + if (self::IsHexColor($HexColorString)) { + $R = hexdec(substr($HexColorString, 0, 2)); + $G = hexdec(substr($HexColorString, 2, 2)); + $B = hexdec(substr($HexColorString, 4, 2)); + return self::ImageColorAllocateAlphaSafe($gdimg_hexcolorallocate, $R, $G, $B, $alpha); + } + if ($dieOnInvalid) { + die('Invalid hex color string: "'.$HexColorString.'"'); + } + return imagecolorallocate($gdimg_hexcolorallocate, 0x00, 0x00, 0x00); + } + + + public static function HexColorXOR($hexcolor) { + return strtoupper(str_pad(dechex(~hexdec($hexcolor) & 0xFFFFFF), 6, '0', STR_PAD_LEFT)); + } + + + public static function GetPixelColor(&$img, $x, $y) { + if (!is_resource($img) && !(is_object($img) && $img instanceOf \GdImage)) { + return false; + } + return @imagecolorsforindex($img, @imagecolorat($img, $x, $y)); + } + + + public static function PixelColorDifferencePercent($currentPixel, $targetPixel) { + $diff = 0; + foreach ($targetPixel as $channel => $currentvalue) { + $diff = max($diff, (max($currentPixel[$channel], $targetPixel[$channel]) - min($currentPixel[$channel], $targetPixel[$channel])) / 255); + } + return $diff * 100; + } + + public static function GrayscaleValue($r, $g, $b) { + return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11)); + } + + + public static function GrayscalePixel($OriginalPixel) { + $gray = self::GrayscaleValue($OriginalPixel[ 'red'], $OriginalPixel[ 'green'], $OriginalPixel[ 'blue']); + return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray); + } + + + public static function GrayscalePixelRGB($rgb) { + $r = ($rgb >> 16) & 0xFF; + $g = ($rgb >> 8) & 0xFF; + $b = $rgb & 0xFF; + return ($r * 0.299) + ($g * 0.587) + ($b * 0.114); + } + + + public static function ScaleToFitInBox($width, $height, $maxwidth=null, $maxheight=null, $allow_enlarge=true, $allow_reduce=true) { + $maxwidth = (null === $maxwidth ? $width : $maxwidth); + $maxheight = (null === $maxheight ? $height : $maxheight); + $scale_x = 1; + $scale_y = 1; + if (($width > $maxwidth) || ($width < $maxwidth)) { + $scale_x = ($maxwidth / $width); + } + if (($height > $maxheight) || ($height < $maxheight)) { + $scale_y = ($maxheight / $height); + } + $scale = min($scale_x, $scale_y); + if (!$allow_enlarge) { + $scale = min($scale, 1); + } + if (!$allow_reduce) { + $scale = max($scale, 1); + } + return $scale; + } + + public static function ImageCopyResampleBicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { + // ron at korving dot demon dot nl + // http://www.php.net/imagecopyresampled + + $scaleX = ($src_w - 1) / $dst_w; + $scaleY = ($src_h - 1) / $dst_h; + + $scaleX2 = $scaleX / 2.0; + $scaleY2 = $scaleY / 2.0; + + $isTrueColor = imageistruecolor($src_img); + + for ($y = $src_y; $y < $src_y + $dst_h; $y++) { + $sY = $y * $scaleY; + $siY = (int) $sY; + $siY2 = (int) $sY + $scaleY2; + + for ($x = $src_x; $x < $src_x + $dst_w; $x++) { + $sX = $x * $scaleX; + $siX = (int) $sX; + $siX2 = (int) $sX + $scaleX2; + + if ($isTrueColor) { + + $c1 = imagecolorat($src_img, $siX, $siY2); + $c2 = imagecolorat($src_img, $siX, $siY); + $c3 = imagecolorat($src_img, $siX2, $siY2); + $c4 = imagecolorat($src_img, $siX2, $siY); + + $r = (( $c1 + $c2 + $c3 + $c4 ) >> 2) & 0xFF0000; + $g = ((($c1 & 0x00FF00) + ($c2 & 0x00FF00) + ($c3 & 0x00FF00) + ($c4 & 0x00FF00)) >> 2) & 0x00FF00; + $b = ((($c1 & 0x0000FF) + ($c2 & 0x0000FF) + ($c3 & 0x0000FF) + ($c4 & 0x0000FF)) >> 2); + + } else { + + $c1 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX, $siY2)); + $c2 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX, $siY)); + $c3 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX2, $siY2)); + $c4 = imagecolorsforindex($src_img, imagecolorat($src_img, $siX2, $siY)); + + $r = ($c1['red'] + $c2['red'] + $c3['red'] + $c4['red'] ) << 14; + $g = ($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) << 6; + $b = ($c1['blue'] + $c2['blue'] + $c3['blue'] + $c4['blue'] ) >> 2; + + } + imagesetpixel($dst_img, $dst_x + $x - $src_x, $dst_y + $y - $src_y, $r+$g+$b); + } + } + return true; + } + + + public static function ImageCreateFunction($x_size, $y_size) { + $ImageCreateFunction = 'imagecreate'; + if (self::gd_version() >= 2.0) { + $ImageCreateFunction = 'imagecreatetruecolor'; + } + if (!function_exists($ImageCreateFunction)) { + return phpthumb::ErrorImage($ImageCreateFunction.'() does not exist - no GD support?'); + } + if (($x_size <= 0) || ($y_size <= 0)) { + return phpthumb::ErrorImage('Invalid image dimensions: '.$ImageCreateFunction.'('.$x_size.', '.$y_size.')'); + } + return $ImageCreateFunction(round($x_size), round($y_size)); + } + + + public static function ImageCopyRespectAlpha(&$dst_im, &$src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity_pct=100) { + $opacipct = $opacity_pct / 100; + for ($x = $src_x; $x < $src_w; $x++) { + for ($y = $src_y; $y < $src_h; $y++) { + $RealPixel = self::GetPixelColor($dst_im, $dst_x + $x, $dst_y + $y); + $OverlayPixel = self::GetPixelColor($src_im, $x, $y); + $alphapct = $OverlayPixel['alpha'] / 127; + $overlaypct = (1 - $alphapct) * $opacipct; + + $newcolor = self::ImageColorAllocateAlphaSafe( + $dst_im, + $RealPixel['alpha'] == 127 ? $OverlayPixel['red'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['red'] : (round($RealPixel['red'] * (1 - $overlaypct)) + ($OverlayPixel['red'] * $overlaypct))), + $RealPixel['alpha'] == 127 ? $OverlayPixel['green'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['green'] : (round($RealPixel['green'] * (1 - $overlaypct)) + ($OverlayPixel['green'] * $overlaypct))), + $RealPixel['alpha'] == 127 ? $OverlayPixel['blue'] : ($OverlayPixel['alpha'] == 127 ? $RealPixel['blue'] : (round($RealPixel['blue'] * (1 - $overlaypct)) + ($OverlayPixel['blue'] * $overlaypct))), +// 0); + min([$RealPixel['alpha'], floor($OverlayPixel['alpha'] * $opacipct)]) + ); + + imagesetpixel($dst_im, $dst_x + $x, $dst_y + $y, $newcolor); + } + } + return true; + } + + + public static function ProportionalResize($old_width, $old_height, $new_width=false, $new_height=false) { + $old_aspect_ratio = $old_width / $old_height; + if (($new_width === false) && ($new_height === false)) { + return false; + } elseif ($new_width === false) { + $new_width = $new_height * $old_aspect_ratio; + } elseif ($new_height === false) { + $new_height = $new_width / $old_aspect_ratio; + } + $new_aspect_ratio = $new_width / $new_height; + if ($new_aspect_ratio == $old_aspect_ratio) { + // great, done + } elseif ($new_aspect_ratio < $old_aspect_ratio) { + // limited by width + $new_height = $new_width / $old_aspect_ratio; + } elseif ($new_aspect_ratio > $old_aspect_ratio) { + // limited by height + $new_width = $new_height * $old_aspect_ratio; + } + return array( + (int) round($new_width), + (int) round($new_height) + ); + } + + + public static function FunctionIsDisabled($function) { + static $DisabledFunctions = null; + if (null === $DisabledFunctions) { + $disable_functions_local = explode(',', strtolower(@ini_get('disable_functions'))); + $disable_functions_global = explode(',', strtolower(@get_cfg_var('disable_functions'))); + foreach ($disable_functions_local as $key => $value) { + $DisabledFunctions[trim($value)] = 'local'; + } + foreach ($disable_functions_global as $key => $value) { + $DisabledFunctions[trim($value)] = 'global'; + } + if (@ini_get('safe_mode')) { + $DisabledFunctions['shell_exec'] = 'local'; + $DisabledFunctions['set_time_limit'] = 'local'; + } + } + return isset($DisabledFunctions[strtolower($function)]); + } + + + public static function SafeExec($command) { + static $AllowedExecFunctions = array(); + if (empty($AllowedExecFunctions)) { + $AllowedExecFunctions = array('shell_exec'=>true, 'passthru'=>true, 'system'=>true, 'exec'=>true); + foreach ($AllowedExecFunctions as $key => $value) { + $AllowedExecFunctions[$key] = !self::FunctionIsDisabled($key); + } + } + $command .= ' 2>&1'; // force redirect stderr to stdout + foreach ($AllowedExecFunctions as $execfunction => $is_allowed) { + if (!$is_allowed) { + continue; + } + $returnvalue = false; + switch ($execfunction) { + case 'passthru': + case 'system': + ob_start(); + $execfunction($command); + $returnvalue = ob_get_contents(); + ob_end_clean(); + break; + + case 'exec': + $output = array(); + $lastline = $execfunction($command, $output); + $returnvalue = implode("\n", $output); + break; + + case 'shell_exec': + ob_start(); + $returnvalue = $execfunction($command); + ob_end_clean(); + break; + } + return $returnvalue; + } + return false; + } + + + public static function ApacheLookupURIarray($filename) { + // apache_lookup_uri() only works when PHP is installed as an Apache module. + if (PHP_SAPI == 'apache') { + //$property_exists_exists = function_exists('property_exists'); + $keys = array('status', 'the_request', 'status_line', 'method', 'content_type', 'handler', 'uri', 'filename', 'path_info', 'args', 'boundary', 'no_cache', 'no_local_copy', 'allowed', 'send_bodyct', 'bytes_sent', 'byterange', 'clength', 'unparsed_uri', 'mtime', 'request_time'); + if ($apacheLookupURIobject = @apache_lookup_uri($filename)) { + $apacheLookupURIarray = array(); + foreach ($keys as $key) { + $apacheLookupURIarray[$key] = @$apacheLookupURIobject->$key; + } + return $apacheLookupURIarray; + } + } + return false; + } + + + public static function gd_is_bundled() { + static $isbundled = null; + if (null === $isbundled) { + $gd_info = gd_info(); + $isbundled = (strpos($gd_info['GD Version'], 'bundled') !== false); + } + return $isbundled; + } + + + public static function gd_version($fullstring=false) { + static $cache_gd_version = array(); + if (empty($cache_gd_version)) { + $gd_info = gd_info(); + if (preg_match('#bundled \((.+)\)$#i', $gd_info['GD Version'], $matches)) { + $cache_gd_version[1] = $gd_info['GD Version']; // e.g. "bundled (2.0.15 compatible)" + $cache_gd_version[0] = (float) $matches[1]; // e.g. "2.0" (not "bundled (2.0.15 compatible)") + } else { + $cache_gd_version[1] = $gd_info['GD Version']; // e.g. "1.6.2 or higher" + $cache_gd_version[0] = (float) substr($gd_info['GD Version'], 0, 3); // e.g. "1.6" (not "1.6.2 or higher") + } + } + return $cache_gd_version[ (int) $fullstring ]; + } + + + public static function filesize_remote($remotefile, $timeout=10) { + $size = false; + $parsed_url = self::ParseURLbetter($remotefile); + if ($fp = @fsockopen($parsed_url['host'], $parsed_url['port'], $errno, $errstr, $timeout)) { + fwrite($fp, 'HEAD '.$parsed_url['path'].$parsed_url['query'].' HTTP/1.0'."\r\n".'Host: '.$parsed_url['host']."\r\n\r\n"); + if (self::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) { + stream_set_timeout($fp, $timeout); + } + while (!feof($fp)) { + $headerline = fgets($fp, 4096); + if (preg_match('#^Content-Length: (.*)#i', $headerline, $matches)) { + $size = (int) $matches[ 1]; + break; + } + } + fclose ($fp); + } + return $size; + } + + + public static function filedate_remote($remotefile, $timeout=10) { + $date = false; + $parsed_url = self::ParseURLbetter($remotefile); + if ($fp = @fsockopen($parsed_url['host'], $parsed_url['port'], $errno, $errstr, $timeout)) { + fwrite($fp, 'HEAD '.$parsed_url['path'].$parsed_url['query'].' HTTP/1.0'."\r\n".'Host: '.$parsed_url['host']."\r\n\r\n"); + if (self::version_compare_replacement(PHP_VERSION, '4.3.0', '>=')) { + stream_set_timeout($fp, $timeout); + } + while (!feof($fp)) { + $headerline = fgets($fp, 4096); + if (preg_match('#^Last-Modified: (.*)#i', $headerline, $matches)) { + $date = strtotime($matches[1]) - date('Z'); + break; + } + } + fclose ($fp); + } + return $date; + } + + + public static function md5_file_safe($filename) { + // md5_file() doesn't exist in PHP < 4.2.0 + if (function_exists('md5_file')) { + return md5_file($filename); + } + if ($fp = @fopen($filename, 'rb')) { + $rawData = ''; + do { + $buffer = fread($fp, 8192); + $rawData .= $buffer; + } while (strlen($buffer) > 0); + fclose($fp); + return md5($rawData); + } + return false; + } + + + public static function nonempty_min() { + $arg_list = func_get_args(); + $acceptable = array(); + foreach ($arg_list as $arg) { + if ($arg) { + $acceptable[] = $arg; + } + } + return min($acceptable); + } + + + public static function LittleEndian2String($number, $minbytes=1) { + $intstring = ''; + while ($number > 0) { + $intstring .= chr($number & 255); + $number >>= 8; + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); + } + + public static function OneOfThese() { + // return the first useful (non-empty/non-zero/non-false) value from those passed + $arg_list = func_get_args(); + foreach ($arg_list as $key => $value) { + if ($value) { + return $value; + } + } + return false; + } + + public static function CaseInsensitiveInArray($needle, $haystack) { + $needle = strtolower($needle); + foreach ($haystack as $key => $value) { + if (is_array($value)) { + // skip? + } elseif ($needle == strtolower($value)) { + return true; + } + } + return false; + } + + public static function URLreadFsock($host, $file, &$errstr, $successonly=true, $port=-1, $timeout=10) { + if (!function_exists('fsockopen') || self::FunctionIsDisabled('fsockopen')) { + $errstr = 'URLreadFsock says: function fsockopen() unavailable'; + return false; + } + $port = (int) ($port ? $port : -1); // passing anything as the $port parameter (even empty values like null, false, 0, "") will override the default -1. fsockopen uses -1 as the default port value. + //if ($fp = @fsockopen($host, $port, $errno, $errstr, $timeout)) { + if ($fp = @fsockopen((($port == 443) ? 'ssl://' : '').$host, $port, $errno, $errstr, $timeout)) { // https://github.com/JamesHeinrich/phpThumb/issues/39 + $out = 'GET '.$file.' HTTP/1.0'."\r\n"; + $out .= 'Host: '.$host."\r\n"; + $out .= 'Connection: Close'."\r\n\r\n"; + fwrite($fp, $out); + + $isHeader = true; + $data_header = ''; + $data_body = ''; + $header_newlocation = ''; + while (!feof($fp)) { + $line = fgets($fp, 1024); + if ($isHeader) { + $data_header .= $line; + } else { + $data_body .= $line; + } + if (preg_match('#^HTTP/[\\.\d]+ ([\d]+)\s*(.+)?$#i', rtrim($line), $matches)) { + list( , $errno, $errstr) = $matches; + $errno = (int) $errno; + } elseif (preg_match('#^Location: (.*)$#i', rtrim($line), $matches)) { + $header_newlocation = $matches[1]; + } + if ($isHeader && ($line == "\r\n")) { + $isHeader = false; + if ($successonly) { + switch ($errno) { + case 200: + // great, continue + break; + + default: + $errstr = $errno.' '.$errstr.($header_newlocation ? '; Location: '.$header_newlocation : ''); + fclose($fp); + return false; + break; + } + } + } + } + fclose($fp); + return $data_body; + } + return null; + } + + public static function CleanUpURLencoding($url, $queryseperator='&') { + if (!0 === stripos($url, "http") ) { + return $url; + } + $parsed_url = self::ParseURLbetter($url); + $pathelements = explode('/', $parsed_url['path']); + $CleanPathElements = array(); + $TranslationMatrix = array(' '=>'%20'); + foreach ($pathelements as $key => $pathelement) { + $CleanPathElements[] = strtr($pathelement, $TranslationMatrix); + } + foreach ($CleanPathElements as $key => $value) { + if ($value === '') { + unset($CleanPathElements[$key]); + } + } + + $queries = explode($queryseperator, $parsed_url['query']); + $CleanQueries = array(); + foreach ($queries as $key => $query) { + @list($param, $value) = explode('=', $query); + $CleanQueries[] = strtr($param, $TranslationMatrix).($value ? '='.strtr($value, $TranslationMatrix) : ''); + } + foreach ($CleanQueries as $key => $value) { + if ($value === '') { + unset($CleanQueries[$key]); + } + } + + $cleaned_url = $parsed_url['scheme'].'://'; + $cleaned_url .= ($parsed_url['user'] ? $parsed_url['user'].($parsed_url['pass'] ? ':'.$parsed_url['pass'] : '').'@' : ''); + $cleaned_url .= $parsed_url['host']; + $cleaned_url .= (($parsed_url['port'] && ($parsed_url['port'] != self::URLschemeDefaultPort($parsed_url['scheme']))) ? ':'.$parsed_url['port'] : ''); + $cleaned_url .= '/'.implode('/', $CleanPathElements); + $cleaned_url .= (!empty($CleanQueries) ? '?'.implode($queryseperator, $CleanQueries) : ''); + return $cleaned_url; + } + + public static function URLschemeDefaultPort($scheme) { + static $schemePort = array( + 'ftp' => 21, + 'http' => 80, + 'https' => 443, + ); + return ((!empty($scheme) && isset($schemePort[strtolower($scheme)])) ? $schemePort[strtolower($scheme)] : null); + } + + public static function ParseURLbetter($url) { + $parsedURL = @parse_url($url); + foreach (array('scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment') as $key) { // ensure all possible array keys are always returned + if (!array_key_exists($key, $parsedURL)) { + $parsedURL[$key] = null; + } + } + $parsedURL['port'] = ($parsedURL['port'] ? $parsedURL['port'] : self::URLschemeDefaultPort($parsedURL['scheme'])); + return $parsedURL; + } + + public static function SafeURLread($url, &$error, $timeout=10, $followredirects=true) { + $error = ''; + $errstr = ''; + $rawData = ''; + + $parsed_url = self::ParseURLbetter($url); + $alreadyLookedAtURLs[trim($url)] = true; + + while (true) { + $tryagain = false; + $rawData = self::URLreadFsock($parsed_url['host'], $parsed_url['path'].'?'.$parsed_url['query'], $errstr, true, $parsed_url['port'], $timeout); + if ($followredirects && preg_match('#302 [a-z ]+; Location\\: (http.*)#i', $errstr, $matches)) { + $matches[1] = trim(@$matches[1]); + if (!@$alreadyLookedAtURLs[$matches[1]]) { + // loop through and examine new URL + $error .= 'URL "'.$url.'" redirected to "'.$matches[1].'"'; + + $tryagain = true; + $alreadyLookedAtURLs[$matches[1]] = true; + $parsed_url = self::ParseURLbetter($matches[ 1]); + } + } + if (!$tryagain) { + break; + } + } + + if ($rawData === false) { + $error .= 'Error opening "'.$url.'":'."\n\n".$errstr; + return false; + } elseif ($rawData === null) { + // fall through + $error .= 'Error opening "'.$url.'":'."\n\n".$errstr; + } else { + return $rawData; + } + + if (function_exists('curl_version') && !self::FunctionIsDisabled('curl_exec')) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, (bool) $followredirects); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + $rawData = curl_exec($ch); + curl_close($ch); + if (strlen($rawData) > 0) { + $error .= 'CURL succeeded ('.strlen($rawData).' bytes); '; + return $rawData; + } + $error .= 'CURL available but returned no data; '; + } else { + $error .= 'CURL unavailable; '; + } + + $BrokenURLfopenPHPversions = array('4.4.2'); + if (in_array(PHP_VERSION, $BrokenURLfopenPHPversions)) { + $error .= 'fopen(URL) broken in PHP v'. PHP_VERSION .'; '; + } elseif (@ini_get('allow_url_fopen')) { + $rawData = ''; + $error_fopen = ''; + ob_start(); + if ($fp = fopen($url, 'rb')) { + do { + $buffer = fread($fp, 8192); + $rawData .= $buffer; + } while (strlen($buffer) > 0); + fclose($fp); + } else { + $error_fopen .= trim(strip_tags(ob_get_contents())); + } + ob_end_clean(); + $error .= $error_fopen; + if (!$error_fopen) { + $error .= '; "allow_url_fopen" succeeded ('.strlen($rawData).' bytes); '; + return $rawData; + } + $error .= '; "allow_url_fopen" enabled but returned no data ('.$error_fopen.'); '; + } else { + $error .= '"allow_url_fopen" disabled; '; + } + + return false; + } + + public static function EnsureDirectoryExists($dirname, $mask=0755) { + // https://www.php.net/manual/en/ini.core.php#ini.open-basedir says: + // "Under Windows, separate the directories with a semicolon. On all other systems, separate the directories with a colon." + $config_open_basedir = ini_get('open_basedir'); + $startoffset = 2; // 1-based counting, first element to left of first directory separator will either be drive letter (Windows) or blank (unix). May be overridden below. + if (self::is_windows()) { + $delimiter = ';'; + $case_insensitive_pathname = true; + // unix OSs will always use "/", some Windows configurations you may find "/" used interchangeably with the OS-correct "\", so standardize for ease of comparison + $dirname = str_replace('/', DIRECTORY_SEPARATOR, $dirname); + $config_open_basedir = str_replace('/', DIRECTORY_SEPARATOR, $config_open_basedir); + } else { + $delimiter = ':'; + $case_insensitive_pathname = false; + } + $open_basedirs = explode($delimiter, $config_open_basedir); + foreach ($open_basedirs as $key => $open_basedir) { + if (preg_match('#^'.preg_quote($open_basedir).'#'.($case_insensitive_pathname ? 'i' : ''), $dirname) && (strlen($dirname) > strlen($open_basedir))) { + $startoffset = substr_count($open_basedir, DIRECTORY_SEPARATOR) + 1; + break; + } + } + + $directory_elements = explode(DIRECTORY_SEPARATOR, $dirname); + $endoffset = count($directory_elements); + for ($i = $startoffset; $i <= $endoffset; $i++) { + $test_directory = implode(DIRECTORY_SEPARATOR, array_slice($directory_elements, 0, $i)); + if (!$test_directory) { + continue; + } + if (!@is_dir($test_directory)) { + if (@file_exists($test_directory)) { + // directory name already exists as a file + return false; + } + @mkdir($test_directory, $mask); + @chmod($test_directory, $mask); + if (!@is_dir($test_directory) || !@is_writable($test_directory)) { + return false; + } + } + } + return true; + } + + + public static function GetAllFilesInSubfolders($dirname) { + $AllFiles = array(); + $dirname = rtrim(realpath($dirname), '/\\'); + if ($dirhandle = @opendir($dirname)) { + while (($file = readdir($dirhandle)) !== false) { + $fullfilename = $dirname.DIRECTORY_SEPARATOR.$file; + if (is_file($fullfilename)) { + $AllFiles[] = $fullfilename; + } elseif (is_dir($fullfilename)) { + switch ($file) { + case '.': + case '..': + break; + + default: + $AllFiles[] = $fullfilename; + $subfiles = self::GetAllFilesInSubfolders($fullfilename); + foreach ($subfiles as $filename) { + $AllFiles[] = $filename; + } + break; + } + } else { + // ignore? + } + } + closedir($dirhandle); + } + sort($AllFiles); + return array_unique($AllFiles); + } + + + public static function SanitizeFilename($filename) { + $filename = preg_replace('/[^'.preg_quote(' !#$%^()+,-.;<>=@[]_{}').'a-zA-Z0-9]/', '_', $filename); + if (self::version_compare_replacement(PHP_VERSION, '4.1.0', '>=')) { + $filename = trim($filename, '.'); + } + return $filename; + } + + public static function PasswordStrength($password) { + $strength = 0; + $strength += strlen(preg_replace('#[^a-z]#', '', $password)) * 0.5; // lowercase characters are weak + $strength += strlen(preg_replace('#[^A-Z]#', '', $password)) * 0.8; // uppercase characters are somewhat better + $strength += strlen(preg_replace('#[^0-9]#', '', $password)) * 1.0; // numbers are somewhat better + $strength += strlen(preg_replace('#[a-zA-Z0-9]#', '', $password)) * 2.0; // other non-alphanumeric characters are best + return $strength; + } + +} + + +////////////// END: class phpthumb_functions ////////////// + + +if (!function_exists('gd_info')) { + // built into PHP v4.3.0+ (with bundled GD2 library) + function gd_info() { + static $gd_info = array(); + if (empty($gd_info)) { + // based on code by johnschaefer at gmx dot de + // from PHP help on gd_info() + $gd_info = array( + 'GD Version' => '', + 'FreeType Support' => false, + 'FreeType Linkage' => '', + 'T1Lib Support' => false, + 'GIF Read Support' => false, + 'GIF Create Support' => false, + 'JPG Support' => false, + 'PNG Support' => false, + 'WBMP Support' => false, + 'XBM Support' => false + ); + $phpinfo_array = phpthumb_functions::phpinfo_array(); + foreach ($phpinfo_array as $line) { + $line = trim(strip_tags($line)); + foreach ($gd_info as $key => $value) { + //if (strpos($line, $key) !== false) { + if (strpos($line, $key) === 0) { + $newvalue = trim(str_replace($key, '', $line)); + $gd_info[$key] = $newvalue; + } + } + } + if (empty($gd_info['GD Version'])) { + // probable cause: "phpinfo() disabled for security reasons" + if (function_exists('imagetypes')) { + $imagetypes = imagetypes(); + if ($imagetypes & IMG_PNG) { + $gd_info['PNG Support'] = true; + } + if ($imagetypes & IMG_GIF) { + $gd_info['GIF Create Support'] = true; + } + if ($imagetypes & IMG_JPG) { + $gd_info['JPG Support'] = true; + } + if ($imagetypes & IMG_WBMP) { + $gd_info['WBMP Support'] = true; + } + } + // to determine capability of GIF creation, try to use imagecreatefromgif on a 1px GIF + if (function_exists('imagecreatefromgif')) { + if ($tempfilename = phpthumb::phpThumb_tempnam()) { + if ($fp_tempfile = @fopen($tempfilename, 'wb')) { + fwrite($fp_tempfile, base64_decode('R0lGODlhAQABAIAAAH//AP///ywAAAAAAQABAAACAUQAOw==')); // very simple 1px GIF file base64-encoded as string + fclose($fp_tempfile); + $phpthumb_temp = new phpthumb(); + @chmod($tempfilename, $phpthumb_temp->getParameter('config_file_create_mask')); + + // if we can convert the GIF file to a GD image then GIF create support must be enabled, otherwise it's not + $gd_info['GIF Read Support'] = (bool) @imagecreatefromgif($tempfilename); + } + unlink($tempfilename); + } + } + if (function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) { + $gd_info['GD Version'] = '2.0.1 or higher (assumed)'; + } elseif (function_exists('imagecreate') && @imagecreate(1, 1)) { + $gd_info['GD Version'] = '1.6.0 or higher (assumed)'; + } + } + } + return $gd_info; + } +} + + +if (!function_exists('is_executable')) { + // in PHP v3+, but v5.0+ for Windows + function is_executable($filename) { + // poor substitute, but better than nothing + return file_exists($filename); + } +} + + +if (!function_exists('preg_quote')) { + // included in PHP v3.0.9+, but may be unavailable if not compiled in + function preg_quote($string, $delimiter='\\') { + static $preg_quote_array = array(); + if (empty($preg_quote_array)) { + $escapeables = '.\\+*?[^]$(){}=!<>|:'; + for ($i = 0, $iMax = strlen($escapeables); $i < $iMax; $i++) { + $strtr_preg_quote[$escapeables[$i]] = $delimiter.$escapeables[$i]; + } + } + return strtr($string, $strtr_preg_quote); + } +} + +if (!function_exists('file_get_contents')) { + // included in PHP v4.3.0+ + function file_get_contents($filename) { + if (preg_match('#^(f|ht)tp\://#i', $filename)) { + return SafeURLread($filename, $error); + } + if ($fp = @fopen($filename, 'rb')) { + $rawData = ''; + do { + $buffer = fread($fp, 8192); + $rawData .= $buffer; + } while (strlen($buffer) > 0); + fclose($fp); + return $rawData; + } + return false; + } +} + + +if (!function_exists('file_put_contents')) { + // included in PHP v5.0.0+ + function file_put_contents($filename, $filedata) { + if ($fp = @fopen($filename, 'wb')) { + fwrite($fp, $filedata); + fclose($fp); + return true; + } + return false; + } +} + +if (!function_exists('imagealphablending')) { + // built-in function requires PHP v4.0.6+ *and* GD v2.0.1+ + function imagealphablending(&$img, $blendmode=true) { + // do nothing, this function is declared here just to + // prevent runtime errors if GD2 is not available + return true; + } +} + +if (!function_exists('imagesavealpha')) { + // built-in function requires PHP v4.3.2+ *and* GD v2.0.1+ + function imagesavealpha(&$img, $blendmode=true) { + // do nothing, this function is declared here just to + // prevent runtime errors if GD2 is not available + return true; + } +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.gif.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.gif.php new file mode 100644 index 0000000..3081b9b --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.gif.php @@ -0,0 +1,1175 @@ + = gif_loadFile(filename, [index]) +// = gif_getSize( or filename, &width, &height) +// = gif_outputAsPng(, filename, [bgColor]) +// = gif_outputAsBmp(, filename, [bgcolor]) +// = gif_outputAsJpeg(, filename, [bgcolor]) - use cjpeg if available otherwise uses GD +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Original code by Fabien Ezber +// Modified by James Heinrich for use in phpThumb() - December 10, 2003 +// * Added function gif_loadFileToGDimageResource() - this returns a GD image resource +// * Modified gif_outputAsJpeg() to check if it's running under Windows, or if cjpeg is not +// available, in which case it will attempt to output JPEG using GD functions +// * added @ error-suppression to two lines where it checks: if ($this->m_img->m_bTrans) +// otherwise warnings are generated if error_reporting == E_ALL +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function gif_loadFile($lpszFileName, $iIndex = 0) +{ + $gif = new CGIF(); + if ($gif->loadFile($lpszFileName, $iIndex)) { + return $gif; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Added by James Heinrich - December 10, 2003 +function gif_loadFileToGDimageResource($gifFilename, $bgColor = -1) +{ + if ($gif = gif_loadFile($gifFilename)) { + + if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) { + // shouldn't take nearly this long + set_time_limit(120); + } + // general strategy: convert raw data to PNG then convert PNG data to GD image resource + $PNGdata = $gif->getPng($bgColor); + if ($img = @imagecreatefromstring($PNGdata)) { + + // excellent - PNG image data successfully converted to GD image + return $img; + + } elseif ($img = $gif->getGD_PixelPlotterVersion()) { + + // problem: imagecreatefromstring() didn't like the PNG image data. + // This has been known to happen in PHP v4.0.6 + // solution: take the raw image data and create a new GD image and plot + // pixel-by-pixel on the GD image. This is extremely slow, but it does + // work and a slow solution is better than no solution, right? :) + return $img; + + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function gif_outputAsBmp($gif, $lpszFileName, $bgColor = -1) +{ + if (!isset($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) { + return false; + } + + $fd = $gif->getBmp($bgColor); + if (strlen($fd) <= 0) { + return false; + } + + if (!($fh = @fopen($lpszFileName, 'wb'))) { + return false; + } + @fwrite($fh, $fd, strlen($fd)); + @fflush($fh); + @fclose($fh); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function gif_outputAsPng($gif, $lpszFileName, $bgColor = -1) +{ + if (!isSet($gif) || (@get_class($gif) <> 'cgif') || !$gif->loaded() || ($lpszFileName == '')) { + return false; + } + + $fd = $gif->getPng($bgColor); + if (strlen($fd) <= 0) { + return false; + } + + if (!($fh = @fopen($lpszFileName, 'wb'))) { + return false; + } + @fwrite($fh, $fd, strlen($fd)); + @fflush($fh); + @fclose($fh); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function gif_outputAsJpeg($gif, $lpszFileName, $bgColor = -1) +{ + // JPEG output that does not require cjpeg added by James Heinrich - December 10, 2003 + if ((strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') && (file_exists('/usr/local/bin/cjpeg') || shell_exec('which cjpeg'))) { + + if (gif_outputAsBmp($gif, $lpszFileName.'.bmp', $bgColor)) { + exec('cjpeg '.$lpszFileName.'.bmp >'.$lpszFileName.' 2>/dev/null'); + @unlink($lpszFileName.'.bmp'); + + if (@file_exists($lpszFileName)) { + if (@filesize($lpszFileName) > 0) { + return true; + } + + @unlink($lpszFileName); + } + } + + } else { + + // either Windows, or cjpeg not found in path + if ($img = @imagecreatefromstring($gif->getPng($bgColor))) { + if (@imagejpeg($img, $lpszFileName)) { + return true; + } + } + + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +function gif_getSize($gif, &$width, &$height) +{ + if (isSet($gif) && (@get_class($gif) == 'cgif') && $gif->loaded()) { + $width = $gif->width(); + $height = $gif->height(); + } elseif (@file_exists($gif)) { + $myGIF = new CGIF(); + if (!$myGIF->getSize($gif, $width, $height)) { + return false; + } + } else { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIFLZW +{ + public $MAX_LZW_BITS; + public $Fresh, $CodeSize, $SetCodeSize, $MaxCode, $MaxCodeSize, $FirstCode, $OldCode; + public $ClearCode, $EndCode, $Next, $Vals, $Stack, $sp, $Buf, $CurBit, $LastBit, $Done, $LastByte; + + /////////////////////////////////////////////////////////////////////////// + + // CONSTRUCTOR + public function __construct() + { + $this->MAX_LZW_BITS = 12; + unSet($this->Next); + unSet($this->Vals); + unSet($this->Stack); + unSet($this->Buf); + + $this->Next = range(0, (1 << $this->MAX_LZW_BITS) - 1); + $this->Vals = range(0, (1 << $this->MAX_LZW_BITS) - 1); + $this->Stack = range(0, (1 << ($this->MAX_LZW_BITS + 1)) - 1); + $this->Buf = range(0, 279); + } + + /////////////////////////////////////////////////////////////////////////// + + public function deCompress($data, &$datLen) + { + $stLen = strlen($data); + $datLen = 0; + $ret = ''; + + // INITIALIZATION + $this->LZWCommand($data, true); + + while (($iIndex = $this->LZWCommand($data, false)) >= 0) { + $ret .= chr($iIndex); + } + + $datLen = $stLen - strlen($data); + + if ($iIndex != -2) { + return false; + } + + return $ret; + } + + /////////////////////////////////////////////////////////////////////////// + + public function LZWCommand(&$data, $bInit) + { + if ($bInit) { + $this->SetCodeSize = ord($data[0]); + $data = substr($data, 1); + + $this->CodeSize = $this->SetCodeSize + 1; + $this->ClearCode = 1 << $this->SetCodeSize; + $this->EndCode = $this->ClearCode + 1; + $this->MaxCode = $this->ClearCode + 2; + $this->MaxCodeSize = $this->ClearCode << 1; + + $this->GetCode($data, $bInit); + + $this->Fresh = 1; + for ($i = 0; $i < $this->ClearCode; $i++) { + $this->Next[$i] = 0; + $this->Vals[$i] = $i; + } + + for (; $i < (1 << $this->MAX_LZW_BITS); $i++) { + $this->Next[$i] = 0; + $this->Vals[$i] = 0; + } + + $this->sp = 0; + return 1; + } + + if ($this->Fresh) { + $this->Fresh = 0; + do { + $this->FirstCode = $this->GetCode($data, $bInit); + $this->OldCode = $this->FirstCode; + } + while ($this->FirstCode == $this->ClearCode); + + return $this->FirstCode; + } + + if ($this->sp > 0) { + $this->sp--; + return $this->Stack[$this->sp]; + } + + while (($Code = $this->GetCode($data, $bInit)) >= 0) { + if ($Code == $this->ClearCode) { + for ($i = 0; $i < $this->ClearCode; $i++) { + $this->Next[$i] = 0; + $this->Vals[$i] = $i; + } + + for (; $i < (1 << $this->MAX_LZW_BITS); $i++) { + $this->Next[$i] = 0; + $this->Vals[$i] = 0; + } + + $this->CodeSize = $this->SetCodeSize + 1; + $this->MaxCodeSize = $this->ClearCode << 1; + $this->MaxCode = $this->ClearCode + 2; + $this->sp = 0; + $this->FirstCode = $this->GetCode($data, $bInit); + $this->OldCode = $this->FirstCode; + + return $this->FirstCode; + } + + if ($Code == $this->EndCode) { + return -2; + } + + $InCode = $Code; + if ($Code >= $this->MaxCode) { + $this->Stack[$this->sp] = $this->FirstCode; + $this->sp++; + $Code = $this->OldCode; + } + + while ($Code >= $this->ClearCode) { + $this->Stack[$this->sp] = $this->Vals[$Code]; + $this->sp++; + + if ($Code == $this->Next[$Code]) // Circular table entry, big GIF Error! + return -1; + + $Code = $this->Next[$Code]; + } + + $this->FirstCode = $this->Vals[$Code]; + $this->Stack[$this->sp] = $this->FirstCode; + $this->sp++; + + if (($Code = $this->MaxCode) < (1 << $this->MAX_LZW_BITS)) { + $this->Next[$Code] = $this->OldCode; + $this->Vals[$Code] = $this->FirstCode; + $this->MaxCode++; + + if (($this->MaxCode >= $this->MaxCodeSize) && ($this->MaxCodeSize < (1 << $this->MAX_LZW_BITS))) { + $this->MaxCodeSize *= 2; + $this->CodeSize++; + } + } + + $this->OldCode = $InCode; + if ($this->sp > 0) { + $this->sp--; + return $this->Stack[$this->sp]; + } + } + + return $Code; + } + + /////////////////////////////////////////////////////////////////////////// + + public function GetCode(&$data, $bInit) + { + if ($bInit) { + $this->CurBit = 0; + $this->LastBit = 0; + $this->Done = 0; + $this->LastByte = 2; + return 1; + } + + if (($this->CurBit + $this->CodeSize) >= $this->LastBit) { + if ($this->Done) { + if ($this->CurBit >= $this->LastBit) { + // Ran off the end of my bits + return 0; + } + return -1; + } + + $this->Buf[0] = $this->Buf[$this->LastByte - 2]; + $this->Buf[1] = $this->Buf[$this->LastByte - 1]; + + $Count = ord($data[0]); + $data = substr($data, 1); + + if ($Count) { + for ($i = 0; $i < $Count; $i++) { + $this->Buf[2 + $i] = ord($data[$i]); + } + $data = substr($data, $Count); + } else { + $this->Done = 1; + } + + $this->LastByte = 2 + $Count; + $this->CurBit = ($this->CurBit - $this->LastBit) + 16; + $this->LastBit = (2 + $Count) << 3; + } + + $iRet = 0; + for ($i = $this->CurBit, $j = 0; $j < $this->CodeSize; $i++, $j++) { + $iRet |= (($this->Buf[ (int) ($i / 8) ] & (1 << ($i % 8))) != 0) << $j; + } + + $this->CurBit += $this->CodeSize; + return $iRet; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIFCOLORTABLE +{ + public $m_nColors; + public $m_arColors; + + /////////////////////////////////////////////////////////////////////////// + + // CONSTRUCTOR + public function __construct() + { + unSet($this->m_nColors); + unSet($this->m_arColors); + } + + /////////////////////////////////////////////////////////////////////////// + + public function load($lpData, $num) + { + $this->m_nColors = 0; + $this->m_arColors = array(); + + for ($i = 0; $i < $num; $i++) { + $rgb = substr($lpData, $i * 3, 3); + if (strlen($rgb) < 3) { + return false; + } + + $this->m_arColors[] = (ord($rgb[2]) << 16) + (ord($rgb[1]) << 8) + ord($rgb[0]); + $this->m_nColors++; + } + + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function toString() + { + $ret = ''; + + for ($i = 0; $i < $this->m_nColors; $i++) { + $ret .= + chr($this->m_arColors[ $i] & 0x000000FF) . // R + chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G + chr(($this->m_arColors[$i] & 0x00FF0000) >> 16); // B + } + + return $ret; + } + + /////////////////////////////////////////////////////////////////////////// + + public function toRGBQuad() + { + $ret = ''; + + for ($i = 0; $i < $this->m_nColors; $i++) { + $ret .= + chr(($this->m_arColors[$i] & 0x00FF0000) >> 16) . // B + chr(($this->m_arColors[$i] & 0x0000FF00) >> 8) . // G + chr($this->m_arColors[ $i] & 0x000000FF) . // R + "\x00"; + } + + return $ret; + } + + /////////////////////////////////////////////////////////////////////////// + + public function colorIndex($rgb) + { + $rgb = (int) $rgb & 0xFFFFFF; + $r1 = ($rgb & 0x0000FF); + $g1 = ($rgb & 0x00FF00) >> 8; + $b1 = ($rgb & 0xFF0000) >> 16; + $idx = -1; + $dif = 0; + + for ($i = 0; $i < $this->m_nColors; $i++) { + $r2 = ($this->m_arColors[$i] & 0x000000FF); + $g2 = ($this->m_arColors[$i] & 0x0000FF00) >> 8; + $b2 = ($this->m_arColors[$i] & 0x00FF0000) >> 16; + $d = abs($r2 - $r1) + abs($g2 - $g1) + abs($b2 - $b1); + + if (($idx == -1) || ($d < $dif)) { + $idx = $i; + $dif = $d; + } + } + + return $idx; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIFFILEHEADER +{ + public $m_lpVer; + public $m_nWidth; + public $m_nHeight; + public $m_bGlobalClr; + public $m_nColorRes; + public $m_bSorted; + public $m_nTableSize; + public $m_nBgColor; + public $m_nPixelRatio; + public $m_colorTable; + + /////////////////////////////////////////////////////////////////////////// + + // CONSTRUCTOR + public function __construct() + { + unSet($this->m_lpVer); + unSet($this->m_nWidth); + unSet($this->m_nHeight); + unSet($this->m_bGlobalClr); + unSet($this->m_nColorRes); + unSet($this->m_bSorted); + unSet($this->m_nTableSize); + unSet($this->m_nBgColor); + unSet($this->m_nPixelRatio); + unSet($this->m_colorTable); + } + + /////////////////////////////////////////////////////////////////////////// + + public function load($lpData, &$hdrLen) + { + $hdrLen = 0; + + $this->m_lpVer = substr($lpData, 0, 6); + if (($this->m_lpVer <> 'GIF87a') && ($this->m_lpVer <> 'GIF89a')) { + return false; + } + + $this->m_nWidth = $this->w2i(substr($lpData, 6, 2)); + $this->m_nHeight = $this->w2i(substr($lpData, 8, 2)); + if (!$this->m_nWidth || !$this->m_nHeight) { + return false; + } + + $b = ord($lpData[ 10 ]); + $this->m_bGlobalClr = ($b & 0x80) ? true : false; + $this->m_nColorRes = ($b & 0x70) >> 4; + $this->m_bSorted = ($b & 0x08) ? true : false; + $this->m_nTableSize = 2 << ($b & 0x07); + $this->m_nBgColor = ord($lpData[ 11 ]); + $this->m_nPixelRatio = ord($lpData[ 12 ]); + $hdrLen = 13; + + if ($this->m_bGlobalClr) { + $this->m_colorTable = new CGIFCOLORTABLE(); + if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) { + return false; + } + $hdrLen += 3 * $this->m_nTableSize; + } + + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function w2i($str) + { + return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIFIMAGEHEADER +{ + public $m_nLeft; + public $m_nTop; + public $m_nWidth; + public $m_nHeight; + public $m_bLocalClr; + public $m_bInterlace; + public $m_bSorted; + public $m_nTableSize; + public $m_colorTable; + + /////////////////////////////////////////////////////////////////////////// + + // CONSTRUCTOR + public function __construct() + { + unSet($this->m_nLeft); + unSet($this->m_nTop); + unSet($this->m_nWidth); + unSet($this->m_nHeight); + unSet($this->m_bLocalClr); + unSet($this->m_bInterlace); + unSet($this->m_bSorted); + unSet($this->m_nTableSize); + unSet($this->m_colorTable); + } + + /////////////////////////////////////////////////////////////////////////// + + public function load($lpData, &$hdrLen) + { + $hdrLen = 0; + + $this->m_nLeft = $this->w2i(substr($lpData, 0, 2)); + $this->m_nTop = $this->w2i(substr($lpData, 2, 2)); + $this->m_nWidth = $this->w2i(substr($lpData, 4, 2)); + $this->m_nHeight = $this->w2i(substr($lpData, 6, 2)); + + if (!$this->m_nWidth || !$this->m_nHeight) { + return false; + } + + $b = ord($lpData[8]); + $this->m_bLocalClr = ($b & 0x80) ? true : false; + $this->m_bInterlace = ($b & 0x40) ? true : false; + $this->m_bSorted = ($b & 0x20) ? true : false; + $this->m_nTableSize = 2 << ($b & 0x07); + $hdrLen = 9; + + if ($this->m_bLocalClr) { + $this->m_colorTable = new CGIFCOLORTABLE(); + if (!$this->m_colorTable->load(substr($lpData, $hdrLen), $this->m_nTableSize)) { + return false; + } + $hdrLen += 3 * $this->m_nTableSize; + } + + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function w2i($str) + { + return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIFIMAGE +{ + public $m_disp; + public $m_bUser; + public $m_bTrans; + public $m_nDelay; + public $m_nTrans; + public $m_lpComm; + public $m_gih; + public $m_data; + public $m_lzw; + + /////////////////////////////////////////////////////////////////////////// + + public function __construct() + { + unSet($this->m_disp); + unSet($this->m_bUser); + unSet($this->m_bTrans); + unSet($this->m_nDelay); + unSet($this->m_nTrans); + unSet($this->m_lpComm); + unSet($this->m_data); + $this->m_gih = new CGIFIMAGEHEADER(); + $this->m_lzw = new CGIFLZW(); + } + + /////////////////////////////////////////////////////////////////////////// + + public function load($data, &$datLen) + { + $datLen = 0; + + while (true) { + $b = ord($data[0]); + $data = substr($data, 1); + $datLen++; + + switch($b) { + case 0x21: // Extension + if (!$this->skipExt($data, $len = 0)) { + return false; + } + $datLen += $len; + break; + + case 0x2C: // Image + // LOAD HEADER & COLOR TABLE + if (!$this->m_gih->load($data, $len = 0)) { + return false; + } + $data = substr($data, $len); + $datLen += $len; + + // ALLOC BUFFER + if (!($this->m_data = $this->m_lzw->deCompress($data, $len = 0))) { + return false; + } + $data = substr($data, $len); + $datLen += $len; + + if ($this->m_gih->m_bInterlace) { + $this->deInterlace(); + } + return true; + + case 0x3B: // EOF + default: + return false; + } + } + return false; + } + + /////////////////////////////////////////////////////////////////////////// + + public function skipExt(&$data, &$extLen) + { + $extLen = 0; + + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + + switch($b) { + case 0xF9: // Graphic Control + $b = ord($data[1]); + $this->m_disp = ($b & 0x1C) >> 2; + $this->m_bUser = ($b & 0x02) ? true : false; + $this->m_bTrans = ($b & 0x01) ? true : false; + $this->m_nDelay = $this->w2i(substr($data, 2, 2)); + $this->m_nTrans = ord($data[4]); + break; + + case 0xFE: // Comment + $this->m_lpComm = substr($data, 1, ord($data[0])); + break; + + case 0x01: // Plain text + break; + + case 0xFF: // Application + break; + } + + // SKIP DEFAULT AS DEFS MAY CHANGE + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + while ($b > 0) { + $data = substr($data, $b); + $extLen += $b; + $b = ord($data[0]); + $data = substr($data, 1); + $extLen++; + } + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function w2i($str) + { + return ord($str[ 0 ]) + (ord($str[ 1 ]) << 8); + } + + /////////////////////////////////////////////////////////////////////////// + + public function deInterlace() + { + $data = $this->m_data; + $s = 0; + $y = 0; + + for ($i = 0; $i < 4; $i++) { + switch($i) { + case 0: + $s = 8; + $y = 0; + break; + + case 1: + $s = 8; + $y = 4; + break; + + case 2: + $s = 4; + $y = 2; + break; + + case 3: + $s = 2; + $y = 1; + break; + } + + for (; $y < $this->m_gih->m_nHeight; $y += $s) { + $lne = substr($this->m_data, 0, $this->m_gih->m_nWidth); + $this->m_data = substr($this->m_data, $this->m_gih->m_nWidth); + + $data = + substr($data, 0, $y * $this->m_gih->m_nWidth) . + $lne . + substr($data, ($y + 1) * $this->m_gih->m_nWidth); + } + } + + $this->m_data = $data; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class CGIF +{ + public $m_gfh; + public $m_lpData; + public $m_img; + public $m_bLoaded; + + /////////////////////////////////////////////////////////////////////////// + + // CONSTRUCTOR + public function __construct() + { + $this->m_gfh = new CGIFFILEHEADER(); + $this->m_img = new CGIFIMAGE(); + $this->m_lpData = ''; + $this->m_bLoaded = false; + } + + /////////////////////////////////////////////////////////////////////////// + + public function loadFile($lpszFileName, $iIndex) + { + if ($iIndex < 0) { + return false; + } + + // READ FILE + if (!($fh = @fopen($lpszFileName, 'rb'))) { + return false; + } + $this->m_lpData = @fread($fh, @filesize($lpszFileName)); + fclose($fh); + + // GET FILE HEADER + if (!$this->m_gfh->load($this->m_lpData, $len = 0)) { + return false; + } + $this->m_lpData = substr($this->m_lpData, $len); + + do { + if (!$this->m_img->load($this->m_lpData, $imgLen = 0)) { + return false; + } + $this->m_lpData = substr($this->m_lpData, $imgLen); + } + while ($iIndex-- > 0); + + $this->m_bLoaded = true; + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function getSize($lpszFileName, &$width, &$height) + { + if (!($fh = @fopen($lpszFileName, 'rb'))) { + return false; + } + $data = @fread($fh, @filesize($lpszFileName)); + @fclose($fh); + + $gfh = new CGIFFILEHEADER(); + if (!$gfh->load($data, $len = 0)) { + return false; + } + + $width = $gfh->m_nWidth; + $height = $gfh->m_nHeight; + return true; + } + + /////////////////////////////////////////////////////////////////////////// + + public function getBmp($bgColor) + { + $out = ''; + + if (!$this->m_bLoaded) { + return false; + } + + // PREPARE COLOR TABLE (RGBQUADs) + if ($this->m_img->m_gih->m_bLocalClr) { + $nColors = $this->m_img->m_gih->m_nTableSize; + $rgbq = $this->m_img->m_gih->m_colorTable->toRGBQuad(); + if ($bgColor != -1) { + $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor); + } + } elseif ($this->m_gfh->m_bGlobalClr) { + $nColors = $this->m_gfh->m_nTableSize; + $rgbq = $this->m_gfh->m_colorTable->toRGBQuad(); + if ($bgColor != -1) { + $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor); + } + } else { + $nColors = 0; + $rgbq = ''; + $bgColor = -1; + } + + // PREPARE BITMAP BITS + $data = $this->m_img->m_data; + $nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth; + $bmp = ''; + $nPad = ($this->m_gfh->m_nWidth % 4) ? 4 - ($this->m_gfh->m_nWidth % 4) : 0; + for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) { + for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) { + if ( + ($x >= $this->m_img->m_gih->m_nLeft) && + ($y >= $this->m_img->m_gih->m_nTop) && + ($x < ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) && + ($y < ($this->m_img->m_gih->m_nTop + $this->m_img->m_gih->m_nHeight))) { + // PART OF IMAGE + if (@$this->m_img->m_bTrans && (ord($data[$nPxl]) == $this->m_img->m_nTrans)) { + // TRANSPARENT -> BACKGROUND + if ($bgColor == -1) { + $bmp .= chr($this->m_gfh->m_nBgColor); + } else { + $bmp .= chr($bgColor); + } + } else { + $bmp .= $data[$nPxl]; + } + } else { + // BACKGROUND + if ($bgColor == -1) { + $bmp .= chr($this->m_gfh->m_nBgColor); + } else { + $bmp .= chr($bgColor); + } + } + } + $nPxl -= $this->m_gfh->m_nWidth << 1; + + // ADD PADDING + for ($x = 0; $x < $nPad; $x++) { + $bmp .= "\x00"; + } + } + + // BITMAPFILEHEADER + $out .= 'BM'; + $out .= $this->dword(14 + 40 + ($nColors << 2) + strlen($bmp)); + $out .= "\x00\x00"; + $out .= "\x00\x00"; + $out .= $this->dword(14 + 40 + ($nColors << 2)); + + // BITMAPINFOHEADER + $out .= $this->dword(40); + $out .= $this->dword($this->m_gfh->m_nWidth); + $out .= $this->dword($this->m_gfh->m_nHeight); + $out .= "\x01\x00"; + $out .= "\x08\x00"; + $out .= "\x00\x00\x00\x00"; + $out .= "\x00\x00\x00\x00"; + $out .= "\x12\x0B\x00\x00"; + $out .= "\x12\x0B\x00\x00"; + $out .= $this->dword($nColors % 256); + $out .= "\x00\x00\x00\x00"; + + // COLOR TABLE + if ($nColors > 0) { + $out .= $rgbq; + } + + // DATA + $out .= $bmp; + + return $out; + } + + /////////////////////////////////////////////////////////////////////////// + + public function getPng($bgColor) + { + $out = ''; + + if (!$this->m_bLoaded) { + return false; + } + + // PREPARE COLOR TABLE (RGBQUADs) + if ($this->m_img->m_gih->m_bLocalClr) { + $nColors = $this->m_img->m_gih->m_nTableSize; + $pal = $this->m_img->m_gih->m_colorTable->toString(); + if ($bgColor != -1) { + $bgColor = $this->m_img->m_gih->m_colorTable->colorIndex($bgColor); + } + } elseif ($this->m_gfh->m_bGlobalClr) { + $nColors = $this->m_gfh->m_nTableSize; + $pal = $this->m_gfh->m_colorTable->toString(); + if ($bgColor != -1) { + $bgColor = $this->m_gfh->m_colorTable->colorIndex($bgColor); + } + } else { + $nColors = 0; + $pal = ''; + $bgColor = -1; + } + + // PREPARE BITMAP BITS + $data = $this->m_img->m_data; + $nPxl = 0; + $bmp = ''; + for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) { + $bmp .= "\x00"; + for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) { + if ( + ($x >= $this->m_img->m_gih->m_nLeft) && + ($y >= $this->m_img->m_gih->m_nTop) && + ($x < ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) && + ($y < ($this->m_img->m_gih->m_nTop + $this->m_img->m_gih->m_nHeight))) { + // PART OF IMAGE + $bmp .= $data[$nPxl]; + } else { + // BACKGROUND + if ($bgColor == -1) { + $bmp .= chr($this->m_gfh->m_nBgColor); + } else { + $bmp .= chr($bgColor); + } + } + } + } + $bmp = gzcompress($bmp, 9); + + /////////////////////////////////////////////////////////////////////// + // SIGNATURE + $out .= "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; + /////////////////////////////////////////////////////////////////////// + // HEADER + $out .= "\x00\x00\x00\x0D"; + $tmp = 'IHDR'; + $tmp .= $this->ndword($this->m_gfh->m_nWidth); + $tmp .= $this->ndword($this->m_gfh->m_nHeight); + $tmp .= "\x08\x03\x00\x00\x00"; + $out .= $tmp; + $out .= $this->ndword(crc32($tmp)); + /////////////////////////////////////////////////////////////////////// + // PALETTE + if ($nColors > 0) { + $out .= $this->ndword($nColors * 3); + $tmp = 'PLTE'; + $tmp .= $pal; + $out .= $tmp; + $out .= $this->ndword(crc32($tmp)); + } + /////////////////////////////////////////////////////////////////////// + // TRANSPARENCY + if (@$this->m_img->m_bTrans && ($nColors > 0)) { + $out .= $this->ndword($nColors); + $tmp = 'tRNS'; + for ($i = 0; $i < $nColors; $i++) { + $tmp .= ($i == $this->m_img->m_nTrans) ? "\x00" : "\xFF"; + } + $out .= $tmp; + $out .= $this->ndword(crc32($tmp)); + } + /////////////////////////////////////////////////////////////////////// + // DATA BITS + $out .= $this->ndword(strlen($bmp)); + $tmp = 'IDAT'; + $tmp .= $bmp; + $out .= $tmp; + $out .= $this->ndword(crc32($tmp)); + /////////////////////////////////////////////////////////////////////// + // END OF FILE + $out .= "\x00\x00\x00\x00IEND\xAE\x42\x60\x82"; + + return $out; + } + + /////////////////////////////////////////////////////////////////////////// + + // Added by James Heinrich - January 5, 2003 + + // Takes raw image data and plots it pixel-by-pixel on a new GD image and returns that + // It's extremely slow, but the only solution when imagecreatefromstring() fails + public function getGD_PixelPlotterVersion() + { + if (!$this->m_bLoaded) { + return false; + } + + // PREPARE COLOR TABLE (RGBQUADs) + if ($this->m_img->m_gih->m_bLocalClr) { + $pal = $this->m_img->m_gih->m_colorTable->toString(); + } elseif ($this->m_gfh->m_bGlobalClr) { + $pal = $this->m_gfh->m_colorTable->toString(); + } else { + die('No color table available in getGD_PixelPlotterVersion()'); + } + + $PlottingIMG = imagecreate($this->m_gfh->m_nWidth, $this->m_gfh->m_nHeight); + $NumColorsInPal = floor(strlen($pal) / 3); + $ThisImageColor = array(); + for ($i = 0; $i < $NumColorsInPal; $i++) { + $ThisImageColor[$i] = imagecolorallocate( + $PlottingIMG, + ord($pal[($i * 3) + 0]), + ord($pal[($i * 3) + 1]), + ord($pal[($i * 3) + 2])); + } + + // PREPARE BITMAP BITS + $data = $this->m_img->m_data; + $nPxl = ($this->m_gfh->m_nHeight - 1) * $this->m_gfh->m_nWidth; + for ($y = 0; $y < $this->m_gfh->m_nHeight; $y++) { + if (!phpthumb_functions::FunctionIsDisabled('set_time_limit')) { + set_time_limit(30); + } + for ($x = 0; $x < $this->m_gfh->m_nWidth; $x++, $nPxl++) { + if ( + ($x >= $this->m_img->m_gih->m_nLeft) && + ($y >= $this->m_img->m_gih->m_nTop) && + ($x < ($this->m_img->m_gih->m_nLeft + $this->m_img->m_gih->m_nWidth)) && + ($y < ($this->m_img->m_gih->m_nTop + $this->m_img->m_gih->m_nHeight))) { + // PART OF IMAGE + if (@$this->m_img->m_bTrans && (ord($data[$nPxl]) == $this->m_img->m_nTrans)) { + imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]); + } else { + imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[ord($data[$nPxl])]); + } + } else { + // BACKGROUND + imagesetpixel($PlottingIMG, $x, $this->m_gfh->m_nHeight - $y - 1, $ThisImageColor[$this->m_gfh->m_nBgColor]); + } + } + $nPxl -= $this->m_gfh->m_nWidth << 1; + + } + + return $PlottingIMG; + } + + /////////////////////////////////////////////////////////////////////////// + + public function dword($val) + { + $val = (int) $val; + return chr($val & 0xFF).chr(($val & 0xFF00) >> 8).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF000000) >> 24); + } + + /////////////////////////////////////////////////////////////////////////// + + public function ndword($val) + { + $val = (int) $val; + return chr(($val & 0xFF000000) >> 24).chr(($val & 0xFF0000) >> 16).chr(($val & 0xFF00) >> 8).chr($val & 0xFF); + } + + /////////////////////////////////////////////////////////////////////////// + + public function width() + { + return $this->m_gfh->m_nWidth; + } + + /////////////////////////////////////////////////////////////////////////// + + public function height() + { + return $this->m_gfh->m_nHeight; + } + + /////////////////////////////////////////////////////////////////////////// + + public function comment() + { + return $this->m_img->m_lpComm; + } + + /////////////////////////////////////////////////////////////////////////// + + public function loaded() + { + return $this->m_bLoaded; + } +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.ico.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.ico.php new file mode 100644 index 0000000..9c90613 --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.ico.php @@ -0,0 +1,120 @@ + // +// available at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// +/// // +// phpthumb.ico.php - .ICO output format functions // +// /// +////////////////////////////////////////////////////////////// + + +class phpthumb_ico { + + public function GD2ICOstring(&$gd_image_array) { + $ImageWidths = array(); + $ImageHeights = array(); + $bpp = array(); + $totalcolors = array(); + $icXOR = array(); + $icAND = array(); + $icANDmask = array(); + foreach ($gd_image_array as $key => $gd_image) { + + $ImageWidths[$key] = imagesx($gd_image); + $ImageHeights[$key] = imagesy($gd_image); + $bpp[$key] = imageistruecolor($gd_image) ? 32 : 24; + $totalcolors[$key] = imagecolorstotal($gd_image); + + $icXOR[$key] = ''; + for ($y = $ImageHeights[$key] - 1; $y >= 0; $y--) { + for ($x = 0; $x < $ImageWidths[$key]; $x++) { + $argb = phpthumb_functions::GetPixelColor($gd_image, $x, $y); + $a = round(255 * ((127 - $argb['alpha']) / 127)); + $r = $argb['red']; + $g = $argb['green']; + $b = $argb['blue']; + + if ($bpp[$key] == 32) { + $icXOR[$key] .= chr($b).chr($g).chr($r).chr($a); + } elseif ($bpp[$key] == 24) { + $icXOR[$key] .= chr($b).chr($g).chr($r); + } + + if ($a < 128) { + @$icANDmask[$key][$y] .= '1'; + } else { + @$icANDmask[$key][$y] .= '0'; + } + } + // mask bits are 32-bit aligned per scanline + while (strlen($icANDmask[$key][$y]) % 32) { + $icANDmask[$key][$y] .= '0'; + } + } + $icAND[$key] = ''; + foreach ($icANDmask[$key] as $y => $scanlinemaskbits) { + for ($i = 0, $iMax = strlen($scanlinemaskbits); $i < $iMax; $i += 8) { + $icAND[$key] .= chr(bindec(str_pad(substr($scanlinemaskbits, $i, 8), 8, '0', STR_PAD_LEFT))); + } + } + + } + + foreach ($gd_image_array as $key => $gd_image) { + $biSizeImage = $ImageWidths[$key] * $ImageHeights[$key] * ($bpp[$key] / 8); + + // BITMAPINFOHEADER - 40 bytes + $BitmapInfoHeader[$key] = ''; + $BitmapInfoHeader[$key] .= "\x28\x00\x00\x00"; // DWORD biSize; + $BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageWidths[$key], 4); // LONG biWidth; + // The biHeight member specifies the combined + // height of the XOR and AND masks. + $BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($ImageHeights[$key] * 2, 4); // LONG biHeight; + $BitmapInfoHeader[$key] .= "\x01\x00"; // WORD biPlanes; + $BitmapInfoHeader[$key] .= chr($bpp[$key])."\x00"; // wBitCount; + $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biCompression; + $BitmapInfoHeader[$key] .= phpthumb_functions::LittleEndian2String($biSizeImage, 4); // DWORD biSizeImage; + $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biXPelsPerMeter; + $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // LONG biYPelsPerMeter; + $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrUsed; + $BitmapInfoHeader[$key] .= "\x00\x00\x00\x00"; // DWORD biClrImportant; + } + + + $icondata = "\x00\x00"; // idReserved; // Reserved (must be 0) + $icondata .= "\x01\x00"; // idType; // Resource Type (1 for icons) + $icondata .= phpthumb_functions::LittleEndian2String(count($gd_image_array), 2); // idCount; // How many images? + + $dwImageOffset = 6 + (count($gd_image_array) * 16); + foreach ($gd_image_array as $key => $gd_image) { + // ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) + + $icondata .= chr($ImageWidths[$key]); // bWidth; // Width, in pixels, of the image + $icondata .= chr($ImageHeights[$key]); // bHeight; // Height, in pixels, of the image + $icondata .= chr($totalcolors[$key]); // bColorCount; // Number of colors in image (0 if >=8bpp) + $icondata .= "\x00"; // bReserved; // Reserved ( must be 0) + + $icondata .= "\x01\x00"; // wPlanes; // Color Planes + $icondata .= chr($bpp[$key])."\x00"; // wBitCount; // Bits per pixel + + $dwBytesInRes = 40 + strlen($icXOR[$key]) + strlen($icAND[$key]); + $icondata .= phpthumb_functions::LittleEndian2String($dwBytesInRes, 4); // dwBytesInRes; // How many bytes in this resource? + + $icondata .= phpthumb_functions::LittleEndian2String($dwImageOffset, 4); // dwImageOffset; // Where in the file is this image? + $dwImageOffset += strlen($BitmapInfoHeader[$key]); + $dwImageOffset += strlen($icXOR[$key]); + $dwImageOffset += strlen($icAND[$key]); + } + + foreach ($gd_image_array as $key => $gd_image) { + $icondata .= $BitmapInfoHeader[$key]; + $icondata .= $icXOR[$key]; + $icondata .= $icAND[$key]; + } + + return $icondata; + } + +} diff --git a/videodb/vendor/james-heinrich/phpthumb/phpthumb.unsharp.php b/videodb/vendor/james-heinrich/phpthumb/phpthumb.unsharp.php new file mode 100644 index 0000000..a5c454e --- /dev/null +++ b/videodb/vendor/james-heinrich/phpthumb/phpthumb.unsharp.php @@ -0,0 +1,149 @@ + // +// for use in phpThumb() on 3 February 2003. // +// updated to v2.1.1 on 24 April 2011 // +// // +// phpThumb() is found at http://phpthumb.sourceforge.net // +// and/or https://github.com/JamesHeinrich/phpThumb // +////////////////////////////////////////////////////////////// + +/* +New: +- In version 2.1 (February 26 2007) Tom Bishop has done some important speed enhancements. +- From version 2 (July 17 2006) the script uses the imageconvolution function in PHP + version >= 5.1, which improves the performance considerably. + +Unsharp masking is a traditional darkroom technique that has proven very suitable for +digital imaging. The principle of unsharp masking is to create a blurred copy of the image +and compare it to the underlying original. The difference in colour values +between the two images is greatest for the pixels near sharp edges. When this +difference is subtracted from the original image, the edges will be +accentuated. + +The Amount parameter simply says how much of the effect you want. 100 is 'normal'. +Radius is the radius of the blurring circle of the mask. 'Threshold' is the least +difference in colour values that is allowed between the original and the mask. In practice +this means that low-contrast areas of the picture are left unrendered whereas edges +are treated normally. This is good for pictures of e.g. skin or blue skies. + +Any suggenstions for improvement of the algorithm, especially regarding the speed +and the roundoff errors in the Gaussian blur process, are welcome. +*/ + +class phpUnsharpMask { + + public static function applyUnsharpMask(&$img, $amount, $radius, $threshold) { + + // $img is an image that is already created within php using + // imgcreatetruecolor. No url! $img must be a truecolor image. + + // Attempt to calibrate the parameters to Photoshop: + $amount = min($amount, 500) * 0.016; + $radius = abs(round(min(50, $radius) * 2)); // Only integers make sense. + $threshold = min(255, $threshold); + if ($radius == 0) { + return true; + } + $w = imagesx($img); + $h = imagesy($img); + $imgCanvas = imagecreatetruecolor($w, $h); + $imgBlur = imagecreatetruecolor($w, $h); + + // Gaussian blur matrix: + // + // 1 2 1 + // 2 4 2 + // 1 2 1 + // + ////////////////////////////////////////////////// + + if (function_exists('imageconvolution')) { // PHP >= 5.1 + $matrix = array( + array(1, 2, 1), + array(2, 4, 2), + array(1, 2, 1) + ); + imagecopy($imgBlur, $img, 0, 0, 0, 0, $w, $h); + imageconvolution($imgBlur, $matrix, 16, 0); + + } else { + + // Move copies of the image around one pixel at the time and merge them with weight + // according to the matrix. The same matrix is simply repeated for higher radii. + for ($i = 0; $i < $radius; $i++) { + imagecopy( $imgBlur, $img, 0, 0, 1, 0, $w - 1, $h); // left + imagecopymerge($imgBlur, $img, 1, 0, 0, 0, $w , $h, 50); // right + imagecopymerge($imgBlur, $img, 0, 0, 0, 0, $w , $h, 50); // center + imagecopy( $imgCanvas, $imgBlur, 0, 0, 0, 0, $w , $h); + imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w , $h - 1, 33.33333); // up + imagecopymerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w , $h, 25); // down + } + } + + if ($threshold > 0){ + // Calculate the difference between the blurred pixels and the original + // and set the pixels + for ($x = 0; $x < $w-1; $x++) { // each row + for ($y = 0; $y < $h; $y++) { // each pixel + + $rgbOrig = imagecolorat($img, $x, $y); + $rOrig = (($rgbOrig >> 16) & 0xFF); + $gOrig = (($rgbOrig >> 8) & 0xFF); + $bOrig = ($rgbOrig & 0xFF); + + $rgbBlur = imagecolorat($imgBlur, $x, $y); + + $rBlur = (($rgbBlur >> 16) & 0xFF); + $gBlur = (($rgbBlur >> 8) & 0xFF); + $bBlur = ($rgbBlur & 0xFF); + + // When the masked pixels differ less from the original + // than the threshold specifies, they are set to their original value. + $rNew = ((abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig); + $gNew = ((abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig); + $bNew = ((abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig); + + if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) { + $pixCol = imagecolorallocate($img, $rNew, $gNew, $bNew); + imagesetpixel($img, $x, $y, $pixCol); + } + } + } + } else { + for ($x = 0; $x < $w; $x++) { // each row + for ($y = 0; $y < $h; $y++) { // each pixel + $rgbOrig = imagecolorat($img, $x, $y); + $rOrig = (($rgbOrig >> 16) & 0xFF); + $gOrig = (($rgbOrig >> 8) & 0xFF); + $bOrig = ($rgbOrig & 0xFF); + + $rgbBlur = imagecolorat($imgBlur, $x, $y); + + $rBlur = (($rgbBlur >> 16) & 0xFF); + $gBlur = (($rgbBlur >> 8) & 0xFF); + $bBlur = ($rgbBlur & 0xFF); + + $rNew = min(255, max(0, ($amount * ($rOrig - $rBlur)) + $rOrig)); + $gNew = min(255, max(0, ($amount * ($gOrig - $gBlur)) + $gOrig)); + $bNew = min(255, max(0, ($amount * ($bOrig - $bBlur)) + $bOrig)); + $rgbNew = ($rNew << 16) + ($gNew <<8) + $bNew; + imagesetpixel($img, $x, $y, $rgbNew); + } + } + } + imagedestroy($imgCanvas); + imagedestroy($imgBlur); + return true; + } +} diff --git a/videodb/vendor/paragonie/random_compat/LICENSE b/videodb/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 0000000..45c7017 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/videodb/vendor/paragonie/random_compat/composer.json b/videodb/vendor/paragonie/random_compat/composer.json new file mode 100644 index 0000000..794e7fe --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,38 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "polyfill", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": [ + "lib/random.php" + ] + } +} diff --git a/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 0000000..eb50ebf --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 0000000..6a1d7f3 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.22 (MingW32) + +iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip +QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg +1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW +NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA +NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV +JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74= +=B6+8 +-----END PGP SIGNATURE----- diff --git a/videodb/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/videodb/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 0000000..ef24488 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,195 @@ + RandomCompat_strlen($binary_string)) { + return ''; + } + + return (string) mb_substr( + (string) $binary_string, + (int) $start, + (int) $length, + '8bit' + ); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int|null $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return (string) substr( + (string )$binary_string, + (int) $start, + (int) $length + ); + } + + return (string) substr( + (string) $binary_string, + (int) $start + ); + } + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/cast_to_int.php b/videodb/vendor/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 0000000..1b1bbfe --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,77 @@ + operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param bool $fail_open Set to true to not throw an exception + * + * @return float|int + * @psalm-suppress InvalidReturnType + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_int($number) || is_float($number)) { + $number += 0; + } elseif (is_numeric($number)) { + /** @psalm-suppress InvalidOperand */ + $number += 0; + } + /** @var int|float $number */ + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number)) { + return (int) $number; + } elseif (!$fail_open) { + throw new TypeError( + 'Expected an integer.' + ); + } + return $number; + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/error_polyfill.php b/videodb/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 0000000..c02c5c8 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,49 @@ += 70000) { + return; +} + +if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); +} + +$RandomCompatDIR = dirname(__FILE__); + +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'byte_safe_strings.php'; +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'cast_to_int.php'; +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'error_polyfill.php'; + +if (!is_callable('random_bytes')) { + /** + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. Use libsodium if available. + * 2. fread() /dev/urandom if available (never on Windows) + * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) + * 4. COM('CAPICOM.Utilities.1')->GetRandom() + * + * See RATIONALE.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !is_callable('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + * + * We only want to use mcypt_create_iv() if: + * + * - random_bytes() hasn't already been defined + * - the mcrypt extensions is loaded + * - One of these two conditions is true: + * - We're on Windows (DIRECTORY_SEPARATOR !== '/') + * - We're not on Windows and /dev/urandom is readabale + * (i.e. we're not in a chroot jail) + * - Special case: + * - If we're not on Windows, but the PHP version is between + * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will + * hang indefinitely. This is bad. + * - If we're on Windows, we want to use PHP >= 5.3.7 or else + * we get insufficient entropy errors. + */ + if ( + !is_callable('random_bytes') + && + // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be. + (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307) + && + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) + && + extension_loaded('mcrypt') + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_mcrypt.php'; + } + $RandomCompatUrandom = null; + + /** + * This is a Windows-specific fallback, for when the mcrypt extension + * isn't loaded. + */ + if ( + !is_callable('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + /** @psalm-suppress TypeDoesNotContainType */ + if (is_callable(array($RandomCompatCOMtest, 'GetRandom'))) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!is_callable('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + * + * @param mixed $length + * @psalm-suppress InvalidReturnType + * @throws Exception + * @return string + */ + function random_bytes($length) + { + unset($length); // Suppress "variable not used" warnings. + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + return ''; + } + } +} + +if (!is_callable('random_int')) { + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_int.php'; +} + +$RandomCompatDIR = null; diff --git a/videodb/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/videodb/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 0000000..537d02b --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,91 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return (string) RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/videodb/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 0000000..c4e31cc --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,190 @@ + $st */ + $st = fstat($fp); + if (($st['mode'] & 0170000) !== 020000) { + fclose($fp); + $fp = false; + } + } + } + + if (is_resource($fp)) { + /** + * stream_set_read_buffer() does not exist in HHVM + * + * If we don't set the stream's read buffer to 0, PHP will + * internally buffer 8192 bytes, which can waste entropy + * + * stream_set_read_buffer returns 0 on success + */ + if (is_callable('stream_set_read_buffer')) { + stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER); + } + if (is_callable('stream_set_chunk_size')) { + stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER); + } + } + } + + try { + /** @var int $bytes */ + $bytes = RandomCompat_intval($bytes); + } catch (TypeError $ex) { + throw new TypeError( + 'random_bytes(): $bytes must be an integer' + ); + } + + if ($bytes < 1) { + throw new Error( + 'Length must be greater than 0' + ); + } + + /** + * This if() block only runs if we managed to open a file handle + * + * It does not belong in an else {} block, because the above + * if (empty($fp)) line is logic that should only be run once per + * page load. + */ + if (is_resource($fp)) { + /** + * @var int + */ + $remaining = $bytes; + + /** + * @var string|bool + */ + $buf = ''; + + /** + * We use fread() in a loop to protect against partial reads + */ + do { + /** + * @var string|bool + */ + $read = fread($fp, $remaining); + if (!is_string($read)) { + /** + * We cannot safely read from the file. Exit the + * do-while loop and trigger the exception condition + * + * @var string|bool + */ + $buf = false; + break; + } + /** + * Decrease the number of bytes returned from remaining + */ + $remaining -= RandomCompat_strlen($read); + /** + * @var string $buf + */ + $buf .= $read; + } while ($remaining > 0); + + /** + * Is our result valid? + * @var string|bool $buf + */ + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 0000000..2e56290 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,91 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + /** @var string|bool $buf */ + $buf = \Sodium\randombytes_buf($bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 0000000..f78b219 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,93 @@ + 2147483647) { + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf((int) $n); + } + } else { + $buf .= Sodium::randombytes_buf((int) $bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/videodb/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/videodb/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 0000000..0b13fa7 --- /dev/null +++ b/videodb/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,79 @@ + operators might accidentally let a float + * through. + */ + + try { + /** @var int $min */ + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + /** @var int $max */ + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return (int) $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + /** @var int $attempts */ + /** @var int $bits */ + /** @var int $bytes */ + /** @var int $mask */ + /** @var int $valueShift */ + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + * + * @var int|float $range + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + /** @var int $mask */ + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + /** @var int $mask */ + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + /** @var int $val */ + $val = 0; + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + /** @psalm-suppress RedundantCondition */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val &= 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + /** @var int $val */ + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; + } +} diff --git a/videodb/vendor/pear/console_getopt/.gitignore b/videodb/vendor/pear/console_getopt/.gitignore new file mode 100644 index 0000000..7835828 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/.gitignore @@ -0,0 +1,6 @@ +# composer related +composer.lock +composer.phar +vendor +README.html +dist/ diff --git a/videodb/vendor/pear/console_getopt/.travis.yml b/videodb/vendor/pear/console_getopt/.travis.yml new file mode 100644 index 0000000..2711415 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/.travis.yml @@ -0,0 +1,9 @@ +language: php +php: + - 7 + - 5.6 + - 5.5 + - 5.4 +sudo: false +script: + - pear run-tests -r tests/ diff --git a/videodb/vendor/pear/console_getopt/Console/Getopt.php b/videodb/vendor/pear/console_getopt/Console/Getopt.php new file mode 100644 index 0000000..e5793bb --- /dev/null +++ b/videodb/vendor/pear/console_getopt/Console/Getopt.php @@ -0,0 +1,365 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause + * @version CVS: $Id$ + * @link http://pear.php.net/package/Console_Getopt + */ + +require_once 'PEAR.php'; + +/** + * Command-line options parsing class. + * + * @category Console + * @package Console_Getopt + * @author Andrei Zmievski + * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause + * @link http://pear.php.net/package/Console_Getopt + */ +class Console_Getopt +{ + + /** + * Parses the command-line options. + * + * The first parameter to this function should be the list of command-line + * arguments without the leading reference to the running program. + * + * The second parameter is a string of allowed short options. Each of the + * option letters can be followed by a colon ':' to specify that the option + * requires an argument, or a double colon '::' to specify that the option + * takes an optional argument. + * + * The third argument is an optional array of allowed long options. The + * leading '--' should not be included in the option name. Options that + * require an argument should be followed by '=', and options that take an + * option argument should be followed by '=='. + * + * The return value is an array of two elements: the list of parsed + * options and the list of non-option command-line arguments. Each entry in + * the list of parsed options is a pair of elements - the first one + * specifies the option, and the second one specifies the option argument, + * if there was one. + * + * Long and short options can be mixed. + * + * Most of the semantics of this function are based on GNU getopt_long(). + * + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return array two-element array containing the list of parsed options and + * the non-option arguments + */ + public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false) + { + return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown); + } + + /** + * This function expects $args to start with the script name (POSIX-style). + * Preserved for backwards compatibility. + * + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * + * @see getopt2() + * @return array two-element array containing the list of parsed options and + * the non-option arguments + */ + public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false) + { + return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown); + } + + /** + * The actual implementation of the argument parsing code. + * + * @param int $version Version to use + * @param array $args an array of command-line arguments + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return array + */ + public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false) + { + // in case you pass directly readPHPArgv() as the first arg + if (PEAR::isError($args)) { + return $args; + } + + if (empty($args)) { + return array(array(), array()); + } + + $non_opts = $opts = array(); + + settype($args, 'array'); + + if ($long_options) { + sort($long_options); + } + + /* + * Preserve backwards compatibility with callers that relied on + * erroneous POSIX fix. + */ + if ($version < 2) { + if (isset($args[0][0]) && $args[0][0] != '-') { + array_shift($args); + } + } + + for ($i = 0; $i < count($args); $i++) { + $arg = $args[$i]; + /* The special element '--' means explicit end of + options. Treat the rest of the arguments as non-options + and end the loop. */ + if ($arg == '--') { + $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); + break; + } + + if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) { + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } elseif (strlen($arg) > 1 && $arg[1] == '-') { + $error = Console_Getopt::_parseLongOption(substr($arg, 2), + $long_options, + $opts, + $i, + $args, + $skip_unknown); + if (PEAR::isError($error)) { + return $error; + } + } elseif ($arg == '-') { + // - is stdin + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } else { + $error = Console_Getopt::_parseShortOption(substr($arg, 1), + $short_options, + $opts, + $i, + $args, + $skip_unknown); + if (PEAR::isError($error)) { + return $error; + } + } + } + + return array($opts, $non_opts); + } + + /** + * Parse short option + * + * @param string $arg Argument + * @param string[] $short_options Available short options + * @param string[][] &$opts + * @param int &$argIdx + * @param string[] $args + * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option + * + * @return void + */ + protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown) + { + for ($i = 0; $i < strlen($arg); $i++) { + $opt = $arg[$i]; + $opt_arg = null; + + /* Try to find the short option in the specifier string. */ + if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') { + if ($skip_unknown === true) { + break; + } + + $msg = "Console_Getopt: unrecognized option -- $opt"; + return PEAR::raiseError($msg); + } + + if (strlen($spec) > 1 && $spec[1] == ':') { + if (strlen($spec) > 2 && $spec[2] == ':') { + if ($i + 1 < strlen($arg)) { + /* Option takes an optional argument. Use the remainder of + the arg string if there is anything left. */ + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + } else { + /* Option requires an argument. Use the remainder of the arg + string if there is anything left. */ + if ($i + 1 < strlen($arg)) { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } else if (isset($args[++$argIdx])) { + $opt_arg = $args[$argIdx]; + /* Else use the next argument. */; + if (Console_Getopt::_isShortOpt($opt_arg) + || Console_Getopt::_isLongOpt($opt_arg)) { + $msg = "option requires an argument --$opt"; + return PEAR::raiseError("Console_Getopt: " . $msg); + } + } else { + $msg = "option requires an argument --$opt"; + return PEAR::raiseError("Console_Getopt: " . $msg); + } + } + } + + $opts[] = array($opt, $opt_arg); + } + } + + /** + * Checks if an argument is a short option + * + * @param string $arg Argument to check + * + * @return bool + */ + protected static function _isShortOpt($arg) + { + return strlen($arg) == 2 && $arg[0] == '-' + && preg_match('/[a-zA-Z]/', $arg[1]); + } + + /** + * Checks if an argument is a long option + * + * @param string $arg Argument to check + * + * @return bool + */ + protected static function _isLongOpt($arg) + { + return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' && + preg_match('/[a-zA-Z]+$/', substr($arg, 2)); + } + + /** + * Parse long option + * + * @param string $arg Argument + * @param string[] $long_options Available long options + * @param string[][] &$opts + * @param int &$argIdx + * @param string[] $args + * + * @return void|PEAR_Error + */ + protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown) + { + @list($opt, $opt_arg) = explode('=', $arg, 2); + + $opt_len = strlen($opt); + + for ($i = 0; $i < count($long_options); $i++) { + $long_opt = $long_options[$i]; + $opt_start = substr($long_opt, 0, $opt_len); + + $long_opt_name = str_replace('=', '', $long_opt); + + /* Option doesn't match. Go on to the next one. */ + if ($long_opt_name != $opt) { + continue; + } + + $opt_rest = substr($long_opt, $opt_len); + + /* Check that the options uniquely matches one of the allowed + options. */ + if ($i + 1 < count($long_options)) { + $next_option_rest = substr($long_options[$i + 1], $opt_len); + } else { + $next_option_rest = ''; + } + + if ($opt_rest != '' && $opt[0] != '=' && + $i + 1 < count($long_options) && + $opt == substr($long_options[$i+1], 0, $opt_len) && + $next_option_rest != '' && + $next_option_rest[0] != '=') { + + $msg = "Console_Getopt: option --$opt is ambiguous"; + return PEAR::raiseError($msg); + } + + if (substr($long_opt, -1) == '=') { + if (substr($long_opt, -2) != '==') { + /* Long option requires an argument. + Take the next argument if one wasn't specified. */; + if (!strlen($opt_arg)) { + if (!isset($args[++$argIdx])) { + $msg = "Console_Getopt: option requires an argument --$opt"; + return PEAR::raiseError($msg); + } + $opt_arg = $args[$argIdx]; + } + + if (Console_Getopt::_isShortOpt($opt_arg) + || Console_Getopt::_isLongOpt($opt_arg)) { + $msg = "Console_Getopt: option requires an argument --$opt"; + return PEAR::raiseError($msg); + } + } + } else if ($opt_arg) { + $msg = "Console_Getopt: option --$opt doesn't allow an argument"; + return PEAR::raiseError($msg); + } + + $opts[] = array('--' . $opt, $opt_arg); + return; + } + + if ($skip_unknown === true) { + return; + } + + return PEAR::raiseError("Console_Getopt: unrecognized option --$opt"); + } + + /** + * Safely read the $argv PHP array across different PHP configurations. + * Will take care on register_globals and register_argc_argv ini directives + * + * @return mixed the $argv PHP array or PEAR error if not registered + */ + public static function readPHPArgv() + { + global $argv; + if (!is_array($argv)) { + if (!@is_array($_SERVER['argv'])) { + if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) { + $msg = "Could not read cmd args (register_argc_argv=Off?)"; + return PEAR::raiseError("Console_Getopt: " . $msg); + } + return $GLOBALS['HTTP_SERVER_VARS']['argv']; + } + return $_SERVER['argv']; + } + return $argv; + } + +} diff --git a/videodb/vendor/pear/console_getopt/LICENSE b/videodb/vendor/pear/console_getopt/LICENSE new file mode 100644 index 0000000..452b088 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2001-2015, The PEAR developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/videodb/vendor/pear/console_getopt/README.rst b/videodb/vendor/pear/console_getopt/README.rst new file mode 100644 index 0000000..64e5b41 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/README.rst @@ -0,0 +1,26 @@ +******************************************* +Console_Getopt - Command-line option parser +******************************************* + +This is a PHP implementation of "getopt" supporting both short and long options. +It helps parsing command line options in your PHP script. + +Homepage: http://pear.php.net/package/Console_Getopt + +.. image:: https://travis-ci.org/pear/Console_Getopt.svg?branch=master + :target: https://travis-ci.org/pear/Console_Getopt + + +Alternatives +============ + +* Console_CommandLine__ (recommended) +* Console_GetoptPlus__ + +__ http://pear.php.net/package/Console_CommandLine +__ http://pear.php.net/package/Console_GetoptPlus + + +License +======= +BSD-2-Clause diff --git a/videodb/vendor/pear/console_getopt/composer.json b/videodb/vendor/pear/console_getopt/composer.json new file mode 100644 index 0000000..4dc7e7c --- /dev/null +++ b/videodb/vendor/pear/console_getopt/composer.json @@ -0,0 +1,35 @@ +{ + "authors": [ + { + "email": "andrei@php.net", + "name": "Andrei Zmievski", + "role": "Lead" + }, + { + "email": "stig@php.net", + "name": "Stig Bakken", + "role": "Developer" + }, + { + "email": "cellog@php.net", + "name": "Greg Beaver", + "role": "Helper" + } + ], + "autoload": { + "psr-0": { + "Console": "./" + } + }, + "description": "More info available on: http://pear.php.net/package/Console_Getopt", + "include-path": [ + "./" + ], + "license": "BSD-2-Clause", + "name": "pear/console_getopt", + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt", + "source": "https://github.com/pear/Console_Getopt" + }, + "type": "library" +} diff --git a/videodb/vendor/pear/console_getopt/package.xml b/videodb/vendor/pear/console_getopt/package.xml new file mode 100644 index 0000000..d3fd784 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/package.xml @@ -0,0 +1,302 @@ + + + Console_Getopt + pear.php.net + Command-line option parser + This is a PHP implementation of "getopt" supporting both +short and long options. + + Andrei Zmievski + andrei + andrei@php.net + no + + + Stig Bakken + ssb + stig@php.net + no + + + Greg Beaver + cellog + cellog@php.net + no + + + 2019-11-20 + + 1.4.3 + 1.4.0 + + + stable + stable + + BSD-2-Clause + + +* PR #4: Fix PHP 7.4 deprecation: array/string curly braces access +* PR #5: fix phplint warnings + + + + + + + + + + + + + + + + + + PEAR + pear.php.net + 1.4.0 + 1.999.999 + + + + + + 5.4.0 + + + 1.8.0 + + + + + + + + + + 2019-11-20 + + 1.4.3 + 1.4.0 + + + stable + stable + + BSD-2-Clause + + * PR #4: Fix PHP 7.4 deprecation: array/string curly braces access + * PR #5: fix phplint warnings + + + + + 2019-02-06 + + 1.4.2 + 1.4.0 + + + stable + stable + + BSD-2-Clause + + * Remove use of each(), which is removed in PHP 8 + + + + + 2015-07-20 + + 1.4.1 + 1.4.0 + + + stable + stable + + BSD-2-Clause + + * Fix unit test on PHP 7 [cweiske] + + + + + 2015-02-22 + + 1.4.0 + 1.4.0 + + + stable + stable + + BSD-2-Clause + + * Change license to BSD-2-Clause + * Set minimum PHP version to 5.4.0 + * Mark static methods with "static" keyword + + + + + 2011-03-07 + + 1.3.1 + 1.3.0 + + + stable + stable + + PHP License + + * Change the minimum PEAR installer dep to be lower + + + + + 2010-12-11 + + + 1.3.0 + 1.3.0 + + + stable + stable + + PHP License + + * Implement Request #13140: [PATCH] to skip unknown parameters. [patch by rquadling, improved on by dufuz] + + + + + 2007-06-12 + + 1.2.3 + 1.2.1 + + + stable + stable + + PHP License + +* fix Bug #11068: No way to read plain "-" option [cardoe] + + + + + 1.2.2 + 1.2.1 + + + stable + stable + + 2007-02-17 + PHP License + +* fix Bug #4475: An ambiguous error occurred when specifying similar longoption name. +* fix Bug #10055: Not failing properly on short options missing required values + + + + + 1.2.1 + 1.2.1 + + + stable + stable + + 2006-12-08 + PHP License + +Fixed bugs #4448 (Long parameter values truncated with longoption parameter) and #7444 (Trailing spaces after php closing tag) + + + + + 1.2 + 1.2 + + + stable + stable + + 2003-12-11 + PHP License + +Fix to preserve BC with 1.0 and allow correct behaviour for new users + + + + + 1.0 + 1.0 + + + stable + stable + + 2002-09-13 + PHP License + +Stable release + + + + + 0.11 + 0.11 + + + beta + beta + + 2002-05-26 + PHP License + +POSIX getopt compatibility fix: treat first element of args + array as command name + + + + + 0.10 + 0.10 + + + beta + beta + + 2002-05-12 + PHP License + +Packaging fix + + + + + 0.9 + 0.9 + + + beta + beta + + 2002-05-12 + PHP License + +Initial release + + + + diff --git a/videodb/vendor/pear/console_getopt/tests/001-getopt.phpt b/videodb/vendor/pear/console_getopt/tests/001-getopt.phpt new file mode 100644 index 0000000..75ae848 --- /dev/null +++ b/videodb/vendor/pear/console_getopt/tests/001-getopt.phpt @@ -0,0 +1,63 @@ +--TEST-- +Console_Getopt +--FILE-- + $d) { + if ($i++ > 0) { + print ", "; + } + print $d[0] . '=' . $d[1]; + } + print "\n"; + print "params: " . implode(", ", $non_opts) . "\n"; + print "\n"; +} + +test("-abc", "abc"); +test("-abc foo", "abc"); +test("-abc foo", "abc:"); +test("-abc foo bar gazonk", "abc"); +test("-abc foo bar gazonk", "abc:"); +test("-a -b -c", "abc"); +test("-a -b -c", "abc:"); +test("-abc", "ab:c"); +test("-abc foo -bar gazonk", "abc"); +?> +--EXPECT-- +options: a=, b=, c= +params: + +options: a=, b=, c= +params: foo + +options: a=, b=, c=foo +params: + +options: a=, b=, c= +params: foo, bar, gazonk + +options: a=, b=, c=foo +params: bar, gazonk + +options: a=, b=, c= +params: + +Console_Getopt: option requires an argument --c + +options: a=, b=c +params: + +options: a=, b=, c= +params: foo, -bar, gazonk diff --git a/videodb/vendor/pear/console_getopt/tests/bug10557.phpt b/videodb/vendor/pear/console_getopt/tests/bug10557.phpt new file mode 100644 index 0000000..08b72ac --- /dev/null +++ b/videodb/vendor/pear/console_getopt/tests/bug10557.phpt @@ -0,0 +1,22 @@ +--TEST-- +Console_Getopt [bug 10557] +--SKIPIF-- +--FILE-- +getMessage()."\n"; + echo 'FATAL'; + exit; +} + +print_r($ret); +?> +--EXPECT-- +Console_Getopt: option requires an argument --to +FATAL \ No newline at end of file diff --git a/videodb/vendor/pear/console_getopt/tests/bug11068.phpt b/videodb/vendor/pear/console_getopt/tests/bug11068.phpt new file mode 100644 index 0000000..8bbe4bf --- /dev/null +++ b/videodb/vendor/pear/console_getopt/tests/bug11068.phpt @@ -0,0 +1,44 @@ +--TEST-- +Console_Getopt [bug 11068] +--SKIPIF-- +--FILE-- +getMessage()."\n"; + echo 'FATAL'; + exit; +} + +print_r($ret); +?> +--EXPECT-- +Array +( + [0] => Array + ( + [0] => Array + ( + [0] => f + [1] => jjohnston@mail.com + ) + + [1] => Array + ( + [0] => --to + [1] => hi + ) + + ) + + [1] => Array + ( + [0] => - + ) + +) \ No newline at end of file diff --git a/videodb/vendor/pear/console_getopt/tests/bug13140.phpt b/videodb/vendor/pear/console_getopt/tests/bug13140.phpt new file mode 100644 index 0000000..4ce13bb --- /dev/null +++ b/videodb/vendor/pear/console_getopt/tests/bug13140.phpt @@ -0,0 +1,75 @@ +--TEST-- +Console_Getopt [bug 13140] +--SKIPIF-- +--FILE-- +getopt2($cg->readPHPArgv(), 't', array('test'), true)); +print_r($cg->getopt2($cg->readPHPArgv(), 'bar', array('foo'), true)); +?> +--EXPECT-- +Array +( + [0] => Array + ( + [0] => Array + ( + [0] => --test + [1] => + ) + + ) + + [1] => Array + ( + [0] => thisshouldbehere + ) + +) +Array +( + [0] => Array + ( + [0] => Array + ( + [0] => --foo + [1] => + ) + + [1] => Array + ( + [0] => b + [1] => + ) + + [2] => Array + ( + [0] => a + [1] => + ) + + [3] => Array + ( + [0] => r + [1] => + ) + + [4] => Array + ( + [0] => r + [1] => + ) + + ) + + [1] => Array + ( + [0] => thisshouldbehere + ) + +) diff --git a/videodb/vendor/pear/ole/.gitattributes b/videodb/vendor/pear/ole/.gitattributes new file mode 100644 index 0000000..d147983 --- /dev/null +++ b/videodb/vendor/pear/ole/.gitattributes @@ -0,0 +1,3 @@ +# Debug with: git archive --format=tar HEAD | tar t + +/tests export-ignore diff --git a/videodb/vendor/pear/ole/.github/workflows/ci.yaml b/videodb/vendor/pear/ole/.github/workflows/ci.yaml new file mode 100644 index 0000000..8dd2970 --- /dev/null +++ b/videodb/vendor/pear/ole/.github/workflows/ci.yaml @@ -0,0 +1,60 @@ +# yamllint disable rule:line-length +# yamllint disable rule:braces + +name: Continuous Integration + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + operating-system: ['ubuntu-latest'] + php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] + + name: CI on ${{ matrix.operating-system }} with PHP ${{ matrix.php-version }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: composer:v2 + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}-${{ matrix.composer-flags }} + restore-keys: | + composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}- + composer-${{ runner.os }}-${{ matrix.php-version }}- + composer-${{ runner.os }}- + composer- + + - name: Install dependencies + run: | + composer update --no-interaction --prefer-dist --no-progress ${{ matrix.composer-flags }} + + - name: Run tests + run: | + vendor/bin/phpunit + + - name: Lint code + run: | + find OLE* -type f -name \*.php | xargs -n1 php -l diff --git a/videodb/vendor/pear/ole/.github/workflows/cs.yaml b/videodb/vendor/pear/ole/.github/workflows/cs.yaml new file mode 100644 index 0000000..1c13a4a --- /dev/null +++ b/videodb/vendor/pear/ole/.github/workflows/cs.yaml @@ -0,0 +1,38 @@ +name: Coding Standards + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + coding-standards: + name: Coding Standards + runs-on: ubuntu-latest + + env: + PHP_CS_FIXER_VERSION: v2.17.3 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: php-cs-fixer:${{ env.PHP_CS_FIXER_VERSION }} + + - name: Restore PHP-CS-Fixer cache + uses: actions/cache@v2 + with: + path: .php_cs.cache + key: "php-cs-fixer" + restore-keys: "php-cs-fixer" + + - name: Run PHP-CS-Fixer, version ${{ env.PHP_CS_FIXER_VERSION }} + run: | + php-cs-fixer fix --diff --diff-format=udiff --dry-run --verbose diff --git a/videodb/vendor/pear/ole/.gitignore b/videodb/vendor/pear/ole/.gitignore new file mode 100644 index 0000000..869f498 --- /dev/null +++ b/videodb/vendor/pear/ole/.gitignore @@ -0,0 +1,4 @@ +# composer related +composer.lock +composer.phar +vendor diff --git a/videodb/vendor/pear/ole/.php_cs.dist b/videodb/vendor/pear/ole/.php_cs.dist new file mode 100644 index 0000000..68e3ed1 --- /dev/null +++ b/videodb/vendor/pear/ole/.php_cs.dist @@ -0,0 +1,11 @@ +setRules([ + '@PSR1' => true, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__) + ) +; diff --git a/videodb/vendor/pear/ole/.travis.yml b/videodb/vendor/pear/ole/.travis.yml new file mode 100644 index 0000000..c65b721 --- /dev/null +++ b/videodb/vendor/pear/ole/.travis.yml @@ -0,0 +1,43 @@ +sudo: false + +language: php +php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - 7.3 + - 7.4 + - 8.0 + - nightly + +stages: + - analyze + - test + +jobs: + fast_finish: true + allow_failures: + - php: nightly + include: + - stage: analyze + php: 7.4 + install: + - composer install --prefer-dist + script: + - php vendor/bin/php-cs-fixer fix --dry-run --diff + - composer validate + - find OLE* -type f -name \*.php | xargs -n1 php -l + +cache: + directories: + - $HOME/.composer/cache + - $HOME/.cache/cache + +install: + - composer remove --no-update --dev + friendsofphp/php-cs-fixer + - composer install --prefer-dist + +script: + - vendor/bin/phpunit --verbose diff --git a/videodb/vendor/pear/ole/OLE.php b/videodb/vendor/pear/ole/OLE.php new file mode 100644 index 0000000..dba5d5e --- /dev/null +++ b/videodb/vendor/pear/ole/OLE.php @@ -0,0 +1,616 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +/** +* Constants for OLE package +*/ +define('OLE_PPS_TYPE_ROOT', 0x05); +define('OLE_PPS_TYPE_DIR', 0x01); +define('OLE_PPS_TYPE_FILE', 0x02); +define('OLE_DATA_SIZE_SMALL', 0x1000); +define('OLE_LONG_INT_SIZE', 4); +define('OLE_PPS_SIZE', 0x80); +define('OLE_DIFSECT', 0xFFFFFFFC); +define('OLE_FATSECT', 0xFFFFFFFD); +define('OLE_ENDOFCHAIN', 0xFFFFFFFE); +define('OLE_FREESECT', 0xFFFFFFFF); +define('OLE_LITTLE_ENDIAN', 0xFFFE); +define('OLE_VERSION_MAJOR_3', 0x0003); +define('OLE_VERSION_MINOR', 0x003E); +define('OLE_SECTOR_SHIFT_3', 0x0009); +define('OLE_MINI_SECTOR_SHIFT', 0x0006); +define('OLE_CFB_SIGNATURE', "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"); + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +/** +* Array for storing OLE instances that are accessed from +* OLE_ChainedBlockStream::stream_open(). +* @var array +*/ +$GLOBALS['_OLE_INSTANCES'] = array(); + +/** +* OLE package base class. +* +* @category Structures +* @package OLE +* @author Xavier Noguer +* @author Christian Schmidt +*/ +class OLE extends PEAR +{ + + /** + * The file handle for reading an OLE container + * @var resource + */ + var $_file_handle; + + /** + * Reference to the sbat stream + * @var resource + */ + var $_small_handle; + + /** + * Array of PPS's found on the OLE container + * @var array + */ + var $_list; + + /** + * Root directory of OLE container + * @var OLE_PPS_Root + */ + var $root; + + /** + * Big Block Allocation Table + * @var array (blockId => nextBlockId) + */ + var $bbat; + + /** + * Short Block Allocation Table + * @var array (blockId => nextBlockId) + */ + var $sbat; + + /** + * Size of big blocks. This is usually 512. + * @var int number of octets per block. + */ + var $bigBlockSize; + + /** + * Size of small blocks. This is usually 64. + * @var int number of octets per block + */ + var $smallBlockSize; + + /** + * Creates a new OLE object + * @access public + */ + function __construct() + { + $this->_list = array(); + } + + /** + * Destructor (using PEAR) + * Just closes the file handle on the OLE file. + * + * @access private + */ + function _OLE() + { + fclose($this->_file_handle); + } + + /** + * Reads an OLE container from the contents of the file given. + * + * @access public + * @param string $file + * @return mixed true on success, PEAR_Error on failure + */ + function read($file) + { + $fh = @fopen($file, "r"); + if (!$fh) { + return $this->raiseError("Can't open file $file"); + } + + return $this->readStream($fh); + } + + /** + * Reads an OLE container from the contents of the stream given. + * + * @access public + * @param resource $fh + * @return mixed true on success, PEAR_Error on failure + */ + function readStream($fh) + { + $this->_file_handle = $fh; + + $signature = fread($fh, 8); + if (OLE_CFB_SIGNATURE != $signature) { + return $this->raiseError("File doesn't seem to be an OLE container."); + } + fseek($fh, 28); + if ($this->_readInt2($fh) != OLE_LITTLE_ENDIAN) { + // This shouldn't be a problem in practice + return $this->raiseError("Only Little-Endian encoding is supported."); + } + // Size of blocks and short blocks in bytes + $this->bigBlockSize = pow(2, $this->_readInt2($fh)); + $this->smallBlockSize = pow(2, $this->_readInt2($fh)); + + // Skip UID, revision number and version number + fseek($fh, 44); + // Number of blocks in Big Block Allocation Table + $bbatBlockCount = $this->_readInt4($fh); + + // Root chain 1st block + $directoryFirstBlockId = $this->_readInt4($fh); + + // Skip unused bytes + fseek($fh, 56); + // Streams shorter than this are stored using small blocks + $this->bigBlockThreshold = $this->_readInt4($fh); + // Block id of first sector in Short Block Allocation Table + $sbatFirstBlockId = $this->_readInt4($fh); + // Number of blocks in Short Block Allocation Table + $sbbatBlockCount = $this->_readInt4($fh); + // Block id of first sector in Master Block Allocation Table + $mbatFirstBlockId = $this->_readInt4($fh); + // Number of blocks in Master Block Allocation Table + $mbbatBlockCount = $this->_readInt4($fh); + $this->bbat = array(); + + // Remaining 4 * 109 bytes of current block is beginning of Master + // Block Allocation Table + $mbatBlocks = array(); + for ($i = 0; $i < 109; $i++) { + $mbatBlocks[] = $this->_readInt4($fh); + } + + // Read rest of Master Block Allocation Table (if any is left) + $pos = $this->_getBlockOffset($mbatFirstBlockId); + for ($i = 0; $i < $mbbatBlockCount; $i++) { + fseek($fh, $pos); + for ($j = 0; $j < $this->bigBlockSize / 4 - 1; $j++) { + $mbatBlocks[] = $this->_readInt4($fh); // ffix - invalid block address check + } + // Last block id in each block points to next block + $chainBlock = $this->_readInt4($fh); + if ($chainBlock === OLE_ENDOFCHAIN) { // ENDOFCHAIN + break; + } + $pos = $this->_getBlockOffset($chainBlock); + } + + + // Read Big Block Allocation Table according to chain specified by + // $mbatBlocks + for ($i = 0; $i < $bbatBlockCount; $i++) { + $pos = $this->_getBlockOffset($mbatBlocks[$i]); + fseek($fh, $pos); + for ($j = 0 ; $j < $this->bigBlockSize / 4; $j++) { + $this->bbat[] = $this->_readInt4($fh); + } + } + + // Read short block allocation table (SBAT) + $this->sbat = array(); + $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4; + $sbatFh = $this->getStream($sbatFirstBlockId); + if (!$sbatFh) { + // Avoid an infinite loop if ChainedBlockStream.php somehow is + // missing + return false; + } + + for ($blockId = 0; $blockId < $shortBlockCount; $blockId++) { + $this->sbat[$blockId] = $this->_readInt4($sbatFh); + } + fclose($sbatFh); + + $this->_readPpsWks($directoryFirstBlockId); + + return true; + } + + /** + * @param int $blockId block id + * @return int byte offset from beginning of file + * @access private + */ + function _getBlockOffset($blockId) + { + return 512 + $blockId * $this->bigBlockSize; + } + + /** + * Returns a stream for use with fread() etc. External callers should + * use OLE_PPS_File::getStream(). + * @param int|PPS $blockIdOrPps block id or PPS + * @return resource read-only stream + */ + function getStream($blockIdOrPps) + { + include_once 'OLE/ChainedBlockStream.php'; + static $isRegistered = false; + if (!$isRegistered) { + stream_wrapper_register('ole-chainedblockstream', + 'OLE_ChainedBlockStream'); + $isRegistered = true; + } + + // Store current instance in global array, so that it can be accessed + // in OLE_ChainedBlockStream::stream_open(). + // Object is removed from self::$instances in OLE_Stream::close(). + $GLOBALS['_OLE_INSTANCES'][] = $this; + $keys = array_keys($GLOBALS['_OLE_INSTANCES']); + $instanceId = end($keys); + + $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId; + if (is_a($blockIdOrPps, 'OLE_PPS')) { + $path .= '&blockId=' . $blockIdOrPps->_StartBlock; + $path .= '&size=' . $blockIdOrPps->Size; + } else { + $path .= '&blockId=' . $blockIdOrPps; + } + return fopen($path, 'r'); + } + + /** + * Reads a signed char. + * @param resource $fh file handle + * @return int + * @access private + */ + function _readInt1($fh) + { + list(, $tmp) = unpack("c", fread($fh, 1)); + return $tmp; + } + + /** + * Reads an unsigned short (2 octets). + * @param resource $fh file handle + * @return int + * @access private + */ + function _readInt2($fh) + { + list(, $tmp) = unpack("v", fread($fh, 2)); + return $tmp; + } + + /** + * Reads an unsigned long (4 octets). + * @param resource file handle + * @return int + * @access private + */ + function _readInt4($fh) + { + list(, $tmp) = unpack("V", fread($fh, 4)); + return $tmp; + } + + /** + * Gets information about all PPS's on the OLE container from the PPS WK's + * creates an OLE_PPS object for each one. + * + * @access private + * @param integer $blockId the block id of the first block + * @return mixed true on success, PEAR_Error on failure + */ + function _readPpsWks($blockId) + { + $fh = $this->getStream($blockId); + for ($pos = 0; ; $pos += 128) { + fseek($fh, $pos, SEEK_SET); + $nameUtf16 = fread($fh, 64); + $nameLength = $this->_readInt2($fh); + $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2); + // Simple conversion from UTF-16LE to ISO-8859-1 + $name = str_replace("\x00", "", $nameUtf16); + $type = $this->_readInt1($fh); + switch ($type) { + case OLE_PPS_TYPE_ROOT: + require_once 'OLE/PPS/Root.php'; + $pps = new OLE_PPS_Root(null, null, array()); + $this->root = $pps; + break; + case OLE_PPS_TYPE_DIR: + $pps = new OLE_PPS(null, null, null, null, null, + null, null, null, null, array()); + break; + case OLE_PPS_TYPE_FILE: + require_once 'OLE/PPS/File.php'; + $pps = new OLE_PPS_File($name); + break; + default: + continue 2; + } + fseek($fh, 1, SEEK_CUR); // skip Color Flag + $pps->Type = $type; + $pps->Name = $name; + $pps->PrevPps = $this->_readInt4($fh); // Left Sibling ID + $pps->NextPps = $this->_readInt4($fh); // Right Sibling ID + $pps->DirPps = $this->_readInt4($fh); // Child ID + fseek($fh, 20, SEEK_CUR); // skip CLSID (16 bytes) + State Bits + $pps->Time1st = OLE::OLE2LocalDate(fread($fh, 8)); + $pps->Time2nd = OLE::OLE2LocalDate(fread($fh, 8)); + $pps->_StartBlock = $this->_readInt4($fh); + $pps->Size = $this->_readInt4($fh); + $pps->No = count($this->_list); + $this->_list[] = $pps; + + if ($type == OLE_PPS_TYPE_ROOT) { + $this->_small_handle = $this->getStream($pps->_StartBlock); + } + + // check if the PPS tree (starting from root) is complete + if (isset($this->root) && + $this->_ppsTreeComplete($this->root->No)) { + + break; + } + } + fclose($fh); + + // Initialize $pps->children on directories + foreach ($this->_list as $pps) { + if ($pps->Type == OLE_PPS_TYPE_DIR || $pps->Type == OLE_PPS_TYPE_ROOT) { + $nos = array($pps->DirPps); + $pps->children = array(); + while ($nos) { + $no = array_pop($nos); + if ($no != OLE_FREESECT) { + $childPps = $this->_list[$no]; + $nos[] = $childPps->PrevPps; + $nos[] = $childPps->NextPps; + $pps->children[] = $childPps; + } + } + } + } + + return true; + } + + /** + * It checks whether the PPS tree is complete (all PPS's read) + * starting with the given PPS (not necessarily root) + * + * @access private + * @param integer $index The index of the PPS from which we are checking + * @return boolean Whether the PPS tree for the given PPS is complete + */ + function _ppsTreeComplete($index) + { + return isset($this->_list[$index]) && + ($pps = $this->_list[$index]) && + ($pps->PrevPps == OLE_FREESECT || + $this->_ppsTreeComplete($pps->PrevPps)) && + ($pps->NextPps == OLE_FREESECT || + $this->_ppsTreeComplete($pps->NextPps)) && + ($pps->DirPps == OLE_FREESECT || + $this->_ppsTreeComplete($pps->DirPps)); + } + + /** + * Checks whether a PPS is a File PPS or not. + * If there is no PPS for the index given, it will return false. + * @param integer $index The index for the PPS + * @return bool true if it's a File PPS, false otherwise + * @access public + */ + function isFile($index) + { + if (isset($this->_list[$index])) { + return ($this->_list[$index]->Type == OLE_PPS_TYPE_FILE); + } + return false; + } + + /** + * Checks whether a PPS is a Root PPS or not. + * If there is no PPS for the index given, it will return false. + * @param integer $index The index for the PPS. + * @return bool true if it's a Root PPS, false otherwise + * @access public + */ + function isRoot($index) + { + if (isset($this->_list[$index])) { + return ($this->_list[$index]->Type == OLE_PPS_TYPE_ROOT); + } + return false; + } + + /** + * Gives the total number of PPS's found in the OLE container. + * @return integer The total number of PPS's found in the OLE container + * @access public + */ + function ppsTotal() + { + return count($this->_list); + } + + /** + * Gets data from a PPS + * If there is no PPS for the index given, it will return an empty string. + * @param integer $index The index for the PPS + * @param integer $position The position from which to start reading + * (relative to the PPS) + * @param integer $length The amount of bytes to read (at most) + * @return string The binary string containing the data requested + * @access public + * @see OLE_PPS_File::getStream() + */ + function getData($index, $position, $length) + { + // if position is not valid return empty string + if (!isset($this->_list[$index]) || + $position >= $this->_list[$index]->Size || + $position < 0) { + + return ''; + } + $fh = $this->getStream($this->_list[$index]); + $data = stream_get_contents($fh, $length, $position); + fclose($fh); + return $data; + } + + /** + * Gets the data length from a PPS + * If there is no PPS for the index given, it will return 0. + * @param integer $index The index for the PPS + * @return integer The amount of bytes in data the PPS has + * @access public + */ + function getDataLength($index) + { + if (isset($this->_list[$index])) { + return $this->_list[$index]->Size; + } + return 0; + } + + /** + * Utility function to transform ASCII text to Unicode + * + * @access public + * @static + * @param string $ascii The ASCII string to transform + * @return string The string in Unicode + */ + static function Asc2Ucs($ascii) + { + $rawname = ''; + for ($i = 0; $i < strlen($ascii); $i++) { + $rawname .= $ascii[$i] . "\x00"; + } + return $rawname; + } + + /** + * Utility function + * Returns a string for the OLE container with the date given + * + * @access public + * @static + * @param integer $date A timestamp + * @return string The string for the OLE container + */ + static function LocalDate2OLE($date = null) + { + if (!isset($date)) { + return "\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + // factor used for separating numbers into 4 bytes parts + $factor = pow(2, 32); + + // days from 1-1-1601 until the beggining of UNIX era + $days = 134774; + // calculate seconds + $big_date = $days * 24 * 3600 + + gmmktime(date("H",$date),date("i",$date),date("s",$date), + date("m",$date),date("d",$date),date("Y",$date)); + // multiply just to make MS happy + $big_date *= 10000000; + + $high_part = floor($big_date / $factor); + // lower 4 bytes + $low_part = floor((($big_date / $factor) - $high_part) * $factor); + + // Make HEX string + $res = ''; + + for ($i = 0; $i < 4; $i++) { + $hex = $low_part % 0x100; + $res .= pack('c', $hex); + $low_part /= 0x100; + } + for ($i = 0; $i < 4; $i++) { + $hex = $high_part % 0x100; + $res .= pack('c', $hex); + $high_part /= 0x100; + } + return $res; + } + + /** + * Returns a timestamp from an OLE container's date + * @param integer $string A binary string with the encoded date + * @return string The timestamp corresponding to the string + * @access public + * @static + */ + static function OLE2LocalDate($string) + { + if (strlen($string) != 8) { + return new PEAR_Error("Expecting 8 byte string"); + } + + // factor used for separating numbers into 4 bytes parts + $factor = pow(2,32); + $high_part = 0; + for ($i = 0; $i < 4; $i++) { + list(, $high_part) = unpack('C', $string[(7 - $i)]); + if ($i < 3) { + $high_part *= 0x100; + } + } + $low_part = 0; + for ($i = 4; $i < 8; $i++) { + list(, $low_part) = unpack('C', $string[(7 - $i)]); + if ($i < 7) { + $low_part *= 0x100; + } + } + $big_date = ($high_part * $factor) + $low_part; + // translate to seconds + $big_date /= 10000000; + + // days from 1-1-1601 until the beggining of UNIX era + $days = 134774; + + // translate to seconds from beggining of UNIX era + $big_date -= $days * 24 * 3600; + return floor($big_date); + } +} diff --git a/videodb/vendor/pear/ole/OLE/ChainedBlockStream.php b/videodb/vendor/pear/ole/OLE/ChainedBlockStream.php new file mode 100644 index 0000000..353f36d --- /dev/null +++ b/videodb/vendor/pear/ole/OLE/ChainedBlockStream.php @@ -0,0 +1,246 @@ + + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id$ + * @link http://pear.php.net/package/OLE + * @since File available since Release 0.6.0 + */ + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +if (!class_exists('OLE')) { + require_once 'OLE.php'; +} + + +/** + * Stream wrapper for reading data stored in an OLE file. Implements methods + * for PHP's stream_wrapper_register(). For creating streams using this + * wrapper, use OLE_PPS_File::getStream(). + * + * @category Structures + * @package OLE + * @author Christian Schmidt + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/OLE + * @since Class available since Release 0.6.0 + */ +class OLE_ChainedBlockStream extends PEAR +{ + /** + * The OLE container of the file that is being read. + * @var OLE + */ + var $ole; + + /** + * Parameters specified by fopen(). + * @var array + */ + var $params; + + /** + * The binary data of the file. + * @var string + */ + var $data; + + /** + * The file pointer. + * @var int byte offset + */ + var $pos; + + /** + * Implements support for fopen(). + * For creating streams using this wrapper, use OLE_PPS_File::getStream(). + * @param string resource name including scheme, e.g. + * ole-chainedblockstream://oleInstanceId=1 + * @param string only "r" is supported + * @param int mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH + * @param string absolute path of the opened stream (out parameter) + * @return bool true on success + */ + function stream_open($path, $mode, $options, &$openedPath) + { + if ($mode != 'r') { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error('Only reading is supported', E_USER_WARNING); + } + return false; + } + + // 25 is length of "ole-chainedblockstream://" + parse_str(substr($path, 25), $this->params); + if (!isset($this->params['oleInstanceId'], + $this->params['blockId'], + $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { + + if ($options & STREAM_REPORT_ERRORS) { + trigger_error('OLE stream not found', E_USER_WARNING); + } + return false; + } + $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; + + $blockId = $this->params['blockId']; + $this->data = ''; + if (isset($this->params['size']) && + $this->params['size'] < $this->ole->bigBlockThreshold && + $blockId != $this->ole->root->_StartBlock) { + + // Block id refers to small blocks + $rootPos = 0; + while ($blockId != OLE_ENDOFCHAIN) { + $pos = $rootPos + $blockId * $this->ole->smallBlockSize; + + $blockId = $this->ole->sbat[$blockId]; + fseek($this->ole->_small_handle, $pos); + $this->data .= fread($this->ole->_small_handle, $this->ole->smallBlockSize); + } + } else { + // Block id refers to big blocks + while ($blockId != OLE_ENDOFCHAIN) { + $pos = $this->ole->_getBlockOffset($blockId); + fseek($this->ole->_file_handle, $pos); + $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); + $blockId = $this->ole->bbat[$blockId]; + } + } + if (isset($this->params['size'])) { + $this->data = substr($this->data, 0, $this->params['size']); + } + + if ($options & STREAM_USE_PATH) { + $openedPath = $path; + } + + return true; + } + + /** + * Implements support for fclose(). + */ + function stream_close() + { + $this->ole = null; + + // $GLOBALS is not always defined in stream_close + if (isset($GLOBALS['_OLE_INSTANCES'])) { + unset($GLOBALS['_OLE_INSTANCES']); + } + } + + /** + * Implements support for fread(), fgets() etc. + * @param int maximum number of bytes to read + * @return string + */ + function stream_read($count) + { + if ($this->stream_eof()) { + return false; + } + $s = substr($this->data, $this->pos, $count); + $this->pos += $count; + return $s; + } + + /** + * Implements support for feof(). + * @return bool TRUE if the file pointer is at EOF; otherwise FALSE + */ + function stream_eof() + { + $eof = $this->pos >= strlen($this->data); + // Workaround for bug in PHP 5.0.x: http://bugs.php.net/27508 + if (version_compare(PHP_VERSION, '5.0', '>=') && + version_compare(PHP_VERSION, '5.1', '<')) { + + $eof = !$eof; + } + return $eof; + } + + /** + * Returns the position of the file pointer, i.e. its offset into the file + * stream. Implements support for ftell(). + * @return int + */ + function stream_tell() + { + return $this->pos; + } + + /** + * Implements support for fseek(). + * @param int byte offset + * @param int SEEK_SET, SEEK_CUR or SEEK_END + * @return bool + */ + function stream_seek($offset, $whence) + { + if ($whence == SEEK_SET && $offset >= 0) { + $this->pos = $offset; + } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) { + $this->pos += $offset; + } elseif ($whence == SEEK_END && -$offset <= strlen($this->data)) { + $this->pos = strlen($this->data) + $offset; + } else { + return false; + } + return true; + } + + /** + * Implements support for fstat(). Currently the only supported field is + * "size". + * @return array + */ + function stream_stat() + { + return array( + 'size' => strlen($this->data), + ); + } + + /** + * PHP 5.6 for some reason wants this to be implemented. Currently returning false as if it wasn't implemented. + * @return boolean + */ + function stream_flush() + { + // If not implemented, FALSE is assumed as the return value. + return false; + } + + // Methods used by stream_wrapper_register() that are not implemented: + // int stream_write ( string data ) + // bool rename ( string path_from, string path_to ) + // bool mkdir ( string path, int mode, int options ) + // bool rmdir ( string path, int options ) + // bool dir_opendir ( string path, int options ) + // array url_stat ( string path, int flags ) + // string dir_readdir ( void ) + // bool dir_rewinddir ( void ) + // bool dir_closedir ( void ) +} diff --git a/videodb/vendor/pear/ole/OLE/PPS.php b/videodb/vendor/pear/ole/OLE/PPS.php new file mode 100644 index 0000000..e83719e --- /dev/null +++ b/videodb/vendor/pear/ole/OLE/PPS.php @@ -0,0 +1,244 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +if (!class_exists('OLE')) { + require_once 'OLE.php'; +} + +/** +* Class for creating PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS extends PEAR +{ + /** + * The PPS index + * @var integer + */ + var $No; + + /** + * The PPS name (in Unicode) + * @var string + */ + var $Name; + + /** + * The PPS type. Dir, Root or File + * @var integer + */ + var $Type; + + /** + * The index of the previous PPS + * @var integer + */ + var $PrevPps; + + /** + * The index of the next PPS + * @var integer + */ + var $NextPps; + + /** + * The index of it's first child if this is a Dir or Root PPS + * @var integer + */ + var $DirPps; + + /** + * A timestamp + * @var integer + */ + var $Time1st; + + /** + * A timestamp + * @var integer + */ + var $Time2nd; + + /** + * Starting block (small or big) for this PPS's data inside the container + * @var integer + */ + var $_StartBlock; + + /** + * The size of the PPS's data (in bytes) + * @var integer + */ + var $Size; + + /** + * The PPS's data (only used if it's not using a temporary file) + * @var string + */ + var $_data; + + /** + * Array of child PPS's (only used by Root and Dir PPS's) + * @var array + */ + var $children = array(); + + /** + * Pointer to OLE container + * @var OLE + */ + var $ole; + + /** + * The constructor + * + * @access public + * @param integer $No The PPS index + * @param string $name The PPS name + * @param integer $type The PPS type. Dir, Root or File + * @param integer $prev The index of the previous PPS + * @param integer $next The index of the next PPS + * @param integer $dir The index of it's first child if this is a Dir or Root PPS + * @param integer $time_1st A timestamp + * @param integer $time_2nd A timestamp + * @param string $data The (usually binary) source data of the PPS + * @param array $children Array containing children PPS for this PPS + */ + function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children) + { + $this->No = $No; + $this->Name = $name; + $this->Type = $type; + $this->PrevPps = $prev; + $this->NextPps = $next; + $this->DirPps = $dir; + $this->Time1st = $time_1st; + $this->Time2nd = $time_2nd; + $this->_data = $data; + $this->children = $children; + if ($data != '') { + $this->Size = strlen($data); + } else { + $this->Size = 0; + } + } + + /** + * Returns the amount of data saved for this PPS + * + * @access private + * @return integer The amount of data (in bytes) + */ + function _DataLen() + { + if (!isset($this->_data)) { + return 0; + } + if (isset($this->_PPS_FILE)) { + fseek($this->_PPS_FILE, 0); + $stats = fstat($this->_PPS_FILE); + return $stats[7]; + } else { + return strlen($this->_data); + } + } + + /** + * Returns a string with the PPS's WK (What is a WK?) + * + * @access private + * @return string The binary string + */ + function _getPpsWk() + { + $ret = $this->Name; + for ($i = 0; $i < (64 - strlen($this->Name)); $i++) { + $ret .= "\x00"; + } + $ret .= pack("v", strlen($this->Name) + 2) // 66 + . pack("c", $this->Type) // 67 + . pack("c", 0x00) //UK // 68 + . pack("V", $this->PrevPps) //Prev // 72 + . pack("V", $this->NextPps) //Next // 76 + . pack("V", $this->DirPps) //Dir // 80 + . "\x00\x09\x02\x00" // 84 + . "\x00\x00\x00\x00" // 88 + . "\xc0\x00\x00\x00" // 92 + . "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root + . "\x00\x00\x00\x00" // 100 + . OLE::LocalDate2OLE($this->Time1st) // 108 + . OLE::LocalDate2OLE($this->Time2nd) // 116 + . pack("V", isset($this->_StartBlock)? + $this->_StartBlock:0) // 120 + . pack("V", $this->Size) // 124 + . pack("V", 0); // 128 + return $ret; + } + + /** + * Updates index and pointers to previous, next and children PPS's for this + * PPS. I don't think it'll work with Dir PPS's. + * + * @access private + * @param array &$pps_array Reference to the array of PPS's for the whole OLE + * container + * @return integer The index for this PPS + */ + static function _savePpsSetPnt(&$raList, $to_save, $depth = 0) + { + if ( !is_array($to_save) || (count($to_save) == 0) ) { + return OLE_FREESECT; + } + elseif( count($to_save) == 1 ) { + $cnt = count($raList); + // If the first entry, it's the root... Don't clone it! + $raList[$cnt] = ( $depth == 0 ) ? $to_save[0] : clone $to_save[0]; + $raList[$cnt]->No = $cnt; + $raList[$cnt]->PrevPps = OLE_FREESECT; + $raList[$cnt]->NextPps = OLE_FREESECT; + $raList[$cnt]->DirPps = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); + return $cnt; + } + else { + $iPos = floor(count($to_save) / 2); + $aPrev = array_slice($to_save, 0, $iPos); + $aNext = array_slice($to_save, $iPos + 1); + + $cnt = count($raList); + // If the first entry, it's the root... Don't clone it! + $raList[$cnt] = ( $depth == 0 ) ? $to_save[$iPos] : clone $to_save[$iPos]; + $raList[$cnt]->No = $cnt; + $raList[$cnt]->PrevPps = self::_savePpsSetPnt($raList, $aPrev, $depth++); + $raList[$cnt]->NextPps = self::_savePpsSetPnt($raList, $aNext, $depth++); + $raList[$cnt]->DirPps = self::_savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); + + return $cnt; + } + } +} diff --git a/videodb/vendor/pear/ole/OLE/PPS/File.php b/videodb/vendor/pear/ole/OLE/PPS/File.php new file mode 100644 index 0000000..a08856b --- /dev/null +++ b/videodb/vendor/pear/ole/OLE/PPS/File.php @@ -0,0 +1,130 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id$ + + +if (!class_exists('OLE_PPS')) { + require_once 'OLE/PPS.php'; +} + +if (!class_exists('System')) { + require_once 'System.php'; +} + +/** +* Class for creating File PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS_File extends OLE_PPS +{ + /** + * The temporary dir for storing the OLE file + * @var string + */ + var $_tmp_dir; + + /** + * The constructor + * + * @access public + * @param string $name The name of the file (in Unicode) + * @see OLE::Asc2Ucs() + */ + function __construct($name) + { + $system = new System(); + $this->_tmp_dir = $system->tmpdir(); + parent::__construct( + null, + $name, + OLE_PPS_TYPE_FILE, + null, + null, + null, + null, + null, + '', + array()); + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return boolean true if given dir is valid, false otherwise + */ + function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } + + /** + * Initialization method. Has to be called right after OLE_PPS_File(). + * + * @access public + * @return mixed true on success. PEAR_Error on failure + */ + function init() + { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_File"); + $fh = @fopen($this->_tmp_filename, "w+b"); + if ($fh == false) { + return $this->raiseError("Can't create temporary file"); + } + $this->_PPS_FILE = $fh; + if ($this->_PPS_FILE) { + fseek($this->_PPS_FILE, 0); + } + + return true; + } + + /** + * Append data to PPS + * + * @access public + * @param string $data The data to append + */ + function append($data) + { + if ($this->_PPS_FILE) { + fwrite($this->_PPS_FILE, $data); + } else { + $this->_data .= $data; + } + } + + /** + * Returns a stream for reading this file using fread() etc. + * @return resource a read-only stream + */ + function getStream() + { + $this->ole->getStream($this); + } +} diff --git a/videodb/vendor/pear/ole/OLE/PPS/Root.php b/videodb/vendor/pear/ole/OLE/PPS/Root.php new file mode 100644 index 0000000..a2e423f --- /dev/null +++ b/videodb/vendor/pear/ole/OLE/PPS/Root.php @@ -0,0 +1,724 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id$ + +if (!class_exists('OLE_PPS')) { + require_once 'OLE/PPS.php'; +} + +if (!class_exists('System')) { + require_once 'System.php'; +} + +/** +* Class for creating Root PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS_Root extends OLE_PPS +{ + /** + * Flag to enable new logic + * @var bool + */ + var $new_func = true; + + /** + * The temporary dir for storing the OLE file + * @var string + */ + var $_tmp_dir; + + /** + * Constructor + * + * @access public + * @param integer $time_1st A timestamp + * @param integer $time_2nd A timestamp + */ + function __construct($time_1st, $time_2nd, $raChild) + { + $system = new System(); + $this->_tmp_dir = $system->tmpdir(); + parent::__construct( + null, + OLE::Asc2Ucs('Root Entry'), + OLE_PPS_TYPE_ROOT, + null, + null, + null, + $time_1st, + $time_2nd, + null, + $raChild); + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return true if given dir is valid, false otherwise + */ + function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } + + /** + * Method for saving the whole OLE container (including files). + * In fact, if called with an empty argument (or '-'), it saves to a + * temporary file and then outputs it's contents to stdout. + * + * @param string $filename The name of the file where to save the OLE container + * @access public + * @return mixed true on success, PEAR_Error on failure + */ + function save($filename) + { + // Initial Setting for saving + $this->_BIG_BLOCK_SIZE = pow(2, + ((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE) : 9)); + $this->_SMALL_BLOCK_SIZE= pow(2, + ((isset($this->_SMALL_BLOCK_SIZE))? $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6)); + + // Open temp file if we are sending output to stdout + if (($filename == '-') || ($filename == '')) { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); + $this->_FILEH_ = @fopen($this->_tmp_filename,"w+b"); + if ($this->_FILEH_ == false) { + return $this->raiseError("Can't create temporary file."); + } + } else { + $this->_FILEH_ = @fopen($filename, "wb"); + if ($this->_FILEH_ == false) { + return $this->raiseError("Can't open $filename. It may be in use or protected."); + } + } + // Make an array of PPS's (for Save) + $aList = array(); + OLE_PPS_Root::_savePpsSetPnt($aList, array($this)); + // calculate values for header + list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); + // Save Header + $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); + + // Make Small Data string (write SBD) + $this->_data = $this->_makeSmallData($aList); + + // Write BB + $this->_saveBigData($iSBDcnt, $aList); + // Write PPS + $this->_savePps($aList); + // Write Big Block Depot and BDList and Adding Header informations + $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); + // Close File, send it to stdout if necessary + if (($filename == '-') || ($filename == '')) { + fseek($this->_FILEH_, 0); + fpassthru($this->_FILEH_); + @fclose($this->_FILEH_); + // Delete the temporary file. + @unlink($this->_tmp_filename); + } else { + @fclose($this->_FILEH_); + } + + return true; + } + + /** + * Calculate some numbers + * + * @access private + * @param array $raList Reference to an array of PPS's + * @return array The array of numbers + */ + function _calcSize(&$raList) + { + // Calculate Basic Setting + $iBBcnt = 0; + $iSBcnt = 0; + for ($i = 0; $i < count($raList); $i++) { + if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { + $raList[$i]->Size = $raList[$i]->_DataLen(); + if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { + $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); + } else { + $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); + } + } + } + $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; + $iSlCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); + $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0); + $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + + (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0)); + $iCnt = count($raList); + $iBdCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; + $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); + + return array($iSBDcnt, $iBBcnt, $iPPScnt); + } + + /** + * Helper function for caculating a magic value for block sizes + * + * @access private + * @param integer $i2 The argument + * @see save() + * @return integer + */ + function _adjust2($i2) + { + $iWk = log($i2)/log(2); + return ($iWk > floor($iWk))? floor($iWk)+1:$iWk; + } + + /** + * Save OLE header + * + * @access private + * @param integer $iSBDcnt + * @param integer $iBBcnt + * @param integer $iPPScnt + */ + function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) + { + $FILE = $this->_FILEH_; + + if($this->new_func) + return $this->_create_header($iSBDcnt, $iBBcnt, $iPPScnt); + + // Calculate Basic Setting + $iBlCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); + + // Calculate BD count + if ($iBdCnt > $i1stBdL) { + while (1) { + $iBdExL++; + $iAllW++; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); + if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) { + break; + } + } + } + + // Save Header + fwrite($FILE, + OLE_CFB_SIGNATURE // Header Signature (8 bytes) + . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Header CLSID (16 bytes) + . pack("v", OLE_VERSION_MINOR) // Minor Version (2 bytes) + . pack("v", OLE_VERSION_MAJOR_3) // Major Version (2 bytes) + . pack("v", OLE_LITTLE_ENDIAN) // Byte Order (2 bytes) + . pack("v", OLE_SECTOR_SHIFT_3) // Sector Shift (2 bytes) + . pack("v", OLE_MINI_SECTOR_SHIFT) // Mini Sector Shift (2 bytes) + . "\x00\x00\x00\x00\x00\x00" // Reserved (6 bytes) + . "\x00\x00\x00\x00" // Number of Directory Sectors (4 bytes) + . pack("V", $iBdCnt) // Number of FAT Sectors (4 bytes) + . pack("V", $iBBcnt+$iSBDcnt) //ROOT START, First Directory Sector Location (4 bytes) + . pack("V", 0) // Transaction Signature Number (4 bytes) + . pack("V", 0x00001000) // Mini Stream Cutoff Size (4 bytes) + . pack("V", $iSBDcnt ? 0 : OLE_ENDOFCHAIN) // First Mini FAT Sector Location (4 bytes) + . pack("V", $iSBDcnt) // Number of Mini FAT Sectors (4 bytes) + ); + // Extra BDList Start, Count + if ($iBdCnt < $i1stBdL) { + fwrite($FILE, + pack("V", OLE_ENDOFCHAIN). // Extra BDList Start + pack("V", 0) // Extra BDList Count + ); + } else { + fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL)); + } + + // BDList + for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; $i++) { + fwrite($FILE, pack("V", $iAll+$i)); + } + if ($i < $i1stBdL) { // free sectors + for ($j = 0; $j < ($i1stBdL-$i); $j++) { + fwrite($FILE, (pack("V", OLE_FREESECT))); + } + } + } + + /** + * Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL) + * + * @access private + * @param integer $iStBlk + * @param array &$raList Reference to array of PPS's + */ + function _saveBigData($iStBlk, &$raList) + { + $FILE = $this->_FILEH_; + + // cycle through PPS's + for ($i = 0; $i < count($raList); $i++) { + if ($raList[$i]->Type != OLE_PPS_TYPE_DIR) { + $raList[$i]->Size = $raList[$i]->_DataLen(); + if (($raList[$i]->Size >= OLE_DATA_SIZE_SMALL) || + (($raList[$i]->Type == OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) + { + // Write Data + if (isset($raList[$i]->_PPS_FILE)) { + $iLen = 0; + fseek($raList[$i]->_PPS_FILE, 0); // To The Top + while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { + $iLen += strlen($sBuff); + fwrite($FILE, $sBuff); + } + } else { + fwrite($FILE, $raList[$i]->_data); + } + + if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) { + for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); $j++) { + fwrite($FILE, "\x00"); + } + } + // Set For PPS + $raList[$i]->_StartBlock = $iStBlk; + $iStBlk += + (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); + } + // Close file for each PPS, and unlink it + if (isset($raList[$i]->_PPS_FILE)) { + @fclose($raList[$i]->_PPS_FILE); + $raList[$i]->_PPS_FILE = null; + @unlink($raList[$i]->_tmp_filename); + } + } + } + } + + /** + * get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL) + * + * @access private + * @param array &$raList Reference to array of PPS's + */ + function _makeSmallData(&$raList) + { + $sRes = ''; + $FILE = $this->_FILEH_; + $iSmBlk = 0; + + for ($i = 0; $i < count($raList); $i++) { + // Make SBD, small data string + if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { + if ($raList[$i]->Size <= 0) { + continue; + } + if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { + $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); + // Add to SBD + for ($j = 0; $j < ($iSmbCnt-1); $j++) { + fwrite($FILE, pack("V", $j+$iSmBlk+1)); + } + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); + + // Add to Data String(this will be written for RootEntry) + if ($raList[$i]->_PPS_FILE) { + fseek($raList[$i]->_PPS_FILE, 0); // To The Top + while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { + $sRes .= $sBuff; + } + } else { + $sRes .= $raList[$i]->_data; + } + if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) { + for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); $j++) { + $sRes .= "\x00"; + } + } + // Set for PPS + $raList[$i]->_StartBlock = $iSmBlk; + $iSmBlk += $iSmbCnt; + } + } + } + $iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); + if ($iSmBlk % $iSbCnt) { + for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) { + fwrite($FILE, pack("V", OLE_FREESECT)); + } + } + return $sRes; + } + + /** + * Saves all the PPS's WKs + * + * @access private + * @param array $raList Reference to an array with all PPS's + */ + function _savePps(&$raList) + { + // Save each PPS WK + for ($i = 0; $i < count($raList); $i++) { + fwrite($this->_FILEH_, $raList[$i]->_getPpsWk()); + } + // Adjust for Block + $iCnt = count($raList); + $iBCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; + if ($iCnt % $iBCnt) { + for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * OLE_PPS_SIZE); $i++) { + fwrite($this->_FILEH_, "\x00"); + } + } + } + + /** + * Saving Big Block Depot + * + * @access private + * @param integer $iSbdSize + * @param integer $iBsize + * @param integer $iPpsCnt + */ + function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) + { + if($this->new_func) + return $this->_create_big_block_chain($iSbdSize, $iBsize, $iPpsCnt); + + $FILE = $this->_FILEH_; + // Calculate Basic Setting + $iBbCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBsize + $iPpsCnt + $iSbdSize; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); + // Calculate BD count + if ($iBdCnt >$i1stBdL) { + while (1) { + $iBdExL++; + $iAllW++; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); + if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) { + break; + } + } + } + + // Making BD + // Set for SBD + if ($iSbdSize > 0) { + for ($i = 0; $i < ($iSbdSize - 1); $i++) { + fwrite($FILE, pack("V", $i+1)); + } + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); + } + // Set for B + for ($i = 0; $i < ($iBsize - 1); $i++) { + fwrite($FILE, pack("V", $i+$iSbdSize+1)); + } + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); + + // Set for PPS + for ($i = 0; $i < ($iPpsCnt - 1); $i++) { + fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); + } + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); + // Set for BBD itself ( 0xFFFFFFFD : BBD) + for ($i = 0; $i < $iBdCnt; $i++) { + fwrite($FILE, pack("V", OLE_FATSECT)); + } + // Set for ExtraBDList + for ($i = 0; $i < $iBdExL; $i++) { + fwrite($FILE, pack("V", OLE_DIFSECT)); + } + // Adjust for Block + if (($iAllW + $iBdCnt) % $iBbCnt) { + for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) { + fwrite($FILE, pack("V", OLE_FREESECT)); + } + } + // Extra BDList + if ($iBdCnt > $i1stBdL) { + $iN=0; + $iNb=0; + for ($i = $i1stBdL;$i < $iBdCnt; $i++, $iN++) { + if ($iN >= ($iBbCnt - 1)) { + $iN = 0; + $iNb++; + fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb)); + } + fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); + } + if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) { + for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); $i++) { + fwrite($FILE, pack("V", OLE_FREESECT)); + } + } + fwrite($FILE, pack("V", OLE_ENDOFCHAIN)); + } + } + + + + /** + * New method to store Bigblock chain + * + * @access private + * @param integer $num_sb_blocks - number of Smallblock depot blocks + * @param integer $num_bb_blocks - number of Bigblock depot blocks + * @param integer $num_pps_blocks - number of PropertySetStorage blocks + */ + function _create_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) + { + $FILE = $this->_FILEH_; + + $bbd_info = $this->_calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks); + + $data = ""; + + if($num_sb_blocks > 0) + { + for($i = 0; $i<($num_sb_blocks-1); $i++) + $data .= pack("V", $i+1); + $data .= pack("V", OLE_ENDOFCHAIN); + } + + for($i = 0; $i<($num_bb_blocks-1); $i++) + $data .= pack("V", $i + $num_sb_blocks + 1); + $data .= pack("V", OLE_ENDOFCHAIN); + + for($i = 0; $i<($num_pps_blocks-1); $i++) + $data .= pack("V", $i + $num_sb_blocks + $num_bb_blocks + 1); + $data .= pack("V", OLE_ENDOFCHAIN); + + for($i = 0; $i < $bbd_info["0xFFFFFFFD_blockchain_entries"]; $i++) + $data .= pack("V", OLE_FATSECT); + + for($i = 0; $i < $bbd_info["0xFFFFFFFC_blockchain_entries"]; $i++) + $data .= pack("V", OLE_DIFSECT); + + // Adjust for Block + $all_entries = $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]; + if($all_entries % $bbd_info["entries_per_block"]) + { + $rest = $bbd_info["entries_per_block"] - ($all_entries % $bbd_info["entries_per_block"]); + for($i = 0; $i < $rest; $i++) + $data .= pack("V", OLE_FREESECT); + } + + // Extra BDList + if($bbd_info["blockchain_list_entries"] > $bbd_info["header_blockchain_list_entries"]) + { + $iN=0; + $iNb=0; + for($i = $bbd_info["header_blockchain_list_entries"]; $i < $bbd_info["blockchain_list_entries"]; $i++, $iN++) + { + if($iN >= ($bbd_info["entries_per_block"]-1)) + { + $iN = 0; + $iNb++; + $data .= pack("V", $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $iNb); + } + + $data .= pack("V", $num_bb_blocks + $num_sb_blocks + $num_pps_blocks + $i); + } + + $all_entries = $bbd_info["blockchain_list_entries"] - $bbd_info["header_blockchain_list_entries"]; + if(($all_entries % ($bbd_info["entries_per_block"] - 1))) + { + $rest = ($bbd_info["entries_per_block"] - 1) - ($all_entries % ($bbd_info["entries_per_block"] - 1)); + for($i = 0; $i < $rest; $i++) + $data .= pack("V", OLE_FREESECT); + } + + $data .= pack("V", OLE_ENDOFCHAIN); + } + + /* + $this->dump($data, 0, strlen($data)); + die; + */ + + fwrite($FILE, $data); + } + + /** + * New method to store Header + * + * @access private + * @param integer $num_sb_blocks - number of Smallblock depot blocks + * @param integer $num_bb_blocks - number of Bigblock depot blocks + * @param integer $num_pps_blocks - number of PropertySetStorage blocks + */ + function _create_header($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) + { + $FILE = $this->_FILEH_; + + $bbd_info = $this->_calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks); + + // Save Header + fwrite($FILE, + OLE_CFB_SIGNATURE // Header Signature (8 bytes) + . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Header CLSID (16 bytes) + . pack("v", OLE_VERSION_MINOR) // Minor Version (2 bytes) + . pack("v", OLE_VERSION_MAJOR_3) // Major Version (2 bytes) + . pack("v", OLE_LITTLE_ENDIAN) // Byte Order (2 bytes) + . pack("v", OLE_SECTOR_SHIFT_3) // Sector Shift (2 bytes) + . pack("v", OLE_MINI_SECTOR_SHIFT) // Mini Sector Shift (2 bytes) + . "\x00\x00\x00\x00\x00\x00" // Reserved (6 bytes) + . "\x00\x00\x00\x00" // Number of Directory Sectors (4 bytes) + . pack("V", $bbd_info["blockchain_list_entries"]) // Number of FAT Sectors (4 bytes) + . pack("V", $num_sb_blocks + $num_bb_blocks) //ROOT START, First Directory Sector Location (4 bytes) + . pack("V", 0) // Transaction Signature Number (4 bytes) + . pack("V", 0x00001000) // Mini Stream Cutoff Size (4 bytes) + . pack("V", $num_sb_blocks > 0 ? 0 : OLE_ENDOFCHAIN) // First Mini FAT Sector Location (4 bytes) + . pack("V", $num_sb_blocks) // Number of Mini FAT Sectors (4 bytes) + ); + + // Extra BDList Start, Count + if($bbd_info["blockchain_list_entries"] < $bbd_info["header_blockchain_list_entries"]) + { + fwrite($FILE, + pack("V", OLE_ENDOFCHAIN). // Extra BDList Start + pack("V", 0) // Extra BDList Count + ); + } + else + { + fwrite($FILE, pack("V", $num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"]) . pack("V", $bbd_info["0xFFFFFFFC_blockchain_entries"])); + } + + // BDList + for ($i=0; $i < $bbd_info["header_blockchain_list_entries"] and $i < $bbd_info["blockchain_list_entries"]; $i++) + { + fwrite($FILE, pack("V", $num_bb_blocks + $num_sb_blocks + $num_pps_blocks + $i)); + } + + if($i < $bbd_info["header_blockchain_list_entries"]) + { + for($j = 0; $j < ($bbd_info["header_blockchain_list_entries"]-$i); $j++) + { + fwrite($FILE, (pack("V", OLE_FREESECT))); + } + } + } + + /** + * New method to calculate Bigblock chain + * + * @access private + * @param integer $num_sb_blocks - number of Smallblock depot blocks + * @param integer $num_bb_blocks - number of Bigblock depot blocks + * @param integer $num_pps_blocks - number of PropertySetStorage blocks + */ + function _calculate_big_block_chain($num_sb_blocks, $num_bb_blocks, $num_pps_blocks) + { + $bbd_info["entries_per_block"] = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + $bbd_info["header_blockchain_list_entries"] = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; + $bbd_info["blockchain_entries"] = $num_sb_blocks + $num_bb_blocks + $num_pps_blocks; + $bbd_info["0xFFFFFFFD_blockchain_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"]); + $bbd_info["blockchain_list_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"]); + + // do some magic + $bbd_info["ext_blockchain_list_entries"] = 0; + $bbd_info["0xFFFFFFFC_blockchain_entries"] = 0; + if($bbd_info["blockchain_list_entries"] > $bbd_info["header_blockchain_list_entries"]) + { + do + { + $bbd_info["blockchain_list_entries"] = $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]); + $bbd_info["ext_blockchain_list_entries"] = $bbd_info["blockchain_list_entries"] - $bbd_info["header_blockchain_list_entries"]; + $bbd_info["0xFFFFFFFC_blockchain_entries"] = $this->get_number_of_pointer_blocks($bbd_info["ext_blockchain_list_entries"]); + $bbd_info["0xFFFFFFFD_blockchain_entries"] = $this->get_number_of_pointer_blocks($num_sb_blocks + $num_bb_blocks + $num_pps_blocks + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"]); + } + while($bbd_info["blockchain_list_entries"] < $this->get_number_of_pointer_blocks($bbd_info["blockchain_entries"] + $bbd_info["0xFFFFFFFD_blockchain_entries"] + $bbd_info["0xFFFFFFFC_blockchain_entries"])); + } + + return $bbd_info; + } + + /** + * Calculates number of pointer blocks + * + * @access public + * @param integer $num_pointers - number of pointers + */ + function get_number_of_pointer_blocks($num_pointers) + { + $pointers_per_block = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + + return floor($num_pointers / $pointers_per_block) + (($num_pointers % $pointers_per_block)? 1: 0); + } + + /** + * Support method for some hexdumping + * + * @access public + * @param string $data - Binary data + * @param integer $from - Start offset of data to dump + * @param integer $to - Target offset of data to dump + */ + function dump($data, $from, $to) + { + $chars = array(); + for($i = $from; $i < $to; $i++) + { + if(sizeof($chars) == 16) + { + printf("%08X (% 12d) |", $i-16, $i-16); + foreach($chars as $char) + printf(" %02X", $char); + print " |\n"; + + $chars = array(); + } + + $chars[] = ord($data[$i]); + } + + if(sizeof($chars)) + { + printf("%08X (% 12d) |", $i-sizeof($chars), $i-sizeof($chars)); + foreach($chars as $char) + printf(" %02X", $char); + print " |\n"; + } + } +} diff --git a/videodb/vendor/pear/ole/README.md b/videodb/vendor/pear/ole/README.md new file mode 100644 index 0000000..3e961ee --- /dev/null +++ b/videodb/vendor/pear/ole/README.md @@ -0,0 +1,25 @@ +[![Build Status](https://travis-ci.org/pear/OLE.svg?branch=master)](https://travis-ci.org/pear/OLE) +[![Latest Stable Version](https://poser.pugx.org/pear/ole/v/stable)](https://packagist.org/packages/pear/ole) + +This package is http://pear.php.net/package/OLE and has been migrated from https://svn.php.net/repository/pear/packages/OLE + +Please report all new issues via the PEAR bug tracker. + +If this package is marked as unmaintained and you have fixes, please submit your pull requests and start discussion on the pear-qa mailing list. + +To test, run + + $ composer install + $ vendor/bin/phpunit + +To build, simply + + $ pear package + +To install from scratch + + $ pear install package.xml + +To upgrade + + $ pear upgrade -f package.xml diff --git a/videodb/vendor/pear/ole/composer.json b/videodb/vendor/pear/ole/composer.json new file mode 100644 index 0000000..64de9af --- /dev/null +++ b/videodb/vendor/pear/ole/composer.json @@ -0,0 +1,45 @@ +{ + "name": "pear/ole", + "type": "library", + "description": "This package allows reading and writing of OLE (Object Linking and Embedding) compound documents. This format is used as container for Excel (.xls), Word (.doc) and other Microsoft file formats.", + "license": "PHP-3.01", + "authors": [ + { + "name": "Christian Schmidt", + "email": "schmidt@php.net", + "role": "Lead" + }, + { + "name": "Xavier Noguer", + "email": "xnoguer@php.net", + "role": "Lead" + } + ], + "require": { + "php": ">=5.6", + "pear/pear_exception": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2", + "pear/pear-core-minimal": "^1.10", + "phpunit/phpunit": ">=5 <10", + "sanmai/phpunit-legacy-adapter": "^6 || ^8" + }, + "autoload": { + "psr-0": { + "OLE": "./" + } + }, + "autoload-dev": { + "psr-0": { + "OLE": "tests/" + } + }, + "include-path": [ + "./" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=OLE", + "source": "https://github.com/pear/OLE" + } +} diff --git a/videodb/vendor/pear/ole/package.xml b/videodb/vendor/pear/ole/package.xml new file mode 100644 index 0000000..1f8d8d8 --- /dev/null +++ b/videodb/vendor/pear/ole/package.xml @@ -0,0 +1,212 @@ + + + OLE + pear.php.net + Package for reading and writing OLE containers + This package allows reading and writing of OLE (Object Linking and Embedding) compound documents. This format is used as container for Excel (.xls), Word (.doc) and other Microsoft file formats. + + Christian Schmidt + schmidt + schmidt@php.net + yes + + + Xavier Noguer + xnoguer + xnoguer@php.net + no + + 2017-06-20 + + 1.0.0RC3 + 1.0.0RC3 + + + beta + beta + + PHP + +Bug #19284: RC2 breaks header in excel files from Spreadsheet_Excel_Writer +Bug #21216: Call to undefined method PEAR::OLE_PPS() + + + + + + + + + + + + + + 5.0.0 + + + 1.4.0b1 + + + + + + + + 0.2.1 + 0.2.1 + + + alpha + alpha + + 2003-05-12 + PHP + +Fixing install dir + + + + + 0.3 + 0.3 + + + beta + beta + + 2003-08-21 + PHP + +-added OLE_PPS_File::init() initialization method. +-better error handling. + + + + + 0.4 + 0.4 + + + beta + beta + + 2003-09-25 + PHP + +-deleting tmp files (Herman Kuiper). +-fixed hardcoded tmp dir (Herman Kuiper). +-fixed pass by reference warning (Herman Kuiper). + + + + + 0.5 + 0.5 + + + beta + beta + + 2003-12-14 + PHP + +- BC break!!! OLE/OLE.php file moved to OLE.php to comply with PEAR + standards. You will have to change your require('OLE/OLE.php')'s + for require('OLE.php')'s +- If you are using Spreadsheet_Excel_Writer, do not upgrade to this + version yet. A new version of Spreadsheet_Excel_Writer will be + released soon so the BC break won't affect you. +- allowing setting of temp dir for OLE_PPS_File and OLE_PPS_Root objects +- fixed problem when reading files (not reading the whole OLE tree) + + + + + 0.6.0 + 0.6.0 + + + beta + beta + + 2007-12-09 + PHP + +Rewrite of parser (no change to writer): +- Files inside OLE container are now saved in directory structure. +- Parser now properly uses Big Block, Small Block and Master Block Allocation Tables. +- Added stream interface for reading files inside OLE container. + +- Bug #6516. Fix "PPS at 1 has unknown type" errors. (Christian Schmidt) +- Coding Standard cleanups (by helgi) +- Bug #3951 OLE_PPS_File::init() does not return true on success (by helgi) +- Bug #3955 OLE::_readPpsWks() does not return true on success (by helgi) + + + + + 0.6.1 + 0.6.1 + + + beta + beta + + 2007-12-18 + PHP + +- fixed bug #12693: wrong order of require_once +- added missing file to package: ChainedBlockStream.php + + + + + 0.6.2 + 0.6.2 + + + beta + beta + + 2012-01-26 + PHP + +- fixed bug #12944: Incompatibility open_basedir restriction. + + + + + 1.0.0RC2 + 1.0.0RC2 + + + beta + beta + + 2012-01-26 + PHP + +QA release +Bug #15904 Invalid Bigblock chain with files > 200MB +Bug #17685 OLE doesn't save multistreams + + + + + 1.0.0RC3 + 1.0.0RC3 + + + beta + beta + + 2017-06-20 + PHP + +Bug #19284: RC2 breaks header in excel files from Spreadsheet_Excel_Writer +Bug #21216: Call to undefined method PEAR::OLE_PPS() + + + + diff --git a/videodb/vendor/pear/ole/phpunit.xml.dist b/videodb/vendor/pear/ole/phpunit.xml.dist new file mode 100644 index 0000000..4321913 --- /dev/null +++ b/videodb/vendor/pear/ole/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + tests/ + + + + + + OLE/ + OLE.php + + + + + + + + diff --git a/videodb/vendor/pear/pear-core-minimal/README.rst b/videodb/vendor/pear/pear-core-minimal/README.rst new file mode 100644 index 0000000..9e412fb --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/README.rst @@ -0,0 +1,26 @@ +****************************** +Minimal set of PEAR core files +****************************** + +This repository provides a set of files from ``pear-core`` +that are often used in PEAR packages. + +It follows the `pear-core`__ repository and gets updated whenever a new +PEAR version is released. + +It's meant to be used as dependency for composer packages. + +__ https://github.com/pear/pear-core + +============== +Included files +============== +- ``OS/Guess.php`` +- ``PEAR.php`` +- ``PEAR/Error.php`` +- ``PEAR/ErrorStack.php`` +- ``System.php`` + + +``PEAR/Error.php`` is a dummy file that only includes ``PEAR.php``, +to make autoloaders work without problems. diff --git a/videodb/vendor/pear/pear-core-minimal/composer.json b/videodb/vendor/pear/pear-core-minimal/composer.json new file mode 100644 index 0000000..d805f56 --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/composer.json @@ -0,0 +1,32 @@ +{ + "name": "pear/pear-core-minimal", + "description": "Minimal set of PEAR core files to be used as composer dependency", + "license": "BSD-3-Clause", + "authors": [ + { + "email": "cweiske@php.net", + "name": "Christian Weiske", + "role": "Lead" + } + ], + "autoload": { + "psr-0": { + "": "src/" + } + }, + "include-path": [ + "src/" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR", + "source": "https://github.com/pear/pear-core-minimal" + }, + "type": "library", + "require": { + "pear/console_getopt": "~1.4", + "pear/pear_exception": "~1.0" + }, + "replace": { + "rsky/pear-core-min": "self.version" + } +} diff --git a/videodb/vendor/pear/pear-core-minimal/src/OS/Guess.php b/videodb/vendor/pear/pear-core-minimal/src/OS/Guess.php new file mode 100644 index 0000000..88cd659 --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/src/OS/Guess.php @@ -0,0 +1,395 @@ + + * @author Gregory Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR + * @since File available since PEAR 0.1 + */ + +// {{{ uname examples + +// php_uname() without args returns the same as 'uname -a', or a PHP-custom +// string for Windows. +// PHP versions prior to 4.3 return the uname of the host where PHP was built, +// as of 4.3 it returns the uname of the host running the PHP code. +// +// PC RedHat Linux 7.1: +// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown +// +// PC Debian Potato: +// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown +// +// PC FreeBSD 3.3: +// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.3: +// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5 w/uname from GNU shellutils: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown +// +// HP 9000/712 HP-UX 10: +// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license +// +// HP 9000/712 HP-UX 10 w/uname from GNU shellutils: +// HP-UX host B.10.10 A 9000/712 unknown +// +// IBM RS6000/550 AIX 4.3: +// AIX host 3 4 000003531C00 +// +// AIX 4.3 w/uname from GNU shellutils: +// AIX host 3 4 000003531C00 unknown +// +// SGI Onyx IRIX 6.5 w/uname from GNU shellutils: +// IRIX64 host 6.5 01091820 IP19 mips +// +// SGI Onyx IRIX 6.5: +// IRIX64 host 6.5 01091820 IP19 +// +// SparcStation 20 Solaris 8 w/uname from GNU shellutils: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc +// +// SparcStation 20 Solaris 8: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 +// +// Mac OS X (Darwin) +// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh +// +// Mac OS X early versions +// + +// }}} + +/* TODO: + * - define endianness, to allow matchSignature("bigend") etc. + */ + +/** + * Retrieves information about the current operating system + * + * This class uses php_uname() to grok information about the current OS + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Gregory Beaver + * @copyright 1997-2020 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @since Class available since Release 0.1 + */ +class OS_Guess +{ + var $sysname; + var $nodename; + var $cpu; + var $release; + var $extra; + + function __construct($uname = null) + { + list($this->sysname, + $this->release, + $this->cpu, + $this->extra, + $this->nodename) = $this->parseSignature($uname); + } + + function parseSignature($uname = null) + { + static $sysmap = array( + 'HP-UX' => 'hpux', + 'IRIX64' => 'irix', + ); + static $cpumap = array( + 'i586' => 'i386', + 'i686' => 'i386', + 'ppc' => 'powerpc', + ); + if ($uname === null) { + $uname = php_uname(); + } + $parts = preg_split('/\s+/', trim($uname)); + $n = count($parts); + + $release = $machine = $cpu = ''; + $sysname = $parts[0]; + $nodename = $parts[1]; + $cpu = $parts[$n-1]; + $extra = ''; + if ($cpu == 'unknown') { + $cpu = $parts[$n - 2]; + } + + switch ($sysname) { + case 'AIX' : + $release = "$parts[3].$parts[2]"; + break; + case 'Windows' : + $release = $parts[1]; + if ($release == '95/98') { + $release = '9x'; + } + $cpu = 'i386'; + break; + case 'Linux' : + $extra = $this->_detectGlibcVersion(); + // use only the first two digits from the kernel version + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); + break; + case 'Mac' : + $sysname = 'darwin'; + $nodename = $parts[2]; + $release = $parts[3]; + $cpu = $this->_determineIfPowerpc($cpu, $parts); + break; + case 'Darwin' : + $cpu = $this->_determineIfPowerpc($cpu, $parts); + $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]); + break; + default: + $release = preg_replace('/-.*/', '', $parts[2]); + break; + } + + if (isset($sysmap[$sysname])) { + $sysname = $sysmap[$sysname]; + } else { + $sysname = strtolower($sysname); + } + if (isset($cpumap[$cpu])) { + $cpu = $cpumap[$cpu]; + } + return array($sysname, $release, $cpu, $extra, $nodename); + } + + function _determineIfPowerpc($cpu, $parts) + { + $n = count($parts); + if ($cpu == 'Macintosh' && $parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + return $cpu; + } + + function _detectGlibcVersion() + { + static $glibc = false; + if ($glibc !== false) { + return $glibc; // no need to run this multiple times + } + $major = $minor = 0; + include_once "System.php"; + + // Let's try reading possible libc.so.6 symlinks + $libcs = array( + '/lib64/libc.so.6', + '/lib/libc.so.6', + '/lib/i386-linux-gnu/libc.so.6' + ); + $versions = array(); + foreach ($libcs as $file) { + $versions = $this->_readGlibCVersionFromSymlink($file); + if ($versions != []) { + list($major, $minor) = $versions; + break; + } + } + + // Use glibc's header file to + // get major and minor version number: + if (!($major && $minor)) { + $versions = $this->_readGlibCVersionFromFeaturesHeaderFile(); + } + if (is_array($versions) && $versions != []) { + list($major, $minor) = $versions; + } + + if (!($major && $minor)) { + return $glibc = ''; + } + + return $glibc = "glibc{$major}.{$minor}"; + } + + function _readGlibCVersionFromSymlink($file) + { + $versions = array(); + if (@is_link($file) + && (preg_match('/^libc-(.*)\.so$/', basename(readlink($file)), $matches)) + ) { + $versions = explode('.', $matches[1]); + } + return $versions; + } + + + function _readGlibCVersionFromFeaturesHeaderFile() + { + $features_header_file = '/usr/include/features.h'; + if (!(@file_exists($features_header_file) + && @is_readable($features_header_file)) + ) { + return array(); + } + if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) { + return $this-_parseFeaturesHeaderFile($features_header_file); + } // no cpp + + return $this->_fromGlibCTest(); + } + + function _parseFeaturesHeaderFile($features_header_file) + { + $features_file = fopen($features_header_file, 'rb'); + while (!feof($features_file)) { + $line = fgets($features_file, 8192); + if (!$this->_IsADefinition($line)) { + continue; + } + if (strpos($line, '__GLIBC__')) { + // major version number #define __GLIBC__ version + $line = preg_split('/\s+/', $line); + $glibc_major = trim($line[2]); + if (isset($glibc_minor)) { + break; + } + continue; + } + + if (strpos($line, '__GLIBC_MINOR__')) { + // got the minor version number + // #define __GLIBC_MINOR__ version + $line = preg_split('/\s+/', $line); + $glibc_minor = trim($line[2]); + if (isset($glibc_major)) { + break; + } + } + } + fclose($features_file); + if (!isset($glibc_major) || !isset($glibc_minor)) { + return array(); + } + return array(trim($glibc_major), trim($glibc_minor)); + } + + function _IsADefinition($line) + { + if ($line === false) { + return false; + } + return strpos(trim($line), '#define') !== false; + } + + function _fromGlibCTest() + { + $major = null; + $minor = null; + + $tmpfile = System::mktemp("glibctest"); + $fp = fopen($tmpfile, "w"); + fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); + fclose($fp); + $cpp = popen("/usr/bin/cpp $tmpfile", "r"); + while ($line = fgets($cpp, 1024)) { + if ($line[0] == '#' || trim($line) == '') { + continue; + } + + if (list($major, $minor) = explode(' ', trim($line))) { + break; + } + } + pclose($cpp); + unlink($tmpfile); + if ($major !== null && $minor !== null) { + return [$major, $minor]; + } + } + + function getSignature() + { + if (empty($this->extra)) { + return "{$this->sysname}-{$this->release}-{$this->cpu}"; + } + return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; + } + + function getSysname() + { + return $this->sysname; + } + + function getNodename() + { + return $this->nodename; + } + + function getCpu() + { + return $this->cpu; + } + + function getRelease() + { + return $this->release; + } + + function getExtra() + { + return $this->extra; + } + + function matchSignature($match) + { + $fragments = is_array($match) ? $match : explode('-', $match); + $n = count($fragments); + $matches = 0; + if ($n > 0) { + $matches += $this->_matchFragment($fragments[0], $this->sysname); + } + if ($n > 1) { + $matches += $this->_matchFragment($fragments[1], $this->release); + } + if ($n > 2) { + $matches += $this->_matchFragment($fragments[2], $this->cpu); + } + if ($n > 3) { + $matches += $this->_matchFragment($fragments[3], $this->extra); + } + return ($matches == $n); + } + + function _matchFragment($fragment, $value) + { + if (strcspn($fragment, '*?') < strlen($fragment)) { + $expression = str_replace( + array('*', '?', '/'), + array('.*', '.', '\\/'), + $fragment + ); + $reg = '/^' . $expression . '\\z/'; + return preg_match($reg, $value); + } + return ($fragment == '*' || !strcasecmp($fragment, $value)); + } +} +/* + * Local Variables: + * indent-tabs-mode: nil + * c-basic-offset: 4 + * End: + */ diff --git a/videodb/vendor/pear/pear-core-minimal/src/PEAR.php b/videodb/vendor/pear/pear-core-minimal/src/PEAR.php new file mode 100644 index 0000000..fee6638 --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/src/PEAR.php @@ -0,0 +1,1135 @@ + + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Greg Beaver + * @copyright 1997-2010 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/**#@+ + * ERROR constants + */ +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +/**#@-*/ + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +@ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new PEAR_child; + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Greg Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR + * @see PEAR_Error + * @since Class available since PHP 4.0.2 + * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear + */ +class PEAR +{ + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + /** + * List of methods that can be called both statically and non-statically. + * @var array + */ + protected static $bivalentMethods = array( + 'setErrorHandling' => true, + 'raiseError' => true, + 'throwError' => true, + 'pushErrorHandling' => true, + 'popErrorHandling' => true, + ); + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function __construct($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + + if ($error_class !== null) { + $this->_error_class = $error_class; + } + + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = $this; + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + /** + * Only here for backwards compatibility. + * E.g. Archive_Tar calls $this->PEAR() in its constructor. + * + * @param string $error_class Which class to use for error objects, + * defaults to PEAR_Error. + */ + public function PEAR($error_class = null) + { + self::__construct($error_class); + } + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + public function __call($method, $arguments) + { + if (!isset(self::$bivalentMethods[$method])) { + trigger_error( + 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR + ); + } + return call_user_func_array( + array(get_class(), '_' . $method), + array_merge(array($this), $arguments) + ); + } + + public static function __callStatic($method, $arguments) + { + if (!isset(self::$bivalentMethods[$method])) { + trigger_error( + 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR + ); + } + return call_user_func_array( + array(get_class(), '_' . $method), + array_merge(array(null), $arguments) + ); + } + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + public static function &getStaticProperty($class, $var) + { + static $properties; + if (!isset($properties[$class])) { + $properties[$class] = array(); + } + + if (!array_key_exists($var, $properties[$class])) { + $properties[$class][$var] = null; + } + + return $properties[$class][$var]; + } + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * + * @return void + */ + public static function registerShutdownFunc($func, $args = array()) + { + // if we are called statically, there is a potential + // that no shutdown func is registered. Bug #6445 + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * + * @return bool true if parameter is an error + */ + public static function isError($data, $code = null) + { + if (!is_a($data, 'PEAR_Error')) { + return false; + } + + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } + + return $data->getCode() == $code; + } + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param object $object + * Object the method was called on (non-static mode) + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + protected static function _setErrorHandling( + $object, $mode = null, $options = null + ) { + if ($object !== null) { + $setmode = &$object->_default_error_mode; + $setoptions = &$object->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return count($this->_expected_errors); + } + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + foreach ($this->_expected_errors as $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + + return $deleted; + } + + /** + * This method deletes all occurrences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; we walk through it trying + // to unset all values + foreach ($error_code as $key => $error) { + $deleted = $this->_checkDelExpect($error) ? true : false; + } + + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } + + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + protected static function _raiseError($object, + $message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + } + + if ( + $object !== null && + isset($object->_expected_errors) && + count($object->_expected_errors) > 0 && + count($exp = end($object->_expected_errors)) + ) { + if ($exp[0] === "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp)) + ) { + $mode = PEAR_ERROR_RETURN; + } + } + + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if ($object !== null && isset($object->_default_error_mode)) { + $mode = $object->_default_error_mode; + $options = $object->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif ($object !== null && isset($object->_error_class)) { + $ec = $object->_error_class; + } else { + $ec = 'PEAR_Error'; + } + + if ($skipmsg) { + $a = new $ec($code, $mode, $options, $userinfo); + } else { + $a = new $ec($message, $code, $mode, $options, $userinfo); + } + + return $a; + } + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @return object a PEAR error object + * @see PEAR::raiseError + */ + protected static function _throwError($object, $message = null, $code = null, $userinfo = null) + { + if ($object !== null) { + $a = $object->raiseError($message, $code, null, null, $userinfo); + return $a; + } + + $a = PEAR::raiseError($message, $code, null, null, $userinfo); + return $a; + } + + public static function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + public static function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + protected static function _pushErrorHandling($object, $mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if ($object !== null) { + $def_mode = &$object->_default_error_mode; + $def_options = &$object->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if ($object !== null) { + $object->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + protected static function _popErrorHandling($object) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if ($object !== null) { + $object->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + /** + * OS independent PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + public static function loadExtension($ext) + { + if (extension_loaded($ext)) { + return true; + } + + // if either returns true dl() will produce a FATAL error, stop that + if ( + function_exists('dl') === false || + ini_get('enable_dl') != 1 + ) { + return false; + } + + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + + /** + * Get SOURCE_DATE_EPOCH environment variable + * See https://reproducible-builds.org/specs/source-date-epoch/ + * + * @return int + * @access public + */ + static function getSourceDateEpoch() + { + if ($source_date_epoch = getenv('SOURCE_DATE_EPOCH')) { + if (preg_match('/^\d+$/', $source_date_epoch)) { + return (int) $source_date_epoch; + } else { + // "If the value is malformed, the build process SHOULD exit with a non-zero error code." + self::raiseError("Invalid SOURCE_DATE_EPOCH: $source_date_epoch"); + exit(1); + } + } else { + return time(); + } + } +} + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + + $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo'); + + if ($destructLifoExists) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + + foreach ($_PEAR_destructor_object_list as $k => $objref) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if ( + isset($GLOBALS['_PEAR_shutdown_funcs']) && + is_array($GLOBALS['_PEAR_shutdown_funcs']) && + !empty($GLOBALS['_PEAR_shutdown_funcs']) + ) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +/** + * Standard PEAR error class for PHP 4 + * + * This class is supserseded by {@link PEAR_Exception} in PHP 5 + * + * @category pear + * @package PEAR + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Gregory Beaver + * @copyright 1997-2006 The PHP Group + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/manual/en/core.pear.pear-error.php + * @see PEAR::raiseError(), PEAR::throwError() + * @since Class available since PHP 4.0.2 + */ +class PEAR_Error +{ + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function __construct($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + + $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace'); + + if (!$skiptrace) { + $this->backtrace = debug_backtrace(); + if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) { + unset($this->backtrace[0]['object']); + } + } + + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + + $this->level = $options; + $this->callback = null; + } + + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + + printf($format, $this->getMessage()); + } + + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + printf($format, $msg); + exit($code); + } + + if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);throw($e);'); + } + } + + /** + * Only here for backwards compatibility. + * + * Class "Cache_Error" still uses it, among others. + * + * @param string $message Message + * @param int $code Error code + * @param int $mode Error mode + * @param mixed $options See __construct() + * @param string $userinfo Additional user/debug info + */ + public function PEAR_Error( + $message = 'unknown error', $code = null, $mode = null, + $options = null, $userinfo = null + ) { + self::__construct($message, $code, $mode, $options, $userinfo); + } + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() + { + return $this->mode; + } + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() + { + return $this->callback; + } + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if (defined('PEAR_IGNORE_BACKTRACE')) { + return null; + } + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + function __toString() + { + return $this->getMessage(); + } + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() + { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/videodb/vendor/pear/pear-core-minimal/src/PEAR/Error.php b/videodb/vendor/pear/pear-core-minimal/src/PEAR/Error.php new file mode 100644 index 0000000..96efff7 --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/src/PEAR/Error.php @@ -0,0 +1,14 @@ + + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR + */ +require_once __DIR__ . '/../PEAR.php'; +?> \ No newline at end of file diff --git a/videodb/vendor/pear/pear-core-minimal/src/PEAR/ErrorStack.php b/videodb/vendor/pear/pear-core-minimal/src/PEAR/ErrorStack.php new file mode 100644 index 0000000..6619fbb --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/src/PEAR/ErrorStack.php @@ -0,0 +1,979 @@ + + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR_ErrorStack + */ + +/** + * Singleton storage + * + * Format: + *
        + * array(
        + *  'package1' => PEAR_ErrorStack object,
        + *  'package2' => PEAR_ErrorStack object,
        + *  ...
        + * )
        + * 
        + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] + */ +$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); + +/** + * Global error callback (default) + * + * This is only used if set to non-false. * is the default callback for + * all packages, whereas specific packages may set a default callback + * for all instances, regardless of whether they are a singleton or not. + * + * To exclude non-singletons, only set the local callback for the singleton + * @see PEAR_ErrorStack::setDefaultCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( + '*' => false, +); + +/** + * Global Log object (default) + * + * This is only used if set to non-false. Use to set a default log object for + * all stacks, regardless of instantiation order or location + * @see PEAR_ErrorStack::setDefaultLogger() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; + +/** + * Global Overriding Callback + * + * This callback will override any error callbacks that specific loggers have set. + * Use with EXTREME caution + * @see PEAR_ErrorStack::staticPushCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + +/**#@+ + * One of four possible return values from the error Callback + * @see PEAR_ErrorStack::_errorCallback() + */ +/** + * If this is returned, then the error will be both pushed onto the stack + * and logged. + */ +define('PEAR_ERRORSTACK_PUSHANDLOG', 1); +/** + * If this is returned, then the error will only be pushed onto the stack, + * and not logged. + */ +define('PEAR_ERRORSTACK_PUSH', 2); +/** + * If this is returned, then the error will only be logged, but not pushed + * onto the error stack. + */ +define('PEAR_ERRORSTACK_LOG', 3); +/** + * If this is returned, then the error is completely ignored. + */ +define('PEAR_ERRORSTACK_IGNORE', 4); +/** + * If this is returned, then the error is logged and die() is called. + */ +define('PEAR_ERRORSTACK_DIE', 5); +/**#@-*/ + +/** + * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in + * the singleton method. + */ +define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); + +/** + * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} + * that has no __toString() method + */ +define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); +/** + * Error Stack Implementation + * + * Usage: + * + * // global error stack + * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); + * // local error stack + * $local_stack = new PEAR_ErrorStack('MyPackage'); + * + * @author Greg Beaver + * @version @package_version@ + * @package PEAR_ErrorStack + * @category Debugging + * @copyright 2004-2008 Greg Beaver + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR_ErrorStack + */ +class PEAR_ErrorStack { + /** + * Errors are stored in the order that they are pushed on the stack. + * @since 0.4alpha Errors are no longer organized by error level. + * This renders pop() nearly unusable, and levels could be more easily + * handled in a callback anyway + * @var array + * @access private + */ + var $_errors = array(); + + /** + * Storage of errors by level. + * + * Allows easy retrieval and deletion of only errors from a particular level + * @since PEAR 1.4.0dev + * @var array + * @access private + */ + var $_errorsByLevel = array(); + + /** + * Package name this error stack represents + * @var string + * @access protected + */ + var $_package; + + /** + * Determines whether a PEAR_Error is thrown upon every error addition + * @var boolean + * @access private + */ + var $_compat = false; + + /** + * If set to a valid callback, this will be used to generate the error + * message from the error code, otherwise the message passed in will be + * used + * @var false|string|array + * @access private + */ + var $_msgCallback = false; + + /** + * If set to a valid callback, this will be used to generate the error + * context for an error. For PHP-related errors, this will be a file + * and line number as retrieved from debug_backtrace(), but can be + * customized for other purposes. The error might actually be in a separate + * configuration file, or in a database query. + * @var false|string|array + * @access protected + */ + var $_contextCallback = false; + + /** + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one an PEAR_ERRORSTACK_* constant + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @var false|string|array + * @access protected + */ + var $_errorCallback = array(); + + /** + * PEAR::Log object for logging errors + * @var false|Log + * @access protected + */ + var $_logger = false; + + /** + * Error messages - designed to be overridden + * @var array + * @abstract + */ + var $_errorMsgs = array(); + + /** + * Set up a new error stack + * + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + */ + function __construct($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false) + { + $this->_package = $package; + $this->setMessageCallback($msgCallback); + $this->setContextCallback($contextCallback); + $this->_compat = $throwPEAR_Error; + } + + /** + * Return a single error stack for this package. + * + * Note that all parameters are ignored if the stack for package $package + * has already been instantiated + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + * @param string $stackClass class to instantiate + * + * @return PEAR_ErrorStack + */ + public static function &singleton( + $package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack' + ) { + if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; + } + if (!class_exists($stackClass)) { + if (function_exists('debug_backtrace')) { + $trace = debug_backtrace(); + } + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, + 'exception', array('stackclass' => $stackClass), + 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', + false, $trace); + } + $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = + new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); + + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; + } + + /** + * Internal error handler for PEAR_ErrorStack class + * + * Dies if the error is an exception (and would have died anyway) + * @access private + */ + function _handleError($err) + { + if ($err['level'] == 'exception') { + $message = $err['message']; + if (isset($_SERVER['REQUEST_URI'])) { + echo '
        '; + } else { + echo "\n"; + } + var_dump($err['context']); + die($message); + } + } + + /** + * Set up a PEAR::Log object for all error stacks that don't have one + * @param Log $log + */ + public static function setDefaultLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } elseif (is_callable($log)) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } + } + + /** + * Set up a PEAR::Log object for this error stack + * @param Log $log + */ + function setLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $this->_logger = &$log; + } elseif (is_callable($log)) { + $this->_logger = &$log; + } + } + + /** + * Set an error code => error message mapping callback + * + * This method sets the callback that can be used to generate error + * messages for any instance + * @param array|string Callback function/method + */ + function setMessageCallback($msgCallback) + { + if (!$msgCallback) { + $this->_msgCallback = array(&$this, 'getErrorMessage'); + } else { + if (is_callable($msgCallback)) { + $this->_msgCallback = $msgCallback; + } + } + } + + /** + * Get an error code => error message mapping callback + * + * This method returns the current callback that can be used to generate error + * messages + * @return array|string|false Callback function/method or false if none + */ + function getMessageCallback() + { + return $this->_msgCallback; + } + + /** + * Sets a default callback to be used by all error stacks + * + * This method sets the callback that can be used to generate error + * messages for a singleton + * @param array|string Callback function/method + * @param string Package name, or false for all packages + */ + public static function setDefaultCallback($callback = false, $package = false) + { + if (!is_callable($callback)) { + $callback = false; + } + $package = $package ? $package : '*'; + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; + } + + /** + * Set a callback that generates context information (location of error) for an error stack + * + * This method sets the callback that can be used to generate context + * information for an error. Passing in NULL will disable context generation + * and remove the expensive call to debug_backtrace() + * @param array|string|null Callback function/method + */ + function setContextCallback($contextCallback) + { + if ($contextCallback === null) { + return $this->_contextCallback = false; + } + if (!$contextCallback) { + $this->_contextCallback = array(&$this, 'getFileLine'); + } else { + if (is_callable($contextCallback)) { + $this->_contextCallback = $contextCallback; + } + } + } + + /** + * Set an error Callback + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one of the ERRORSTACK_* constants. + * + * This functionality can be used to emulate PEAR's pushErrorHandling, and + * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of + * the error stack or logging + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see popCallback() + * @param string|array $cb + */ + function pushCallback($cb) + { + array_push($this->_errorCallback, $cb); + } + + /** + * Remove a callback from the error callback stack + * @see pushCallback() + * @return array|string|false + */ + function popCallback() + { + if (!count($this->_errorCallback)) { + return false; + } + return array_pop($this->_errorCallback); + } + + /** + * Set a temporary overriding error callback for every package error stack + * + * Use this to temporarily disable all existing callbacks (can be used + * to emulate the @ operator, for instance) + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see staticPopCallback(), pushCallback() + * @param string|array $cb + */ + public static function staticPushCallback($cb) + { + array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); + } + + /** + * Remove a temporary overriding error callback + * @see staticPushCallback() + * @return array|string|false + */ + public static function staticPopCallback() + { + $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); + if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { + $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + } + return $ret; + } + + /** + * Add an error to the stack + * + * If the message generator exists, it is called with 2 parameters. + * - the current Error Stack object + * - an array that is in the same format as an error. Available indices + * are 'code', 'package', 'time', 'params', 'level', and 'context' + * + * Next, if the error should contain context information, this is + * handled by the context grabbing method. + * Finally, the error is pushed onto the proper error stack + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also + * thrown. If a PEAR_Error is returned, the userinfo + * property is set to the following array: + * + * + * array( + * 'code' => $code, + * 'params' => $params, + * 'package' => $this->_package, + * 'level' => $level, + * 'time' => time(), + * 'context' => $context, + * 'message' => $msg, + * //['repackage' => $err] repackaged error array/Exception class + * ); + * + * + * Normally, the previous array is returned. + */ + function push($code, $level = 'error', $params = array(), $msg = false, + $repackage = false, $backtrace = false) + { + $context = false; + // grab error context + if ($this->_contextCallback) { + if (!$backtrace) { + $backtrace = debug_backtrace(); + } + $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); + } + + // save error + $time = explode(' ', microtime()); + $time = $time[1] + $time[0]; + $err = array( + 'code' => $code, + 'params' => $params, + 'package' => $this->_package, + 'level' => $level, + 'time' => $time, + 'context' => $context, + 'message' => $msg, + ); + + if ($repackage) { + $err['repackage'] = $repackage; + } + + // set up the error message, if necessary + if ($this->_msgCallback) { + $msg = call_user_func_array($this->_msgCallback, + array(&$this, $err)); + $err['message'] = $msg; + } + $push = $log = true; + $die = false; + // try the overriding callback first + $callback = $this->staticPopCallback(); + if ($callback) { + $this->staticPushCallback($callback); + } + if (!is_callable($callback)) { + // try the local callback next + $callback = $this->popCallback(); + if (is_callable($callback)) { + $this->pushCallback($callback); + } else { + // try the default callback + $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; + } + } + if (is_callable($callback)) { + switch(call_user_func($callback, $err)){ + case PEAR_ERRORSTACK_IGNORE: + return $err; + break; + case PEAR_ERRORSTACK_PUSH: + $log = false; + break; + case PEAR_ERRORSTACK_LOG: + $push = false; + break; + case PEAR_ERRORSTACK_DIE: + $die = true; + break; + // anything else returned has the same effect as pushandlog + } + } + if ($push) { + array_unshift($this->_errors, $err); + if (!isset($this->_errorsByLevel[$err['level']])) { + $this->_errorsByLevel[$err['level']] = array(); + } + $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; + } + if ($log) { + if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { + $this->_log($err); + } + } + if ($die) { + die(); + } + if ($this->_compat && $push) { + return $this->raiseError($msg, $code, null, null, $err); + } + return $err; + } + + /** + * Static version of {@link push()} + * + * @param string $package Package name this error belongs to + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also + * thrown. see docs for {@link push()} + */ + public static function staticPush( + $package, $code, $level = 'error', $params = array(), + $msg = false, $repackage = false, $backtrace = false + ) { + $s = &PEAR_ErrorStack::singleton($package); + if ($s->_contextCallback) { + if (!$backtrace) { + if (function_exists('debug_backtrace')) { + $backtrace = debug_backtrace(); + } + } + } + return $s->push($code, $level, $params, $msg, $repackage, $backtrace); + } + + /** + * Log an error using PEAR::Log + * @param array $err Error array + * @param array $levels Error level => Log constant map + * @access protected + */ + function _log($err) + { + if ($this->_logger) { + $logger = &$this->_logger; + } else { + $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; + } + if (is_a($logger, 'Log')) { + $levels = array( + 'exception' => PEAR_LOG_CRIT, + 'alert' => PEAR_LOG_ALERT, + 'critical' => PEAR_LOG_CRIT, + 'error' => PEAR_LOG_ERR, + 'warning' => PEAR_LOG_WARNING, + 'notice' => PEAR_LOG_NOTICE, + 'info' => PEAR_LOG_INFO, + 'debug' => PEAR_LOG_DEBUG); + if (isset($levels[$err['level']])) { + $level = $levels[$err['level']]; + } else { + $level = PEAR_LOG_INFO; + } + $logger->log($err['message'], $level, $err); + } else { // support non-standard logs + call_user_func($logger, $err); + } + } + + + /** + * Pop an error off of the error stack + * + * @return false|array + * @since 0.4alpha it is no longer possible to specify a specific error + * level to return - the last error pushed will be returned, instead + */ + function pop() + { + $err = @array_shift($this->_errors); + if (!is_null($err)) { + @array_pop($this->_errorsByLevel[$err['level']]); + if (!count($this->_errorsByLevel[$err['level']])) { + unset($this->_errorsByLevel[$err['level']]); + } + } + return $err; + } + + /** + * Pop an error off of the error stack, static method + * + * @param string package name + * @return boolean + * @since PEAR1.5.0a1 + */ + static function staticPop($package) + { + if ($package) { + if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return false; + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); + } + } + + /** + * Determine whether there are any errors on the stack + * @param string|array Level name. Use to determine if any errors + * of level (string), or levels (array) have been pushed + * @return boolean + */ + function hasErrors($level = false) + { + if ($level) { + return isset($this->_errorsByLevel[$level]); + } + return count($this->_errors); + } + + /** + * Retrieve all errors since last purge + * + * @param boolean set in order to empty the error stack + * @param string level name, to return only errors of a particular severity + * @return array + */ + function getErrors($purge = false, $level = false) + { + if (!$purge) { + if ($level) { + if (!isset($this->_errorsByLevel[$level])) { + return array(); + } else { + return $this->_errorsByLevel[$level]; + } + } else { + return $this->_errors; + } + } + if ($level) { + $ret = $this->_errorsByLevel[$level]; + foreach ($this->_errorsByLevel[$level] as $i => $unused) { + // entries are references to the $_errors array + $this->_errorsByLevel[$level][$i] = false; + } + // array_filter removes all entries === false + $this->_errors = array_filter($this->_errors); + unset($this->_errorsByLevel[$level]); + return $ret; + } + $ret = $this->_errors; + $this->_errors = array(); + $this->_errorsByLevel = array(); + return $ret; + } + + /** + * Determine whether there are any errors on a single error stack, or on any error stack + * + * The optional parameter can be used to test the existence of any errors without the need of + * singleton instantiation + * @param string|false Package name to check for errors + * @param string Level name to check for a particular severity + * @return boolean + */ + public static function staticHasErrors($package = false, $level = false) + { + if ($package) { + if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return false; + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + if ($obj->hasErrors($level)) { + return true; + } + } + return false; + } + + /** + * Get a list of all errors since last purge, organized by package + * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be + * @param boolean $purge Set to purge the error stack of existing errors + * @param string $level Set to a level name in order to retrieve only errors of a particular level + * @param boolean $merge Set to return a flat array, not organized by package + * @param array $sortfunc Function used to sort a merged array - default + * sorts by time, and should be good for most cases + * + * @return array + */ + public static function staticGetErrors( + $purge = false, $level = false, $merge = false, + $sortfunc = array('PEAR_ErrorStack', '_sortErrors') + ) { + $ret = array(); + if (!is_callable($sortfunc)) { + $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); + if ($test) { + if ($merge) { + $ret = array_merge($ret, $test); + } else { + $ret[$package] = $test; + } + } + } + if ($merge) { + usort($ret, $sortfunc); + } + return $ret; + } + + /** + * Error sorting function, sorts by time + * @access private + */ + public static function _sortErrors($a, $b) + { + if ($a['time'] == $b['time']) { + return 0; + } + if ($a['time'] < $b['time']) { + return 1; + } + return -1; + } + + /** + * Standard file/line number/function/class context callback + * + * This function uses a backtrace generated from {@link debug_backtrace()} + * and so will not work at all in PHP < 4.3.0. The frame should + * reference the frame that contains the source of the error. + * @return array|false either array('file' => file, 'line' => line, + * 'function' => function name, 'class' => class name) or + * if this doesn't work, then false + * @param unused + * @param integer backtrace frame. + * @param array Results of debug_backtrace() + */ + public static function getFileLine($code, $params, $backtrace = null) + { + if ($backtrace === null) { + return false; + } + $frame = 0; + $functionframe = 1; + if (!isset($backtrace[1])) { + $functionframe = 0; + } else { + while (isset($backtrace[$functionframe]['function']) && + $backtrace[$functionframe]['function'] == 'eval' && + isset($backtrace[$functionframe + 1])) { + $functionframe++; + } + } + if (isset($backtrace[$frame])) { + if (!isset($backtrace[$frame]['file'])) { + $frame++; + } + $funcbacktrace = $backtrace[$functionframe]; + $filebacktrace = $backtrace[$frame]; + $ret = array('file' => $filebacktrace['file'], + 'line' => $filebacktrace['line']); + // rearrange for eval'd code or create function errors + if (strpos($filebacktrace['file'], '(') && + preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], + $matches)) { + $ret['file'] = $matches[1]; + $ret['line'] = $matches[2] + 0; + } + if (isset($funcbacktrace['function']) && isset($backtrace[1])) { + if ($funcbacktrace['function'] != 'eval') { + if ($funcbacktrace['function'] == '__lambda_func') { + $ret['function'] = 'create_function() code'; + } else { + $ret['function'] = $funcbacktrace['function']; + } + } + } + if (isset($funcbacktrace['class']) && isset($backtrace[1])) { + $ret['class'] = $funcbacktrace['class']; + } + return $ret; + } + return false; + } + + /** + * Standard error message generation callback + * + * This method may also be called by a custom error message generator + * to fill in template values from the params array, simply + * set the third parameter to the error message template string to use + * + * The special variable %__msg% is reserved: use it only to specify + * where a message passed in by the user should be placed in the template, + * like so: + * + * Error message: %msg% - internal error + * + * If the message passed like so: + * + * + * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); + * + * + * The returned error message will be "Error message: server error 500 - + * internal error" + * @param PEAR_ErrorStack + * @param array + * @param string|false Pre-generated error message template + * + * @return string + */ + public static function getErrorMessage(&$stack, $err, $template = false) + { + if ($template) { + $mainmsg = $template; + } else { + $mainmsg = $stack->getErrorMessageTemplate($err['code']); + } + $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); + if (is_array($err['params']) && count($err['params'])) { + foreach ($err['params'] as $name => $val) { + if (is_array($val)) { + // @ is needed in case $val is a multi-dimensional array + $val = @implode(', ', $val); + } + if (is_object($val)) { + if (method_exists($val, '__toString')) { + $val = $val->__toString(); + } else { + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, + 'warning', array('obj' => get_class($val)), + 'object %obj% passed into getErrorMessage, but has no __toString() method'); + $val = 'Object'; + } + } + $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); + } + } + return $mainmsg; + } + + /** + * Standard Error Message Template generator from code + * @return string + */ + function getErrorMessageTemplate($code) + { + if (!isset($this->_errorMsgs[$code])) { + return '%__msg%'; + } + return $this->_errorMsgs[$code]; + } + + /** + * Set the Error Message Template array + * + * The array format must be: + *
        +     * array(error code => 'message template',...)
        +     * 
        + * + * Error message parameters passed into {@link push()} will be used as input + * for the error message. If the template is 'message %foo% was %bar%', and the + * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will + * be 'message one was six' + * @return string + */ + function setErrorMessageTemplate($template) + { + $this->_errorMsgs = $template; + } + + + /** + * emulate PEAR::raiseError() + * + * @return PEAR_Error + */ + function raiseError() + { + require_once 'PEAR.php'; + $args = func_get_args(); + return call_user_func_array(array('PEAR', 'raiseError'), $args); + } +} +$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); +$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); +?> diff --git a/videodb/vendor/pear/pear-core-minimal/src/System.php b/videodb/vendor/pear/pear-core-minimal/src/System.php new file mode 100644 index 0000000..a7ef465 --- /dev/null +++ b/videodb/vendor/pear/pear-core-minimal/src/System.php @@ -0,0 +1,630 @@ + + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR + * @since File available since Release 0.1 + */ + +/** + * base class + */ +require_once 'PEAR.php'; +require_once 'Console/Getopt.php'; + +$GLOBALS['_System_temp_files'] = array(); + +/** +* System offers cross platform compatible system functions +* +* Static functions for different operations. Should work under +* Unix and Windows. The names and usage has been taken from its respectively +* GNU commands. The functions will return (bool) false on error and will +* trigger the error with the PHP trigger_error() function (you can silence +* the error by prefixing a '@' sign after the function call, but this +* is not recommended practice. Instead use an error handler with +* {@link set_error_handler()}). +* +* Documentation on this class you can find in: +* http://pear.php.net/manual/ +* +* Example usage: +* if (!@System::rm('-r file1 dir1')) { +* print "could not delete file1 or dir1"; +* } +* +* In case you need to to pass file names with spaces, +* pass the params as an array: +* +* System::rm(array('-r', $file1, $dir1)); +* +* @category pear +* @package System +* @author Tomas V.V. Cox +* @copyright 1997-2006 The PHP Group +* @license http://opensource.org/licenses/bsd-license.php New BSD License +* @version Release: @package_version@ +* @link http://pear.php.net/package/PEAR +* @since Class available since Release 0.1 +* @static +*/ +class System +{ + /** + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + */ + public static function _parseArgs($argv, $short_options, $long_options = null) + { + if (!is_array($argv) && $argv !== null) { + /* + // Quote all items that are a short option + $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((? $a) { + if (empty($a)) { + continue; + } + $argv[$k] = trim($a) ; + } + } + + return Console_Getopt::getopt2($argv, $short_options, $long_options); + } + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + */ + protected static function raiseError($error) + { + if (PEAR::isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } + + /** + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @param bool $silent if true, do not emit errors. + * @return array the structure of the dir + */ + protected static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false) + { + $struct = array('dirs' => array(), 'files' => array()); + if (($dir = @opendir($sPath)) === false) { + if (!$silent) { + System::raiseError("Could not open dir $sPath"); + } + return $struct; // XXX could not open error + } + + $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ? + $list = array(); + while (false !== ($file = readdir($dir))) { + if ($file != '.' && $file != '..') { + $list[] = $file; + } + } + + closedir($dir); + natsort($list); + if ($aktinst < $maxinst || $maxinst == 0) { + foreach ($list as $val) { + $path = $sPath . DIRECTORY_SEPARATOR . $val; + if (is_dir($path) && !is_link($path)) { + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent); + $struct = array_merge_recursive($struct, $tmp); + } else { + $struct['files'][] = $path; + } + } + } + + return $struct; + } + + /** + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @static + * @see System::_dirToStruct() + */ + protected static function _multipleToStruct($files) + { + $struct = array('dirs' => array(), 'files' => array()); + settype($files, 'array'); + foreach ($files as $file) { + if (is_dir($file) && !is_link($file)) { + $tmp = System::_dirToStruct($file, 0); + $struct = array_merge_recursive($tmp, $struct); + } else { + if (!in_array($file, $struct['files'])) { + $struct['files'][] = $file; + } + } + } + return $struct; + } + + /** + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + * @static + * @access public + */ + public static function rm($args) + { + $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-) + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach ($opts[0] as $opt) { + if ($opt[0] == 'r') { + $do_recursive = true; + } + } + $ret = true; + if (isset($do_recursive)) { + $struct = System::_multipleToStruct($opts[1]); + foreach ($struct['files'] as $file) { + if (!@unlink($file)) { + $ret = false; + } + } + + rsort($struct['dirs']); + foreach ($struct['dirs'] as $dir) { + if (!@rmdir($dir)) { + $ret = false; + } + } + } else { + foreach ($opts[1] as $file) { + $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; + if (!@$delete($file)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Make directories. + * + * The -p option will create parent directories + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + */ + public static function mkDir($args) + { + $opts = System::_parseArgs($args, 'pm:'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + + $mode = 0777; // default mode + foreach ($opts[0] as $opt) { + if ($opt[0] == 'p') { + $create_parents = true; + } elseif ($opt[0] == 'm') { + // if the mode is clearly an octal number (starts with 0) + // convert it to decimal + if (strlen($opt[1]) && $opt[1][0] == '0') { + $opt[1] = octdec($opt[1]); + } else { + // convert to int + $opt[1] += 0; + } + $mode = $opt[1]; + } + } + + $ret = true; + if (isset($create_parents)) { + foreach ($opts[1] as $dir) { + $dirstack = array(); + while ((!file_exists($dir) || !is_dir($dir)) && + $dir != DIRECTORY_SEPARATOR) { + array_unshift($dirstack, $dir); + $dir = dirname($dir); + } + + while ($newdir = array_shift($dirstack)) { + if (!is_writeable(dirname($newdir))) { + $ret = false; + break; + } + + if (!mkdir($newdir, $mode)) { + $ret = false; + } + } + } + } else { + foreach($opts[1] as $dir) { + if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) { + $ret = false; + } + } + } + + return $ret; + } + + /** + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also + * + * @param string $args the arguments + * @return boolean true on success + */ + public static function &cat($args) + { + $ret = null; + $files = array(); + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + + $count_args = count($args); + for ($i = 0; $i < $count_args; $i++) { + if ($args[$i] == '>') { + $mode = 'wb'; + $outputfile = $args[$i+1]; + break; + } elseif ($args[$i] == '>>') { + $mode = 'ab+'; + $outputfile = $args[$i+1]; + break; + } else { + $files[] = $args[$i]; + } + } + $outputfd = false; + if (isset($mode)) { + if (!$outputfd = fopen($outputfile, $mode)) { + $err = System::raiseError("Could not open $outputfile"); + return $err; + } + $ret = true; + } + foreach ($files as $file) { + if (!$fd = fopen($file, 'r')) { + System::raiseError("Could not open $file"); + continue; + } + while ($cont = fread($fd, 2048)) { + if (is_resource($outputfd)) { + fwrite($outputfd, $cont); + } else { + $ret .= $cont; + } + } + fclose($fd); + } + if (is_resource($outputfd)) { + fclose($outputfd); + } + return $ret; + } + + /** + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + */ + public static function mktemp($args = null) + { + static $first_time = true; + $opts = System::_parseArgs($args, 't:d'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + + foreach ($opts[0] as $opt) { + if ($opt[0] == 'd') { + $tmp_is_dir = true; + } elseif ($opt[0] == 't') { + $tmpdir = $opt[1]; + } + } + + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; + if (!isset($tmpdir)) { + $tmpdir = System::tmpdir(); + } + + if (!System::mkDir(array('-p', $tmpdir))) { + return false; + } + + $tmp = tempnam($tmpdir, $prefix); + if (isset($tmp_is_dir)) { + unlink($tmp); // be careful possible race condition here + if (!mkdir($tmp, 0700)) { + return System::raiseError("Unable to create temporary directory $tmpdir"); + } + } + + $GLOBALS['_System_temp_files'][] = $tmp; + if (isset($tmp_is_dir)) { + //$GLOBALS['_System_temp_files'][] = dirname($tmp); + } + + if ($first_time) { + PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); + $first_time = false; + } + + return $tmp; + } + + /** + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + */ + public static function _removeTmpFiles() + { + if (count($GLOBALS['_System_temp_files'])) { + $delete = $GLOBALS['_System_temp_files']; + array_unshift($delete, '-r'); + System::rm($delete); + $GLOBALS['_System_temp_files'] = array(); + } + } + + /** + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @return string The temporary directory on the system + */ + public static function tmpdir() + { + if (OS_WINDOWS) { + if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + return $var; + } + if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { + return $var; + } + if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) { + return $var; + } + if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { + return $var; + } + return getenv('SystemRoot') . '\temp'; + } + if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { + return $var; + } + return realpath(function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp'); + } + + /** + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @param mixed $fallback Value to return if $program is not found + * + * @return mixed A string with the full path or false if not found + * @author Stig Bakken + */ + public static function which($program, $fallback = false) + { + // enforce API + if (!is_string($program) || '' == $program) { + return $fallback; + } + + // full path given + if (basename($program) != $program) { + $path_elements[] = dirname($program); + $program = basename($program); + } else { + $path = getenv('PATH'); + if (!$path) { + $path = getenv('Path'); // some OSes are just stupid enough to do this + } + + $path_elements = explode(PATH_SEPARATOR, $path); + } + + if (OS_WINDOWS) { + $exe_suffixes = getenv('PATHEXT') + ? explode(PATH_SEPARATOR, getenv('PATHEXT')) + : array('.exe','.bat','.cmd','.com'); + // allow passing a command.exe param + if (strpos($program, '.') !== false) { + array_unshift($exe_suffixes, ''); + } + } else { + $exe_suffixes = array(''); + } + + foreach ($exe_suffixes as $suff) { + foreach ($path_elements as $dir) { + $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; + // It's possible to run a .bat on Windows that is_executable + // would return false for. The is_executable check is meaningless... + if (OS_WINDOWS) { + if (file_exists($file)) { + return $file; + } + } else { + if (is_executable($file)) { + return $file; + } + } + } + } + return $fallback; + } + + /** + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implemented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + */ + public static function find($args) + { + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + $dir = realpath(array_shift($args)); + if (!$dir) { + return array(); + } + $patterns = array(); + $depth = 0; + $do_files = $do_dirs = true; + $args_count = count($args); + for ($i = 0; $i < $args_count; $i++) { + switch ($args[$i]) { + case '-type': + if (in_array($args[$i+1], array('d', 'f'))) { + if ($args[$i+1] == 'd') { + $do_files = false; + } else { + $do_dirs = false; + } + } + $i++; + break; + case '-name': + $name = preg_quote($args[$i+1], '#'); + // our magic characters ? and * have just been escaped, + // so now we change the escaped versions to PCRE operators + $name = strtr($name, array('\?' => '.', '\*' => '.*')); + $patterns[] = '('.$name.')'; + $i++; + break; + case '-maxdepth': + $depth = $args[$i+1]; + break; + } + } + $path = System::_dirToStruct($dir, $depth, 0, true); + if ($do_files && $do_dirs) { + $files = array_merge($path['files'], $path['dirs']); + } elseif ($do_dirs) { + $files = $path['dirs']; + } else { + $files = $path['files']; + } + if (count($patterns)) { + $dsq = preg_quote(DIRECTORY_SEPARATOR, '#'); + $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#'; + $ret = array(); + $files_count = count($files); + for ($i = 0; $i < $files_count; $i++) { + // only search in the part of the file below the current directory + $filepart = basename($files[$i]); + if (preg_match($pattern, $filepart)) { + $ret[] = $files[$i]; + } + } + return $ret; + } + return $files; + } +} diff --git a/videodb/vendor/pear/pear_exception/LICENSE b/videodb/vendor/pear/pear_exception/LICENSE new file mode 100644 index 0000000..a00a242 --- /dev/null +++ b/videodb/vendor/pear/pear_exception/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 1997-2009, + Stig Bakken , + Gregory Beaver , + Helgi Þormar Þorbjörnsson , + Tomas V.V.Cox , + Martin Jansen . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/videodb/vendor/pear/pear_exception/PEAR/Exception.php b/videodb/vendor/pear/pear_exception/PEAR/Exception.php new file mode 100644 index 0000000..a8ef6e0 --- /dev/null +++ b/videodb/vendor/pear/pear_exception/PEAR/Exception.php @@ -0,0 +1,456 @@ + + * @author Hans Lellelid + * @author Bertrand Mansion + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @link http://pear.php.net/package/PEAR_Exception + * @since File available since Release 1.0.0 + */ + + +/** + * Base PEAR_Exception Class + * + * 1) Features: + * + * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) + * - Definable triggers, shot when exceptions occur + * - Pretty and informative error messages + * - Added more context info available (like class, method or cause) + * - cause can be a PEAR_Exception or an array of mixed + * PEAR_Exceptions/PEAR_ErrorStack warnings + * - callbacks for specific exception classes and their children + * + * 2) Ideas: + * + * - Maybe a way to define a 'template' for the output + * + * 3) Inherited properties from PHP Exception Class: + * + * protected $message + * protected $code + * protected $line + * protected $file + * private $trace + * + * 4) Inherited methods from PHP Exception Class: + * + * __clone + * __construct + * getMessage + * getCode + * getFile + * getLine + * getTraceSafe + * getTraceSafeAsString + * __toString + * + * 5) Usage example + * + * + * require_once 'PEAR/Exception.php'; + * + * class Test { + * function foo() { + * throw new PEAR_Exception('Error Message', ERROR_CODE); + * } + * } + * + * function myLogger($pear_exception) { + * echo $pear_exception->getMessage(); + * } + * // each time a exception is thrown the 'myLogger' will be called + * // (its use is completely optional) + * PEAR_Exception::addObserver('myLogger'); + * $test = new Test; + * try { + * $test->foo(); + * } catch (PEAR_Exception $e) { + * print $e; + * } + * + * + * @category PEAR + * @package PEAR_Exception + * @author Tomas V.V.Cox + * @author Hans Lellelid + * @author Bertrand Mansion + * @author Greg Beaver + * @copyright 1997-2009 The Authors + * @license http://opensource.org/licenses/bsd-license.php New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/PEAR_Exception + * @since Class available since Release 1.0.0 + */ +class PEAR_Exception extends Exception +{ + const OBSERVER_PRINT = -2; + const OBSERVER_TRIGGER = -4; + const OBSERVER_DIE = -8; + protected $cause; + private static $_observers = array(); + private static $_uniqueid = 0; + private $_trace; + + /** + * Supported signatures: + * - PEAR_Exception(string $message); + * - PEAR_Exception(string $message, int $code); + * - PEAR_Exception(string $message, Exception $cause); + * - PEAR_Exception(string $message, Exception $cause, int $code); + * - PEAR_Exception(string $message, PEAR_Error $cause); + * - PEAR_Exception(string $message, PEAR_Error $cause, int $code); + * - PEAR_Exception(string $message, array $causes); + * - PEAR_Exception(string $message, array $causes, int $code); + * + * @param string $message exception message + * @param int|Exception|PEAR_Error|array|null $p2 exception cause + * @param int|null $p3 exception code or null + */ + public function __construct($message, $p2 = null, $p3 = null) + { + if (is_int($p2)) { + $code = $p2; + $this->cause = null; + } elseif (is_object($p2) || is_array($p2)) { + // using is_object allows both Exception and PEAR_Error + if (is_object($p2) && !($p2 instanceof Exception)) { + if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) { + throw new PEAR_Exception( + 'exception cause must be Exception, ' . + 'array, or PEAR_Error' + ); + } + } + $code = $p3; + if (is_array($p2) && isset($p2['message'])) { + // fix potential problem of passing in a single warning + $p2 = array($p2); + } + $this->cause = $p2; + } else { + $code = null; + $this->cause = null; + } + parent::__construct($message, (int) $code); + $this->signal(); + } + + /** + * Add an exception observer + * + * @param mixed $callback - A valid php callback, see php func is_callable() + * - A PEAR_Exception::OBSERVER_* constant + * - An array(const PEAR_Exception::OBSERVER_*, + * mixed $options) + * @param string $label The name of the observer. Use this if you want + * to remove it later with removeObserver() + * + * @return void + */ + public static function addObserver($callback, $label = 'default') + { + self::$_observers[$label] = $callback; + } + + /** + * Remove an exception observer + * + * @param string $label Name of the observer + * + * @return void + */ + public static function removeObserver($label = 'default') + { + unset(self::$_observers[$label]); + } + + /** + * Generate a unique ID for an observer + * + * @return int unique identifier for an observer + */ + public static function getUniqueId() + { + return self::$_uniqueid++; + } + + /** + * Send a signal to all observers + * + * @return void + */ + protected function signal() + { + foreach (self::$_observers as $func) { + if (is_callable($func)) { + call_user_func($func, $this); + continue; + } + settype($func, 'array'); + switch ($func[0]) { + case self::OBSERVER_PRINT : + $f = (isset($func[1])) ? $func[1] : '%s'; + printf($f, $this->getMessage()); + break; + case self::OBSERVER_TRIGGER : + $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; + trigger_error($this->getMessage(), $f); + break; + case self::OBSERVER_DIE : + $f = (isset($func[1])) ? $func[1] : '%s'; + die(printf($f, $this->getMessage())); + break; + default: + trigger_error('invalid observer type', E_USER_WARNING); + } + } + } + + /** + * Return specific error information that can be used for more detailed + * error messages or translation. + * + * This method may be overridden in child exception classes in order + * to add functionality not present in PEAR_Exception and is a placeholder + * to define API + * + * The returned array must be an associative array of parameter => value like so: + *
        +     * array('name' => $name, 'context' => array(...))
        +     * 
        + * + * @return array + */ + public function getErrorData() + { + return array(); + } + + /** + * Returns the exception that caused this exception to be thrown + * + * @return Exception|array The context of the exception + */ + public function getCause() + { + return $this->cause; + } + + /** + * Function must be public to call on caused exceptions + * + * @param array $causes Array that gets filled. + * + * @return void + */ + public function getCauseMessage(&$causes) + { + $trace = $this->getTraceSafe(); + $cause = array('class' => get_class($this), + 'message' => $this->message, + 'file' => 'unknown', + 'line' => 'unknown'); + if (isset($trace[0])) { + if (isset($trace[0]['file'])) { + $cause['file'] = $trace[0]['file']; + $cause['line'] = $trace[0]['line']; + } + } + $causes[] = $cause; + if ($this->cause instanceof PEAR_Exception) { + $this->cause->getCauseMessage($causes); + } elseif ($this->cause instanceof Exception) { + $causes[] = array('class' => get_class($this->cause), + 'message' => $this->cause->getMessage(), + 'file' => $this->cause->getFile(), + 'line' => $this->cause->getLine()); + } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) { + $causes[] = array('class' => get_class($this->cause), + 'message' => $this->cause->getMessage(), + 'file' => 'unknown', + 'line' => 'unknown'); + } elseif (is_array($this->cause)) { + foreach ($this->cause as $cause) { + if ($cause instanceof PEAR_Exception) { + $cause->getCauseMessage($causes); + } elseif ($cause instanceof Exception) { + $causes[] = array('class' => get_class($cause), + 'message' => $cause->getMessage(), + 'file' => $cause->getFile(), + 'line' => $cause->getLine()); + } elseif (class_exists('PEAR_Error') + && $cause instanceof PEAR_Error + ) { + $causes[] = array('class' => get_class($cause), + 'message' => $cause->getMessage(), + 'file' => 'unknown', + 'line' => 'unknown'); + } elseif (is_array($cause) && isset($cause['message'])) { + // PEAR_ErrorStack warning + $causes[] = array( + 'class' => $cause['package'], + 'message' => $cause['message'], + 'file' => isset($cause['context']['file']) ? + $cause['context']['file'] : + 'unknown', + 'line' => isset($cause['context']['line']) ? + $cause['context']['line'] : + 'unknown', + ); + } + } + } + } + + /** + * Build a backtrace and return it + * + * @return array Backtrace + */ + public function getTraceSafe() + { + if (!isset($this->_trace)) { + $this->_trace = $this->getTrace(); + if (empty($this->_trace)) { + $backtrace = debug_backtrace(); + $this->_trace = array($backtrace[count($backtrace)-1]); + } + } + return $this->_trace; + } + + /** + * Gets the first class of the backtrace + * + * @return string Class name + */ + public function getErrorClass() + { + $trace = $this->getTraceSafe(); + return $trace[0]['class']; + } + + /** + * Gets the first method of the backtrace + * + * @return string Method/function name + */ + public function getErrorMethod() + { + $trace = $this->getTraceSafe(); + return $trace[0]['function']; + } + + /** + * Converts the exception to a string (HTML or plain text) + * + * @return string String representation + * + * @see toHtml() + * @see toText() + */ + public function __toString() + { + if (isset($_SERVER['REQUEST_URI'])) { + return $this->toHtml(); + } + return $this->toText(); + } + + /** + * Generates a HTML representation of the exception + * + * @return string HTML code + */ + public function toHtml() + { + $trace = $this->getTraceSafe(); + $causes = array(); + $this->getCauseMessage($causes); + $html = '' . "\n"; + foreach ($causes as $i => $cause) { + $html .= '\n"; + } + $html .= '' . "\n" + . '' + . '' + . '' . "\n"; + + foreach ($trace as $k => $v) { + $html .= '' + . '' + . '' . "\n"; + } + $html .= '' + . '' + . '' . "\n" + . '
        ' + . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' + . htmlspecialchars($cause['message']) + . ' in ' . $cause['file'] . ' ' + . 'on line ' . $cause['line'] . '' + . "
        Exception trace
        #FunctionLocation
        ' . $k . ''; + if (!empty($v['class'])) { + $html .= $v['class'] . $v['type']; + } + $html .= $v['function']; + $args = array(); + if (!empty($v['args'])) { + foreach ($v['args'] as $arg) { + if (is_null($arg)) { + $args[] = 'null'; + } else if (is_array($arg)) { + $args[] = 'Array'; + } else if (is_object($arg)) { + $args[] = 'Object('.get_class($arg).')'; + } else if (is_bool($arg)) { + $args[] = $arg ? 'true' : 'false'; + } else if (is_int($arg) || is_double($arg)) { + $args[] = $arg; + } else { + $arg = (string)$arg; + $str = htmlspecialchars(substr($arg, 0, 16)); + if (strlen($arg) > 16) { + $str .= '…'; + } + $args[] = "'" . $str . "'"; + } + } + } + $html .= '(' . implode(', ', $args) . ')' + . '' . (isset($v['file']) ? $v['file'] : 'unknown') + . ':' . (isset($v['line']) ? $v['line'] : 'unknown') + . '
        ' . ($k+1) . '{main} 
        '; + return $html; + } + + /** + * Generates text representation of the exception and stack trace + * + * @return string + */ + public function toText() + { + $causes = array(); + $this->getCauseMessage($causes); + $causeMsg = ''; + foreach ($causes as $i => $cause) { + $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' + . $cause['message'] . ' in ' . $cause['file'] + . ' on line ' . $cause['line'] . "\n"; + } + return $causeMsg . $this->getTraceAsString(); + } +} +?> diff --git a/videodb/vendor/pear/pear_exception/composer.json b/videodb/vendor/pear/pear_exception/composer.json new file mode 100644 index 0000000..9ffba9b --- /dev/null +++ b/videodb/vendor/pear/pear_exception/composer.json @@ -0,0 +1,41 @@ +{ + "name": "pear/pear_exception", + "description": "The PEAR Exception base class.", + "type": "class", + "keywords": [ + "exception" + ], + "homepage": "https://github.com/pear/PEAR_Exception", + "license": "BSD-2-Clause", + "authors": [ + { + "name": "Helgi Thormar", + "email": "dufuz@php.net" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net" + } + ], + "require": { + "php": ">=5.2.0" + }, + "autoload": { + "classmap": ["PEAR/"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "include-path": [ + "." + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception", + "source": "https://github.com/pear/PEAR_Exception" + }, + "require-dev": { + "phpunit/phpunit": "<9" + } +} diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/ci.yaml b/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/ci.yaml new file mode 100644 index 0000000..f50f278 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/ci.yaml @@ -0,0 +1,60 @@ +# yamllint disable rule:line-length +# yamllint disable rule:braces + +name: Continuous Integration + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + tests: + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + operating-system: ['ubuntu-latest'] + php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0'] + + name: CI on ${{ matrix.operating-system }} with PHP ${{ matrix.php-version }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + tools: composer:v2 + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}-${{ matrix.composer-flags }} + restore-keys: | + composer-${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('composer.*') }}- + composer-${{ runner.os }}-${{ matrix.php-version }}- + composer-${{ runner.os }}- + composer- + + - name: Install dependencies + run: | + composer update --no-interaction --prefer-dist --no-progress ${{ matrix.composer-flags }} + + - name: Run tests + run: | + vendor/bin/phpunit + + - name: Lint code + run: | + find Spreadsheet/ -type f -name \*.php | xargs -n1 php -l diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/cs.yaml b/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/cs.yaml new file mode 100644 index 0000000..1c13a4a --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/.github/workflows/cs.yaml @@ -0,0 +1,38 @@ +name: Coding Standards + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + coding-standards: + name: Coding Standards + runs-on: ubuntu-latest + + env: + PHP_CS_FIXER_VERSION: v2.17.3 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + tools: php-cs-fixer:${{ env.PHP_CS_FIXER_VERSION }} + + - name: Restore PHP-CS-Fixer cache + uses: actions/cache@v2 + with: + path: .php_cs.cache + key: "php-cs-fixer" + restore-keys: "php-cs-fixer" + + - name: Run PHP-CS-Fixer, version ${{ env.PHP_CS_FIXER_VERSION }} + run: | + php-cs-fixer fix --diff --diff-format=udiff --dry-run --verbose diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/.gitignore b/videodb/vendor/pear/spreadsheet_excel_writer/.gitignore new file mode 100644 index 0000000..8492a7a --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/.gitignore @@ -0,0 +1,7 @@ +# composer related +composer.lock +composer.phar +vendor + +# build logs +build diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/.php_cs.dist b/videodb/vendor/pear/spreadsheet_excel_writer/.php_cs.dist new file mode 100644 index 0000000..1af4afe --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/.php_cs.dist @@ -0,0 +1,11 @@ +setRules([ + 'native_function_invocation' => null, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__) + ) +; diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/.travis.yml b/videodb/vendor/pear/spreadsheet_excel_writer/.travis.yml new file mode 100644 index 0000000..796fc16 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/.travis.yml @@ -0,0 +1,25 @@ +language: php +php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - 7.3 + - 7.4 + +cache: + directories: + - $HOME/.composer/cache + - $HOME/.cache/cache + - build/cache + +install: + - composer install --prefer-dist + - mkdir -p build/cache + +script: + - php vendor/bin/phpunit --coverage-clover build/logs/clover.xml + - vendor/bin/php-cs-fixer --cache-file=build/cache/.php_cs.cache --diff --dry-run --stop-on-violation --verbose fix + +after_success: + - travis_retry php vendor/bin/php-coveralls diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/README.md b/videodb/vendor/pear/spreadsheet_excel_writer/README.md new file mode 100644 index 0000000..013368b --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/README.md @@ -0,0 +1,166 @@ +[![Build Status](https://travis-ci.org/pear/Spreadsheet_Excel_Writer.svg?branch=master)](https://travis-ci.org/pear/Spreadsheet_Excel_Writer) +[![Latest Stable Version](https://poser.pugx.org/pear/spreadsheet_excel_writer/v/stable)](https://packagist.org/packages/pear/spreadsheet_excel_writer) +[![Coverage Status](https://coveralls.io/repos/github/pear/Spreadsheet_Excel_Writer/badge.svg?branch=master)](https://coveralls.io/github/pear/Spreadsheet_Excel_Writer?branch=master) + +# Spreadsheet_Excel_Writer + +This package is [Spreadsheet_Excel_Writer](http://pear.php.net/package/Spreadsheet_Excel_Writer) and has been migrated from [svn.php.net](https://svn.php.net/repository/pear/packages/Spreadsheet_Excel_Writer). + +Please report all new issues [via the PEAR bug tracker](http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Spreadsheet_Excel_Writer&order_by=ts1&direction=DESC&status=Open). + +If this package is marked as unmaintained and you have fixes, please submit your pull requests and start discussion on the pear-qa mailing list. + + +# Installation + +## Pear + +To test, run + + $ phpunit + +To build, simply + + $ pear package + +To install from scratch + + $ pear install package.xml + +To upgrade + + $ pear upgrade -f package.xml + +## Composer + +This package comes with support for Composer. + +To install from Composer + + $ composer require pear/spreadsheet_excel_writer + +To install the latest development version + + $ composer require pear/spreadsheet_excel_writer:dev-master + +# Features + +- writing Excel (.XLS) spreadsheets +- support: strings (with formatting for text and cells), formulas, images (BMP) + +# Limitations +Library support only 2 types of format for writing XLS, also known as Binary Interchange File Format ([BIFF](https://www.openoffice.org/sc/excelfileformat.pdf)): +- BIFF5 (Excel 5.0 - Excel 95) +- BIFF8 (Excel 98 - Excel 2003) + +**Some important limitations:** + +| Limit | BIFF5 | BIFF8 | +| --- | --- | --- | +| Maximum number of rows | 16384 | 65535 | +| Maximum number of columns | 255 | 255 | +| Maximum data size of a record | 2080 bytes | 8224 bytes | +| Unicode support | CodePage based character encoding | UTF-16LE | + +Explanation of formats and specifications you can find [here](https://www.loc.gov/preservation/digital/formats/fdd/fdd000510.shtml) (section "Useful references") + +Correct output only guaranteed with `mbstring.func_overload = 0` otherwise, you should use workround `mb_internal_encoding('latin1');` + +# Usage + +## Basic usage +```php +use Spreadsheet_Excel_Writer; + + +$filePath = __DIR__ . '/output/out.xls'; +$xls = new Spreadsheet_Excel_Writer($filePath); + +// 8 = BIFF8 +$xls->setVersion(8); + +$sheet = $xls->addWorksheet('info'); + +// only available with BIFF8 +$sheet->setInputEncoding('UTF-8'); + +$headers = [ + 'id', + 'name', + 'email', + 'code', + 'address' +]; + +$row = $col = 0; +foreach ($headers as $header) { + $sheet->write($row, $col, $header); + $col++; +} + +for ($id = 1; $id < 100; $id++) { + $data = [ + 'id' => $id, + 'name' => 'Name Surname', + 'email' => 'mail@gmail.com', + 'password' => 'cfcd208495d565ef66e7dff9f98764da', + 'address' => '00000 North Tantau Avenue. Cupertino, CA 12345. (000) 1234567' + ]; + $sheet->writeRow($id, 0, $data); +} + +$xls->close(); +``` + +## Format usage +```php +$xls = new Spreadsheet_Excel_Writer(); + +$titleFormat = $xls->addFormat(); +$titleFormat->setFontFamily('Helvetica'); +$titleFormat->setBold(); +$titleFormat->setSize(10); +$titleFormat->setColor('orange'); +$titleFormat->setBorder(1); +$titleFormat->setBottom(2); +$titleFormat->setBottomColor(44); +$titleFormat->setAlign('center'); + +$sheet = $xls->addWorksheet('info'); + +$sheet->write(0, 0, 'Text 123', $titleFormat); +``` + +## Header usage (Sending HTTP header for download dialog) +```php +$xls = new Spreadsheet_Excel_Writer(); +$xls->send('excel_'.date("Y-m-d__H:i:s").'.xls'); +``` + + +# Performance + +**Platform:** +Intel(R) Core(TM) i5-4670 CPU @ 3.40GHz +PHP 7.4 + +**Test case:** +Write xls (BIFF8 format, UTF-8), by 5 cells (1x number, 4x string without format/styles, average line length = 120 char) in each row + +**Estimated performance:** + +| Number of rows | Time (seconds) | Peak memory usage (MB) | +| --- | --- | --- | +| 10000 | 0.2 | 4 | +| 20000 | 0.4 | 4 | +| 30000 | 0.6 | 6 | +| 40000 | 0.8 | 6 | +| 50000 | 1.0 | 8 | +| 65534 | 1.2 | 8 | + +# Alternative solutions + +- [PHPOffice/PhpSpreadsheet](https://github.com/PHPOffice/PhpSpreadsheet) +File formats supported: https://phpspreadsheet.readthedocs.io/en/latest/ +- [box/spout](https://github.com/box/spout) +File formats supported: https://opensource.box.com/spout/ diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer.php new file mode 100644 index 0000000..8bb710c --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer.php @@ -0,0 +1,105 @@ + +* +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +if (!class_exists('Spreadsheet_Excel_Writer_Workbook')) { + require_once 'Spreadsheet/Excel/Writer/Workbook.php'; +} + +/** +* Class for writing Excel Spreadsheets. This class should change COMPLETELY. +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook +{ + /** + * The constructor. It just creates a Workbook + * + * @param string $filename The optional filename for the Workbook. + * @return Spreadsheet_Excel_Writer_Workbook The Workbook created + */ + public function __construct($filename = '') + { + $this->_filename = $filename; + parent::__construct($filename); + } + + /** + * Send HTTP headers for the Excel file. + * + * @param string $filename The filename to use for HTTP headers + * @access public + */ + public function send($filename) + { + $filename = addslashes($filename); + header("Content-type: application/vnd.ms-excel"); + header("Content-Disposition: attachment; filename=\"$filename\""); + header("Expires: 0"); + header("Cache-Control: must-revalidate, post-check=0,pre-check=0"); + header("Pragma: public"); + } + + /** + * Utility function for writing formulas + * Converts a cell's coordinates to the A1 format. + * + * @access public + * @static + * @param integer $row Row for the cell to convert (0-indexed). + * @param integer $col Column for the cell to convert (0-indexed). + * @return string The cell identifier in A1 format + */ + function rowcolToCell($row, $col) + { + if ($col > 255) { //maximum column value exceeded + return new PEAR_Error("Maximum column value exceeded: $col"); + } + + $int = (int)($col / 26); + $frac = $col % 26; + $chr1 = ''; + + if ($int > 0) { + $chr1 = chr(ord('A') + $int - 1); + } + + $chr2 = chr(ord('A') + $frac); + $row++; + + return $chr1 . $chr2 . $row; + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/BIFFwriter.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/BIFFwriter.php new file mode 100644 index 0000000..9d59894 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/BIFFwriter.php @@ -0,0 +1,268 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@php.net +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +/** +* Class for writing Excel BIFF records. +* +* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation): +* +* BIFF (BInary File Format) is the file format in which Excel documents are +* saved on disk. A BIFF file is a complete description of an Excel document. +* BIFF files consist of sequences of variable-length records. There are many +* different types of BIFF records. For example, one record type describes a +* formula entered into a cell; one describes the size and location of a +* window into a document; another describes a picture format. +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR +{ + /** + * The BIFF/Excel version (5). + * @var integer + */ + public $_BIFF_version = 0x0500; + + /** + * The byte order of this architecture. 0 => little endian, 1 => big endian + * @var integer + */ + public $_byte_order; + + /** + * The string containing the data of the BIFF stream + * @var string + */ + public $_data; + + /** + * The size of the data in bytes. Should be the same as strlen($this->_data) + * @var integer + */ + public $_datasize; + + /** + * The maximun length for a BIFF record. See _addContinue() + * @var integer + * @see _addContinue() + */ + public $_limit; + + /** + * The temporary dir for storing the OLE file + * @var string + */ + public $_tmp_dir; + + /** + * The temporary file for storing the OLE file + * @var string + */ + public $_tmp_file; + + /** + * Constructor + * + * @access public + */ + public function __construct() + { + $this->_byte_order = ''; + $this->_data = ''; + $this->_datasize = 0; + $this->_limit = 2080; + $this->_tmp_dir = ''; + // Set the byte order + $this->_setByteOrder(); + } + + /** + * Determine the byte order and store it as class data to avoid + * recalculating it for each call to new(). + * + * @access private + */ + protected function _setByteOrder() + { + // Check if "pack" gives the required IEEE 64bit float + $teststr = pack("d", 1.2345); + $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); + if ($number == $teststr) { + $byte_order = 0; // Little Endian + } elseif ($number == strrev($teststr)){ + $byte_order = 1; // Big Endian + } else { + // Give up. I'll fix this in a later version. + return $this->raiseError("Required floating point format ". + "not supported on this platform."); + } + $this->_byte_order = $byte_order; + } + + /** + * General storage public function + * + * @param string $data binary data to prepend + * @access private + */ + protected function _prepend($data) + { + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + $this->_data = $data.$this->_data; + $this->_datasize += strlen($data); + } + + /** + * General storage public function + * + * @param string $data binary data to append + * @access private + */ + protected function _append($data) + { + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + $this->_data .= $data; + $this->_datasize += strlen($data); + } + + /** + * Writes Excel BOF record to indicate the beginning of a stream or + * sub-stream in the BIFF file. + * + * @param integer $type Type of BIFF file to write: 0x0005 Workbook, + * 0x0010 Worksheet. + * @access private + */ + protected function _storeBof($type) + { + $record = 0x0809; // Record identifier + + // According to the SDK $build and $year should be set to zero. + // However, this throws a warning in Excel 5. So, use magic numbers. + if ($this->_BIFF_version == 0x0500) { + $length = 0x0008; + $unknown = ''; + $build = 0x096C; + $year = 0x07C9; + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x0010; + $unknown = pack("VV", 0x00000041, 0x00000006); //unknown last 8 bytes for BIFF8 + $build = 0x0DBB; + $year = 0x07CC; + } + $version = $this->_BIFF_version; + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $version, $type, $build, $year); + $this->_prepend($header . $data . $unknown); + } + + /** + * Writes Excel EOF record to indicate the end of a BIFF stream. + * + * @access private + */ + protected function _storeEof() + { + $record = 0x000A; // Record identifier + $length = 0x0000; // Number of bytes to follow + $header = pack("vv", $record, $length); + $this->_append($header); + } + + /** + * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In + * Excel 97 the limit is 8228 bytes. Records that are longer than these limits + * must be split up into CONTINUE blocks. + * + * This public function takes a long BIFF record and inserts CONTINUE records as + * necessary. + * + * @param string $data The original binary data to be written + * @return string A very convenient string of continue blocks + * @access private + */ + protected function _addContinue($data) + { + $limit = $this->_limit; + $record = 0x003C; // Record identifier + + // The first 2080/8224 bytes remain intact. However, we have to change + // the length field of the record. + $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4); + + $header = pack("vv", $record, $limit); // Headers for continue records + + // Retrieve chunks of 2080/8224 bytes +4 for the header. + $data_length = strlen($data); + for ($i = $limit; $i < ($data_length - $limit); $i += $limit) { + $tmp .= $header; + $tmp .= substr($data, $i, $limit); + } + + // Retrieve the last chunk of data + $header = pack("vv", $record, strlen($data) - $i); + $tmp .= $header; + $tmp .= substr($data, $i, strlen($data) - $i); + + return $tmp; + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return true if given dir is valid, false otherwise + */ + public function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Format.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Format.php new file mode 100644 index 0000000..c19d4e5 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Format.php @@ -0,0 +1,1110 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +/** +* Class for generating Excel XF records (formats) +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Format extends PEAR +{ + /** + * The index given by the workbook when creating a new format. + * @var integer + */ + public $_xf_index; + + /** + * Index to the FONT record. + * @var integer + */ + public $font_index; + + /** + * The font name (ASCII). + * @var string + */ + public $_font_name; + + /** + * Height of font (1/20 of a point) + * @var integer + */ + public $_size; + + /** + * Bold style + * @var integer + */ + public $_bold; + + /** + * Bit specifiying if the font is italic. + * @var integer + */ + public $_italic; + + /** + * Index to the cell's color + * @var integer + */ + public $_color; + + /** + * The text underline property + * @var integer + */ + public $_underline; + + /** + * Bit specifiying if the font has strikeout. + * @var integer + */ + public $_font_strikeout; + + /** + * Bit specifiying if the font has outline. + * @var integer + */ + public $_font_outline; + + /** + * Bit specifiying if the font has shadow. + * @var integer + */ + public $_font_shadow; + + /** + * 2 bytes specifiying the script type for the font. + * @var integer + */ + public $_font_script; + + /** + * Byte specifiying the font family. + * @var integer + */ + public $_font_family; + + /** + * Byte specifiying the font charset. + * @var integer + */ + public $_font_charset; + + /** + * An index (2 bytes) to a FORMAT record (number format). + * @var integer + */ + public $_num_format; + + /** + * Bit specifying if formulas are hidden. + * @var integer + */ + public $_hidden; + + /** + * Bit specifying if the cell is locked. + * @var integer + */ + public $_locked; + + /** + * The three bits specifying the text horizontal alignment. + * @var integer + */ + public $_text_h_align; + + /** + * Bit specifying if the text is wrapped at the right border. + * @var integer + */ + public $_text_wrap; + + /** + * The three bits specifying the text vertical alignment. + * @var integer + */ + public $_text_v_align; + + /** + * 1 bit, apparently not used. + * @var integer + */ + public $_text_justlast; + + /** + * The two bits specifying the text rotation. + * @var integer + */ + public $_rotation; + + /** + * The cell's foreground color. + * @var integer + */ + public $_fg_color; + + /** + * The cell's background color. + * @var integer + */ + public $_bg_color; + + /** + * The cell's background fill pattern. + * @var integer + */ + public $_pattern; + + /** + * Style of the bottom border of the cell + * @var integer + */ + public $_bottom; + + /** + * Color of the bottom border of the cell. + * @var integer + */ + public $_bottom_color; + + /** + * Style of the top border of the cell + * @var integer + */ + public $_top; + + /** + * Color of the top border of the cell. + * @var integer + */ + public $_top_color; + + /** + * Style of the left border of the cell + * @var integer + */ + public $_left; + + /** + * Color of the left border of the cell. + * @var integer + */ + public $_left_color; + + /** + * Style of the right border of the cell + * @var integer + */ + public $_right; + + /** + * Color of the right border of the cell. + * @var integer + */ + public $_right_color; + + /** + * Constructor + * + * @access private + * @param integer $index the XF index for the format. + * @param array $properties array with properties to be set on initialization. + */ + public function __construct($BIFF_version, $index = 0, $properties = array()) + { + $this->_xf_index = $index; + $this->_BIFF_version = $BIFF_version; + $this->font_index = 0; + $this->_font_name = 'Arial'; + $this->_size = 10; + $this->_bold = 0x0190; + $this->_italic = 0; + $this->_color = 0x7FFF; + $this->_underline = 0; + $this->_font_strikeout = 0; + $this->_font_outline = 0; + $this->_font_shadow = 0; + $this->_font_script = 0; + $this->_font_family = 0; + $this->_font_charset = 0; + + $this->_num_format = 0; + + $this->_hidden = 0; + $this->_locked = 0; + + $this->_text_h_align = 0; + $this->_text_wrap = 0; + $this->_text_v_align = 2; + $this->_text_justlast = 0; + $this->_rotation = 0; + + $this->_fg_color = 0x40; + $this->_bg_color = 0x41; + + $this->_pattern = 0; + + $this->_bottom = 0; + $this->_top = 0; + $this->_left = 0; + $this->_right = 0; + $this->_diag = 0; + + $this->_bottom_color = 0x40; + $this->_top_color = 0x40; + $this->_left_color = 0x40; + $this->_right_color = 0x40; + $this->_diag_color = 0x40; + + // Set properties passed to Spreadsheet_Excel_Writer_Workbook::addFormat() + foreach ($properties as $property => $value) + { + if (method_exists($this, 'set'.ucwords($property))) { + $method_name = 'set'.ucwords($property); + $this->$method_name($value); + } + } + } + + + /** + * Generate an Excel BIFF XF record (style or cell). + * + * @param string $style The type of the XF record ('style' or 'cell'). + * @return string The XF record + */ + public function getXf($style) + { + // Set the type of the XF record and some of the attributes. + if ($style == 'style') { + $style = 0xFFF5; + } else { + $style = $this->_locked; + $style |= $this->_hidden << 1; + } + + // Flags to indicate if attributes have been set. + $atr_num = ($this->_num_format != 0)?1:0; + $atr_fnt = ($this->font_index != 0)?1:0; + $atr_alc = ($this->_text_wrap)?1:0; + $atr_bdr = ($this->_bottom || + $this->_top || + $this->_left || + $this->_right)?1:0; + $atr_pat = (($this->_fg_color != 0x40) || + ($this->_bg_color != 0x41) || + $this->_pattern)?1:0; + $atr_prot = $this->_locked | $this->_hidden; + + // Zero the default border colour if the border has not been set. + if ($this->_bottom == 0) { + $this->_bottom_color = 0; + } + if ($this->_top == 0) { + $this->_top_color = 0; + } + if ($this->_right == 0) { + $this->_right_color = 0; + } + if ($this->_left == 0) { + $this->_left_color = 0; + } + if ($this->_diag == 0) { + $this->_diag_color = 0; + } + + $record = 0x00E0; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x0010; // Number of bytes to follow + } + if ($this->_BIFF_version == 0x0600) { + $length = 0x0014; + } + + $ifnt = $this->font_index; // Index to FONT record + $ifmt = $this->_num_format; // Index to FORMAT record + if ($this->_BIFF_version == 0x0500) { + $align = $this->_text_h_align; // Alignment + $align |= $this->_text_wrap << 3; + $align |= $this->_text_v_align << 4; + $align |= $this->_text_justlast << 7; + $align |= $this->_rotation << 8; + $align |= $atr_num << 10; + $align |= $atr_fnt << 11; + $align |= $atr_alc << 12; + $align |= $atr_bdr << 13; + $align |= $atr_pat << 14; + $align |= $atr_prot << 15; + + $icv = $this->_fg_color; // fg and bg pattern colors + $icv |= $this->_bg_color << 7; + + $fill = $this->_pattern; // Fill and border line style + $fill |= $this->_bottom << 6; + $fill |= $this->_bottom_color << 9; + + $border1 = $this->_top; // Border line style and color + $border1 |= $this->_left << 3; + $border1 |= $this->_right << 6; + $border1 |= $this->_top_color << 9; + + $border2 = $this->_left_color; // Border color + $border2 |= $this->_right_color << 7; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align, + $icv, $fill, + $border1, $border2); + } elseif ($this->_BIFF_version == 0x0600) { + $align = $this->_text_h_align; // Alignment + $align |= $this->_text_wrap << 3; + $align |= $this->_text_v_align << 4; + $align |= $this->_text_justlast << 7; + + $used_attrib = $atr_num << 2; + $used_attrib |= $atr_fnt << 3; + $used_attrib |= $atr_alc << 4; + $used_attrib |= $atr_bdr << 5; + $used_attrib |= $atr_pat << 6; + $used_attrib |= $atr_prot << 7; + + $icv = $this->_fg_color; // fg and bg pattern colors + $icv |= $this->_bg_color << 7; + + $border1 = $this->_left; // Border line style and color + $border1 |= $this->_right << 4; + $border1 |= $this->_top << 8; + $border1 |= $this->_bottom << 12; + $border1 |= $this->_left_color << 16; + $border1 |= $this->_right_color << 23; + $diag_tl_to_rb = 0; // FIXME: add method + $diag_tr_to_lb = 0; // FIXME: add method + $border1 |= $diag_tl_to_rb << 30; + $border1 |= $diag_tr_to_lb << 31; + + $border2 = $this->_top_color; // Border color + $border2 |= $this->_bottom_color << 7; + $border2 |= $this->_diag_color << 14; + $border2 |= $this->_diag << 21; + $border2 |= $this->_pattern << 26; + + $header = pack("vv", $record, $length); + + $rotation = $this->_rotation; + $biff8_options = 0x00; + $data = pack("vvvC", $ifnt, $ifmt, $style, $align); + $data .= pack("CCC", $rotation, $biff8_options, $used_attrib); + $data .= pack("VVv", $border1, $border2, $icv); + } + + return($header . $data); + } + + /** + * Generate an Excel BIFF FONT record. + * + * @return string The FONT record + */ + public function getFont() + { + $dyHeight = $this->_size * 20; // Height of font (1/20 of a point) + $icv = $this->_color; // Index to color palette + $bls = $this->_bold; // Bold style + $sss = $this->_font_script; // Superscript/subscript + $uls = $this->_underline; // Underline + $bFamily = $this->_font_family; // Font family + $bCharSet = $this->_font_charset; // Character set + $encoding = 0; // TODO: Unicode support + + $cch = strlen($this->_font_name); // Length of font name + $record = 0x31; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x0F + $cch; // Record length + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x10 + $cch; + } + $reserved = 0x00; // Reserved + $grbit = 0x00; // Font attributes + if ($this->_italic) { + $grbit |= 0x02; + } + if ($this->_font_strikeout) { + $grbit |= 0x08; + } + if ($this->_font_outline) { + $grbit |= 0x10; + } + if ($this->_font_shadow) { + $grbit |= 0x20; + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0500) { + $data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls, + $sss, $uls, $bFamily, + $bCharSet, $reserved, $cch); + } elseif ($this->_BIFF_version == 0x0600) { + $data = pack("vvvvvCCCCCC", $dyHeight, $grbit, $icv, $bls, + $sss, $uls, $bFamily, + $bCharSet, $reserved, $cch, $encoding); + } + return($header . $data . $this->_font_name); + } + + /** + * Returns a unique hash key for a font. + * Used by Spreadsheet_Excel_Writer_Workbook::_storeAllFonts() + * + * The elements that form the key are arranged to increase the probability of + * generating a unique key. Elements that hold a large range of numbers + * (eg. _color) are placed between two binary elements such as _italic + * + * @return string A key for this font + */ + public function getFontKey() + { + $key = "$this->_font_name$this->_size"; + $key .= "$this->_font_script$this->_underline"; + $key .= "$this->_font_strikeout$this->_bold$this->_font_outline"; + $key .= "$this->_font_family$this->_font_charset"; + $key .= "$this->_font_shadow$this->_color$this->_italic"; + $key = str_replace(' ', '_', $key); + return ($key); + } + + /** + * Returns the index used by Spreadsheet_Excel_Writer_Worksheet::_XF() + * + * @return integer The index for the XF record + */ + public function getXfIndex() + { + return($this->_xf_index); + } + + /** + * Used in conjunction with the set_xxx_color methods to convert a color + * string into a number. Color range is 0..63 but we will restrict it + * to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15. + * + * @access private + * @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional. + * @return integer The color index + */ + protected function _getColor($name_color = '') + { + $colors = array( + 'aqua' => 0x07, + 'cyan' => 0x07, + 'black' => 0x00, + 'blue' => 0x04, + 'brown' => 0x10, + 'magenta' => 0x06, + 'fuchsia' => 0x06, + 'gray' => 0x17, + 'grey' => 0x17, + 'green' => 0x11, + 'lime' => 0x03, + 'navy' => 0x12, + 'orange' => 0x35, + 'purple' => 0x14, + 'red' => 0x02, + 'silver' => 0x16, + 'white' => 0x01, + 'yellow' => 0x05 + ); + + // Return the default color, 0x7FFF, if undef, + if ($name_color === '') { + return(0x7FFF); + } + + // or the color string converted to an integer, + if (isset($colors[$name_color])) { + return($colors[$name_color]); + } + + // or the default color if string is unrecognised, + if (preg_match("/\D/",$name_color)) { + return(0x7FFF); + } + + // or the default color if arg is outside range, + if ($name_color > 63) { + return(0x7FFF); + } + + // or an integer in the valid range + return($name_color); + } + + /** + * Set cell alignment. + * + * @access public + * @param string $location alignment for the cell ('left', 'right', etc...). + */ + public function setAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'left') { + $this->_text_h_align = 1; + } + if ($location == 'centre') { + $this->_text_h_align = 2; + } + if ($location == 'center') { + $this->_text_h_align = 2; + } + if ($location == 'right') { + $this->_text_h_align = 3; + } + if ($location == 'fill') { + $this->_text_h_align = 4; + } + if ($location == 'justify') { + $this->_text_h_align = 5; + } + if ($location == 'merge') { + $this->_text_h_align = 6; + } + if ($location == 'equal_space') { // For T.K. + $this->_text_h_align = 7; + } + if ($location == 'top') { + $this->_text_v_align = 0; + } + if ($location == 'vcentre') { + $this->_text_v_align = 1; + } + if ($location == 'vcenter') { + $this->_text_v_align = 1; + } + if ($location == 'bottom') { + $this->_text_v_align = 2; + } + if ($location == 'vjustify') { + $this->_text_v_align = 3; + } + if ($location == 'vequal_space') { // For T.K. + $this->_text_v_align = 4; + } + } + + /** + * Set cell horizontal alignment. + * + * @access public + * @param string $location alignment for the cell ('left', 'right', etc...). + */ + public function setHAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'left') { + $this->_text_h_align = 1; + } + if ($location == 'centre') { + $this->_text_h_align = 2; + } + if ($location == 'center') { + $this->_text_h_align = 2; + } + if ($location == 'right') { + $this->_text_h_align = 3; + } + if ($location == 'fill') { + $this->_text_h_align = 4; + } + if ($location == 'justify') { + $this->_text_h_align = 5; + } + if ($location == 'merge') { + $this->_text_h_align = 6; + } + if ($location == 'equal_space') { // For T.K. + $this->_text_h_align = 7; + } + } + + /** + * Set cell vertical alignment. + * + * @access public + * @param string $location alignment for the cell ('top', 'vleft', 'vright', etc...). + */ + public function setVAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'top') { + $this->_text_v_align = 0; + } + if ($location == 'vcentre') { + $this->_text_v_align = 1; + } + if ($location == 'vcenter') { + $this->_text_v_align = 1; + } + if ($location == 'bottom') { + $this->_text_v_align = 2; + } + if ($location == 'vjustify') { + $this->_text_v_align = 3; + } + if ($location == 'vequal_space') { // For T.K. + $this->_text_v_align = 4; + } + } + + /** + * This is an alias for the unintuitive setAlign('merge') + * + * @access public + */ + public function setMerge() + { + $this->setAlign('merge'); + } + + /** + * Sets the boldness of the text. + * Bold has a range 100..1000. + * 0 (400) is normal. 1 (700) is bold. + * + * @access public + * @param integer $weight Weight for the text, 0 maps to 400 (normal text), + 1 maps to 700 (bold text). Valid range is: 100-1000. + It's Optional, default is 1 (bold). + */ + public function setBold($weight = 1) + { + if ($weight == 1) { + $weight = 0x2BC; // Bold text + } + if ($weight == 0) { + $weight = 0x190; // Normal text + } + if ($weight < 0x064) { + $weight = 0x190; // Lower bound + } + if ($weight > 0x3E8) { + $weight = 0x190; // Upper bound + } + $this->_bold = $weight; + } + + + /************************************ + * FUNCTIONS FOR SETTING CELLS BORDERS + */ + + /** + * Sets the width for the bottom border of the cell + * + * @access public + * @param integer $style style of the cell border. 1 => thin, 2 => thick. + */ + public function setBottom($style) + { + $this->_bottom = $style; + } + + /** + * Sets the width for the top border of the cell + * + * @access public + * @param integer $style style of the cell top border. 1 => thin, 2 => thick. + */ + public function setTop($style) + { + $this->_top = $style; + } + + /** + * Sets the width for the left border of the cell + * + * @access public + * @param integer $style style of the cell left border. 1 => thin, 2 => thick. + */ + public function setLeft($style) + { + $this->_left = $style; + } + + /** + * Sets the width for the right border of the cell + * + * @access public + * @param integer $style style of the cell right border. 1 => thin, 2 => thick. + */ + public function setRight($style) + { + $this->_right = $style; + } + + + /** + * Set cells borders to the same style + * + * @access public + * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick. + */ + public function setBorder($style) + { + $this->setBottom($style); + $this->setTop($style); + $this->setLeft($style); + $this->setRight($style); + } + + + /******************************************* + * FUNCTIONS FOR SETTING CELLS BORDERS COLORS + */ + + /** + * Sets all the cell's borders to the same color + * + * @access public + * @param mixed $color The color we are setting. Either a string (like 'blue'), + * or an integer (range is [8...63]). + */ + public function setBorderColor($color) + { + $this->setBottomColor($color); + $this->setTopColor($color); + $this->setLeftColor($color); + $this->setRightColor($color); + } + + /** + * Sets the cell's bottom border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setBottomColor($color) + { + $value = $this->_getColor($color); + $this->_bottom_color = $value; + } + + /** + * Sets the cell's top border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setTopColor($color) + { + $value = $this->_getColor($color); + $this->_top_color = $value; + } + + /** + * Sets the cell's left border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setLeftColor($color) + { + $value = $this->_getColor($color); + $this->_left_color = $value; + } + + /** + * Sets the cell's right border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setRightColor($color) + { + $value = $this->_getColor($color); + $this->_right_color = $value; + } + + + /** + * Sets the cell's foreground color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setFgColor($color) + { + $value = $this->_getColor($color); + $this->_fg_color = $value; + if ($this->_pattern == 0) { // force color to be seen + $this->_pattern = 1; + } + } + + /** + * Sets the cell's background color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setBgColor($color) + { + $value = $this->_getColor($color); + $this->_bg_color = $value; + if ($this->_pattern == 0) { // force color to be seen + $this->_pattern = 1; + } + } + + /** + * Sets the cell's color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + public function setColor($color) + { + $value = $this->_getColor($color); + $this->_color = $value; + } + + /** + * Sets the fill pattern attribute of a cell + * + * @access public + * @param integer $arg Optional. Defaults to 1. Meaningful values are: 0-18, + * 0 meaning no background. + */ + public function setPattern($arg = 1) + { + $this->_pattern = $arg; + } + + /** + * Sets the underline of the text + * + * @access public + * @param integer $underline The value for underline. Possible values are: + * 1 => underline, 2 => double underline. + */ + public function setUnderline($underline) + { + $this->_underline = $underline; + } + + /** + * Sets the font style as italic + * + * @access public + */ + public function setItalic() + { + $this->_italic = 1; + } + + /** + * Sets the font size + * + * @access public + * @param integer $size The font size (in pixels I think). + */ + public function setSize($size) + { + $this->_size = $size; + } + + /** + * Sets text wrapping + * + * @access public + */ + public function setTextWrap() + { + $this->_text_wrap = 1; + } + + /** + * Sets the orientation of the text + * + * @access public + * @param integer $angle The rotation angle for the text (clockwise). Possible + values are: 0, 90, 270 and -1 for stacking top-to-bottom. + */ + public function setTextRotation($angle) + { + switch ($angle) + { + case 0: + $this->_rotation = 0; + break; + case 90: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 3; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 180; + } + break; + case 270: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 2; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 90; + } + break; + case -1: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 1; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 255; + } + break; + default : + return $this->raiseError("Invalid value for angle.". + " Possible values are: 0, 90, 270 and -1 ". + "for stacking top-to-bottom."); + $this->_rotation = 0; + break; + } + } + + /** + * Sets the numeric format. + * It can be date, time, currency, etc... + * + * @access public + * @param integer $num_format The numeric format. + */ + public function setNumFormat($num_format) + { + $this->_num_format = $num_format; + } + + /** + * Sets font as strikeout. + * + * @access public + */ + public function setStrikeOut() + { + $this->_font_strikeout = 1; + } + + /** + * Sets outlining for a font. + * + * @access public + */ + public function setOutLine() + { + $this->_font_outline = 1; + } + + /** + * Sets font as shadow. + * + * @access public + */ + public function setShadow() + { + $this->_font_shadow = 1; + } + + /** + * Sets the script type of the text + * + * @access public + * @param integer $script The value for script type. Possible values are: + * 1 => superscript, 2 => subscript. + */ + public function setScript($script) + { + $this->_font_script = $script; + } + + /** + * Locks a cell. + * + * @access public + */ + public function setLocked() + { + $this->_locked = 1; + } + + /** + * Unlocks a cell. Useful for unprotecting particular cells of a protected sheet. + * + * @access public + */ + public function setUnLocked() + { + $this->_locked = 0; + } + + /** + * Sets the font family name. + * + * @access public + * @param string $fontfamily The font family name. Possible values are: + * 'Times New Roman', 'Arial', 'Courier'. + */ + public function setFontFamily($font_family) + { + $this->_font_name = $font_family; + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Parser.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Parser.php new file mode 100644 index 0000000..0909969 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Parser.php @@ -0,0 +1,1704 @@ +" +*/ +define('SPREADSHEET_EXCEL_WRITER_GT', ">"); + +/** +* @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<" +*/ +define('SPREADSHEET_EXCEL_WRITER_LT', "<"); + +/** +* @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<=" +*/ +define('SPREADSHEET_EXCEL_WRITER_LE', "<="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">=" +*/ +define('SPREADSHEET_EXCEL_WRITER_GE', ">="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "=" +*/ +define('SPREADSHEET_EXCEL_WRITER_EQ', "="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>" +*/ +define('SPREADSHEET_EXCEL_WRITER_NE', "<>"); + +/** +* * @const SPREADSHEET_EXCEL_WRITER_CONCAT token identifier for character "&" +*/ +define('SPREADSHEET_EXCEL_WRITER_CONCAT', "&"); + +if (!class_exists('PEAR')) { + require_once 'PEAR.php'; +} + +/** +* Class for parsing Excel formulas +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Parser extends PEAR +{ + /** + * The index of the character we are currently looking at + * @var integer + */ + public $_current_char; + + /** + * The token we are working on. + * @var string + */ + public $_current_token; + + /** + * The formula to parse + * @var string + */ + public $_formula; + + /** + * The character ahead of the current char + * @var string + */ + public $_lookahead; + + /** + * The parse tree to be generated + * @var string + */ + public $_parse_tree; + + /** + * The byte order. 1 => big endian, 0 => little endian. + * @var integer + */ + public $_byte_order; + + /** + * Array of external sheets + * @var array + */ + public $_ext_sheets; + + /** + * Array of sheet references in the form of REF structures + * @var array + */ + public $_references; + + /** + * The BIFF version for the workbook + * @var integer + */ + public $_BIFF_version; + + /** + * The class constructor + * + * @param integer $byte_order The byte order (Little endian or Big endian) of the architecture + (optional). 1 => big endian, 0 (default) little endian. + */ + public function __construct($byte_order, $biff_version) + { + $this->_current_char = 0; + $this->_BIFF_version = $biff_version; + $this->_current_token = ''; // The token we are working on. + $this->_formula = ''; // The formula to parse. + $this->_lookahead = ''; // The character ahead of the current char. + $this->_parse_tree = ''; // The parse tree to be generated. + $this->_initializeHashes(); // Initialize the hashes: ptg's and public function's ptg's + $this->_byte_order = $byte_order; // Little Endian or Big Endian + $this->_ext_sheets = array(); + $this->_references = array(); + } + + /** + * Initialize the ptg and public function hashes. + * + * @access private + */ + protected function _initializeHashes() + { + // The Excel ptg indices + $this->ptg = array( + 'ptgExp' => 0x01, + 'ptgTbl' => 0x02, + 'ptgAdd' => 0x03, + 'ptgSub' => 0x04, + 'ptgMul' => 0x05, + 'ptgDiv' => 0x06, + 'ptgPower' => 0x07, + 'ptgConcat' => 0x08, + 'ptgLT' => 0x09, + 'ptgLE' => 0x0A, + 'ptgEQ' => 0x0B, + 'ptgGE' => 0x0C, + 'ptgGT' => 0x0D, + 'ptgNE' => 0x0E, + 'ptgIsect' => 0x0F, + 'ptgUnion' => 0x10, + 'ptgRange' => 0x11, + 'ptgUplus' => 0x12, + 'ptgUminus' => 0x13, + 'ptgPercent' => 0x14, + 'ptgParen' => 0x15, + 'ptgMissArg' => 0x16, + 'ptgStr' => 0x17, + 'ptgAttr' => 0x19, + 'ptgSheet' => 0x1A, + 'ptgEndSheet' => 0x1B, + 'ptgErr' => 0x1C, + 'ptgBool' => 0x1D, + 'ptgInt' => 0x1E, + 'ptgNum' => 0x1F, + 'ptgArray' => 0x20, + 'ptgFunc' => 0x21, + 'ptgFuncVar' => 0x22, + 'ptgName' => 0x23, + 'ptgRef' => 0x24, + 'ptgArea' => 0x25, + 'ptgMemArea' => 0x26, + 'ptgMemErr' => 0x27, + 'ptgMemNoMem' => 0x28, + 'ptgMemFunc' => 0x29, + 'ptgRefErr' => 0x2A, + 'ptgAreaErr' => 0x2B, + 'ptgRefN' => 0x2C, + 'ptgAreaN' => 0x2D, + 'ptgMemAreaN' => 0x2E, + 'ptgMemNoMemN' => 0x2F, + 'ptgNameX' => 0x39, + 'ptgRef3d' => 0x3A, + 'ptgArea3d' => 0x3B, + 'ptgRefErr3d' => 0x3C, + 'ptgAreaErr3d' => 0x3D, + 'ptgArrayV' => 0x40, + 'ptgFuncV' => 0x41, + 'ptgFuncVarV' => 0x42, + 'ptgNameV' => 0x43, + 'ptgRefV' => 0x44, + 'ptgAreaV' => 0x45, + 'ptgMemAreaV' => 0x46, + 'ptgMemErrV' => 0x47, + 'ptgMemNoMemV' => 0x48, + 'ptgMemFuncV' => 0x49, + 'ptgRefErrV' => 0x4A, + 'ptgAreaErrV' => 0x4B, + 'ptgRefNV' => 0x4C, + 'ptgAreaNV' => 0x4D, + 'ptgMemAreaNV' => 0x4E, + 'ptgMemNoMemN' => 0x4F, + 'ptgFuncCEV' => 0x58, + 'ptgNameXV' => 0x59, + 'ptgRef3dV' => 0x5A, + 'ptgArea3dV' => 0x5B, + 'ptgRefErr3dV' => 0x5C, + 'ptgAreaErr3d' => 0x5D, + 'ptgArrayA' => 0x60, + 'ptgFuncA' => 0x61, + 'ptgFuncVarA' => 0x62, + 'ptgNameA' => 0x63, + 'ptgRefA' => 0x64, + 'ptgAreaA' => 0x65, + 'ptgMemAreaA' => 0x66, + 'ptgMemErrA' => 0x67, + 'ptgMemNoMemA' => 0x68, + 'ptgMemFuncA' => 0x69, + 'ptgRefErrA' => 0x6A, + 'ptgAreaErrA' => 0x6B, + 'ptgRefNA' => 0x6C, + 'ptgAreaNA' => 0x6D, + 'ptgMemAreaNA' => 0x6E, + 'ptgMemNoMemN' => 0x6F, + 'ptgFuncCEA' => 0x78, + 'ptgNameXA' => 0x79, + 'ptgRef3dA' => 0x7A, + 'ptgArea3dA' => 0x7B, + 'ptgRefErr3dA' => 0x7C, + 'ptgAreaErr3d' => 0x7D + ); + + // Thanks to Michael Meeks and Gnumeric for the initial arg values. + // + // The following hash was generated by "function_locale.pl" in the distro. + // Refer to function_locale.pl for non-English public function names. + // + // The array elements are as follow: + // ptg: The Excel public function ptg code. + // args: The number of arguments that the public function takes: + // >=0 is a fixed number of arguments. + // -1 is a variable number of arguments. + // class: The reference, value or array class of the public function args. + // vol: The public function is volatile. + // + $this->_functions = array( + // public function ptg args class vol + 'COUNT' => array( 0, -1, 0, 0 ), + 'IF' => array( 1, -1, 1, 0 ), + 'ISNA' => array( 2, 1, 1, 0 ), + 'ISERROR' => array( 3, 1, 1, 0 ), + 'SUM' => array( 4, -1, 0, 0 ), + 'AVERAGE' => array( 5, -1, 0, 0 ), + 'MIN' => array( 6, -1, 0, 0 ), + 'MAX' => array( 7, -1, 0, 0 ), + 'ROW' => array( 8, -1, 0, 0 ), + 'COLUMN' => array( 9, -1, 0, 0 ), + 'NA' => array( 10, 0, 0, 0 ), + 'NPV' => array( 11, -1, 1, 0 ), + 'STDEV' => array( 12, -1, 0, 0 ), + 'DOLLAR' => array( 13, -1, 1, 0 ), + 'FIXED' => array( 14, -1, 1, 0 ), + 'SIN' => array( 15, 1, 1, 0 ), + 'COS' => array( 16, 1, 1, 0 ), + 'TAN' => array( 17, 1, 1, 0 ), + 'ATAN' => array( 18, 1, 1, 0 ), + 'PI' => array( 19, 0, 1, 0 ), + 'SQRT' => array( 20, 1, 1, 0 ), + 'EXP' => array( 21, 1, 1, 0 ), + 'LN' => array( 22, 1, 1, 0 ), + 'LOG10' => array( 23, 1, 1, 0 ), + 'ABS' => array( 24, 1, 1, 0 ), + 'INT' => array( 25, 1, 1, 0 ), + 'SIGN' => array( 26, 1, 1, 0 ), + 'ROUND' => array( 27, 2, 1, 0 ), + 'LOOKUP' => array( 28, -1, 0, 0 ), + 'INDEX' => array( 29, -1, 0, 1 ), + 'REPT' => array( 30, 2, 1, 0 ), + 'MID' => array( 31, 3, 1, 0 ), + 'LEN' => array( 32, 1, 1, 0 ), + 'VALUE' => array( 33, 1, 1, 0 ), + 'TRUE' => array( 34, 0, 1, 0 ), + 'FALSE' => array( 35, 0, 1, 0 ), + 'AND' => array( 36, -1, 0, 0 ), + 'OR' => array( 37, -1, 0, 0 ), + 'NOT' => array( 38, 1, 1, 0 ), + 'MOD' => array( 39, 2, 1, 0 ), + 'DCOUNT' => array( 40, 3, 0, 0 ), + 'DSUM' => array( 41, 3, 0, 0 ), + 'DAVERAGE' => array( 42, 3, 0, 0 ), + 'DMIN' => array( 43, 3, 0, 0 ), + 'DMAX' => array( 44, 3, 0, 0 ), + 'DSTDEV' => array( 45, 3, 0, 0 ), + 'VAR' => array( 46, -1, 0, 0 ), + 'DVAR' => array( 47, 3, 0, 0 ), + 'TEXT' => array( 48, 2, 1, 0 ), + 'LINEST' => array( 49, -1, 0, 0 ), + 'TREND' => array( 50, -1, 0, 0 ), + 'LOGEST' => array( 51, -1, 0, 0 ), + 'GROWTH' => array( 52, -1, 0, 0 ), + 'PV' => array( 56, -1, 1, 0 ), + 'FV' => array( 57, -1, 1, 0 ), + 'NPER' => array( 58, -1, 1, 0 ), + 'PMT' => array( 59, -1, 1, 0 ), + 'RATE' => array( 60, -1, 1, 0 ), + 'MIRR' => array( 61, 3, 0, 0 ), + 'IRR' => array( 62, -1, 0, 0 ), + 'RAND' => array( 63, 0, 1, 1 ), + 'MATCH' => array( 64, -1, 0, 0 ), + 'DATE' => array( 65, 3, 1, 0 ), + 'TIME' => array( 66, 3, 1, 0 ), + 'DAY' => array( 67, 1, 1, 0 ), + 'MONTH' => array( 68, 1, 1, 0 ), + 'YEAR' => array( 69, 1, 1, 0 ), + 'WEEKDAY' => array( 70, -1, 1, 0 ), + 'HOUR' => array( 71, 1, 1, 0 ), + 'MINUTE' => array( 72, 1, 1, 0 ), + 'SECOND' => array( 73, 1, 1, 0 ), + 'NOW' => array( 74, 0, 1, 1 ), + 'AREAS' => array( 75, 1, 0, 1 ), + 'ROWS' => array( 76, 1, 0, 1 ), + 'COLUMNS' => array( 77, 1, 0, 1 ), + 'OFFSET' => array( 78, -1, 0, 1 ), + 'SEARCH' => array( 82, -1, 1, 0 ), + 'TRANSPOSE' => array( 83, 1, 1, 0 ), + 'TYPE' => array( 86, 1, 1, 0 ), + 'ATAN2' => array( 97, 2, 1, 0 ), + 'ASIN' => array( 98, 1, 1, 0 ), + 'ACOS' => array( 99, 1, 1, 0 ), + 'CHOOSE' => array( 100, -1, 1, 0 ), + 'HLOOKUP' => array( 101, -1, 0, 0 ), + 'VLOOKUP' => array( 102, -1, 0, 0 ), + 'ISREF' => array( 105, 1, 0, 0 ), + 'LOG' => array( 109, -1, 1, 0 ), + 'CHAR' => array( 111, 1, 1, 0 ), + 'LOWER' => array( 112, 1, 1, 0 ), + 'UPPER' => array( 113, 1, 1, 0 ), + 'PROPER' => array( 114, 1, 1, 0 ), + 'LEFT' => array( 115, -1, 1, 0 ), + 'RIGHT' => array( 116, -1, 1, 0 ), + 'EXACT' => array( 117, 2, 1, 0 ), + 'TRIM' => array( 118, 1, 1, 0 ), + 'REPLACE' => array( 119, 4, 1, 0 ), + 'SUBSTITUTE' => array( 120, -1, 1, 0 ), + 'CODE' => array( 121, 1, 1, 0 ), + 'FIND' => array( 124, -1, 1, 0 ), + 'CELL' => array( 125, -1, 0, 1 ), + 'ISERR' => array( 126, 1, 1, 0 ), + 'ISTEXT' => array( 127, 1, 1, 0 ), + 'ISNUMBER' => array( 128, 1, 1, 0 ), + 'ISBLANK' => array( 129, 1, 1, 0 ), + 'T' => array( 130, 1, 0, 0 ), + 'N' => array( 131, 1, 0, 0 ), + 'DATEVALUE' => array( 140, 1, 1, 0 ), + 'TIMEVALUE' => array( 141, 1, 1, 0 ), + 'SLN' => array( 142, 3, 1, 0 ), + 'SYD' => array( 143, 4, 1, 0 ), + 'DDB' => array( 144, -1, 1, 0 ), + 'INDIRECT' => array( 148, -1, 1, 1 ), + 'CALL' => array( 150, -1, 1, 0 ), + 'CLEAN' => array( 162, 1, 1, 0 ), + 'MDETERM' => array( 163, 1, 2, 0 ), + 'MINVERSE' => array( 164, 1, 2, 0 ), + 'MMULT' => array( 165, 2, 2, 0 ), + 'IPMT' => array( 167, -1, 1, 0 ), + 'PPMT' => array( 168, -1, 1, 0 ), + 'COUNTA' => array( 169, -1, 0, 0 ), + 'PRODUCT' => array( 183, -1, 0, 0 ), + 'FACT' => array( 184, 1, 1, 0 ), + 'DPRODUCT' => array( 189, 3, 0, 0 ), + 'ISNONTEXT' => array( 190, 1, 1, 0 ), + 'STDEVP' => array( 193, -1, 0, 0 ), + 'VARP' => array( 194, -1, 0, 0 ), + 'DSTDEVP' => array( 195, 3, 0, 0 ), + 'DVARP' => array( 196, 3, 0, 0 ), + 'TRUNC' => array( 197, -1, 1, 0 ), + 'ISLOGICAL' => array( 198, 1, 1, 0 ), + 'DCOUNTA' => array( 199, 3, 0, 0 ), + 'ROUNDUP' => array( 212, 2, 1, 0 ), + 'ROUNDDOWN' => array( 213, 2, 1, 0 ), + 'RANK' => array( 216, -1, 0, 0 ), + 'ADDRESS' => array( 219, -1, 1, 0 ), + 'DAYS360' => array( 220, -1, 1, 0 ), + 'TODAY' => array( 221, 0, 1, 1 ), + 'VDB' => array( 222, -1, 1, 0 ), + 'MEDIAN' => array( 227, -1, 0, 0 ), + 'SUMPRODUCT' => array( 228, -1, 2, 0 ), + 'SINH' => array( 229, 1, 1, 0 ), + 'COSH' => array( 230, 1, 1, 0 ), + 'TANH' => array( 231, 1, 1, 0 ), + 'ASINH' => array( 232, 1, 1, 0 ), + 'ACOSH' => array( 233, 1, 1, 0 ), + 'ATANH' => array( 234, 1, 1, 0 ), + 'DGET' => array( 235, 3, 0, 0 ), + 'INFO' => array( 244, 1, 1, 1 ), + 'DB' => array( 247, -1, 1, 0 ), + 'FREQUENCY' => array( 252, 2, 0, 0 ), + 'ERROR.TYPE' => array( 261, 1, 1, 0 ), + 'REGISTER.ID' => array( 267, -1, 1, 0 ), + 'AVEDEV' => array( 269, -1, 0, 0 ), + 'BETADIST' => array( 270, -1, 1, 0 ), + 'GAMMALN' => array( 271, 1, 1, 0 ), + 'BETAINV' => array( 272, -1, 1, 0 ), + 'BINOMDIST' => array( 273, 4, 1, 0 ), + 'CHIDIST' => array( 274, 2, 1, 0 ), + 'CHIINV' => array( 275, 2, 1, 0 ), + 'COMBIN' => array( 276, 2, 1, 0 ), + 'CONFIDENCE' => array( 277, 3, 1, 0 ), + 'CRITBINOM' => array( 278, 3, 1, 0 ), + 'EVEN' => array( 279, 1, 1, 0 ), + 'EXPONDIST' => array( 280, 3, 1, 0 ), + 'FDIST' => array( 281, 3, 1, 0 ), + 'FINV' => array( 282, 3, 1, 0 ), + 'FISHER' => array( 283, 1, 1, 0 ), + 'FISHERINV' => array( 284, 1, 1, 0 ), + 'FLOOR' => array( 285, 2, 1, 0 ), + 'GAMMADIST' => array( 286, 4, 1, 0 ), + 'GAMMAINV' => array( 287, 3, 1, 0 ), + 'CEILING' => array( 288, 2, 1, 0 ), + 'HYPGEOMDIST' => array( 289, 4, 1, 0 ), + 'LOGNORMDIST' => array( 290, 3, 1, 0 ), + 'LOGINV' => array( 291, 3, 1, 0 ), + 'NEGBINOMDIST' => array( 292, 3, 1, 0 ), + 'NORMDIST' => array( 293, 4, 1, 0 ), + 'NORMSDIST' => array( 294, 1, 1, 0 ), + 'NORMINV' => array( 295, 3, 1, 0 ), + 'NORMSINV' => array( 296, 1, 1, 0 ), + 'STANDARDIZE' => array( 297, 3, 1, 0 ), + 'ODD' => array( 298, 1, 1, 0 ), + 'PERMUT' => array( 299, 2, 1, 0 ), + 'POISSON' => array( 300, 3, 1, 0 ), + 'TDIST' => array( 301, 3, 1, 0 ), + 'WEIBULL' => array( 302, 4, 1, 0 ), + 'SUMXMY2' => array( 303, 2, 2, 0 ), + 'SUMX2MY2' => array( 304, 2, 2, 0 ), + 'SUMX2PY2' => array( 305, 2, 2, 0 ), + 'CHITEST' => array( 306, 2, 2, 0 ), + 'CORREL' => array( 307, 2, 2, 0 ), + 'COVAR' => array( 308, 2, 2, 0 ), + 'FORECAST' => array( 309, 3, 2, 0 ), + 'FTEST' => array( 310, 2, 2, 0 ), + 'INTERCEPT' => array( 311, 2, 2, 0 ), + 'PEARSON' => array( 312, 2, 2, 0 ), + 'RSQ' => array( 313, 2, 2, 0 ), + 'STEYX' => array( 314, 2, 2, 0 ), + 'SLOPE' => array( 315, 2, 2, 0 ), + 'TTEST' => array( 316, 4, 2, 0 ), + 'PROB' => array( 317, -1, 2, 0 ), + 'DEVSQ' => array( 318, -1, 0, 0 ), + 'GEOMEAN' => array( 319, -1, 0, 0 ), + 'HARMEAN' => array( 320, -1, 0, 0 ), + 'SUMSQ' => array( 321, -1, 0, 0 ), + 'KURT' => array( 322, -1, 0, 0 ), + 'SKEW' => array( 323, -1, 0, 0 ), + 'ZTEST' => array( 324, -1, 0, 0 ), + 'LARGE' => array( 325, 2, 0, 0 ), + 'SMALL' => array( 326, 2, 0, 0 ), + 'QUARTILE' => array( 327, 2, 0, 0 ), + 'PERCENTILE' => array( 328, 2, 0, 0 ), + 'PERCENTRANK' => array( 329, -1, 0, 0 ), + 'MODE' => array( 330, -1, 2, 0 ), + 'TRIMMEAN' => array( 331, 2, 0, 0 ), + 'TINV' => array( 332, 2, 1, 0 ), + 'CONCATENATE' => array( 336, -1, 1, 0 ), + 'POWER' => array( 337, 2, 1, 0 ), + 'RADIANS' => array( 342, 1, 1, 0 ), + 'DEGREES' => array( 343, 1, 1, 0 ), + 'SUBTOTAL' => array( 344, -1, 0, 0 ), + 'SUMIF' => array( 345, -1, 0, 0 ), + 'COUNTIF' => array( 346, 2, 0, 0 ), + 'COUNTBLANK' => array( 347, 1, 0, 0 ), + 'ROMAN' => array( 354, -1, 1, 0 ) + ); + } + + /** + * Convert a token to the proper ptg value. + * + * @access private + * @param mixed $token The token to convert. + * @return mixed the converted token on success. PEAR_Error if the token + * is not recognized + */ + protected function _convert($token) + { + if (preg_match("/^\"[^\"]{0,255}\"$/", $token)) { + return $this->_convertString($token); + + } elseif (is_numeric($token)) { + return $this->_convertNumber($token); + + // match references like A1 or $A$1 + } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token)) { + return $this->_convertRef2d($token); + + // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 + } elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z](\d+)$/u",$token)) { + return $this->_convertRef3d($token); + + // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 + } elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z](\d+)$/u",$token)) { + return $this->_convertRef3d($token); + + // match ranges like A1:B2 + } elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token)) { + return $this->_convertRange2d($token); + + // match ranges like A1..B2 + } elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token)) { + return $this->_convertRange2d($token); + + // match external ranges like Sheet1!A1 or Sheet1:Sheet2!A1:B2 + } elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/u",$token)) { + return $this->_convertRange3d($token); + + // match external ranges like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1:B2 + } elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/u",$token)) { + return $this->_convertRange3d($token); + + // operators (including parentheses) + } elseif (isset($this->ptg[$token])) { + return pack("C", $this->ptg[$token]); + + // commented so argument number can be processed correctly. See toReversePolish(). + /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token)) + { + return($this->_convertFunction($token,$this->_func_args)); + }*/ + + // if it's an argument, ignore the token (the argument remains) + } elseif ($token == 'arg') { + return ''; + } + // TODO: use real error codes + return $this->raiseError("Unknown token $token"); + } + + /** + * Convert a number token to ptgInt or ptgNum + * + * @access private + * @param mixed $num an integer or double for conversion to its ptg value + */ + protected function _convertNumber($num) + { + // Integer in the range 0..2**16-1 + if ((preg_match("/^\d+$/", $num)) and ($num <= 65535)) { + return pack("Cv", $this->ptg['ptgInt'], $num); + } else { // A float + if ($this->_byte_order) { // if it's Big Endian + $num = strrev($num); + } + return pack("Cd", $this->ptg['ptgNum'], $num); + } + } + + /** + * Convert a string token to ptgStr + * + * @access private + * @param string $string A string for conversion to its ptg value. + * @return mixed the converted token on success. PEAR_Error if the string + * is longer than 255 characters. + */ + protected function _convertString($string) + { + // chop away beggining and ending quotes + $string = substr($string, 1, strlen($string) - 2); + if (strlen($string) > 255) { + return $this->raiseError("String is too long"); + } + + if ($this->_BIFF_version == 0x0500) { + return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string; + } elseif ($this->_BIFF_version == 0x0600) { + $encoding = 0; // TODO: Unicode support + return pack("CCC", $this->ptg['ptgStr'], strlen($string), $encoding).$string; + } + } + + /** + * Convert a public function to a ptgFunc or ptgFuncVarV depending on the number of + * args that it takes. + * + * @access private + * @param string $token The name of the public function for convertion to ptg value. + * @param integer $num_args The number of arguments the public function receives. + * @return string The packed ptg for the public function + */ + protected function _convertFunction($token, $num_args) + { + $args = $this->_functions[$token][1]; + $volatile = $this->_functions[$token][3]; + + // Fixed number of args eg. TIME($i,$j,$k). + if ($args >= 0) { + return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]); + } + // Variable number of args eg. SUM($i,$j,$k, ..). + if ($args == -1) { + return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]); + } + } + + /** + * Convert an Excel range such as A1:D4 to a ptgRefV. + * + * @access private + * @param string $range An Excel range in the A1:A2 or A1..A2 format. + */ + protected function _convertRange2d($range, $class=0) + { + + // TODO: possible class value 0,1,2 check Formula.pm + // Split the range into 2 cell refs + if (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\:([A-Ia-i]?[A-Za-z])(\d+)$/", $range)) { + list($cell1, $cell2) = explode(':', $range); + } elseif (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\.\.([A-Ia-i]?[A-Za-z])(\d+)$/", $range)) { + list($cell1, $cell2) = explode('..', $range); + + } else { + // TODO: use real error codes + return $this->raiseError("Unknown range separator", 0, PEAR_ERROR_DIE); + } + + // Convert the cell references + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if (PEAR::isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if (PEAR::isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea']); + } elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgAreaV']); + } elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgAreaA']); + } else { + // TODO: use real error codes + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + return $ptgArea . $row1 . $row2 . $col1. $col2; + } + + /** + * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to + * a ptgArea3d. + * + * @access private + * @param string $token An Excel range in the Sheet1!A1:A2 format. + * @return mixed The packed ptgArea3d token on success, PEAR_Error on failure. + */ + protected function _convertRange3d($token) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $range) = explode('!', $token); + + // Convert the external reference part (different for BIFF8) + if ($this->_BIFF_version == 0x0500) { + $ext_ref = $this->_packExtRef($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } elseif ($this->_BIFF_version == 0x0600) { + $ext_ref = $this->_getRefIndex($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } + + // Split the range into 2 cell refs + list($cell1, $cell2) = explode(':', $range); + + // Convert the cell references + if (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/", $cell1)) { + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if (PEAR::isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if (PEAR::isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + } else { // It's a rows range (like 26:27) + $cells_array = $this->_rangeToPackedRange($cell1.':'.$cell2); + if (PEAR::isError($cells_array)) { + return $cells_array; + } + list($row1, $col1, $row2, $col2) = $cells_array; + } + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea3d']); + } elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgArea3dV']); + } elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgArea3dA']); + } else { + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + + return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2; + } + + /** + * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. + * + * @access private + * @param string $cell An Excel cell reference + * @return string The cell in packed() format with the corresponding ptg + */ + protected function _convertRef2d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Convert the cell reference + $cell_array = $this->_cellToPackedRowcol($cell); + if (PEAR::isError($cell_array)) { + return $cell_array; + } + list($row, $col) = $cell_array; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef']); + } elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRefV']); + } elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRefA']); + } else { + // TODO: use real error codes + return $this->raiseError("Unknown class $class"); + } + return $ptgRef.$row.$col; + } + + /** + * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a + * ptgRef3d. + * + * @access private + * @param string $cell An Excel cell reference + * @return mixed The packed ptgRef3d token on success, PEAR_Error on failure. + */ + protected function _convertRef3d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $cell) = explode('!', $cell); + + // Convert the external reference part (different for BIFF8) + if ($this->_BIFF_version == 0x0500) { + $ext_ref = $this->_packExtRef($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } elseif ($this->_BIFF_version == 0x0600) { + $ext_ref = $this->_getRefIndex($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } + + // Convert the cell reference part + list($row, $col) = $this->_cellToPackedRowcol($cell); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef3d']); + } elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRef3dV']); + } elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRef3dA']); + } else { + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + + return $ptgRef . $ext_ref. $row . $col; + } + + /** + * Convert the sheet name part of an external reference, for example "Sheet1" or + * "Sheet1:Sheet2", to a packed structure. + * + * @access private + * @param string $ext_ref The name of the external reference + * @return string The reference index in packed() format + */ + protected function _packExtRef($ext_ref) + { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match("/:/", $ext_ref)) { + list($sheet_name1, $sheet_name2) = explode(':', $ext_ref); + + $sheet1 = $this->_getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name1 in formula"); + } + $sheet2 = $this->_getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name2 in formula"); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + list($sheet1, $sheet2) = array($sheet2, $sheet1); + } + } else { // Single sheet name only. + $sheet1 = $this->_getSheetIndex($ext_ref); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $ext_ref in formula"); + } + $sheet2 = $sheet1; + } + + // References are stored relative to 0xFFFF. + $offset = -1 - $sheet1; + + return pack('vdvv', $offset, 0x00, $sheet1, $sheet2); + } + + /** + * Look up the REF index that corresponds to an external sheet name + * (or range). If it doesn't exist yet add it to the workbook's references + * array. It assumes all sheet names given must exist. + * + * @access private + * @param string $ext_ref The name of the external reference + * @return mixed The reference index in packed() format on success, + * PEAR_Error on failure + */ + protected function _getRefIndex($ext_ref) + { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match("/:/", $ext_ref)) { + list($sheet_name1, $sheet_name2) = explode(':', $ext_ref); + + $sheet1 = $this->_getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name1 in formula"); + } + $sheet2 = $this->_getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name2 in formula"); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + list($sheet1, $sheet2) = array($sheet2, $sheet1); + } + } else { // Single sheet name only. + $sheet1 = $this->_getSheetIndex($ext_ref); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $ext_ref in formula"); + } + $sheet2 = $sheet1; + } + + // assume all references belong to this document + $supbook_index = 0x00; + $ref = pack('vvv', $supbook_index, $sheet1, $sheet2); + $total_references = count($this->_references); + $index = -1; + for ($i = 0; $i < $total_references; $i++) { + if ($ref == $this->_references[$i]) { + $index = $i; + break; + } + } + // if REF was not found add it to references array + if ($index == -1) { + $this->_references[$total_references] = $ref; + $index = $total_references; + } + + return pack('v', $index); + } + + /** + * Look up the index that corresponds to an external sheet name. The hash of + * sheet names is updated by the addworksheet() method of the + * Spreadsheet_Excel_Writer_Workbook class. + * + * @access private + * @return integer The sheet index, -1 if the sheet was not found + */ + protected function _getSheetIndex($sheet_name) + { + if (!isset($this->_ext_sheets[$sheet_name])) { + return -1; + } else { + return $this->_ext_sheets[$sheet_name]; + } + } + + /** + * This method is used to update the array of sheet names. It is + * called by the addWorksheet() method of the + * Spreadsheet_Excel_Writer_Workbook class. + * + * @access public + * @see Spreadsheet_Excel_Writer_Workbook::addWorksheet() + * @param string $name The name of the worksheet being added + * @param integer $index The index of the worksheet being added + */ + public function setExtSheet($name, $index) + { + $this->_ext_sheets[$name] = $index; + } + + /** + * pack() row and column into the required 3 or 4 byte format. + * + * @access private + * @param string $cell The Excel cell reference to be packed + * @return array Array containing the row and column in packed() format + */ + protected function _cellToPackedRowcol($cell) + { + $cell = strtoupper($cell); + list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell); + if ($col >= 256) { + return $this->raiseError("Column in: $cell greater than 255"); + } + // FIXME: change for BIFF8 + if ($row >= 16384) { + return $this->raiseError("Row in: $cell greater than 16384 "); + } + + // Set the high bits to indicate if row or col are relative. + if ($this->_BIFF_version == 0x0500) { + $row |= $col_rel << 14; + $row |= $row_rel << 15; + $col = pack('C', $col); + } elseif ($this->_BIFF_version == 0x0600) { + $col |= $col_rel << 14; + $col |= $row_rel << 15; + $col = pack('v', $col); + } + $row = pack('v', $row); + + return array($row, $col); + } + + /** + * pack() row range into the required 3 or 4 byte format. + * Just using maximum col/rows, which is probably not the correct solution + * + * @access private + * @param string $range The Excel range to be packed + * @return array Array containing (row1,col1,row2,col2) in packed() format + */ + protected function _rangeToPackedRange($range) + { + preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match); + // return absolute rows if there is a $ in the ref + $row1_rel = empty($match[1]) ? 1 : 0; + $row1 = $match[2]; + $row2_rel = empty($match[3]) ? 1 : 0; + $row2 = $match[4]; + // Convert 1-index to zero-index + $row1--; + $row2--; + // Trick poor inocent Excel + $col1 = 0; + $col2 = 16383; // FIXME: maximum possible value for Excel 5 (change this!!!) + + // FIXME: this changes for BIFF8 + if (($row1 >= 16384) or ($row2 >= 16384)) { + return $this->raiseError("Row in: $range greater than 16384 "); + } + + // Set the high bits to indicate if rows are relative. + if ($this->_BIFF_version == 0x0500) { + $row1 |= $row1_rel << 14; // FIXME: probably a bug + $row2 |= $row2_rel << 15; + $col1 = pack('C', $col1); + $col2 = pack('C', $col2); + } elseif ($this->_BIFF_version == 0x0600) { + $col1 |= $row1_rel << 15; + $col2 |= $row2_rel << 15; + $col1 = pack('v', $col1); + $col2 = pack('v', $col2); + } + $row1 = pack('v', $row1); + $row2 = pack('v', $row2); + + return array($row1, $col1, $row2, $col2); + } + + /** + * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero + * indexed row and column number. Also returns two (0,1) values to indicate + * whether the row or column are relative references. + * + * @access private + * @param string $cell The Excel cell reference in A1 format. + * @return array + */ + protected function _cellToRowcol($cell) + { + preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match); + // return absolute column if there is a $ in the ref + $col_rel = empty($match[1]) ? 1 : 0; + $col_ref = $match[2]; + $row_rel = empty($match[3]) ? 1 : 0; + $row = $match[4]; + + // Convert base26 column string to a number. + $expn = strlen($col_ref) - 1; + $col = 0; + $col_ref_length = strlen($col_ref); + for ($i = 0; $i < $col_ref_length; $i++) { + $col += (ord($col_ref[$i]) - ord('A') + 1) * pow(26, $expn); + $expn--; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return array($row, $col, $row_rel, $col_rel); + } + + /** + * Advance to the next valid token. + * + * @access private + */ + protected function _advance() + { + $i = $this->_current_char; + $formula_length = strlen($this->_formula); + // eat up white spaces + if ($i < $formula_length) { + while ($this->_formula[$i] == " ") { + $i++; + } + + if ($i < ($formula_length - 1)) { + $this->_lookahead = $this->_formula[$i+1]; + } + $token = ''; + } + + while ($i < $formula_length) { + $token .= $this->_formula[$i]; + if ($i < ($formula_length - 1)) { + $this->_lookahead = $this->_formula[$i+1]; + } else { + $this->_lookahead = ''; + } + + if ($this->_match($token) != '') { + //if ($i < strlen($this->_formula) - 1) { + // $this->_lookahead = $this->_formula{$i+1}; + //} + $this->_current_char = $i + 1; + $this->_current_token = $token; + return 1; + } + + if ($i < ($formula_length - 2)) { + $this->_lookahead = $this->_formula[$i+2]; + } else { // if we run out of characters _lookahead becomes empty + $this->_lookahead = ''; + } + $i++; + } + //die("Lexical error ".$this->_current_char); + } + + /** + * Checks if it's a valid token. + * + * @access private + * @param mixed $token The token to check. + * @return mixed The checked token or false on failure + */ + protected function _match($token) + { + switch($token) { + case SPREADSHEET_EXCEL_WRITER_ADD: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_SUB: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_MUL: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_DIV: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_OPEN: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_CLOSE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_COMA: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_SEMICOLON: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_GT: + if ($this->_lookahead == '=') { // it's a GE token + break; + } + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_LT: + // it's a LE or a NE token + if (($this->_lookahead == '=') or ($this->_lookahead == '>')) { + break; + } + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_GE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_LE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_EQ: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_NE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_CONCAT: + return $token; + break; + default: + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.') and + ($this->_lookahead != '!')) + { + return $token; + } + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z][0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return $token; + } + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z][0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return $token; + } + // if it's a range (A1:A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // if it's a range (A1..A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like Sheet1!A1 or Sheet1:Sheet2!A1:B2 + elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1:B2 + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's a number (check that it's not a sheet name or range) + elseif (is_numeric($token) and + (!is_numeric($token.$this->_lookahead) or ($this->_lookahead == '')) and + ($this->_lookahead != '!') and ($this->_lookahead != ':')) + { + return $token; + } + // If it's a string (of maximum 255 characters) + elseif (preg_match("/^\"[^\"]{0,255}\"$/",$token)) + { + return $token; + } + // if it's a public function call + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$token) and ($this->_lookahead == "(")) + { + return $token; + } + return ''; + } + } + + /** + * The parsing method. It parses a formula. + * + * @access public + * @param string $formula The formula to parse, without the initial equal + * sign (=). + * @return mixed true on success, PEAR_Error on failure + */ + public function parse($formula) + { + $this->_current_char = 0; + $this->_formula = $formula; + $this->_lookahead = $formula[1]; + $this->_advance(); + $this->_parse_tree = $this->_condition(); + if (PEAR::isError($this->_parse_tree)) { + return $this->_parse_tree; + } + return true; + } + + /** + * It parses a condition. It assumes the following rule: + * Cond -> Expr [(">" | "<") Expr] + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + protected function _condition() + { + $result = $this->_expression(); + if (PEAR::isError($result)) { + return $result; + } + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLT', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGT', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_EQ) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgEQ', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_NE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgNE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_CONCAT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgConcat', $result, $result2); + } + return $result; + } + + /** + * It parses a expression. It assumes the following rule: + * Expr -> Term [("+" | "-") Term] + * -> "string" + * -> "-" Term + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + protected function _expression() + { + // If it's a string return a string node + if (preg_match("/^\"[^\"]{0,255}\"$/", $this->_current_token)) { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB) { + // catch "-" Term + $this->_advance(); + $result2 = $this->_expression(); + $result = $this->_createTree('ptgUminus', $result2, ''); + return $result; + } + $result = $this->_term(); + if (PEAR::isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB)) { + /**/ + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) { + $this->_advance(); + $result2 = $this->_term(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgAdd', $result, $result2); + } else { + $this->_advance(); + $result2 = $this->_term(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgSub', $result, $result2); + } + } + return $result; + } + + /** + * This public function just introduces a ptgParen element in the tree, so that Excel + * doesn't get confused when working with a parenthesized formula afterwards. + * + * @access private + * @see _fact() + * @return array The parsed ptg'd tree + */ + protected function _parenthesizedExpression() + { + $result = $this->_createTree('ptgParen', $this->_expression(), ''); + return $result; + } + + /** + * It parses a term. It assumes the following rule: + * Term -> Fact [("*" | "/") Fact] + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + protected function _term() + { + $result = $this->_fact(); + if (PEAR::isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_DIV)) { + /**/ + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) { + $this->_advance(); + $result2 = $this->_fact(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgMul', $result, $result2); + } else { + $this->_advance(); + $result2 = $this->_fact(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgDiv', $result, $result2); + } + } + return $result; + } + + /** + * It parses a factor. It assumes the following rule: + * Fact -> ( Expr ) + * | CellRef + * | CellRange + * | Number + * | Function + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + protected function _fact() + { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_OPEN) { + $this->_advance(); // eat the "(" + $result = $this->_parenthesizedExpression(); + if ($this->_current_token != SPREADSHEET_EXCEL_WRITER_CLOSE) { + return $this->raiseError("')' token expected."); + } + $this->_advance(); // eat the ")" + return $result; + } + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z][0-9]+$/u",$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z][0-9]+$/u",$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // if it's a range + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token) or + preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + // If it's an external range (Sheet1!A1 or Sheet1!A1:B2) + elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + // If it's an external range ('Sheet1'!A1 or 'Sheet1'!A1:B2) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + elseif (is_numeric($this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // if it's a public function call + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$this->_current_token)) + { + $result = $this->_func(); + return $result; + } + return $this->raiseError("Syntax error: ".$this->_current_token. + ", lookahead: ".$this->_lookahead. + ", current char: ".$this->_current_char); + } + + /** + * It parses a public function call. It assumes the following rule: + * Func -> ( Expr [,Expr]* ) + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + protected function _func() + { + $num_args = 0; // number of arguments received + $function = strtoupper($this->_current_token); + $result = ''; // initialize result + $this->_advance(); + $this->_advance(); // eat the "(" + while ($this->_current_token != ')') { + /**/ + if ($num_args > 0) { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_COMA or + $this->_current_token == SPREADSHEET_EXCEL_WRITER_SEMICOLON) + { + $this->_advance(); // eat the "," or ";" + } else { + return $this->raiseError("Syntax error: comma expected in ". + "public function $function, arg #{$num_args}"); + } + $result2 = $this->_condition(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', $result, $result2); + } else { // first argument + $result2 = $this->_condition(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', '', $result2); + } + $num_args++; + } + if (!isset($this->_functions[$function])) { + return $this->raiseError("Function $function() doesn't exist"); + } + $args = $this->_functions[$function][1]; + // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid. + if (($args >= 0) and ($args != $num_args)) { + return $this->raiseError("Incorrect number of arguments in public function $function() "); + } + + $result = $this->_createTree($function, $result, $num_args); + $this->_advance(); // eat the ")" + return $result; + } + + /** + * Creates a tree. In fact an array which may have one or two arrays (sub-trees) + * as elements. + * + * @access private + * @param mixed $value The value of this node. + * @param mixed $left The left array (sub-tree) or a final node. + * @param mixed $right The right array (sub-tree) or a final node. + * @return array A tree + */ + protected function _createTree($value, $left, $right) + { + return array('value' => $value, 'left' => $left, 'right' => $right); + } + + /** + * Builds a string containing the tree in reverse polish notation (What you + * would use in a HP calculator stack). + * The following tree: + * + * + + * / \ + * 2 3 + * + * produces: "23+" + * + * The following tree: + * + * + + * / \ + * 3 * + * / \ + * 6 A1 + * + * produces: "36A1*+" + * + * In fact all operands, functions, references, etc... are written as ptg's + * + * @access public + * @param array $tree The optional tree to convert. + * @return string The tree in reverse polish notation + */ + public function toReversePolish($tree = array()) + { + $polish = ""; // the string we are going to return + if (empty($tree)) { // If it's the first call use _parse_tree + $tree = $this->_parse_tree; + } + if (is_array($tree['left'])) { + $converted_tree = $this->toReversePolish($tree['left']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } elseif ($tree['left'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['left']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + if (is_array($tree['right'])) { + $converted_tree = $this->toReversePolish($tree['right']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } elseif ($tree['right'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['right']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + // if it's a public function convert it here (so we can set it's arguments) + if (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/",$tree['value']) and + !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/',$tree['value']) and + !preg_match("/^[A-Ia-i]?[A-Za-z](\d+)\.\.[A-Ia-i]?[A-Za-z](\d+)$/",$tree['value']) and + !is_numeric($tree['value']) and + !isset($this->ptg[$tree['value']])) + { + // left subtree for a public function is always an array. + if ($tree['left'] != '') { + $left_tree = $this->toReversePolish($tree['left']); + } else { + $left_tree = ''; + } + if (PEAR::isError($left_tree)) { + return $left_tree; + } + // add it's left subtree and return. + return $left_tree.$this->_convertFunction($tree['value'], $tree['right']); + } else { + $converted_tree = $this->_convert($tree['value']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + } + $polish .= $converted_tree; + return $polish; + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Validator.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Validator.php new file mode 100644 index 0000000..e2bd4cf --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Validator.php @@ -0,0 +1,228 @@ + +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +//require_once('PEAR.php'); + +// Possible operator types + +/* +FIXME: change prefixes +*/ +define("OP_BETWEEN", 0x00); +define("OP_NOTBETWEEN", 0x01); +define("OP_EQUAL", 0x02); +define("OP_NOTEQUAL", 0x03); +define("OP_GT", 0x04); +define("OP_LT", 0x05); +define("OP_GTE", 0x06); +define("OP_LTE", 0x07); + +/** +* Baseclass for generating Excel DV records (validations) +* +* @author Herman Kuiper +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ +class Spreadsheet_Excel_Writer_Validator +{ + public $_type; + public $_style; + public $_fixedList; + public $_blank; + public $_incell; + public $_showprompt; + public $_showerror; + public $_title_prompt; + public $_descr_prompt; + public $_title_error; + public $_descr_error; + public $_operator; + public $_formula1; + public $_formula2; + /** + * The parser from the workbook. Used to parse validation formulas also + * @var Spreadsheet_Excel_Writer_Parser + */ + public $_parser; + + public function __construct($parser) + { + $this->_parser = $parser; + $this->_type = 0x01; // FIXME: add method for setting datatype + $this->_style = 0x00; + $this->_fixedList = false; + $this->_blank = false; + $this->_incell = false; + $this->_showprompt = false; + $this->_showerror = true; + $this->_title_prompt = "\x00"; + $this->_descr_prompt = "\x00"; + $this->_title_error = "\x00"; + $this->_descr_error = "\x00"; + $this->_operator = 0x00; // default is equal + $this->_formula1 = ''; + $this->_formula2 = ''; + } + + public function setPrompt($promptTitle = "\x00", $promptDescription = "\x00", $showPrompt = true) + { + $this->_showprompt = $showPrompt; + $this->_title_prompt = $promptTitle; + $this->_descr_prompt = $promptDescription; + } + + public function setError($errorTitle = "\x00", $errorDescription = "\x00", $showError = true) + { + $this->_showerror = $showError; + $this->_title_error = $errorTitle; + $this->_descr_error = $errorDescription; + } + + public function allowBlank() + { + $this->_blank = true; + } + + public function onInvalidStop() + { + $this->_style = 0x00; + } + + public function onInvalidWarn() + { + $this->_style = 0x01; + } + + public function onInvalidInfo() + { + $this->_style = 0x02; + } + + public function setFormula1($formula) + { + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if (PEAR::isError($error)) { + return $this->_formula1; + } + + $this->_formula1 = $this->_parser->toReversePolish(); + if (PEAR::isError($this->_formula1)) { + return $this->_formula1; + } + return true; + } + + public function setFormula2($formula) + { + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if (PEAR::isError($error)) { + return $this->_formula2; + } + + $this->_formula2 = $this->_parser->toReversePolish(); + if (PEAR::isError($this->_formula2)) { + return $this->_formula2; + } + return true; + } + + protected function _getOptions() + { + $options = $this->_type; + $options |= $this->_style << 3; + if ($this->_fixedList) { + $options |= 0x80; + } + if ($this->_blank) { + $options |= 0x100; + } + if (!$this->_incell) { + $options |= 0x200; + } + if ($this->_showprompt) { + $options |= 0x40000; + } + if ($this->_showerror) { + $options |= 0x80000; + } + $options |= $this->_operator << 20; + + return $options; + } + + protected function _getData() + { + $title_prompt_len = strlen($this->_title_prompt); + $descr_prompt_len = strlen($this->_descr_prompt); + $title_error_len = strlen($this->_title_error); + $descr_error_len = strlen($this->_descr_error); + + $formula1_size = strlen($this->_formula1); + $formula2_size = strlen($this->_formula2); + + $data = pack("V", $this->_getOptions()); + $data .= pack("vC", $title_prompt_len, 0x00) . $this->_title_prompt; + $data .= pack("vC", $title_error_len, 0x00) . $this->_title_error; + $data .= pack("vC", $descr_prompt_len, 0x00) . $this->_descr_prompt; + $data .= pack("vC", $descr_error_len, 0x00) . $this->_descr_error; + + $data .= pack("vv", $formula1_size, 0x0000) . $this->_formula1; + $data .= pack("vv", $formula2_size, 0x0000) . $this->_formula2; + + return $data; + } +} + +/*class Spreadsheet_Excel_Writer_Validation_List extends Spreadsheet_Excel_Writer_Validation +{ + public function Spreadsheet_Excel_Writer_Validation_list() + { + parent::Spreadsheet_Excel_Writer_Validation(); + $this->_type = 0x03; + } + + public function setList($source, $incell = true) + { + $this->_incell = $incell; + $this->_fixedList = true; + + $source = implode("\x00", $source); + $this->_formula1 = pack("CCC", 0x17, strlen($source), 0x0c) . $source; + } + + public function setRow($row, $col1, $col2, $incell = true) + { + $this->_incell = $incell; + //$this->_formula1 = ...; + } + + public function setCol($col, $row1, $row2, $incell = true) + { + $this->_incell = $incell; + //$this->_formula1 = ...; + } +}*/ \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Workbook.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Workbook.php new file mode 100644 index 0000000..63eb839 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Workbook.php @@ -0,0 +1,1629 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +if (!class_exists('Spreadsheet_Excel_Writer_BIFFwriter')) { + require_once 'Spreadsheet/Excel/Writer/Format.php'; + require_once 'Spreadsheet/Excel/Writer/BIFFwriter.php'; + require_once 'Spreadsheet/Excel/Writer/Worksheet.php'; + require_once 'Spreadsheet/Excel/Writer/Parser.php'; +} + +if (!class_exists('OLE')) { + require_once 'OLE.php'; +} + +if (!class_exists('OLE_PPS_Root')) { + require_once 'OLE/PPS/Root.php'; +} + +if (!class_exists('OLE_PPS_File')) { + require_once 'OLE/PPS/File.php'; +} + +/** +* Class for generating Excel Spreadsheets +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter +{ + /** + * Filename for the Workbook + * @var string + */ + public $_filename; + + /** + * Formula parser + * @var Spreadsheet_Excel_Writer_Parser + */ + public $_parser; + + /** + * Flag for 1904 date system (0 => base date is 1900, 1 => base date is 1904) + * @var integer + */ + public $_1904; + + /** + * The active worksheet of the workbook (0 indexed) + * @var integer + */ + public $_activesheet; + + /** + * 1st displayed worksheet in the workbook (0 indexed) + * @var integer + */ + public $_firstsheet; + + /** + * Number of workbook tabs selected + * @var integer + */ + public $_selected; + + /** + * Index for creating adding new formats to the workbook + * @var integer + */ + public $_xf_index; + + /** + * Flag for preventing close from being called twice. + * @var integer + * @see close() + */ + public $_fileclosed; + + /** + * The BIFF file size for the workbook. + * @var integer + * @see _calcSheetOffsets() + */ + public $_biffsize; + + /** + * The default sheetname for all sheets created. + * @var string + */ + public $_sheetname; + + /** + * The default XF format. + * @var Spreadsheet_Excel_Writer_Format + */ + public $_tmp_format; + + /** + * Array containing references to all of this workbook's worksheets + * @var array + */ + public $_worksheets; + + /** + * Array of sheetnames for creating the EXTERNSHEET records + * @var array + */ + public $_sheetnames; + + /** + * Array containing references to all of this workbook's formats + * @var array + */ + public $_formats; + + /** + * Array containing the colour palette + * @var array + */ + public $_palette; + + /** + * The default format for URLs. + * @var Spreadsheet_Excel_Writer_Format + */ + public $_url_format; + + /** + * The codepage indicates the text encoding used for strings + * @var integer + */ + public $_codepage; + + /** + * The country code used for localization + * @var integer + */ + public $_country_code; + + /** + * number of bytes for sizeinfo of strings + * @var integer + */ + public $_string_sizeinfo_size; + + /** @var int */ + public $_timestamp; + + /** + * Class constructor + * + * @param string filename for storing the workbook. "-" for writing to stdout. + * @access public + */ + public function __construct($filename) + { + // It needs to call its parent's constructor explicitly + parent::__construct(); + + $this->_filename = $filename; + $this->_parser = new Spreadsheet_Excel_Writer_Parser($this->_byte_order, $this->_BIFF_version); + $this->_1904 = 0; + $this->_activesheet = 0; + $this->_firstsheet = 0; + $this->_selected = 0; + $this->_xf_index = 16; // 15 style XF's and 1 cell XF. + $this->_fileclosed = 0; + $this->_biffsize = 0; + $this->_sheetname = 'Sheet'; + $this->_tmp_format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version); + $this->_worksheets = array(); + $this->_sheetnames = array(); + $this->_formats = array(); + $this->_palette = array(); + $this->_codepage = 0x04E4; // FIXME: should change for BIFF8 + $this->_country_code = -1; + $this->_string_sizeinfo = 3; + + // Add the default format for hyperlinks + $this->_url_format = $this->addFormat(array('color' => 'blue', 'underline' => 1)); + $this->_str_total = 0; + $this->_str_unique = 0; + $this->_str_table = array(); + $this->_timestamp = time(); + + $this->_setPaletteXl97(); + } + + /** + * Calls finalization methods. + * This method should always be the last one to be called on every workbook + * + * @access public + * @return mixed true on success. PEAR_Error on failure + */ + public function close() + { + if ($this->_fileclosed) { // Prevent close() from being called twice. + return true; + } + $res = $this->_storeWorkbook(); + if ($this->isError($res)) { + return $this->raiseError($res->getMessage()); + } + $this->_fileclosed = 1; + return true; + } + + /** + * An accessor for the _worksheets[] array + * Returns an array of the worksheet objects in a workbook + * It actually calls to worksheets() + * + * @access public + * @see worksheets() + * @return array + */ + public function sheets() + { + return $this->worksheets(); + } + + /** + * An accessor for the _worksheets[] array. + * Returns an array of the worksheet objects in a workbook + * + * @access public + * @return array + */ + public function worksheets() + { + return $this->_worksheets; + } + + /** + * Sets the BIFF version. + * This method exists just to access experimental public functionality + * from BIFF8. It will be deprecated ! + * Only possible value is 8 (Excel 97/2000). + * For any other value it fails silently. + * + * @access public + * @param integer $version The BIFF version + */ + public function setVersion($version) + { + if ($version == 8) { // only accept version 8 + $version = 0x0600; + $this->_BIFF_version = $version; + // change BIFFwriter limit for CONTINUE records + $this->_limit = 8228; + $this->_tmp_format->_BIFF_version = $version; + $this->_url_format->_BIFF_version = $version; + $this->_parser->_BIFF_version = $version; + $this->_codepage = 0x04B0; + + $total_worksheets = count($this->_worksheets); + // change version for all worksheets too + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_worksheets[$i]->_BIFF_version = $version; + } + + $total_formats = count($this->_formats); + // change version for all formats too + for ($i = 0; $i < $total_formats; $i++) { + $this->_formats[$i]->_BIFF_version = $version; + } + } + } + + /** + * Set the country identifier for the workbook + * + * @access public + * @param integer $code Is the international calling country code for the + * chosen country. + */ + public function setCountry($code) + { + $this->_country_code = $code; + } + + /** + * Add a new worksheet to the Excel workbook. + * If no name is given the name of the worksheet will be Sheeti$i, with + * $i in [1..]. + * + * @access public + * @param string $name the optional name of the worksheet + * @return mixed reference to a worksheet object on success, PEAR_Error + * on failure + */ + public function addWorksheet($name = '') + { + $index = count($this->_worksheets); + $sheetname = $this->_sheetname; + + if ($name == '') { + $name = $sheetname.($index+1); + } + + // Check that sheetname is <= 31 chars (Excel limit before BIFF8). + if ($this->_BIFF_version != 0x0600) + { + if (strlen($name) > 31) { + return $this->raiseError("Sheetname $name must be <= 31 chars"); + } + } else { + if(function_exists('iconv')) { + $name = iconv('UTF-8','UTF-16LE',$name); + } + } + + // Check that the worksheet name doesn't already exist: a fatal Excel error. + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + if ($this->_worksheets[$i]->getName() == $name) { + return $this->raiseError("Worksheet '$name' already exists"); + } + } + + $worksheet = new Spreadsheet_Excel_Writer_Worksheet($this->_BIFF_version, + $name, $index, + $this->_activesheet, $this->_firstsheet, + $this->_str_total, $this->_str_unique, + $this->_str_table, $this->_url_format, + $this->_parser, $this->_tmp_dir); + + $this->_worksheets[$index] = $worksheet; // Store ref for iterator + $this->_sheetnames[$index] = $name; // Store EXTERNSHEET names + $this->_parser->setExtSheet($name, $index); // Register worksheet name with parser + return $worksheet; + } + + /** + * Add a new format to the Excel workbook. + * Also, pass any properties to the Format constructor. + * + * @access public + * @param array $properties array with properties for initializing the format. + * @return Spreadsheet_Excel_Writer_Format + */ + public function addFormat($properties = array()) + { + $format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version, $this->_xf_index, $properties); + $this->_xf_index += 1; + $this->_formats[] = $format; + return $format; + } + + /** + * Create new validator. + * + * @access public + * @return Spreadsheet_Excel_Writer_Validator + */ + public function addValidator() + { + if (!class_exists('Spreadsheet_Excel_Writer_Validator')) { + include_once 'Spreadsheet/Excel/Writer/Validator.php'; + } + /* FIXME: check for successful inclusion*/ + $valid = new Spreadsheet_Excel_Writer_Validator($this->_parser); + return $valid; + } + + /** + * Change the RGB components of the elements in the colour palette. + * + * @access public + * @param integer $index colour index + * @param integer $red red RGB value [0-255] + * @param integer $green green RGB value [0-255] + * @param integer $blue blue RGB value [0-255] + * @return integer The palette index for the custom color + */ + public function setCustomColor($index, $red, $green, $blue) + { + // Match a HTML #xxyyzz style parameter + /*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) { + @_ = ($_[0], hex $1, hex $2, hex $3); + }*/ + + // Check that the colour index is the right range + if ($index < 8 or $index > 64) { + // TODO: assign real error codes + return $this->raiseError("Color index $index outside range: 8 <= index <= 64"); + } + + // Check that the colour components are in the right range + if (($red < 0 or $red > 255) || + ($green < 0 or $green > 255) || + ($blue < 0 or $blue > 255)) + { + return $this->raiseError("Color component outside range: 0 <= color <= 255"); + } + + $index -= 8; // Adjust colour index (wingless dragonfly) + + // Set the RGB value + $this->_palette[$index] = array($red, $green, $blue, 0); + return($index + 8); + } + + /** + * Sets the colour palette to the Excel 97+ default. + * + * @access private + */ + protected function _setPaletteXl97() + { + $this->_palette = array( + array(0x00, 0x00, 0x00, 0x00), // 8 + array(0xff, 0xff, 0xff, 0x00), // 9 + array(0xff, 0x00, 0x00, 0x00), // 10 + array(0x00, 0xff, 0x00, 0x00), // 11 + array(0x00, 0x00, 0xff, 0x00), // 12 + array(0xff, 0xff, 0x00, 0x00), // 13 + array(0xff, 0x00, 0xff, 0x00), // 14 + array(0x00, 0xff, 0xff, 0x00), // 15 + array(0x80, 0x00, 0x00, 0x00), // 16 + array(0x00, 0x80, 0x00, 0x00), // 17 + array(0x00, 0x00, 0x80, 0x00), // 18 + array(0x80, 0x80, 0x00, 0x00), // 19 + array(0x80, 0x00, 0x80, 0x00), // 20 + array(0x00, 0x80, 0x80, 0x00), // 21 + array(0xc0, 0xc0, 0xc0, 0x00), // 22 + array(0x80, 0x80, 0x80, 0x00), // 23 + array(0x99, 0x99, 0xff, 0x00), // 24 + array(0x99, 0x33, 0x66, 0x00), // 25 + array(0xff, 0xff, 0xcc, 0x00), // 26 + array(0xcc, 0xff, 0xff, 0x00), // 27 + array(0x66, 0x00, 0x66, 0x00), // 28 + array(0xff, 0x80, 0x80, 0x00), // 29 + array(0x00, 0x66, 0xcc, 0x00), // 30 + array(0xcc, 0xcc, 0xff, 0x00), // 31 + array(0x00, 0x00, 0x80, 0x00), // 32 + array(0xff, 0x00, 0xff, 0x00), // 33 + array(0xff, 0xff, 0x00, 0x00), // 34 + array(0x00, 0xff, 0xff, 0x00), // 35 + array(0x80, 0x00, 0x80, 0x00), // 36 + array(0x80, 0x00, 0x00, 0x00), // 37 + array(0x00, 0x80, 0x80, 0x00), // 38 + array(0x00, 0x00, 0xff, 0x00), // 39 + array(0x00, 0xcc, 0xff, 0x00), // 40 + array(0xcc, 0xff, 0xff, 0x00), // 41 + array(0xcc, 0xff, 0xcc, 0x00), // 42 + array(0xff, 0xff, 0x99, 0x00), // 43 + array(0x99, 0xcc, 0xff, 0x00), // 44 + array(0xff, 0x99, 0xcc, 0x00), // 45 + array(0xcc, 0x99, 0xff, 0x00), // 46 + array(0xff, 0xcc, 0x99, 0x00), // 47 + array(0x33, 0x66, 0xff, 0x00), // 48 + array(0x33, 0xcc, 0xcc, 0x00), // 49 + array(0x99, 0xcc, 0x00, 0x00), // 50 + array(0xff, 0xcc, 0x00, 0x00), // 51 + array(0xff, 0x99, 0x00, 0x00), // 52 + array(0xff, 0x66, 0x00, 0x00), // 53 + array(0x66, 0x66, 0x99, 0x00), // 54 + array(0x96, 0x96, 0x96, 0x00), // 55 + array(0x00, 0x33, 0x66, 0x00), // 56 + array(0x33, 0x99, 0x66, 0x00), // 57 + array(0x00, 0x33, 0x00, 0x00), // 58 + array(0x33, 0x33, 0x00, 0x00), // 59 + array(0x99, 0x33, 0x00, 0x00), // 60 + array(0x99, 0x33, 0x66, 0x00), // 61 + array(0x33, 0x33, 0x99, 0x00), // 62 + array(0x33, 0x33, 0x33, 0x00), // 63 + ); + } + + /** + * Assemble worksheets into a workbook and send the BIFF data to an OLE + * storage. + * + * @access private + * @return mixed true on success. PEAR_Error on failure + */ + protected function _storeWorkbook() + { + if (count($this->_worksheets) == 0) { + return true; + } + + // Ensure that at least one worksheet has been selected. + if ($this->_activesheet == 0) { + $this->_worksheets[0]->selected = 1; + } + + // Calculate the number of selected worksheet tabs and call the finalization + // methods for each worksheet + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + if ($this->_worksheets[$i]->selected) { + $this->_selected++; + } + $this->_worksheets[$i]->close($this->_sheetnames); + } + + // Add Workbook globals + $this->_storeBof(0x0005); + $this->_storeCodepage(); + if ($this->_BIFF_version == 0x0600) { + $this->_storeWindow1(); + } + if ($this->_BIFF_version == 0x0500) { + $this->_storeExterns(); // For print area and repeat rows + } + $this->_storeNames(); // For print area and repeat rows + if ($this->_BIFF_version == 0x0500) { + $this->_storeWindow1(); + } + $this->_storeDatemode(); + $this->_storeAllFonts(); + $this->_storeAllNumFormats(); + $this->_storeAllXfs(); + $this->_storeAllStyles(); + $this->_storePalette(); + $this->_calcSheetOffsets(); + + // Add BOUNDSHEET records + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset); + } + + if ($this->_country_code != -1) { + $this->_storeCountry(); + } + + if ($this->_BIFF_version == 0x0600) { + //$this->_storeSupbookInternal(); + /* TODO: store external SUPBOOK records and XCT and CRN records + in case of external references for BIFF8 */ + //$this->_storeExternsheetBiff8(); + $this->_storeSharedStringsTable(); + } + + // End Workbook globals + $this->_storeEof(); + + // Store the workbook in an OLE container + $res = $this->_storeOLEFile(); + if ($this->isError($res)) { + return $this->raiseError($res->getMessage()); + } + return true; + } + + /** + * Store the workbook in an OLE container + * + * @access private + * @return mixed true on success. PEAR_Error on failure + */ + protected function _storeOLEFile() + { + if($this->_BIFF_version == 0x0600) { + $OLE = new OLE_PPS_File(OLE::Asc2Ucs('Workbook')); + } else { + $OLE = new OLE_PPS_File(OLE::Asc2Ucs('Book')); + } + if ($this->_tmp_dir != '') { + $OLE->setTempDir($this->_tmp_dir); + } + $res = $OLE->init(); + if ($this->isError($res)) { + return $this->raiseError("OLE Error: ".$res->getMessage()); + } + $OLE->append($this->_data); + + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + while ($tmp = $this->_worksheets[$i]->getData(true)) { + $OLE->append($tmp); + } + } + + $root = new OLE_PPS_Root($this->_timestamp, $this->_timestamp, array($OLE)); + if ($this->_tmp_dir != '') { + $root->setTempDir($this->_tmp_dir); + } + + $res = $root->save($this->_filename); + if ($this->isError($res)) { + return $this->raiseError("OLE Error: ".$res->getMessage()); + } + return true; + } + + /** + * Calculate offsets for Worksheet BOF records. + * + * @access private + */ + protected function _calcSheetOffsets() + { + if ($this->_BIFF_version == 0x0600) { + $boundsheet_length = 12; // fixed length for a BOUNDSHEET record + } else { + $boundsheet_length = 11; + } + $EOF = 4; + $offset = $this->_datasize; + + if ($this->_BIFF_version == 0x0600) { + // add the length of the SST + /* TODO: check this works for a lot of strings (> 8224 bytes) */ + $offset += $this->_calculateSharedStringsSizes(); + if ($this->_country_code != -1) { + $offset += 8; // adding COUNTRY record + } + // add the lenght of SUPBOOK, EXTERNSHEET and NAME records + //$offset += 8; // FIXME: calculate real value when storing the records + } + $total_worksheets = count($this->_worksheets); + // add the length of the BOUNDSHEET records + for ($i = 0; $i < $total_worksheets; $i++) { + $offset += $boundsheet_length + strlen($this->_worksheets[$i]->name); + } + $offset += $EOF; + + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_worksheets[$i]->offset = $offset; + $offset += $this->_worksheets[$i]->_datasize; + } + $this->_biffsize = $offset; + } + + /** + * Store the Excel FONT records. + * + * @access private + */ + protected function _storeAllFonts() + { + // tmp_format is added by the constructor. We use this to write the default XF's + $format = $this->_tmp_format; + $font = $format->getFont(); + + // Note: Fonts are 0-indexed. According to the SDK there is no index 4, + // so the following fonts are 0, 1, 2, 3, 5 + // + for ($i = 1; $i <= 5; $i++){ + $this->_append($font); + } + + // Iterate through the XF objects and write a FONT record if it isn't the + // same as the default FONT and if it hasn't already been used. + // + $fonts = array(); + $index = 6; // The first user defined FONT + + $key = $format->getFontKey(); // The default font from _tmp_format + $fonts[$key] = 0; // Index of the default font + + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $key = $this->_formats[$i]->getFontKey(); + if (isset($fonts[$key])) { + // FONT has already been used + $this->_formats[$i]->font_index = $fonts[$key]; + } else { + // Add a new FONT record + $fonts[$key] = $index; + $this->_formats[$i]->font_index = $index; + $index++; + $font = $this->_formats[$i]->getFont(); + $this->_append($font); + } + } + } + + /** + * Store user defined numerical formats i.e. FORMAT records + * + * @access private + */ + protected function _storeAllNumFormats() + { + // Leaning num_format syndrome + $hash_num_formats = array(); + $num_formats = array(); + $index = 164; + + // Iterate through the XF objects and write a FORMAT record if it isn't a + // built-in format type and if the FORMAT string hasn't already been used. + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $num_format = $this->_formats[$i]->_num_format; + + // Check if $num_format is an index to a built-in format. + // Also check for a string of zeros, which is a valid format string + // but would evaluate to zero. + // + if (!preg_match("/^0+\d/", $num_format)) { + if (preg_match("/^\d+$/", $num_format)) { // built-in format + continue; + } + } + + if (isset($hash_num_formats[$num_format])) { + // FORMAT has already been used + $this->_formats[$i]->_num_format = $hash_num_formats[$num_format]; + } else{ + // Add a new FORMAT + $hash_num_formats[$num_format] = $index; + $this->_formats[$i]->_num_format = $index; + array_push($num_formats,$num_format); + $index++; + } + } + + // Write the new FORMAT records starting from 0xA4 + $index = 164; + foreach ($num_formats as $num_format) { + $this->_storeNumFormat($num_format,$index); + $index++; + } + } + + /** + * Write all XF records. + * + * @access private + */ + protected function _storeAllXfs() + { + // _tmp_format is added by the constructor. We use this to write the default XF's + // The default font index is 0 + // + $format = $this->_tmp_format; + for ($i = 0; $i <= 14; $i++) { + $xf = $format->getXf('style'); // Style XF + $this->_append($xf); + } + + $xf = $format->getXf('cell'); // Cell XF + $this->_append($xf); + + // User defined XFs + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $xf = $this->_formats[$i]->getXf('cell'); + $this->_append($xf); + } + } + + /** + * Write all STYLE records. + * + * @access private + */ + protected function _storeAllStyles() + { + $this->_storeStyle(); + } + + /** + * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for + * the NAME records. + * + * @access private + */ + protected function _storeExterns() + + { + // Create EXTERNCOUNT with number of worksheets + $this->_storeExterncount(count($this->_worksheets)); + + // Create EXTERNSHEET for each worksheet + foreach ($this->_sheetnames as $sheetname) { + $this->_storeExternsheet($sheetname); + } + } + + /** + * Write the NAME record to define the print area and the repeat rows and cols. + * + * @access private + */ + protected function _storeNames() + { + // Create the print area NAME records + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + // Write a Name record if the print area has been defined + if (isset($this->_worksheets[$i]->print_rowmin)) { + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x06, // NAME type + $this->_worksheets[$i]->print_rowmin, + $this->_worksheets[$i]->print_rowmax, + $this->_worksheets[$i]->print_colmin, + $this->_worksheets[$i]->print_colmax + ); + } + } + + // Create the print title NAME records + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + $rowmin = $this->_worksheets[$i]->title_rowmin; + $rowmax = $this->_worksheets[$i]->title_rowmax; + $colmin = $this->_worksheets[$i]->title_colmin; + $colmax = $this->_worksheets[$i]->title_colmax; + + // Determine if row + col, row, col or nothing has been defined + // and write the appropriate record + // + if (isset($rowmin) && isset($colmin)) { + // Row and column titles have been defined. + // Row title has been defined. + $this->_storeNameLong( + $this->_worksheets[$i]->index, + 0x07, // NAME type + $rowmin, + $rowmax, + $colmin, + $colmax + ); + } elseif (isset($rowmin)) { + // Row title has been defined. + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x07, // NAME type + $rowmin, + $rowmax, + 0x00, + 0xff + ); + } elseif (isset($colmin)) { + // Column title has been defined. + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x07, // NAME type + 0x0000, + 0x3fff, + $colmin, + $colmax + ); + } else { + // Print title hasn't been defined. + } + } + } + + + + + /****************************************************************************** + * + * BIFF RECORDS + * + */ + + /** + * Stores the CODEPAGE biff record. + * + * @access private + */ + protected function _storeCodepage() + { + $record = 0x0042; // Record identifier + $length = 0x0002; // Number of bytes to follow + $cv = $this->_codepage; // The code page + + $header = pack('vv', $record, $length); + $data = pack('v', $cv); + + $this->_append($header . $data); + } + + /** + * Write Excel BIFF WINDOW1 record. + * + * @access private + */ + protected function _storeWindow1() + { + $record = 0x003D; // Record identifier + $length = 0x0012; // Number of bytes to follow + + $xWn = 0x0000; // Horizontal position of window + $yWn = 0x0000; // Vertical position of window + $dxWn = 0x25BC; // Width of window + $dyWn = 0x1572; // Height of window + + $grbit = 0x0038; // Option flags + $ctabsel = $this->_selected; // Number of workbook tabs selected + $wTabRatio = 0x0258; // Tab to scrollbar ratio + + $itabFirst = $this->_firstsheet; // 1st displayed worksheet + $itabCur = $this->_activesheet; // Active worksheet + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn, + $grbit, + $itabCur, $itabFirst, + $ctabsel, $wTabRatio); + $this->_append($header . $data); + } + + /** + * Writes Excel BIFF BOUNDSHEET record. + * FIXME: inconsistent with BIFF documentation + * + * @param string $sheetname Worksheet name + * @param integer $offset Location of worksheet BOF + * @access private + */ + protected function _storeBoundsheet($sheetname,$offset) + { + $record = 0x0085; // Record identifier + if ($this->_BIFF_version == 0x0600) { + $length = 0x08 + strlen($sheetname); // Number of bytes to follow + } else { + $length = 0x07 + strlen($sheetname); // Number of bytes to follow + } + + $grbit = 0x0000; // Visibility and sheet type + if ($this->_BIFF_version == 0x0600) { + $cch = mb_strlen($sheetname,'UTF-16LE'); // Length of sheet name + } else { + $cch = strlen($sheetname); // Length of sheet name + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("VvCC", $offset, $grbit, $cch, 0x1); + } else { + $data = pack("VvC", $offset, $grbit, $cch); + } + $this->_append($header.$data.$sheetname); + } + + /** + * Write Internal SUPBOOK record + * + * @access private + */ + protected function _storeSupbookInternal() + { + $record = 0x01AE; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", count($this->_worksheets), 0x0104); + $this->_append($header . $data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. + * + * @param string $sheetname Worksheet name + * @access private + */ + protected function _storeExternsheetBiff8() + { + $total_references = count($this->_parser->_references); + $record = 0x0017; // Record identifier + $length = 2 + 6 * $total_references; // Number of bytes to follow + + $supbook_index = 0; // FIXME: only using internal SUPBOOK record + $header = pack("vv", $record, $length); + $data = pack('v', $total_references); + for ($i = 0; $i < $total_references; $i++) { + $data .= $this->_parser->_references[$i]; + } + $this->_append($header . $data); + } + + /** + * Write Excel BIFF STYLE records. + * + * @access private + */ + protected function _storeStyle() + { + $record = 0x0293; // Record identifier + $length = 0x0004; // Bytes to follow + + $ixfe = 0x8000; // Index to style XF + $BuiltIn = 0x00; // Built-in style + $iLevel = 0xff; // Outline style level + + $header = pack("vv", $record, $length); + $data = pack("vCC", $ixfe, $BuiltIn, $iLevel); + $this->_append($header . $data); + } + + + /** + * Writes Excel FORMAT record for non "built-in" numerical formats. + * + * @param string $format Custom format string + * @param integer $ifmt Format index code + * @access private + */ + protected function _storeNumFormat($format, $ifmt) + { + $record = 0x041E; // Record identifier + + if ($this->_BIFF_version == 0x0600) { + $length = 5 + strlen($format); // Number of bytes to follow + $encoding = 0x0; + } elseif ($this->_BIFF_version == 0x0500) { + $length = 3 + strlen($format); // Number of bytes to follow + } + + if ( $this->_BIFF_version == 0x0600 && function_exists('iconv') ) { // Encode format String + if (mb_detect_encoding($format, 'auto') !== 'UTF-16LE'){ + $format = iconv(mb_detect_encoding($format, 'auto'),'UTF-16LE',$format); + } + $encoding = 1; + $cch = function_exists('mb_strlen') ? mb_strlen($format, 'UTF-16LE') : (strlen($format) / 2); + } else { + $encoding = 0; + $cch = strlen($format); // Length of format string + } + $length = strlen($format); + + if ($this->_BIFF_version == 0x0600) { + $header = pack("vv", $record, 5 + $length); + $data = pack("vvC", $ifmt, $cch, $encoding); + } elseif ($this->_BIFF_version == 0x0500) { + $header = pack("vv", $record, 3 + $length); + $data = pack("vC", $ifmt, $cch); + } + $this->_append($header . $data . $format); + } + + /** + * Write DATEMODE record to indicate the date system in use (1904 or 1900). + * + * @access private + */ + protected function _storeDatemode() + { + $record = 0x0022; // Record identifier + $length = 0x0002; // Bytes to follow + + $f1904 = $this->_1904; // Flag for 1904 date system + + $header = pack("vv", $record, $length); + $data = pack("v", $f1904); + $this->_append($header . $data); + } + + + /** + * Write BIFF record EXTERNCOUNT to indicate the number of external sheet + * references in the workbook. + * + * Excel only stores references to external sheets that are used in NAME. + * The workbook NAME record is required to define the print area and the repeat + * rows and columns. + * + * A similar method is used in Worksheet.php for a slightly different purpose. + * + * @param integer $cxals Number of external references + * @access private + */ + protected function _storeExterncount($cxals) + { + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cxals); + $this->_append($header . $data); + } + + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. NAME record is required to define the print area and the repeat + * rows and columns. + * + * A similar method is used in Worksheet.php for a slightly different purpose. + * + * @param string $sheetname Worksheet name + * @access private + */ + protected function _storeExternsheet($sheetname) + { + $record = 0x0017; // Record identifier + $length = 0x02 + strlen($sheetname); // Number of bytes to follow + + $cch = strlen($sheetname); // Length of sheet name + $rgch = 0x03; // Filename encoding + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + $this->_append($header . $data . $sheetname); + } + + + /** + * Store the NAME record in the short format that is used for storing the print + * area, repeat rows only and repeat columns only. + * + * @param integer $index Sheet index + * @param integer $type Built-in name type + * @param integer $rowmin Start row + * @param integer $rowmax End row + * @param integer $colmin Start colum + * @param integer $colmax End column + * @access private + */ + protected function _storeNameShort($index, $type, $rowmin, $rowmax, $colmin, $colmax) + { + $record = 0x0018; // Record identifier + $length = 0x0024; // Number of bytes to follow + + $grbit = 0x0020; // Option flags + $chKey = 0x00; // Keyboard shortcut + $cch = 0x01; // Length of text name + $cce = 0x0015; // Length of text definition + $ixals = $index + 1; // Sheet index + $itab = $ixals; // Equal to ixals + $cchCustMenu = 0x00; // Length of cust menu text + $cchDescription = 0x00; // Length of description text + $cchHelptopic = 0x00; // Length of help topic text + $cchStatustext = 0x00; // Length of status bar text + $rgch = $type; // Built-in name type + + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8005; + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + $this->_append($header . $data); + } + + + /** + * Store the NAME record in the long format that is used for storing the repeat + * rows and columns when both are specified. This shares a lot of code with + * _storeNameShort() but we use a separate method to keep the code clean. + * Code abstraction for reuse can be carried too far, and I should know. ;-) + * + * @param integer $index Sheet index + * @param integer $type Built-in name type + * @param integer $rowmin Start row + * @param integer $rowmax End row + * @param integer $colmin Start colum + * @param integer $colmax End column + * @access private + */ + protected function _storeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax) + { + $record = 0x0018; // Record identifier + $length = 0x003d; // Number of bytes to follow + $grbit = 0x0020; // Option flags + $chKey = 0x00; // Keyboard shortcut + $cch = 0x01; // Length of text name + $cce = 0x002e; // Length of text definition + $ixals = $index + 1; // Sheet index + $itab = $ixals; // Equal to ixals + $cchCustMenu = 0x00; // Length of cust menu text + $cchDescription = 0x00; // Length of description text + $cchHelptopic = 0x00; // Length of help topic text + $cchStatustext = 0x00; // Length of status bar text + $rgch = $type; // Built-in name type + + $unknown01 = 0x29; + $unknown02 = 0x002b; + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8008; + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown01); + $data .= pack("v", $unknown02); + // Column definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", 0x0000); + $data .= pack("v", 0x3fff); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + // Row definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", 0x00); + $data .= pack("C", 0xff); + // End of data + $data .= pack("C", 0x10); + $this->_append($header . $data); + } + + /** + * Stores the COUNTRY record for localization + * + * @access private + */ + protected function _storeCountry() + { + $record = 0x008C; // Record identifier + $length = 4; // Number of bytes to follow + + $header = pack('vv', $record, $length); + /* using the same country code always for simplicity */ + $data = pack('vv', $this->_country_code, $this->_country_code); + $this->_append($header . $data); + } + + /** + * Stores the PALETTE biff record. + * + * @access private + */ + protected function _storePalette() + { + $aref = $this->_palette; + + $record = 0x0092; // Record identifier + $length = 2 + 4 * count($aref); // Number of bytes to follow + $ccv = count($aref); // Number of RGB values to follow + $data = ''; // The RGB data + + // Pack the RGB data + foreach ($aref as $color) { + foreach ($color as $byte) { + $data .= pack("C",$byte); + } + } + + $header = pack("vvv", $record, $length, $ccv); + $this->_append($header . $data); + } + + /** + * Calculate + * Handling of the SST continue blocks is complicated by the need to include an + * additional continuation byte depending on whether the string is split between + * blocks or whether it starts at the beginning of the block. (There are also + * additional complications that will arise later when/if Rich Strings are + * supported). + * + * @access private + */ + protected function _calculateSharedStringsSizes() + { + /* Iterate through the strings to calculate the CONTINUE block sizes. + For simplicity we use the same size for the SST and CONTINUE records: + 8228 : Maximum Excel97 block size + -4 : Length of block header + -8 : Length of additional SST header information + -8 : Arbitrary number to keep within _add_continue() limit = 8208 + */ + $continue_limit = 8208; + $block_length = 0; + $written = 0; + $this->_block_sizes = array(); + $continue = 0; + + foreach (array_keys($this->_str_table) as $string) { + $string_length = strlen($string); + $headerinfo = unpack("vlength/Cencoding", $string); + $encoding = $headerinfo["encoding"]; + $split_string = 0; + + // Block length is the total length of the strings that will be + // written out in a single SST or CONTINUE block. + $block_length += $string_length; + + // We can write the string if it doesn't cross a CONTINUE boundary + if ($block_length < $continue_limit) { + $written += $string_length; + continue; + } + + // Deal with the cases where the next string to be written will exceed + // the CONTINUE boundary. If the string is very long it may need to be + // written in more than one CONTINUE record. + while ($block_length >= $continue_limit) { + + // We need to avoid the case where a string is continued in the first + // n bytes that contain the string header information. + $header_length = 3; // Min string + header size -1 + $space_remaining = $continue_limit - $written - $continue; + + + /* TODO: Unicode data should only be split on char (2 byte) + boundaries. Therefore, in some cases we need to reduce the + amount of available + */ + $align = 0; + + // Only applies to Unicode strings + if ($encoding == 1) { + // Min string + header size -1 + $header_length = 4; + + if ($space_remaining > $header_length) { + // String contains 3 byte header => split on odd boundary + if (!$split_string && $space_remaining % 2 != 1) { + $space_remaining--; + $align = 1; + } + // Split section without header => split on even boundary + else if ($split_string && $space_remaining % 2 == 1) { + $space_remaining--; + $align = 1; + } + + $split_string = 1; + } + } + + + if ($space_remaining > $header_length) { + // Write as much as possible of the string in the current block + $written += $space_remaining; + + // Reduce the current block length by the amount written + $block_length -= $continue_limit - $continue - $align; + + // Store the max size for this block + $this->_block_sizes[] = $continue_limit - $align; + + // If the current string was split then the next CONTINUE block + // should have the string continue flag (grbit) set unless the + // split string fits exactly into the remaining space. + if ($block_length > 0) { + $continue = 1; + } else { + $continue = 0; + } + } else { + // Store the max size for this block + $this->_block_sizes[] = $written + $continue; + + // Not enough space to start the string in the current block + $block_length -= $continue_limit - $space_remaining - $continue; + $continue = 0; + + } + + // If the string (or substr) is small enough we can write it in the + // new CONTINUE block. Else, go through the loop again to write it in + // one or more CONTINUE blocks + if ($block_length < $continue_limit) { + $written = $block_length; + } else { + $written = 0; + } + } + } + + // Store the max size for the last block unless it is empty + if ($written + $continue) { + $this->_block_sizes[] = $written + $continue; + } + + + /* Calculate the total length of the SST and associated CONTINUEs (if any). + The SST record will have a length even if it contains no strings. + This length is required to set the offsets in the BOUNDSHEET records since + they must be written before the SST records + */ + + $tmp_block_sizes = $this->_block_sizes; + + $length = 12; + if (!empty($tmp_block_sizes)) { + $length += array_shift($tmp_block_sizes); // SST + } + while (!empty($tmp_block_sizes)) { + $length += 4 + array_shift($tmp_block_sizes); // CONTINUEs + } + + return $length; + } + + /** + * Write all of the workbooks strings into an indexed array. + * See the comments in _calculate_shared_string_sizes() for more information. + * + * The Excel documentation says that the SST record should be followed by an + * EXTSST record. The EXTSST record is a hash table that is used to optimise + * access to SST. However, despite the documentation it doesn't seem to be + * required so we will ignore it. + * + * @access private + */ + protected function _storeSharedStringsTable() + { + $record = 0x00fc; // Record identifier + $length = 0x0008; // Number of bytes to follow + $total = 0x0000; + + // Iterate through the strings to calculate the CONTINUE block sizes + $continue_limit = 8208; + $block_length = 0; + $written = 0; + $continue = 0; + + // sizes are upside down + $tmp_block_sizes = $this->_block_sizes; + // $tmp_block_sizes = array_reverse($this->_block_sizes); + + // The SST record is required even if it contains no strings. Thus we will + // always have a length + // + if (!empty($tmp_block_sizes)) { + $length = 8 + array_shift($tmp_block_sizes); + } + else { + // No strings + $length = 8; + } + + + + // Write the SST block header information + $header = pack("vv", $record, $length); + $data = pack("VV", $this->_str_total, $this->_str_unique); + $this->_append($header . $data); + + + + + /* TODO: not good for performance */ + foreach (array_keys($this->_str_table) as $string) { + + $string_length = strlen($string); + $headerinfo = unpack("vlength/Cencoding", $string); + $encoding = $headerinfo["encoding"]; + $split_string = 0; + + // Block length is the total length of the strings that will be + // written out in a single SST or CONTINUE block. + // + $block_length += $string_length; + + + // We can write the string if it doesn't cross a CONTINUE boundary + if ($block_length < $continue_limit) { + $this->_append($string); + $written += $string_length; + continue; + } + + // Deal with the cases where the next string to be written will exceed + // the CONTINUE boundary. If the string is very long it may need to be + // written in more than one CONTINUE record. + // + while ($block_length >= $continue_limit) { + + // We need to avoid the case where a string is continued in the first + // n bytes that contain the string header information. + // + $header_length = 3; // Min string + header size -1 + $space_remaining = $continue_limit - $written - $continue; + + + // Unicode data should only be split on char (2 byte) boundaries. + // Therefore, in some cases we need to reduce the amount of available + // space by 1 byte to ensure the correct alignment. + $align = 0; + + // Only applies to Unicode strings + if ($encoding == 1) { + // Min string + header size -1 + $header_length = 4; + + if ($space_remaining > $header_length) { + // String contains 3 byte header => split on odd boundary + if (!$split_string && $space_remaining % 2 != 1) { + $space_remaining--; + $align = 1; + } + // Split section without header => split on even boundary + else if ($split_string && $space_remaining % 2 == 1) { + $space_remaining--; + $align = 1; + } + + $split_string = 1; + } + } + + + if ($space_remaining > $header_length) { + // Write as much as possible of the string in the current block + $tmp = substr($string, 0, $space_remaining); + $this->_append($tmp); + + // The remainder will be written in the next block(s) + $string = substr($string, $space_remaining); + + // Reduce the current block length by the amount written + $block_length -= $continue_limit - $continue - $align; + + // If the current string was split then the next CONTINUE block + // should have the string continue flag (grbit) set unless the + // split string fits exactly into the remaining space. + // + if ($block_length > 0) { + $continue = 1; + } else { + $continue = 0; + } + } else { + // Not enough space to start the string in the current block + $block_length -= $continue_limit - $space_remaining - $continue; + $continue = 0; + } + + // Write the CONTINUE block header + if (!empty($this->_block_sizes)) { + $record = 0x003C; + $length = array_shift($tmp_block_sizes); + + $header = pack('vv', $record, $length); + if ($continue) { + $header .= pack('C', $encoding); + } + $this->_append($header); + } + + // If the string (or substr) is small enough we can write it in the + // new CONTINUE block. Else, go through the loop again to write it in + // one or more CONTINUE blocks + // + if ($block_length < $continue_limit) { + $this->_append($string); + $written = $block_length; + } else { + $written = 0; + } + } + } + } + + +} + diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Worksheet.php b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Worksheet.php new file mode 100644 index 0000000..3251649 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/Spreadsheet/Excel/Writer/Worksheet.php @@ -0,0 +1,3609 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +if (!class_exists('Spreadsheet_Excel_Writer_BIFFwriter')) { + require_once 'Spreadsheet/Excel/Writer/Parser.php'; + require_once 'Spreadsheet/Excel/Writer/BIFFwriter.php'; +} + +/** +* Class for generating Excel Spreadsheets +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter +{ + /** + * Name of the Worksheet + * @var string + */ + public $name; + + /** + * Index for the Worksheet + * @var integer + */ + public $index; + + /** + * Reference to the (default) Format object for URLs + * @var Spreadsheet_Excel_Writer_Format + */ + public $_url_format; + + /** + * Reference to the parser used for parsing formulas + * @var Spreadsheet_Excel_Writer_Format + */ + public $_parser; + + /** + * Filehandle to the temporary file for storing data + * @var resource + */ + public $_filehandle; + + /** + * Boolean indicating if we are using a temporary file for storing data + * @var bool + */ + public $_using_tmpfile; + + /** + * Maximum number of rows for an Excel spreadsheet (BIFF5) + * @var integer + */ + public $_xls_rowmax; + + /** + * Maximum number of columns for an Excel spreadsheet (BIFF5) + * @var integer + */ + public $_xls_colmax; + + /** + * Maximum number of characters for a string (LABEL record in BIFF5) + * @var integer + */ + public $_xls_strmax; + + /** + * First row for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + public $_dim_rowmin; + + /** + * Last row for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + public $_dim_rowmax; + + /** + * First column for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + public $_dim_colmin; + + /** + * Last column for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + public $_dim_colmax; + + /** + * Array containing format information for columns + * @var array + */ + public $_colinfo; + + /** + * Array containing the selected area for the worksheet + * @var array + */ + public $_selection; + + /** + * Array containing the panes for the worksheet + * @var array + */ + public $_panes; + + /** + * The active pane for the worksheet + * @var integer + */ + public $_active_pane; + + /** + * Bit specifying if panes are frozen + * @var integer + */ + public $_frozen; + + /** + * Bit specifying if the worksheet is selected + * @var integer + */ + public $selected; + + /** + * The paper size (for printing) (DOCUMENT!!!) + * @var integer + */ + public $_paper_size; + + /** + * Bit specifying paper orientation (for printing). 0 => landscape, 1 => portrait + * @var integer + */ + public $_orientation; + + /** + * The page header caption + * @var string + */ + public $_header; + + /** + * The page footer caption + * @var string + */ + public $_footer; + + /** + * The horizontal centering value for the page + * @var integer + */ + public $_hcenter; + + /** + * The vertical centering value for the page + * @var integer + */ + public $_vcenter; + + /** + * The margin for the header + * @var float + */ + public $_margin_head; + + /** + * The margin for the footer + * @var float + */ + public $_margin_foot; + + /** + * The left margin for the worksheet in inches + * @var float + */ + public $_margin_left; + + /** + * The right margin for the worksheet in inches + * @var float + */ + public $_margin_right; + + /** + * The top margin for the worksheet in inches + * @var float + */ + public $_margin_top; + + /** + * The bottom margin for the worksheet in inches + * @var float + */ + public $_margin_bottom; + + /** + * First row to reapeat on each printed page + * @var integer + */ + public $title_rowmin; + + /** + * Last row to reapeat on each printed page + * @var integer + */ + public $title_rowmax; + + /** + * First column to reapeat on each printed page + * @var integer + */ + public $title_colmin; + + /** + * First row of the area to print + * @var integer + */ + public $print_rowmin; + + /** + * Last row to of the area to print + * @var integer + */ + public $print_rowmax; + + /** + * First column of the area to print + * @var integer + */ + public $print_colmin; + + /** + * Last column of the area to print + * @var integer + */ + public $print_colmax; + + /** + * Whether to display RightToLeft. + * @var integer + */ + public $_Arabic; + + /** + * Whether to use outline. + * @var integer + */ + public $_outline_on; + + /** + * Auto outline styles. + * @var bool + */ + public $_outline_style; + + /** + * Whether to have outline summary below. + * @var bool + */ + public $_outline_below; + + /** + * Whether to have outline summary at the right. + * @var bool + */ + public $_outline_right; + + /** + * Outline row level. + * @var integer + */ + public $_outline_row_level; + + /** + * Whether to fit to page when printing or not. + * @var bool + */ + public $_fit_page; + + /** + * Number of pages to fit wide + * @var integer + */ + public $_fit_width; + + /** + * Number of pages to fit high + * @var integer + */ + public $_fit_height; + + /** + * Reference to the total number of strings in the workbook + * @var integer + */ + public $_str_total; + + /** + * Reference to the number of unique strings in the workbook + * @var integer + */ + public $_str_unique; + + /** + * Reference to the array containing all the unique strings in the workbook + * @var array + */ + public $_str_table; + + /** + * Number of merged cell ranges in actual record + * @var int $_merged_cells_counter + */ + public $_merged_cells_counter = 0; + + /** + * Number of actual mergedcells record + * @var int $_merged_cells_record + */ + public $_merged_cells_record = 0; + + /** + * Merged cell ranges + * @var array + */ + public $_merged_ranges; + + /** + * Charset encoding currently used when calling writeString() + * @var string + */ + public $_input_encoding; + + /** + * Constructor + * + * @param string $name The name of the new worksheet + * @param integer $index The index of the new worksheet + * @param mixed $activesheet The current activesheet of the workbook we belong to + * @param mixed $firstsheet The first worksheet in the workbook we belong to + * @param int &$str_total Reference to the total number of strings in the workbook + * @param int &$str_unique Reference to the number of unique strings in the workbook + * @param array &$str_table Reference to the array containing all the unique strings in the workbook + * @param mixed $url_format The default format for hyperlinks + * @param mixed $parser The formula parser created for the Workbook + * @param string $tmp_dir The path to the directory for temporary files + * @access private + */ + public function __construct($BIFF_version, $name, + $index, $activesheet, + $firstsheet, &$str_total, + &$str_unique, &$str_table, + $url_format, $parser, + $tmp_dir) + { + // It needs to call its parent's constructor explicitly + parent::__construct(); + $this->_BIFF_version = $BIFF_version; + $rowmax = 65536; // 16384 in Excel 5 + $colmax = 256; + + $this->name = $name; + $this->index = $index; + $this->activesheet = $activesheet; + $this->firstsheet = $firstsheet; + // _str_total _str_unique _str_table - are actual references + // everything breaks if they're not + $this->_str_total = &$str_total; + $this->_str_unique = &$str_unique; + $this->_str_table = &$str_table; + $this->_url_format = $url_format; + $this->_parser = $parser; + + //$this->ext_sheets = array(); + $this->_filehandle = ''; + $this->_using_tmpfile = true; + //$this->fileclosed = 0; + //$this->offset = 0; + $this->_xls_rowmax = $rowmax; + $this->_xls_colmax = $colmax; + $this->_xls_strmax = 255; + $this->_dim_rowmin = $rowmax + 1; + $this->_dim_rowmax = 0; + $this->_dim_colmin = $colmax + 1; + $this->_dim_colmax = 0; + $this->_colinfo = array(); + $this->_selection = array(0,0,0,0); + $this->_panes = array(); + $this->_active_pane = 3; + $this->_frozen = 0; + $this->selected = 0; + + $this->_paper_size = 0x0; + $this->_orientation = 0x1; + $this->_header = ''; + $this->_footer = ''; + $this->_hcenter = 0; + $this->_vcenter = 0; + $this->_margin_head = 0.50; + $this->_margin_foot = 0.50; + $this->_margin_left = 0.75; + $this->_margin_right = 0.75; + $this->_margin_top = 1.00; + $this->_margin_bottom = 1.00; + + $this->title_rowmin = null; + $this->title_rowmax = null; + $this->title_colmin = null; + $this->title_colmax = null; + $this->print_rowmin = null; + $this->print_rowmax = null; + $this->print_colmin = null; + $this->print_colmax = null; + + $this->_print_gridlines = 1; + $this->_screen_gridlines = 1; + $this->_print_headers = 0; + + $this->_fit_page = 0; + $this->_fit_width = 0; + $this->_fit_height = 0; + + $this->_hbreaks = array(); + $this->_vbreaks = array(); + + $this->_protect = 0; + $this->_password = null; + + $this->col_sizes = array(); + $this->_row_sizes = array(); + + $this->_zoom = 100; + $this->_print_scale = 100; + + $this->_outline_row_level = 0; + $this->_outline_style = 0; + $this->_outline_below = 1; + $this->_outline_right = 1; + $this->_outline_on = 1; + $this->_Arabic = 0; + + $this->_merged_ranges = array(); + + $this->_input_encoding = ''; + + $this->_dv = array(); + + $this->_tmp_dir = $tmp_dir; + $this->_tmp_file = ''; + + $this->_initialize(); + } + + /** + * Open a tmp file to store the majority of the Worksheet data. If this fails, + * for example due to write permissions, store the data in memory. This can be + * slow for large files. + * + * @access private + */ + protected function _initialize() + { + if ($this->_using_tmpfile == false) { + return; + } + + if ($this->_tmp_dir === '' && ini_get('open_basedir') === true) { + // open_basedir restriction in effect - store data in memory + // ToDo: Let the error actually have an effect somewhere + $this->_using_tmpfile = false; + return new PEAR_Error('Temp file could not be opened since open_basedir restriction in effect - please use setTmpDir() - using memory storage instead'); + } + + // Open tmp file for storing Worksheet data + if ($this->_tmp_dir === '') { + $fh = tmpfile(); + } else { + // For people with open base dir restriction + $this->_tmp_file = tempnam($this->_tmp_dir, "Spreadsheet_Excel_Writer"); + $fh = @fopen($this->_tmp_file, "w+b"); + } + + if ($fh === false) { + // If tmpfile() fails store data in memory + $this->_using_tmpfile = false; + } else { + // Store filehandle + $this->_filehandle = $fh; + } + } + + /** + * Add data to the beginning of the workbook (note the reverse order) + * and to the end of the workbook. + * + * @access public + * @see Spreadsheet_Excel_Writer_Workbook::storeWorkbook() + * @param array $sheetnames The array of sheetnames from the Workbook this + * worksheet belongs to + */ + public function close($sheetnames) + { + $num_sheets = count($sheetnames); + + /*********************************************** + * Prepend in reverse order!! + */ + + // Prepend the sheet dimensions + $this->_storeDimensions(); + + // Prepend the sheet password + $this->_storePassword(); + + // Prepend the sheet protection + $this->_storeProtect(); + + // Prepend the page setup + $this->_storeSetup(); + + /* FIXME: margins are actually appended */ + // Prepend the bottom margin + $this->_storeMarginBottom(); + + // Prepend the top margin + $this->_storeMarginTop(); + + // Prepend the right margin + $this->_storeMarginRight(); + + // Prepend the left margin + $this->_storeMarginLeft(); + + // Prepend the page vertical centering + $this->_storeVcenter(); + + // Prepend the page horizontal centering + $this->_storeHcenter(); + + // Prepend the page footer + $this->_storeFooter(); + + // Prepend the page header + $this->_storeHeader(); + + // Prepend the vertical page breaks + $this->_storeVbreak(); + + // Prepend the horizontal page breaks + $this->_storeHbreak(); + + // Prepend WSBOOL + $this->_storeWsbool(); + + // Prepend GRIDSET + $this->_storeGridset(); + + // Prepend GUTS + if ($this->_BIFF_version == 0x0500) { + $this->_storeGuts(); + } + + // Prepend PRINTGRIDLINES + $this->_storePrintGridlines(); + + // Prepend PRINTHEADERS + $this->_storePrintHeaders(); + + // Prepend EXTERNSHEET references + if ($this->_BIFF_version == 0x0500) { + for ($i = $num_sheets; $i > 0; $i--) { + $sheetname = $sheetnames[$i-1]; + $this->_storeExternsheet($sheetname); + } + } + + // Prepend the EXTERNCOUNT of external references. + if ($this->_BIFF_version == 0x0500) { + $this->_storeExterncount($num_sheets); + } + + // Prepend the COLINFO records if they exist + if (!empty($this->_colinfo)) { + $colcount = count($this->_colinfo); + for ($i = 0; $i < $colcount; $i++) { + $this->_storeColinfo($this->_colinfo[$i]); + } + $this->_storeDefcol(); + } + + // Prepend the BOF record + $this->_storeBof(0x0010); + + /* + * End of prepend. Read upwards from here. + ***********************************************/ + + // Append + $this->_storeWindow2(); + $this->_storeZoom(); + if (!empty($this->_panes)) { + $this->_storePanes($this->_panes); + } + $this->_storeSelection($this->_selection); + $this->_storeMergedCells(); + /* TODO: add data validity */ + /*if ($this->_BIFF_version == 0x0600) { + $this->_storeDataValidity(); + }*/ + $this->_storeEof(); + } + + /** + * Retrieve the worksheet name. + * This is usefull when creating worksheets without a name. + * + * @access public + * @return string The worksheet's name + */ + public function getName() + { + return $this->name; + } + + /** + * Retrieves data from memory in one chunk, or from disk in $buffer + * sized chunks. + * + * @return string The data + */ + public function getData($cleanup = true) + { + $buffer = 4096; + + // Return data stored in memory + if (isset($this->_data)) { + $tmp = $this->_data; + unset($this->_data); + if ($this->_using_tmpfile) { + fseek($this->_filehandle, 0); + } + return $tmp; + } + // Return data stored on disk + if ($this->_using_tmpfile) { + if ($tmp = fread($this->_filehandle, $buffer)) { + return $tmp; + } + + // All data has now been read (both from memory & from file) so we + // can now trash the file + if ($cleanup) { + if ( $this->_filehandle ) { + fclose($this->_filehandle); + $this->_filehandle = ''; + } + @unlink($this->_tmp_file); + $this->_tmp_file = ''; + $this->_using_tmpfile = false; + $this->_datasize = 0; + } + } + + // No data to return + return ''; + } + + /** + * Sets a merged cell range + * + * @access public + * @param integer $first_row First row of the area to merge + * @param integer $first_col First column of the area to merge + * @param integer $last_row Last row of the area to merge + * @param integer $last_col Last column of the area to merge + */ + public function setMerge($first_row, $first_col, $last_row, $last_col) + { + if (($last_row < $first_row) || ($last_col < $first_col)) { + return; + } + + $max_record_ranges = floor(($this->_limit - 6) / 8); + if($this->_merged_cells_counter >= $max_record_ranges) + { + $this->_merged_cells_record++; + $this->_merged_cells_counter = 0; + } + + // don't check rowmin, rowmax, etc... because we don't know when this + // is going to be called + $this->_merged_ranges[$this->_merged_cells_record][] = array($first_row, $first_col, $last_row, $last_col); + $this->_merged_cells_counter++; + } + + /** + * Set this worksheet as a selected worksheet, + * i.e. the worksheet has its tab highlighted. + * + * @access public + */ + public function select() + { + $this->selected = 1; + } + + /** + * Set this worksheet as the active worksheet, + * i.e. the worksheet that is displayed when the workbook is opened. + * Also set it as selected. + * + * @access public + */ + public function activate() + { + $this->selected = 1; + $this->activesheet = $this->index; + } + + /** + * Set this worksheet as the first visible sheet. + * This is necessary when there are a large number of worksheets and the + * activated worksheet is not visible on the screen. + * + * @access public + */ + public function setFirstSheet() + { + $this->firstsheet = $this->index; + } + + /** + * Set the worksheet protection flag + * to prevent accidental modification and to + * hide formulas if the locked and hidden format properties have been set. + * + * @access public + * @param string $password The password to use for protecting the sheet. + */ + public function protect($password) + { + $this->_protect = 1; + $this->_password = $this->_encodePassword($password); + } + + /** + * Set the width of a single column or a range of columns. + * + * @access public + * @param integer $firstcol first column on the range + * @param integer $lastcol last column on the range + * @param integer $width width to set + * @param mixed $format The optional XF format to apply to the columns + * @param integer $hidden The optional hidden atribute + * @param integer $level The optional outline level + */ + public function setColumn($firstcol, $lastcol, $width, $format = null, $hidden = 0, $level = 0) + { // added by Dan Lynn _colinfo as $key => $colinfo) + { + $existing_start = $colinfo[0]; $existing_end = $colinfo[1]; + // if the new range starts within another range + if ($firstcol > $existing_start && $firstcol < $existing_end) + { // trim the existing range to the beginning of the new range + $this->_colinfo[$key][1] = $firstcol - 1; + // if the new range lies WITHIN the existing range + if ($lastcol < $existing_end) + { // split the existing range by adding a range after our new range + $this->_colinfo[] = array($lastcol+1, $existing_end, $colinfo[2], /* format */ $colinfo[3], $colinfo[4], $colinfo[5]); + } + } // if the new range ends inside an existing range + elseif ($lastcol > $existing_start && $lastcol < $existing_end) + { // trim the existing range to the end of the new range + $this->_colinfo[$key][0] = $lastcol + 1; + } // if the new range completely overlaps the existing range + elseif ($firstcol <= $existing_start && $lastcol >= $existing_end) + { + unset($this->_colinfo[$key]); + } + } // added by Dan Lynn _colinfo = array_values($this->_colinfo); + $this->_colinfo[] = array($firstcol, $lastcol, $width, $format, $hidden, $level); + // Set width to zero if column is hidden + $width = ($hidden) ? 0 : $width; + for ($col = $firstcol; $col <= $lastcol; $col++) + { + $this->col_sizes[$col] = $width; + } + } + + /** + * Set which cell or cells are selected in a worksheet + * + * @access public + * @param integer $first_row first row in the selected quadrant + * @param integer $first_column first column in the selected quadrant + * @param integer $last_row last row in the selected quadrant + * @param integer $last_column last column in the selected quadrant + */ + public function setSelection($first_row,$first_column,$last_row,$last_column) + { + $this->_selection = array($first_row,$first_column,$last_row,$last_column); + } + + /** + * Set panes and mark them as frozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + public function freezePanes($panes) + { + $this->_frozen = 1; + $this->_panes = $panes; + } + + /** + * Set panes and mark them as unfrozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + public function thawPanes($panes) + { + $this->_frozen = 0; + $this->_panes = $panes; + } + + /** + * Set the page orientation as portrait. + * + * @access public + */ + public function setPortrait() + { + $this->_orientation = 1; + } + + /** + * Set the page orientation as landscape. + * + * @access public + */ + public function setLandscape() + { + $this->_orientation = 0; + } + + /** + * Set the paper type. Ex. 1 = US Letter, 9 = A4 + * + * @access public + * @param integer $size The type of paper size to use + */ + public function setPaper($size = 0) + { + $this->_paper_size = $size; + } + + + /** + * Set the page header caption and optional margin. + * + * @access public + * @param string $string The header text + * @param float $margin optional head margin in inches. + */ + public function setHeader($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Header string must be less than 255 characters'; + return; + } + $this->_header = $string; + $this->_margin_head = $margin; + } + + /** + * Set the page footer caption and optional margin. + * + * @access public + * @param string $string The footer text + * @param float $margin optional foot margin in inches. + */ + public function setFooter($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Footer string must be less than 255 characters'; + return; + } + $this->_footer = $string; + $this->_margin_foot = $margin; + } + + /** + * Center the page horinzontally. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + public function centerHorizontally($center = 1) + { + $this->_hcenter = $center; + } + + /** + * Center the page vertically. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + public function centerVertically($center = 1) + { + $this->_vcenter = $center; + } + + /** + * Set all the page margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMargins($margin) + { + $this->setMarginLeft($margin); + $this->setMarginRight($margin); + $this->setMarginTop($margin); + $this->setMarginBottom($margin); + } + + /** + * Set the left and right margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMargins_LR($margin) + { + $this->setMarginLeft($margin); + $this->setMarginRight($margin); + } + + /** + * Set the top and bottom margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMargins_TB($margin) + { + $this->setMarginTop($margin); + $this->setMarginBottom($margin); + } + + /** + * Set the left margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMarginLeft($margin = 0.75) + { + $this->_margin_left = $margin; + } + + /** + * Set the right margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMarginRight($margin = 0.75) + { + $this->_margin_right = $margin; + } + + /** + * Set the top margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMarginTop($margin = 1.00) + { + $this->_margin_top = $margin; + } + + /** + * Set the bottom margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + public function setMarginBottom($margin = 1.00) + { + $this->_margin_bottom = $margin; + } + + /** + * Set the rows to repeat at the top of each printed page. + * + * @access public + * @param integer $first_row First row to repeat + * @param integer $last_row Last row to repeat. Optional. + */ + public function repeatRows($first_row, $last_row = null) + { + $this->title_rowmin = $first_row; + if (isset($last_row)) { //Second row is optional + $this->title_rowmax = $last_row; + } else { + $this->title_rowmax = $first_row; + } + } + + /** + * Set the columns to repeat at the left hand side of each printed page. + * + * @access public + * @param integer $first_col First column to repeat + * @param integer $last_col Last column to repeat. Optional. + */ + public function repeatColumns($first_col, $last_col = null) + { + $this->title_colmin = $first_col; + if (isset($last_col)) { // Second col is optional + $this->title_colmax = $last_col; + } else { + $this->title_colmax = $first_col; + } + } + + /** + * Set the area of each worksheet that will be printed. + * + * @access public + * @param integer $first_row First row of the area to print + * @param integer $first_col First column of the area to print + * @param integer $last_row Last row of the area to print + * @param integer $last_col Last column of the area to print + */ + public function printArea($first_row, $first_col, $last_row, $last_col) + { + $this->print_rowmin = $first_row; + $this->print_colmin = $first_col; + $this->print_rowmax = $last_row; + $this->print_colmax = $last_col; + } + + + /** + * Set the option to hide gridlines on the printed page. + * + * @access public + */ + public function hideGridlines() + { + $this->_print_gridlines = 0; + } + + /** + * Set the option to hide gridlines on the worksheet (as seen on the screen). + * + * @access public + */ + public function hideScreenGridlines() + { + $this->_screen_gridlines = 0; + } + + /** + * Set the option to print the row and column headers on the printed page. + * + * @access public + * @param integer $print Whether to print the headers or not. Defaults to 1 (print). + */ + public function printRowColHeaders($print = 1) + { + $this->_print_headers = $print; + } + + /** + * Set the vertical and horizontal number of pages that will define the maximum area printed. + * It doesn't seem to work with OpenOffice. + * + * @access public + * @param integer $width Maximun width of printed area in pages + * @param integer $height Maximun heigth of printed area in pages + * @see setPrintScale() + */ + public function fitToPages($width, $height) + { + $this->_fit_page = 1; + $this->_fit_width = $width; + $this->_fit_height = $height; + } + + /** + * Store the horizontal page breaks on a worksheet (for printing). + * The breaks represent the row after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the horizontal page breaks + */ + public function setHPagebreaks($breaks) + { + foreach ($breaks as $break) { + array_push($this->_hbreaks, $break); + } + } + + /** + * Store the vertical page breaks on a worksheet (for printing). + * The breaks represent the column after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the vertical page breaks + */ + public function setVPagebreaks($breaks) + { + foreach ($breaks as $break) { + array_push($this->_vbreaks, $break); + } + } + + + /** + * Set the worksheet zoom factor. + * + * @access public + * @param integer $scale The zoom factor + */ + public function setZoom($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400"); + $scale = 100; + } + + $this->_zoom = floor($scale); + } + + /** + * Set the scale factor for the printed page. + * It turns off the "fit to page" option + * + * @access public + * @param integer $scale The optional scale factor. Defaults to 100 + */ + public function setPrintScale($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400"); + $scale = 100; + } + + // Turn off "fit to page" option + $this->_fit_page = 0; + + $this->_print_scale = floor($scale); + } + + /** + * Map to the appropriate write method acording to the token recieved. + * + * @access public + * @param integer $row The row of the cell we are writing to + * @param integer $col The column of the cell we are writing to + * @param mixed $token What we are writing + * @param mixed $format The optional format to apply to the cell + */ + public function write($row, $col, $token, $format = null) + { + // Check for a cell reference in A1 notation and substitute row and column + /*if ($_[0] =~ /^\D/) { + @_ = $this->_substituteCellref(@_); + }*/ + + if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) { + // Match number + return $this->writeNumber($row, $col, $token, $format); + } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) { + // Match http or ftp URL + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^mailto:/", $token)) { + // Match mailto: + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) { + // Match internal or external sheet link + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^=/", $token)) { + // Match formula + return $this->writeFormula($row, $col, $token, $format); + } elseif ($token == '') { + // Match blank + return $this->writeBlank($row, $col, $format); + } else { + // Default: match string + return $this->writeString($row, $col, $token, $format); + } + } + + /** + * Write an array of values as a row + * + * @access public + * @param integer $row The row we are writing to + * @param integer $col The first col (leftmost col) we are writing to + * @param array $val The array of values to write + * @param mixed $format The optional format to apply to the cell + * @return mixed PEAR_Error on failure + */ + + public function writeRow($row, $col, $val, $format = null) + { + $retval = ''; + if (is_array($val)) { + foreach ($val as $v) { + if (is_array($v)) { + $this->writeCol($row, $col, $v, $format); + } else { + $this->write($row, $col, $v, $format); + } + $col++; + } + } else { + $retval = new PEAR_Error('$val needs to be an array'); + } + return($retval); + } + + /** + * Write an array of values as a column + * + * @access public + * @param integer $row The first row (uppermost row) we are writing to + * @param integer $col The col we are writing to + * @param array $val The array of values to write + * @param mixed $format The optional format to apply to the cell + * @return mixed PEAR_Error on failure + */ + + public function writeCol($row, $col, $val, $format = null) + { + $retval = ''; + if (is_array($val)) { + foreach ($val as $v) { + $this->write($row, $col, $v, $format); + $row++; + } + } else { + $retval = new PEAR_Error('$val needs to be an array'); + } + return($retval); + } + + /** + * Returns an index to the XF record in the workbook + * + * @access private + * @param mixed $format The optional XF format + * @return integer The XF record index + */ + protected function _XF($format) + { + if ($format) { + return($format->getXfIndex()); + } else { + return(0x0F); + } + } + + + /****************************************************************************** + ******************************************************************************* + * + * Internal methods + */ + + + /** + * Store Worksheet data in memory using the parent's class append() or to a + * temporary file, the default. + * + * @access private + * @param string $data The binary data to append + */ + protected function _append($data) + { + if ($this->_using_tmpfile) { + // Add CONTINUE records if necessary + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + fwrite($this->_filehandle, $data); + $this->_datasize += strlen($data); + } else { + parent::_append($data); + } + } + + /** + * Substitute an Excel cell reference in A1 notation for zero based row and + * column values in an argument list. + * + * Ex: ("A4", "Hello") is converted to (3, 0, "Hello"). + * + * @access private + * @param string $cell The cell reference. Or range of cells. + * @return array + */ + protected function _substituteCellref($cell) + { + $cell = strtoupper($cell); + + // Convert a column range: 'A:A' or 'B:G' + if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/", $cell, $match)) { + list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row + list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row + return(array($col1, $col2)); + } + + // Convert a cell range: 'A1:B7' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/", $cell, $match)) { + list($row1, $col1) = $this->_cellToRowcol($match[1]); + list($row2, $col2) = $this->_cellToRowcol($match[2]); + return(array($row1, $col1, $row2, $col2)); + } + + // Convert a cell reference: 'A1' or 'AD2000' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/", $cell)) { + list($row1, $col1) = $this->_cellToRowcol($match[1]); + return(array($row1, $col1)); + } + + // TODO use real error codes + $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE); + } + + /** + * Convert an Excel cell reference in A1 notation to a zero based row and column + * reference; converts C1 to (0, 2). + * + * @access private + * @param string $cell The cell reference. + * @return array containing (row, column) + */ + protected function _cellToRowcol($cell) + { + preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match); + $col = $match[1]; + $row = $match[2]; + + // Convert base26 column string to number + $chars = explode('', $col); + $expn = 0; + $col = 0; + + while ($chars) { + $char = array_pop($chars); // LS char first + $col += (ord($char) -ord('A') +1) * pow(26,$expn); + $expn++; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return(array($row, $col)); + } + + /** + * Based on the algorithm provided by Daniel Rentz of OpenOffice. + * + * @access private + * @param string $plaintext The password to be encoded in plaintext. + * @return string The encoded password + */ + protected function _encodePassword($plaintext) + { + $password = 0x0000; + $i = 1; // char position + + // split the plain text password in its component characters + $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); + foreach ($chars as $char) { + $value = ord($char) << $i; // shifted ASCII value + $rotated_bits = $value >> 15; // rotated bits beyond bit 15 + $value &= 0x7fff; // first 15 bits + $password ^= ($value | $rotated_bits); + $i++; + } + + $password ^= strlen($plaintext); + $password ^= 0xCE4B; + + return($password); + } + + /** + * This method sets the properties for outlining and grouping. The defaults + * correspond to Excel's defaults. + * + * @param bool $visible + * @param bool $symbols_below + * @param bool $symbols_right + * @param bool $auto_style + */ + public function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false) + { + $this->_outline_on = $visible; + $this->_outline_below = $symbols_below; + $this->_outline_right = $symbols_right; + $this->_outline_style = $auto_style; + + // Ensure this is a boolean vale for Window2 + if ($this->_outline_on) { + $this->_outline_on = 1; + } + } + + /** + * This method sets the worksheet direction to right-to-left (RTL) + * + * @param bool $rtl + */ + public function setRTL($rtl = true) + { + $this->_Arabic = ($rtl ? 1 : 0); + } + + /****************************************************************************** + ******************************************************************************* + * + * BIFF RECORDS + */ + + + /** + * Write a double to the specified row and column (zero indexed). + * An integer can be written as a double. Excel will display an + * integer. $format is optional. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param float $num The number to write + * @param mixed $format The optional XF format + * @return integer + */ + public function writeNumber($row, $col, $num, $format = null) + { + $record = 0x0203; // Record identifier + $length = 0x000E; // Number of bytes to follow + + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $xl_double = pack("d", $num); + if ($this->_byte_order) { // if it's Big Endian + $xl_double = strrev($xl_double); + } + + $this->_append($header.$data.$xl_double); + return(0); + } + + /** + * Write a string to the specified row and column (zero indexed). + * NOTE: there is an Excel 5 defined limit of 255 characters. + * $format is optional. + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $str The string to write + * @param mixed $format The XF format for the cell + * @return integer + */ + public function writeString($row, $col, $str, $format = null) + { + if ($this->_BIFF_version == 0x0600) { + return $this->writeStringBIFF8($row, $col, $str, $format); + } + $strlen = strlen($str); + $record = 0x0204; // Record identifier + $length = 0x0008 + $strlen; // Bytes to follow + $xf = $this->_XF($format); // The cell format + + $str_error = 0; + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars + $str = substr($str, 0, $this->_xls_strmax); + $length = 0x0008 + $this->_xls_strmax; + $strlen = $this->_xls_strmax; + $str_error = -3; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row, $col, $xf, $strlen); + $this->_append($header . $data . $str); + return($str_error); + } + + /** + * Sets Input Encoding for writing strings + * + * @access public + * @param string $encoding The encoding. Ex: 'UTF-16LE', 'utf-8', 'ISO-859-7' + */ + public function setInputEncoding($encoding) + { + if ($encoding != 'UTF-16LE' && !function_exists('iconv')) { + $this->raiseError("Using an input encoding other than UTF-16LE requires PHP support for iconv"); + } + $this->_input_encoding = $encoding; + } + + /** + * Write a string to the specified row and column (zero indexed). + * This is the BIFF8 version (no 255 chars limit). + * $format is optional. + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $str The string to write + * @param mixed $format The XF format for the cell + * @return integer + */ + public function writeStringBIFF8($row, $col, $str, $format = null) + { + // If the string is Unicode and contains any "surrogate pairs" then using mb_strlen($str, 'UTF-16LE') + // as the string length will cause a "found unreadable content" error when opening the worksheet in Excel + // (apparently the length is expected to be the number of 16-bit code points, not the number of characters). + // Instead, always use the byte length divided by two for Unicode strings, and if mb_strlen() exists use + // mb_strlen($str, '8bit') just in case mbstring.func_overload is set to overload strlen(). + if ($this->_input_encoding == 'UTF-16LE') + { + $strlen = (function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str)) / 2; + $encoding = 0x1; + } + elseif ($this->_input_encoding != '') + { + $str = iconv($this->_input_encoding, 'UTF-16LE', $str); + $strlen = (function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str)) / 2; + $encoding = 0x1; + } + else + { + $strlen = function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str); + $encoding = 0x0; + } + $record = 0x00FD; // Record identifier + $length = 0x000A; // Bytes to follow + $xf = $this->_XF($format); // The cell format + + $str_error = 0; + + // Check that row and col are valid and store max and min values + if ($this->_checkRowCol($row, $col) == false) { + return -2; + } + + $str = pack('vC', $strlen, $encoding).$str; + + /* check if string is already present */ + if (!isset($this->_str_table[$str])) { + $this->_str_table[$str] = $this->_str_unique++; + } + $this->_str_total++; + + $header = pack('vv', $record, $length); + $data = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]); + $this->_append($header.$data); + return $str_error; + } + + /** + * Check row and col before writing to a cell, and update the sheet's + * dimensions accordingly + * + * @access private + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @return boolean true for success, false if row and/or col are grester + * then maximums allowed. + */ + protected function _checkRowCol($row, $col) + { + if ($row >= $this->_xls_rowmax) { + return false; + } + if ($col >= $this->_xls_colmax) { + return false; + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + return true; + } + + /** + * Writes a note associated with the cell given by the row and column. + * NOTE records don't have a length limit. + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $note The note to write + */ + public function writeNote($row, $col, $note) + { + $note_length = strlen($note); + $record = 0x001C; // Record identifier + $max_length = 2048; // Maximun length for a NOTE record + //$length = 0x0006 + $note_length; // Bytes to follow + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + // Length for this record is no more than 2048 + 6 + $length = 0x0006 + min($note_length, 2048); + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $note_length); + $this->_append($header . $data . substr($note, 0, 2048)); + + for ($i = $max_length; $i < $note_length; $i += $max_length) { + $chunk = substr($note, $i, $max_length); + $length = 0x0006 + strlen($chunk); + $header = pack("vv", $record, $length); + $data = pack("vvv", -1, 0, strlen($chunk)); + $this->_append($header.$data.$chunk); + } + return(0); + } + + /** + * Write a blank cell to the specified row and column (zero indexed). + * A blank cell is used to specify formatting without adding a string + * or a number. + * + * A blank cell without a format serves no purpose. Therefore, we don't write + * a BLANK record unless a format is specified. + * + * Returns 0 : normal termination (including no format) + * -1 : insufficient number of arguments + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param mixed $format The XF format + */ + public function writeBlank($row, $col, $format) + { + // Don't write a blank cell unless it has a format + if (!$format) { + return(0); + } + + $record = 0x0201; // Record identifier + $length = 0x0006; // Number of bytes to follow + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $this->_append($header . $data); + return 0; + } + + /** + * Write a formula to the specified row and column (zero indexed). + * The textual representation of the formula is passed to the parser in + * Parser.php which returns a packed binary string. + * + * Returns 0 : normal termination + * -1 : formula errors (bad formula) + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $formula The formula text string + * @param mixed $format The optional XF format + * @return integer + */ + public function writeFormula($row, $col, $formula, $format = null) + { + $record = 0x0006; // Record identifier + + // Excel normally stores the last calculated value of the formula in $num. + // Clearly we are not in a position to calculate this a priori. Instead + // we set $num to zero and set the option flags in $grbit to ensure + // automatic calculation of the formula when the file is opened. + // + $xf = $this->_XF($format); // The cell format + $num = 0x00; // Current value of formula + $grbit = 0x03; // Option flags + $unknown = 0x0000; // Must be zero + + + // Check that row and col are valid and store max and min values + if ($this->_checkRowCol($row, $col) == false) { + return -2; + } + + // Strip the '=' or '@' sign at the beginning of the formula string + if (preg_match("/^=/", $formula)) { + $formula = preg_replace("/(^=)/", "", $formula); + } elseif (preg_match("/^@/", $formula)) { + $formula = preg_replace("/(^@)/", "", $formula); + } else { + // Error handling + $this->writeString($row, $col, 'Unrecognised character for formula'); + return -1; + } + + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if ($this->isError($error)) { + $this->writeString($row, $col, $error->getMessage()); + return -1; + } + + $formula = $this->_parser->toReversePolish(); + if ($this->isError($formula)) { + $this->writeString($row, $col, $formula->getMessage()); + return -1; + } + + $formlen = strlen($formula); // Length of the binary string + $length = 0x16 + $formlen; // Length of the record data + + $header = pack("vv", $record, $length); + $data = pack("vvvdvVv", $row, $col, $xf, $num, + $grbit, $unknown, $formlen); + + $this->_append($header . $data . $formula); + return 0; + } + + /** + * Write a hyperlink. + * This is comprised of two elements: the visible label and + * the invisible link. The visible label is the same as the link unless an + * alternative string is specified. The label is written using the + * writeString() method. Therefore the 255 characters string limit applies. + * $string and $format are optional. + * + * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external + * directory url. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Row + * @param integer $col Column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + * @return integer + */ + public function writeUrl($row, $col, $url, $string = '', $format = null) + { + // Add start row and col to arg list + return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format)); + } + + /** + * This is the more general form of writeUrl(). It allows a hyperlink to be + * written to a range of cells. This public function also decides the type of hyperlink + * to be written. These are either, Web (http, ftp, mailto), Internal + * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + * @return integer + */ + + protected function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = null) + { + + // Check for internal/external sheet links or default to web link + if (preg_match('[^internal:]', $url)) { + return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format)); + } + if (preg_match('[^external:]', $url)) { + return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format)); + } + return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format)); + } + + + /** + * Used to write http, ftp and mailto hyperlinks. + * The link type ($options) is 0x03 is the same as absolute dir ref without + * sheet. However it is differentiated by the $unknown2 data stream. + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + protected function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Write the visible label using the writeString() method. + if ($str == '') { + $str = $url; + } + $str_error = is_numeric($str) ? $this->writeNumber($row1, $col1, $str, $format) : $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) || ($str_error == -3)) { + return $str_error; + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); + + // Pack the option flags + $options = pack("V", 0x03); + + // Convert URL to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL + $url_len = pack("V", strlen($url)); + + // Calculate the data length + $length = 0x34 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append($header . $data . + $unknown1 . $options . + $unknown2 . $url_len . $url); + return($str_error); + } + + /** + * Used to write internal reference hyperlinks such as "Sheet1!A1". + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + protected function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Strip URL type + $url = preg_replace('/^internal:/', '', $url); + + // Write the visible label + if ($str == '') { + $str = $url; + } + $str_error = is_numeric($str) ? $this->writeNumber($row1, $col1, $str, $format) : $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) || ($str_error == -3)) { + return $str_error; + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + + // Pack the option flags + $options = pack("V", 0x08); + + // Convert the URL type and to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL as chars (not wchars) + $url_len = pack("V", floor(strlen($url)/2)); + + // Calculate the data length + $length = 0x24 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append($header . $data . + $unknown1 . $options . + $url_len . $url); + return($str_error); + } + + /** + * Write links to external directory names such as 'c:\foo.xls', + * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. + * + * Note: Excel writes some relative links with the $dir_long string. We ignore + * these cases for the sake of simpler code. + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + protected function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + // Network drives are different. We will handle them separately + // MS/Novell network drives and shares start with \\ + if (preg_match('[^external:\\\\]', $url)) { + return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format)); + } + + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Strip URL type and change Unix dir separator to Dos style (if needed) + // + $url = preg_replace('/^external:/', '', $url); + $url = preg_replace('/\//', "\\", $url); + + // Write the visible label + if ($str == '') { + $str = preg_replace('/\#/', ' - ', $url); + } + $str_error = is_numeric($str) ? $this->writeNumber($row1, $col1, $str, $format) : $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) or ($str_error == -3)) { + return $str_error; + } + + // Determine if the link is relative or absolute: + // relative if link contains no dir separator, "somefile.xls" + // relative if link starts with up-dir, "..\..\somefile.xls" + // otherwise, absolute + + $absolute = 0x02; // Bit mask + if (!preg_match("/\\\/", $url)) { + $absolute = 0x00; + } + if (preg_match("/^\.\.\\\/", $url)) { + $absolute = 0x00; + } + $link_type = 0x01 | $absolute; + + // Determine if the link contains a sheet reference and change some of the + // parameters accordingly. + // Split the dir name and sheet name (if it exists) + /*if (preg_match("/\#/", $url)) { + list($dir_long, $sheet) = split("\#", $url); + } else { + $dir_long = $url; + } + + if (isset($sheet)) { + $link_type |= 0x08; + $sheet_len = pack("V", strlen($sheet) + 0x01); + $sheet = join("\0", split('', $sheet)); + $sheet .= "\0\0\0"; + } else { + $sheet_len = ''; + $sheet = ''; + }*/ + $dir_long = $url; + if (preg_match("/\#/", $url)) { + $link_type |= 0x08; + } + + + + // Pack the link type + $link_type = pack("V", $link_type); + + // Calculate the up-level dir count e.g.. (..\..\..\ == 3) + $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless); + $up_count = pack("v", $up_count); + + // Store the short dos dir name (null terminated) + $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0"; + + // Store the long dir name as a wchar string (non-null terminated) + //$dir_long = join("\0", split('', $dir_long)); + $dir_long = $dir_long . "\0"; + + // Pack the lengths of the dir strings + $dir_short_len = pack("V", strlen($dir_short) ); + $dir_long_len = pack("V", strlen($dir_long) ); + $stream_len = pack("V", 0);//strlen($dir_long) + 0x06); + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); + $unknown2 = pack("H*",'0303000000000000C000000000000046' ); + $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000'); + $unknown4 = pack("v", 0x03 ); + + // Pack the main data stream + $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $unknown2 . + $up_count . + $dir_short_len. + $dir_short . + $unknown3 . + $stream_len ;/*. + $dir_long_len . + $unknown4 . + $dir_long . + $sheet_len . + $sheet ;*/ + + // Pack the header data + $length = strlen($data); + $header = pack("vv", $record, $length); + + // Write the packed data + $this->_append($header. $data); + return($str_error); + } + + + /** + * This method is used to set the height and format for a row. + * + * @access public + * @param integer $row The row to set + * @param integer $height Height we are giving to the row. + * Use null to set XF without setting height + * @param mixed $format XF format we are giving to the row + * @param bool $hidden The optional hidden attribute + * @param integer $level The optional outline level for row, in range [0,7] + */ + public function setRow($row, $height, $format = null, $hidden = false, $level = 0) + { + $record = 0x0208; // Record identifier + $length = 0x0010; // Number of bytes to follow + + $colMic = 0x0000; // First defined column + $colMac = 0x0000; // Last defined column + $irwMac = 0x0000; // Used by Excel to optimise loading + $reserved = 0x0000; // Reserved + $grbit = 0x0000; // Option flags + $ixfe = $this->_XF($format); // XF index + + // set _row_sizes so _sizeRow() can use it + $this->_row_sizes[$row] = $height; + + // Use setRow($row, null, $XF) to set XF format without setting height + if ($height != null) { + $miyRw = $height * 20; // row height + } else { + $miyRw = 0xff; // default row height is 256 + } + + $level = max(0, min($level, 7)); // level should be between 0 and 7 + $this->_outline_row_level = max($level, $this->_outline_row_level); + + + // Set the options flags. fUnsynced is used to show that the font and row + // heights are not compatible. This is usually the case for WriteExcel. + // The collapsed flag 0x10 doesn't seem to be used to indicate that a row + // is collapsed. Instead it is used to indicate that the previous row is + // collapsed. The zero height flag, 0x20, is used to collapse a row. + + $grbit |= $level; + if ($hidden) { + $grbit |= 0x0020; + } + $grbit |= 0x0040; // fUnsynced + if ($format) { + $grbit |= 0x0080; + } + $grbit |= 0x0100; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, + $irwMac,$reserved, $grbit, $ixfe); + $this->_append($header.$data); + } + + /** + * Writes Excel DIMENSIONS to define the area in which there is data. + * + * @access private + */ + protected function _storeDimensions() + { + $record = 0x0200; // Record identifier + $row_min = $this->_dim_rowmin; // First row + $row_max = $this->_dim_rowmax + 1; // Last row plus 1 + $col_min = $this->_dim_colmin; // First column + $col_max = $this->_dim_colmax + 1; // Last column plus 1 + $reserved = 0x0000; // Reserved by Excel + + if ($this->_BIFF_version == 0x0500) { + $length = 0x000A; // Number of bytes to follow + $data = pack("vvvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x000E; + $data = pack("VVvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + } + $header = pack("vv", $record, $length); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record Window2. + * + * @access private + */ + protected function _storeWindow2() + { + $record = 0x023E; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x000A; // Number of bytes to follow + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x0012; + } + + $grbit = 0x00B6; // Option flags + $rwTop = 0x0000; // Top row visible in window + $colLeft = 0x0000; // Leftmost column visible in window + + + // The options flags that comprise $grbit + $fDspFmla = 0; // 0 - bit + $fDspGrid = $this->_screen_gridlines; // 1 + $fDspRwCol = 1; // 2 + $fFrozen = $this->_frozen; // 3 + $fDspZeros = 1; // 4 + $fDefaultHdr = 1; // 5 + $fArabic = $this->_Arabic; // 6 + $fDspGuts = $this->_outline_on; // 7 + $fFrozenNoSplit = 0; // 0 - bit + $fSelected = $this->selected; // 1 + $fPaged = 1; // 2 + + $grbit = $fDspFmla; + $grbit |= $fDspGrid << 1; + $grbit |= $fDspRwCol << 2; + $grbit |= $fFrozen << 3; + $grbit |= $fDspZeros << 4; + $grbit |= $fDefaultHdr << 5; + $grbit |= $fArabic << 6; + $grbit |= $fDspGuts << 7; + $grbit |= $fFrozenNoSplit << 8; + $grbit |= $fSelected << 9; + $grbit |= $fPaged << 10; + + $header = pack("vv", $record, $length); + $data = pack("vvv", $grbit, $rwTop, $colLeft); + // FIXME !!! + if ($this->_BIFF_version == 0x0500) { + $rgbHdr = 0x00000000; // Row/column heading and gridline color + $data .= pack("V", $rgbHdr); + } elseif ($this->_BIFF_version == 0x0600) { + $rgbHdr = 0x0040; // Row/column heading and gridline color index + $zoom_factor_page_break = 0x0000; + $zoom_factor_normal = 0x0000; + $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000); + } + $this->_append($header.$data); + } + + /** + * Write BIFF record DEFCOLWIDTH if COLINFO records are in use. + * + * @access private + */ + protected function _storeDefcol() + { + $record = 0x0055; // Record identifier + $length = 0x0002; // Number of bytes to follow + $colwidth = 0x0008; // Default column width + + $header = pack("vv", $record, $length); + $data = pack("v", $colwidth); + $this->_prepend($header . $data); + } + + /** + * Write BIFF record COLINFO to define column widths + * + * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C + * length record. + * + * @access private + * @param array $col_array This is the only parameter received and is composed of the following: + * 0 => First formatted column, + * 1 => Last formatted column, + * 2 => Col width (8.43 is Excel default), + * 3 => The optional XF format of the column, + * 4 => Option flags. + * 5 => Optional outline level + */ + protected function _storeColinfo($col_array) + { + if (isset($col_array[0])) { + $colFirst = $col_array[0]; + } + if (isset($col_array[1])) { + $colLast = $col_array[1]; + } + if (isset($col_array[2])) { + $coldx = $col_array[2]; + } else { + $coldx = 8.43; + } + if (isset($col_array[3])) { + $format = $col_array[3]; + } else { + $format = 0; + } + if (isset($col_array[4])) { + $grbit = $col_array[4]; + } else { + $grbit = 0; + } + if (isset($col_array[5])) { + $level = $col_array[5]; + } else { + $level = 0; + } + $record = 0x007D; // Record identifier + $length = 0x000B; // Number of bytes to follow + + $coldx += 0.72; // Fudge. Excel subtracts 0.72 !? + $coldx *= 256; // Convert to units of 1/256 of a char + + $ixfe = $this->_XF($format); + $reserved = 0x00; // Reserved + + $level = max(0, min($level, 7)); + $grbit |= $level << 8; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvC", $colFirst, $colLast, $coldx, + $ixfe, $grbit, $reserved); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record SELECTION. + * + * @access private + * @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast) + * @see setSelection() + */ + protected function _storeSelection($array) + { + list($rwFirst,$colFirst,$rwLast,$colLast) = $array; + $record = 0x001D; // Record identifier + $length = 0x000F; // Number of bytes to follow + + $pnn = $this->_active_pane; // Pane position + $rwAct = $rwFirst; // Active row + $colAct = $colFirst; // Active column + $irefAct = 0; // Active cell ref + $cref = 1; // Number of refs + + if (!isset($rwLast)) { + $rwLast = $rwFirst; // Last row in reference + } + if (!isset($colLast)) { + $colLast = $colFirst; // Last col in reference + } + + // Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) { + list($rwFirst, $rwLast) = array($rwLast, $rwFirst); + } + + if ($colFirst > $colLast) { + list($colFirst, $colLast) = array($colLast, $colFirst); + } + + $header = pack("vv", $record, $length); + $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, + $irefAct, $cref, + $rwFirst, $rwLast, + $colFirst, $colLast); + $this->_append($header . $data); + } + + /** + * Store the MERGEDCELLS record for all ranges of merged cells + * + * @access private + */ + protected function _storeMergedCells() + { + // if there are no merged cell ranges set, return + if (count($this->_merged_ranges) == 0) { + return; + } + $record = 0x00E5; + foreach($this->_merged_ranges as $ranges) + { + $length = 2 + count($ranges) * 8; + $header = pack('vv', $record, $length); + $data = pack('v', count($ranges)); + foreach ($ranges as $range) + $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]); + $string = $header.$data; + $this->_append($string, true); + } + } + + /** + * Write BIFF record EXTERNCOUNT to indicate the number of external sheet + * references in a worksheet. + * + * Excel only stores references to external sheets that are used in formulas. + * For simplicity we store references to all the sheets in the workbook + * regardless of whether they are used or not. This reduces the overall + * complexity and eliminates the need for a two way dialogue between the formula + * parser the worksheet objects. + * + * @access private + * @param integer $count The number of external sheet references in this worksheet + */ + protected function _storeExterncount($count) + { + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $count); + $this->_prepend($header . $data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. A formula references a sheet name via an index. Since we store a + * reference to all of the external worksheets the EXTERNSHEET index is the same + * as the worksheet index. + * + * @access private + * @param string $sheetname The name of a external worksheet + */ + protected function _storeExternsheet($sheetname) + { + $record = 0x0017; // Record identifier + + // References to the current sheet are encoded differently to references to + // external sheets. + // + if ($this->name == $sheetname) { + $sheetname = ''; + $length = 0x02; // The following 2 bytes + $cch = 1; // The following byte + $rgch = 0x02; // Self reference + } else { + $length = 0x02 + strlen($sheetname); + $cch = strlen($sheetname); + $rgch = 0x03; // Reference to a sheet in the current workbook + } + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + $this->_prepend($header . $data . $sheetname); + } + + /** + * Writes the Excel BIFF PANE record. + * The panes can either be frozen or thawed (unfrozen). + * Frozen panes are specified in terms of an integer number of rows and columns. + * Thawed panes are specified in terms of Excel's units for rows and columns. + * + * @access private + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + protected function _storePanes($panes) + { + $y = $panes[0]; + $x = $panes[1]; + $rwTop = $panes[2]; + $colLeft = $panes[3]; + if (count($panes) > 4) { // if Active pane was received + $pnnAct = $panes[4]; + } else { + $pnnAct = null; + } + $record = 0x0041; // Record identifier + $length = 0x000A; // Number of bytes to follow + + // Code specific to frozen or thawed panes. + if ($this->_frozen) { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = $y; + } + if (!isset($colLeft)) { + $colLeft = $x; + } + } else { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = 0; + } + if (!isset($colLeft)) { + $colLeft = 0; + } + + // Convert Excel's row and column units to the internal units. + // The default row height is 12.75 + // The default column width is 8.43 + // The following slope and intersection values were interpolated. + // + $y = 20*$y + 255; + $x = 113.879*$x + 390; + } + + + // Determine which pane should be active. There is also the undocumented + // option to override this should it be necessary: may be removed later. + // + if (!isset($pnnAct)) { + if ($x != 0 && $y != 0) { + $pnnAct = 0; // Bottom right + } + if ($x != 0 && $y == 0) { + $pnnAct = 1; // Top right + } + if ($x == 0 && $y != 0) { + $pnnAct = 2; // Bottom left + } + if ($x == 0 && $y == 0) { + $pnnAct = 3; // Top left + } + } + + $this->_active_pane = $pnnAct; // Used in _storeSelection + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); + $this->_append($header . $data); + } + + /** + * Store the page setup SETUP BIFF record. + * + * @access private + */ + protected function _storeSetup() + { + $record = 0x00A1; // Record identifier + $length = 0x0022; // Number of bytes to follow + + $iPaperSize = $this->_paper_size; // Paper size + $iScale = $this->_print_scale; // Print scaling factor + $iPageStart = 0x01; // Starting page number + $iFitWidth = $this->_fit_width; // Fit to number of pages wide + $iFitHeight = $this->_fit_height; // Fit to number of pages high + $grbit = 0x00; // Option flags + $iRes = 0x0258; // Print resolution + $iVRes = 0x0258; // Vertical print resolution + $numHdr = $this->_margin_head; // Header Margin + $numFtr = $this->_margin_foot; // Footer Margin + $iCopies = 0x01; // Number of copies + + $fLeftToRight = 0x0; // Print over then down + $fLandscape = $this->_orientation; // Page orientation + $fNoPls = 0x0; // Setup not read from printer + $fNoColor = 0x0; // Print black and white + $fDraft = 0x0; // Print draft quality + $fNotes = 0x0; // Print notes + $fNoOrient = 0x0; // Orientation not set + $fUsePage = 0x0; // Use custom starting page + + $grbit = $fLeftToRight; + $grbit |= $fLandscape << 1; + $grbit |= $fNoPls << 2; + $grbit |= $fNoColor << 3; + $grbit |= $fDraft << 4; + $grbit |= $fNotes << 5; + $grbit |= $fNoOrient << 6; + $grbit |= $fUsePage << 7; + + $numHdr = pack("d", $numHdr); + $numFtr = pack("d", $numFtr); + if ($this->_byte_order) { // if it's Big Endian + $numHdr = strrev($numHdr); + $numFtr = strrev($numFtr); + } + + $header = pack("vv", $record, $length); + $data1 = pack("vvvvvvvv", $iPaperSize, + $iScale, + $iPageStart, + $iFitWidth, + $iFitHeight, + $grbit, + $iRes, + $iVRes); + $data2 = $numHdr.$numFtr; + $data3 = pack("v", $iCopies); + $this->_prepend($header . $data1 . $data2 . $data3); + } + + /** + * Store the header caption BIFF record. + * + * @access private + */ + protected function _storeHeader() + { + $record = 0x0014; // Record identifier + + $str = $this->_header; // header string + $cch = strlen($str); // Length of header string + if ($this->_BIFF_version == 0x0600) { + $encoding = 0x0; // TODO: Unicode support + $length = 3 + $cch; // Bytes to follow + } else { + $length = 1 + $cch; // Bytes to follow + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("vC", $cch, $encoding); + } else { + $data = pack("C", $cch); + } + + $this->_prepend($header.$data.$str); + } + + /** + * Store the footer caption BIFF record. + * + * @access private + */ + protected function _storeFooter() + { + $record = 0x0015; // Record identifier + + $str = $this->_footer; // Footer string + $cch = strlen($str); // Length of footer string + if ($this->_BIFF_version == 0x0600) { + $encoding = 0x0; // TODO: Unicode support + $length = 3 + $cch; // Bytes to follow + } else { + $length = 1 + $cch; + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("vC", $cch, $encoding); + } else { + $data = pack("C", $cch); + } + + $this->_prepend($header . $data . $str); + } + + /** + * Store the horizontal centering HCENTER BIFF record. + * + * @access private + */ + protected function _storeHcenter() + { + $record = 0x0083; // Record identifier + $length = 0x0002; // Bytes to follow + + $fHCenter = $this->_hcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fHCenter); + + $this->_prepend($header.$data); + } + + /** + * Store the vertical centering VCENTER BIFF record. + * + * @access private + */ + protected function _storeVcenter() + { + $record = 0x0084; // Record identifier + $length = 0x0002; // Bytes to follow + + $fVCenter = $this->_vcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fVCenter); + $this->_prepend($header . $data); + } + + /** + * Store the LEFTMARGIN BIFF record. + * + * @access private + */ + protected function _storeMarginLeft() + { + $record = 0x0026; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_left; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the RIGHTMARGIN BIFF record. + * + * @access private + */ + protected function _storeMarginRight() + { + $record = 0x0027; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_right; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the TOPMARGIN BIFF record. + * + * @access private + */ + protected function _storeMarginTop() + { + $record = 0x0028; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_top; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the BOTTOMMARGIN BIFF record. + * + * @access private + */ + protected function _storeMarginBottom() + { + $record = 0x0029; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_bottom; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Merges the area given by its arguments. + * This is an Excel97/2000 method. It is required to perform more complicated + * merging than the normal setAlign('merge'). + * + * @access public + * @param integer $first_row First row of the area to merge + * @param integer $first_col First column of the area to merge + * @param integer $last_row Last row of the area to merge + * @param integer $last_col Last column of the area to merge + */ + public function mergeCells($first_row, $first_col, $last_row, $last_col) + { + $record = 0x00E5; // Record identifier + $length = 0x000A; // Bytes to follow + $cref = 1; // Number of refs + + // Swap last row/col for first row/col as necessary + if ($first_row > $last_row) { + list($first_row, $last_row) = array($last_row, $first_row); + } + + if ($first_col > $last_col) { + list($first_col, $last_col) = array($last_col, $first_col); + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $cref, $first_row, $last_row, + $first_col, $last_col); + + $this->_append($header.$data); + } + + /** + * Write the PRINTHEADERS BIFF record. + * + * @access private + */ + protected function _storePrintHeaders() + { + $record = 0x002a; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintRwCol = $this->_print_headers; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintRwCol); + $this->_prepend($header . $data); + } + + /** + * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the + * GRIDSET record. + * + * @access private + */ + protected function _storePrintGridlines() + { + $record = 0x002b; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintGrid = $this->_print_gridlines; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintGrid); + $this->_prepend($header . $data); + } + + /** + * Write the GRIDSET BIFF record. Must be used in conjunction with the + * PRINTGRIDLINES record. + * + * @access private + */ + protected function _storeGridset() + { + $record = 0x0082; // Record identifier + $length = 0x0002; // Bytes to follow + + $fGridSet = !($this->_print_gridlines); // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fGridSet); + $this->_prepend($header . $data); + } + + /** + * Write the GUTS BIFF record. This is used to configure the gutter margins + * where Excel outline symbols are displayed. The visibility of the gutters is + * controlled by a flag in WSBOOL. + * + * @see _storeWsbool() + * @access private + */ + protected function _storeGuts() + { + $record = 0x0080; // Record identifier + $length = 0x0008; // Bytes to follow + + $dxRwGut = 0x0000; // Size of row gutter + $dxColGut = 0x0000; // Size of col gutter + + $row_level = $this->_outline_row_level; + $col_level = 0; + + // Calculate the maximum column outline level. The equivalent calculation + // for the row outline level is carried out in setRow(). + $colcount = count($this->_colinfo); + for ($i = 0; $i < $colcount; $i++) { + // Skip cols without outline level info. + if (count($this->_colinfo[$i]) >= 6) { + $col_level = max($this->_colinfo[$i][5], $col_level); + } + } + + // Set the limits for the outline levels (0 <= x <= 7). + $col_level = max(0, min($col_level, 7)); + + // The displayed level is one greater than the max outline levels + if ($row_level) { + $row_level++; + } + if ($col_level) { + $col_level++; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level); + + $this->_prepend($header.$data); + } + + + /** + * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction + * with the SETUP record. + * + * @access private + */ + protected function _storeWsbool() + { + $record = 0x0081; // Record identifier + $length = 0x0002; // Bytes to follow + $grbit = 0x0000; + + // The only option that is of interest is the flag for fit to page. So we + // set all the options in one go. + // + /*if ($this->_fit_page) { + $grbit = 0x05c1; + } else { + $grbit = 0x04c1; + }*/ + // Set the option flags + $grbit |= 0x0001; // Auto page breaks visible + if ($this->_outline_style) { + $grbit |= 0x0020; // Auto outline styles + } + if ($this->_outline_below) { + $grbit |= 0x0040; // Outline summary below + } + if ($this->_outline_right) { + $grbit |= 0x0080; // Outline summary right + } + if ($this->_fit_page) { + $grbit |= 0x0100; // Page setup fit to page + } + if ($this->_outline_on) { + $grbit |= 0x0400; // Outline symbols displayed + } + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $this->_prepend($header . $data); + } + + /** + * Write the HORIZONTALPAGEBREAKS BIFF record. + * + * @access private + */ + protected function _storeHbreak() + { + // Return if the user hasn't specified pagebreaks + if (empty($this->_hbreaks)) { + return; + } + + // Sort and filter array of page breaks + $breaks = $this->_hbreaks; + sort($breaks, SORT_NUMERIC); + if ($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001b; // Record identifier + $cbrk = count($breaks); // Number of page breaks + if ($this->_BIFF_version == 0x0600) { + $length = 2 + 6*$cbrk; // Bytes to follow + } else { + $length = 2 + 2*$cbrk; // Bytes to follow + } + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + if ($this->_BIFF_version == 0x0600) { + $data .= pack("vvv", $break, 0x0000, 0x00ff); + } else { + $data .= pack("v", $break); + } + } + + $this->_prepend($header.$data); + } + + + /** + * Write the VERTICALPAGEBREAKS BIFF record. + * + * @access private + */ + protected function _storeVbreak() + { + // Return if the user hasn't specified pagebreaks + if (empty($this->_vbreaks)) { + return; + } + + // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. + // It is slightly higher in Excel 97/200, approx. 1026 + $breaks = array_slice($this->_vbreaks,0,1000); + + // Sort and filter array of page breaks + sort($breaks, SORT_NUMERIC); + if ($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001a; // Record identifier + $cbrk = count($breaks); // Number of page breaks + if ($this->_BIFF_version == 0x0600) { + $length = 2 + 6*$cbrk; // Bytes to follow + } else { + $length = 2 + 2*$cbrk; // Bytes to follow + } + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + if ($this->_BIFF_version == 0x0600) { + $data .= pack("vvv", $break, 0x0000, 0xffff); + } else { + $data .= pack("v", $break); + } + } + + $this->_prepend($header . $data); + } + + /** + * Set the Biff PROTECT record to indicate that the worksheet is protected. + * + * @access private + */ + protected function _storeProtect() + { + // Exit unless sheet protection has been specified + if ($this->_protect == 0) { + return; + } + + $record = 0x0012; // Record identifier + $length = 0x0002; // Bytes to follow + + $fLock = $this->_protect; // Worksheet is protected + + $header = pack("vv", $record, $length); + $data = pack("v", $fLock); + + $this->_prepend($header.$data); + } + + /** + * Write the worksheet PASSWORD record. + * + * @access private + */ + protected function _storePassword() + { + // Exit unless sheet protection and password have been specified + if (($this->_protect == 0) || (!isset($this->_password))) { + return; + } + + $record = 0x0013; // Record identifier + $length = 0x0002; // Bytes to follow + + $wPassword = $this->_password; // Encoded password + + $header = pack("vv", $record, $length); + $data = pack("v", $wPassword); + + $this->_prepend($header . $data); + } + + + /** + * Insert a 24bit bitmap image in a worksheet. + * + * @access public + * @param integer $row The row we are going to insert the bitmap into + * @param integer $col The column we are going to insert the bitmap into + * @param string $bitmap The bitmap filename + * @param integer $x The horizontal position (offset) of the image inside the cell. + * @param integer $y The vertical position (offset) of the image inside the cell. + * @param integer $scale_x The horizontal scale + * @param integer $scale_y The vertical scale + */ + public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) + { + $bitmap_array = $this->_processBitmap($bitmap); + if ($this->isError($bitmap_array)) { + $this->writeString($row, $col, $bitmap_array->getMessage()); + return; + } + list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap); + + // Scale the frame of the image. + $width *= $scale_x; + $height *= $scale_y; + + // Calculate the vertices of the image and write the OBJ record + $this->_positionImage($col, $row, $x, $y, $width, $height); + + // Write the IMDATA record to store the bitmap data + $record = 0x007f; + $length = 8 + $size; + $cf = 0x09; + $env = 0x01; + $lcb = $size; + + $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); + $this->_append($header.$data); + } + + /** + * Calculate the vertices that define the position of the image as required by + * the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be taken into + * account. + * The values of $col_start and $row_start are passed in from the calling + * public function. The values of $col_end and $row_end are calculated by subtracting + * the width and height of the bitmap from the width and height of the + * underlying cells. + * The vertices are expressed as a percentage of the underlying cell width as + * follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * + * @access private + * @note the SDK incorrectly states that the height should be expressed as a + * percentage of 1024. + * @param integer $col_start Col containing upper left corner of object + * @param integer $row_start Row containing top left corner of object + * @param integer $x1 Distance to left side of object + * @param integer $y1 Distance to top of object + * @param integer $width Width of image frame + * @param integer $height Height of image frame + */ + protected function _positionImage($col_start, $row_start, $x1, $y1, $width, $height) + { + // Initialise end cell to the same as the start cell + $col_end = $col_start; // Col containing lower right corner of object + $row_end = $row_start; // Row containing bottom right corner of object + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= $this->_sizeCol($col_start)) { + $x1 = 0; + } + if ($y1 >= $this->_sizeRow($row_start)) { + $y1 = 0; + } + + $width = $width + $x1 -1; + $height = $height + $y1 -1; + + // Subtract the underlying cell widths to find the end cell of the image + while ($width >= $this->_sizeCol($col_end)) { + $width -= $this->_sizeCol($col_end); + $col_end++; + } + + // Subtract the underlying cell heights to find the end cell of the image + while ($height >= $this->_sizeRow($row_end)) { + $height -= $this->_sizeRow($row_end); + $row_end++; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + // with zero eight or width. + // + if ($this->_sizeCol($col_start) == 0) { + return; + } + if ($this->_sizeCol($col_end) == 0) { + return; + } + if ($this->_sizeRow($row_start) == 0) { + return; + } + if ($this->_sizeRow($row_end) == 0) { + return; + } + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / $this->_sizeCol($col_start) * 1024; + $y1 = $y1 / $this->_sizeRow($row_start) * 256; + $x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object + $y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object + + $this->_storeObjPicture($col_start, $x1, + $row_start, $y1, + $col_end, $x2, + $row_end, $y2); + } + + /** + * Convert the width of a cell from user's units to pixels. By interpolation + * the relationship is: y = 7x +5. If the width hasn't been set by the user we + * use the default value. If the col is hidden we use a value of zero. + * + * @access private + * @param integer $col The column + * @return integer The width in pixels + */ + protected function _sizeCol($col) + { + // Look up the cell value to see if it has been changed + if (isset($this->col_sizes[$col])) { + if ($this->col_sizes[$col] == 0) { + return(0); + } else { + return(floor(7 * $this->col_sizes[$col] + 5)); + } + } else { + return(64); + } + } + + /** + * Convert the height of a cell from user's units to pixels. By interpolation + * the relationship is: y = 4/3x. If the height hasn't been set by the user we + * use the default value. If the row is hidden we use a value of zero. (Not + * possible to hide row yet). + * + * @access private + * @param integer $row The row + * @return integer The width in pixels + */ + protected function _sizeRow($row) + { + // Look up the cell value to see if it has been changed + if (isset($this->_row_sizes[$row])) { + if ($this->_row_sizes[$row] == 0) { + return(0); + } else { + return(floor(4/3 * $this->_row_sizes[$row])); + } + } else { + return(17); + } + } + + /** + * Store the OBJ record that precedes an IMDATA record. This could be generalise + * to support other Excel objects. + * + * @access private + * @param integer $colL Column containing upper left corner of object + * @param integer $dxL Distance from left side of cell + * @param integer $rwT Row containing top left corner of object + * @param integer $dyT Distance from top of cell + * @param integer $colR Column containing lower right corner of object + * @param integer $dxR Distance from right of cell + * @param integer $rwB Row containing bottom right corner of object + * @param integer $dyB Distance from bottom of cell + */ + protected function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB) + { + $record = 0x005d; // Record identifier + $length = 0x003c; // Bytes to follow + + $cObj = 0x0001; // Count of objects in file (set to 1) + $OT = 0x0008; // Object type. 8 = Picture + $id = 0x0001; // Object ID + $grbit = 0x0614; // Option flags + + $cbMacro = 0x0000; // Length of FMLA structure + $Reserved1 = 0x0000; // Reserved + $Reserved2 = 0x0000; // Reserved + + $icvBack = 0x09; // Background colour + $icvFore = 0x09; // Foreground colour + $fls = 0x00; // Fill pattern + $fAuto = 0x00; // Automatic fill + $icv = 0x08; // Line colour + $lns = 0xff; // Line style + $lnw = 0x01; // Line weight + $fAutoB = 0x00; // Automatic border + $frs = 0x0000; // Frame style + $cf = 0x0009; // Image format, 9 = bitmap + $Reserved3 = 0x0000; // Reserved + $cbPictFmla = 0x0000; // Length of FMLA structure + $Reserved4 = 0x0000; // Reserved + $grbit2 = 0x0001; // Option flags + $Reserved5 = 0x0000; // Reserved + + + $header = pack("vv", $record, $length); + $data = pack("V", $cObj); + $data .= pack("v", $OT); + $data .= pack("v", $id); + $data .= pack("v", $grbit); + $data .= pack("v", $colL); + $data .= pack("v", $dxL); + $data .= pack("v", $rwT); + $data .= pack("v", $dyT); + $data .= pack("v", $colR); + $data .= pack("v", $dxR); + $data .= pack("v", $rwB); + $data .= pack("v", $dyB); + $data .= pack("v", $cbMacro); + $data .= pack("V", $Reserved1); + $data .= pack("v", $Reserved2); + $data .= pack("C", $icvBack); + $data .= pack("C", $icvFore); + $data .= pack("C", $fls); + $data .= pack("C", $fAuto); + $data .= pack("C", $icv); + $data .= pack("C", $lns); + $data .= pack("C", $lnw); + $data .= pack("C", $fAutoB); + $data .= pack("v", $frs); + $data .= pack("V", $cf); + $data .= pack("v", $Reserved3); + $data .= pack("v", $cbPictFmla); + $data .= pack("v", $Reserved4); + $data .= pack("v", $grbit2); + $data .= pack("V", $Reserved5); + + $this->_append($header . $data); + } + + /** + * Convert a 24 bit bitmap into the modified internal format used by Windows. + * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the + * MSDN library. + * + * @access private + * @param string $bitmap The bitmap to process + * @return array Array with data and properties of the bitmap + */ + protected function _processBitmap($bitmap) + { + // Open file. + $bmp_fd = @fopen($bitmap,"rb"); + if (!$bmp_fd) { + $this->raiseError("Couldn't import $bitmap"); + } + + // Slurp the file into a string. + $data = fread($bmp_fd, filesize($bitmap)); + + // Check that the file is big enough to be a bitmap. + if (strlen($data) <= 0x36) { + $this->raiseError("$bitmap doesn't contain enough data.\n"); + } + + // The first 2 bytes are used to identify the bitmap. + $identity = unpack("A2ident", $data); + if ($identity['ident'] != "BM") { + $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n"); + } + + // Remove bitmap data: ID. + $data = substr($data, 2); + + // Read and remove the bitmap size. This is more reliable than reading + // the data size at offset 0x22. + // + $size_array = unpack("Vsa", substr($data, 0, 4)); + $size = $size_array['sa']; + $data = substr($data, 4); + $size -= 0x36; // Subtract size of bitmap header. + $size += 0x0C; // Add size of BIFF header. + + // Remove bitmap data: reserved, offset, header length. + $data = substr($data, 12); + + // Read and remove the bitmap width and height. Verify the sizes. + $width_and_height = unpack("V2", substr($data, 0, 8)); + $width = $width_and_height[1]; + $height = $width_and_height[2]; + $data = substr($data, 8); + if ($width > 0xFFFF) { + $this->raiseError("$bitmap: largest image width supported is 65k.\n"); + } + if ($height > 0xFFFF) { + $this->raiseError("$bitmap: largest image height supported is 65k.\n"); + } + + // Read and remove the bitmap planes and bpp data. Verify them. + $planes_and_bitcount = unpack("v2", substr($data, 0, 4)); + $data = substr($data, 4); + if ($planes_and_bitcount[2] != 24) { // Bitcount + $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n"); + } + if ($planes_and_bitcount[1] != 1) { + $this->raiseError("$bitmap: only 1 plane supported in bitmap image.\n"); + } + + // Read and remove the bitmap compression. Verify compression. + $compression = unpack("Vcomp", substr($data, 0, 4)); + $data = substr($data, 4); + + //$compression = 0; + if ($compression['comp'] != 0) { + $this->raiseError("$bitmap: compression not supported in bitmap image.\n"); + } + + // Remove bitmap data: data size, hres, vres, colours, imp. colours. + $data = substr($data, 20); + + // Add the BITMAPCOREHEADER data + $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); + $data = $header . $data; + + return (array($width, $height, $size, $data)); + } + + /** + * Store the window zoom factor. This should be a reduced fraction but for + * simplicity we will store all fractions with a numerator of 100. + * + * @access private + */ + protected function _storeZoom() + { + // If scale is 100 we don't need to write a record + if ($this->_zoom == 100) { + return; + } + + $record = 0x00A0; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", $this->_zoom, 100); + $this->_append($header . $data); + } + + /** + * FIXME: add comments + */ + public function setValidation($row1, $col1, $row2, $col2, $validator) + { + $this->_dv[] = $validator->_getData() . + pack("vvvvv", 1, $row1, $row2, $col1, $col2); + } + + /** + * Store the DVAL and DV records. + * + * @access private + */ + protected function _storeDataValidity() + { + $record = 0x01b2; // Record identifier + $length = 0x0012; // Bytes to follow + + $grbit = 0x0002; // Prompt box at cell, no cached validity data at DV records + $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position + $verPos = 0x00000000; // Vertical position of prompt box, if fixed position + $objId = 0xffffffff; // Object identifier of drop down arrow object, or -1 if not visible + + $header = pack('vv', $record, $length); + $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, + count($this->_dv)); + $this->_append($header.$data); + + $record = 0x01be; // Record identifier + foreach ($this->_dv as $dv) { + $length = strlen($dv); // Bytes to follow + $header = pack("vv", $record, $length); + $this->_append($header . $dv); + } + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/composer.json b/videodb/vendor/pear/spreadsheet_excel_writer/composer.json new file mode 100644 index 0000000..29f3f17 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/composer.json @@ -0,0 +1,63 @@ +{ + "name": "pear/spreadsheet_excel_writer", + "type": "library", + "description": "Allows writing of Excel spreadsheets without the need for COM objects. Supports formulas, images (BMP) and all kinds of formatting for text and cells.", + "license": "LGPL-2.1-or-later", + "authors": [ + { + "name": "Carsten Schmitz", + "email": "cschmitz@limesurvey.org", + "role": "Lead" + }, + { + "name": "Xavier Noguer", + "email": "xnoguer@php.net", + "role": "Lead" + }, + { + "name": "Franck Lefevre", + "email": "progi1984@gmail.com", + "role": "Developer" + }, + { + "name": "Mika Tuupola", + "email": "tuupola@appelsiini.net", + "role": "Developer" + }, + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "role": "Lead" + } + ], + "require": { + "php": ">=5.6", + "pear/ole": ">=1.0.0RC4", + "pear/pear-core-minimal": "^1.10" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2", + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": ">=5 <10", + "sanmai/phpunit-legacy-adapter": "^6 || ^8" + }, + "autoload": { + "psr-0": { + "Spreadsheet": "" + } + }, + "autoload-dev": { + "psr-0": { + "Test": "test" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "include-path": [ + "./" + ], + "support": { + "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Spreadsheet_Excel_Writer", + "source": "https://github.com/pear/Spreadsheet_Excel_Writer" + } +} diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/package.xml b/videodb/vendor/pear/spreadsheet_excel_writer/package.xml new file mode 100644 index 0000000..387a4f6 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/package.xml @@ -0,0 +1,357 @@ + + + Spreadsheet_Excel_Writer + pear.php.net + Package for generating Excel spreadsheets + Spreadsheet_Excel_Writer was born as a porting of the Spreadsheet::WriteExcel Perl module to PHP. + It allows writing of Excel spreadsheets without the need for COM objects. + It supports formulas, images (BMP) and all kinds of formatting for text and cells. + It currently supports the BIFF5 format (Excel 5.0), so functionality appeared in the latest Excel versions is not yet available. + + Carsten Schmitz + cschmitz + cschmitz@limesurvey.org + yes + + + Xavier Noguer + xnoguer + xnoguer@php.net + no + + + Alexey Kopytko + sanmai + alexey@kopytko.com + yes + + + Franck Lefevre + progi1984 + progi1984@gmail.com + yes + + + Mika Tuupola + tuupola + tuupola@appelsiini.net + no + + 2017-05-24 + + + 0.9.4 + 0.9.4 + + + beta + beta + + LGPL + +QA release. Comes with support for newer PHP versions. Fixed irritating "pass by reference" errors. + + + + + + + + + + + + + + + + + + + + + + 5.3.3 + + + 1.4.0b1 + + + OLE + pear.php.net + 1.0.0RC2 + + + + + + + + 0.9.3 + 0.9.3 + + + beta + beta + + 2012-01-26 + LGPL + +QA release +Bug #18590 Impossible to work from IIS7 +Bug #18024 deprecated line 180, 186 with php 5.3.2 +Bug #16025 MERGEDCELLS record split by CONTINUE record +Bug #17766 Patch: Avoid deprecated split +Bug #17572 Temporary files are not removed +Bug #16938 open_basedir check is wrong in Spreadsheet_Excel_Writer_Worksheet::_initialize +Bug #14585 open_basedir workaround doesn't work +Bug #12362 named worksheets & utf-8 +Bug #14515 writeUrl only uses writeString, never writeNumber +Bug #14281 Using a row > 16,384 reports error +Request #13486 Set the worksheet direction to right-to-left (RTL) +Bug #12720 Notice thrown when using setColumn (undefined colFirst and colLast) +Bug #12053 With SetVersion(8) setting bottom border color to 0 gives cross borders (!) + + + + + 0.2 + 0.2 + + + beta + beta + + 2003-03-17 + LGPL + +-added several formatting methods: setTextRotation(), setStrikeOut(), +setOutLine(), setShadow(), setScript(). +-fixed bug in Workbook::sheets() (Bjoern Schotte). +-fixed range for references in formulas (Edward). +-added support for external references in formulas. +-added support for comparisons in formulas. +-added support for strings in formulas. + + + + + 0.3 + 0.3 + + + stable + stable + + 2003-05-02 + LGPL + +New features: + -added support for row ranges (JT Hughes) + -added method method Format::setUnLocked() (Ajit Dixit) + -added Worksheet::writeRow() and Worksheet::writeCol() +Bug fixes: + -fixed problem with unparenthesized expresions in formulas (Brent Laminack) + -fixed problems with non ISO-8859-1 characters (KUBO Atsuhiro) + -fixed swapping of columns in formulas (JT Hughes) + -fixed assorted bugs in tokenizing formulas (JT Hughes) + -fixed Worksheet::activate() (JT Hughes) + + + + + 0.4 + 0.4 + + + stable + stable + + 2003-08-21 + LGPL + +New features: + -using OLE package (>= 0.3) to generate files bigger than 7MB + -changed setFgColor() and setBgColor()'s behavior to something more intuitive. +Bug fixes: + -fixed bug #25133, lowercase cell references (jkwiat03 at hotmail dot com) + -fixed Bug #23730, worksheet names containing spaces in formulas (Robin Ericsson) + -fixed Bug #24147, formulas ended in '0' (paul at classical dot com) + -fixed swapping of arguments in variable arguments functions (JT Hughes) + + + + + 0.5 + 0.5 + + + stable + stable + + 2003-10-01 + LGPL + +New features: + -added rowcolToCell() utility method for easy writing of formula's cell references (JT Hughes). + -added Worksheet::setOutline() method (Herman Kuiper) + -added Format::setFontFamily() method (Donnie Miller) +Bug fixes: + -fixed bug #21, cyrillic characters in sheet references (arhip at goldentele dot com) + + + + + 0.6 + 0.6 + + + stable + stable + + 2003-11-15 + LGPL + +New features: + - allow semicolon as argument separator (Axel Pratzner) + - added experimental Excel97 generation. You can test it with setVersion(): + Beware! this method will be deprecated in a future release (when + Excel97 becomes the default). It is only available for testing + purposes. Use it at your own risk. + - strings longer than 255 bytes are now available using the experimental + Excel97 generation. But not all Excel97 features are available yet! +Bug fixes: + - Fixed bug #225, error in writeUrl() (jamesn at tocquigny dot com) + - Fixed bug #59, retval undefined for writeRow() (Bertrand) + + + + + 0.7 + 0.7 + + + stable + stable + + 2004-02-27 + LGPL + +New features: + - allow setting temp dir other than default using setTempDir() (using OLE 5.0 for this). + - added setMerge() for merging (only for experimental Excel97 generation) + - added setCountry() method. + - added setLocked() method. +Bug fixes: + - Fixed bug #415, typo in BIFF8 code (papercrane at reversefold dot com) + + + + + 0.8 + 0.8 + + + stable + stable + + 2004-06-22 + LGPL + +New features: + - added hideScreenGridlines() (Paul Osman) +Bug fixes: + - Fixed SST table (long strings) (Bernd Jaenichen) + - Fixed bug #1218, SST table (boucher dot stephane at free dot fr) + - Fixed bug #781, insertBitmap ignores row height + - Fixed bug #578, setVPageBreaks doesn't handle multiple value arrays (natel at tocquigny dot com) + + + + + 0.9.0 + 0.9.0 + + + stable + stable + + 2005-11-21 + LGPL + +New features: +- adding new methods Format::setVAlign() and Format::setHAlign() +- adding support for uncapitalized functions in formulas (ej: "=sum(B27:B31)") +- adding support for different charsets with method Worksheet::setInputEncoding() +- adding support for sheetnames longer than 31 chars when using setVersion(8). +Bug fixes: +- Fixed Bug #1796, wrong regular expression in _writeUrlInternal +- Fixed Bug #2363, wrong export filename with spaces +- Fixed Bug #2425, Error using writeFormula with Now() and TODAY() +- Fixed Bug #2876, German Umlauts destroy sheet references +- Fixed Bug #1706, Formulas refer to other Worksheets with "spezial" names don't work +- Fixed Bug #2748, setMargins(), setHeader() and setFooter() work in excel but not in openoffice. +- Fixed Bug #5698, preg_replace in _writeUrlInternal +- Fixed Bug #2823, Inpropper results from writeUrl() method + + + + + 0.9.1 + 0.9.1 + + + stable + stable + + 2006-09-26 + LGPL + + + + + + + 0.9.2 + 0.9.2 + + + beta + stable + + 2009-11-28 + LGPL + + + + + + + 0.9.3 + 0.9.3 + + + beta + beta + + 2012-01-26 + LGPL + +QA release +Bug #18590 Impossible to work from IIS7 +Bug #18024 deprecated line 180, 186 with php 5.3.2 +Bug #16025 MERGEDCELLS record split by CONTINUE record +Bug #17766 Patch: Avoid deprecated split +Bug #17572 Temporary files are not removed +Bug #16938 open_basedir check is wrong in Spreadsheet_Excel_Writer_Worksheet::_initialize +Bug #14585 open_basedir workaround doesn't work +Bug #12362 named worksheets & utf-8 +Bug #14515 writeUrl only uses writeString, never writeNumber +Bug #14281 Using a row > 16,384 reports error +Request #13486 Set the worksheet direction to right-to-left (RTL) +Bug #12720 Notice thrown when using setColumn (undefined colFirst and colLast) +Bug #12053 With SetVersion(8) setting bottom border color to 0 gives cross borders (!) + + + + diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/phpunit.xml.dist b/videodb/vendor/pear/spreadsheet_excel_writer/phpunit.xml.dist new file mode 100644 index 0000000..5c26d15 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + test/ + + + + + + Spreadsheet/ + + + + + + + + diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/Writer/WorkbookTest.php b/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/Writer/WorkbookTest.php new file mode 100644 index 0000000..d15590a --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/Writer/WorkbookTest.php @@ -0,0 +1,81 @@ + + * @since 2016-01-17 + */ +class Test_Spreadsheet_Excel_Writer_WorkbookTest extends Test_Spreadsheet_Excel_WriterTestCase +{ + public static function doSetUpBeforeClass() + { + // Preload constants from OLE + @class_exists(OLE::class); + } + + public function testSetVersion() + { + $workbook = $this->getNewWorkbook(); + + $before = get_object_vars($workbook); + + $workbook->setVersion(1); + + $this->assertEquals($before, get_object_vars($workbook), "Version 1 should not change internal state"); + + $workbook->setVersion(8); + + $this->assertNotEquals($before, get_object_vars($workbook), "Version 8 should change internal state"); + + return $workbook; + } + + /** + * @depends testSetVersion + */ + public function testWriteSingleCell(Spreadsheet_Excel_Writer $workbook) + { + $sheet = $workbook->addWorksheet("Example"); + $sheet->write(0, 0, "Example"); + + $this->assertSameAsInFixture('example.xls', $workbook); + } + + public function testWriteWithFormat() + { + $workbook = $this->getNewWorkbook(); + $workbook->setVersion(8); + + $format = $workbook->addFormat(); + $format->setFontFamily('Helvetica'); + $format->setSize(16); + $format->setVAlign('vcenter'); + $format->setBorder(1); + + $sheet = $workbook->addWorksheet('Example report'); + $sheet->setInputEncoding('utf-8'); + + $sheet->setColumn(0, 10, 35); + + $sheet->writeString(0, 0, "Test string", $format); + $sheet->setRow(0, 40); + + $sheet->writeString(1, 0, "こんにちわ"); + + $this->assertSameAsInFixture('with_format.xls', $workbook); + } + + public function testWithDefaultVersion() + { + $workbook = $this->getNewWorkbook(); + + $sheet = $workbook->addWorksheet("Example"); + + for ($i = 0; $i < 10; $i++) { + for ($j = 0; $j < 10; $j++) { + $sheet->write($i, $j, "Row $i $j"); + } + } + + $this->assertSameAsInFixture('example2.xls', $workbook); + } +} diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/WriterTestCase.php b/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/WriterTestCase.php new file mode 100644 index 0000000..1e57b82 --- /dev/null +++ b/videodb/vendor/pear/spreadsheet_excel_writer/test/Test/Spreadsheet/Excel/WriterTestCase.php @@ -0,0 +1,55 @@ + + * @since 2016-01-17 + */ +class Test_Spreadsheet_Excel_WriterTestCase extends \LegacyPHPUnit\TestCase +{ + const FIXTURES_PATH = 'test/fixture/'; + + /** + * @param string $filename + * @return Spreadsheet_Excel_Writer + */ + protected function getNewWorkbook($filename = '') + { + // we're writing to the standard output by defaulr + return new Spreadsheet_Excel_Writer($filename); + } + + protected function assertSameAsInFixture($filename, Spreadsheet_Excel_Writer $workbook) + { + $this->assertEmpty($workbook->_filename, "Testing with fixtures works only for standard output"); + + // we have to fix timestamp for fixtures to work + $workbook->_timestamp = 1000000000; // somewhere in 2001 + + ob_start(); + $workbook->close(); + $data = ob_get_clean(); + + $fullPath = self::FIXTURES_PATH.$filename; + + if ($this->shouldUpdateFixtures()) { + file_put_contents($fullPath, $data); + } + + if (!is_file($fullPath)) { + $this->fail("Fixture $filename not found"); + } + + // TODO: should we save data for future analysis? + //file_put_contents("{$fullPath}.work", $data); + + $this->assertEquals(file_get_contents($fullPath), $data, "Output differs for $filename"); + } + + /** + * We should update golden files + */ + private function shouldUpdateFixtures() + { + return isset($_SERVER['GOLDEN']); + } +} \ No newline at end of file diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example.xls b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example.xls new file mode 100644 index 0000000..f7955b3 Binary files /dev/null and b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example.xls differ diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example2.xls b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example2.xls new file mode 100644 index 0000000..963ff0c Binary files /dev/null and b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/example2.xls differ diff --git a/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/with_format.xls b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/with_format.xls new file mode 100644 index 0000000..97e5015 Binary files /dev/null and b/videodb/vendor/pear/spreadsheet_excel_writer/test/fixture/with_format.xls differ diff --git a/videodb/vendor/psr/http-message/CHANGELOG.md b/videodb/vendor/psr/http-message/CHANGELOG.md new file mode 100644 index 0000000..74b1ef9 --- /dev/null +++ b/videodb/vendor/psr/http-message/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.0.1 - 2016-08-06 + +### Added + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Updated all `@return self` annotation references in interfaces to use + `@return static`, which more closelly follows the semantics of the + specification. +- Updated the `MessageInterface::getHeaders()` return annotation to use the + value `string[][]`, indicating the format is a nested array of strings. +- Updated the `@link` annotation for `RequestInterface::withRequestTarget()` + to point to the correct section of RFC 7230. +- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation + to add the parameter name (`$uploadedFiles`). +- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()` + method to correctly reference the method parameter (it was referencing an + incorrect parameter name previously). + +## 1.0.0 - 2016-05-18 + +Initial stable release; reflects accepted PSR-7 specification. diff --git a/videodb/vendor/psr/http-message/LICENSE b/videodb/vendor/psr/http-message/LICENSE new file mode 100644 index 0000000..c2d8e45 --- /dev/null +++ b/videodb/vendor/psr/http-message/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/videodb/vendor/psr/http-message/README.md b/videodb/vendor/psr/http-message/README.md new file mode 100644 index 0000000..2818533 --- /dev/null +++ b/videodb/vendor/psr/http-message/README.md @@ -0,0 +1,13 @@ +PSR Http Message +================ + +This repository holds all interfaces/classes/traits related to +[PSR-7](http://www.php-fig.org/psr/psr-7/). + +Note that this is not a HTTP message implementation of its own. It is merely an +interface that describes a HTTP message. See the specification for more details. + +Usage +----- + +We'll certainly need some stuff in here. \ No newline at end of file diff --git a/videodb/vendor/psr/http-message/composer.json b/videodb/vendor/psr/http-message/composer.json new file mode 100644 index 0000000..b0d2937 --- /dev/null +++ b/videodb/vendor/psr/http-message/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/http-message", + "description": "Common interface for HTTP messages", + "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"], + "homepage": "https://github.com/php-fig/http-message", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/videodb/vendor/psr/http-message/src/MessageInterface.php b/videodb/vendor/psr/http-message/src/MessageInterface.php new file mode 100644 index 0000000..dd46e5e --- /dev/null +++ b/videodb/vendor/psr/http-message/src/MessageInterface.php @@ -0,0 +1,187 @@ +getHeaders() as $name => $values) { + * echo $name . ": " . implode(", ", $values); + * } + * + * // Emit headers iteratively: + * foreach ($message->getHeaders() as $name => $values) { + * foreach ($values as $value) { + * header(sprintf('%s: %s', $name, $value), false); + * } + * } + * + * While header names are not case-sensitive, getHeaders() will preserve the + * exact case in which headers were originally specified. + * + * @return string[][] Returns an associative array of the message's headers. Each + * key MUST be a header name, and each value MUST be an array of strings + * for that header. + */ + public function getHeaders(); + + /** + * Checks if a header exists by the given case-insensitive name. + * + * @param string $name Case-insensitive header field name. + * @return bool Returns true if any header names match the given header + * name using a case-insensitive string comparison. Returns false if + * no matching header name is found in the message. + */ + public function hasHeader($name); + + /** + * Retrieves a message header value by the given case-insensitive name. + * + * This method returns an array of all the header values of the given + * case-insensitive header name. + * + * If the header does not appear in the message, this method MUST return an + * empty array. + * + * @param string $name Case-insensitive header field name. + * @return string[] An array of string values as provided for the given + * header. If the header does not appear in the message, this method MUST + * return an empty array. + */ + public function getHeader($name); + + /** + * Retrieves a comma-separated string of the values for a single header. + * + * This method returns all of the header values of the given + * case-insensitive header name as a string concatenated together using + * a comma. + * + * NOTE: Not all header values may be appropriately represented using + * comma concatenation. For such headers, use getHeader() instead + * and supply your own delimiter when concatenating. + * + * If the header does not appear in the message, this method MUST return + * an empty string. + * + * @param string $name Case-insensitive header field name. + * @return string A string of values as provided for the given header + * concatenated together using a comma. If the header does not appear in + * the message, this method MUST return an empty string. + */ + public function getHeaderLine($name); + + /** + * Return an instance with the provided value replacing the specified header. + * + * While header names are case-insensitive, the casing of the header will + * be preserved by this function, and returned from getHeaders(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new and/or updated header and value. + * + * @param string $name Case-insensitive header field name. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withHeader($name, $value); + + /** + * Return an instance with the specified header appended with the given value. + * + * Existing values for the specified header will be maintained. The new + * value(s) will be appended to the existing list. If the header did not + * exist previously, it will be added. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * new header and/or value. + * + * @param string $name Case-insensitive header field name to add. + * @param string|string[] $value Header value(s). + * @return static + * @throws \InvalidArgumentException for invalid header names or values. + */ + public function withAddedHeader($name, $value); + + /** + * Return an instance without the specified header. + * + * Header resolution MUST be done without case-sensitivity. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the named header. + * + * @param string $name Case-insensitive header field name to remove. + * @return static + */ + public function withoutHeader($name); + + /** + * Gets the body of the message. + * + * @return StreamInterface Returns the body as a stream. + */ + public function getBody(); + + /** + * Return an instance with the specified message body. + * + * The body MUST be a StreamInterface object. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return a new instance that has the + * new body stream. + * + * @param StreamInterface $body Body. + * @return static + * @throws \InvalidArgumentException When the body is not valid. + */ + public function withBody(StreamInterface $body); +} diff --git a/videodb/vendor/psr/http-message/src/RequestInterface.php b/videodb/vendor/psr/http-message/src/RequestInterface.php new file mode 100644 index 0000000..a96d4fd --- /dev/null +++ b/videodb/vendor/psr/http-message/src/RequestInterface.php @@ -0,0 +1,129 @@ +getQuery()` + * or from the `QUERY_STRING` server param. + * + * @return array + */ + public function getQueryParams(); + + /** + * Return an instance with the specified query string arguments. + * + * These values SHOULD remain immutable over the course of the incoming + * request. They MAY be injected during instantiation, such as from PHP's + * $_GET superglobal, or MAY be derived from some other value such as the + * URI. In cases where the arguments are parsed from the URI, the data + * MUST be compatible with what PHP's parse_str() would return for + * purposes of how duplicate query parameters are handled, and how nested + * sets are handled. + * + * Setting query string arguments MUST NOT change the URI stored by the + * request, nor the values in the server params. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated query string arguments. + * + * @param array $query Array of query string arguments, typically from + * $_GET. + * @return static + */ + public function withQueryParams(array $query); + + /** + * Retrieve normalized file upload data. + * + * This method returns upload metadata in a normalized tree, with each leaf + * an instance of Psr\Http\Message\UploadedFileInterface. + * + * These values MAY be prepared from $_FILES or the message body during + * instantiation, or MAY be injected via withUploadedFiles(). + * + * @return array An array tree of UploadedFileInterface instances; an empty + * array MUST be returned if no data is present. + */ + public function getUploadedFiles(); + + /** + * Create a new instance with the specified uploaded files. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param array $uploadedFiles An array tree of UploadedFileInterface instances. + * @return static + * @throws \InvalidArgumentException if an invalid structure is provided. + */ + public function withUploadedFiles(array $uploadedFiles); + + /** + * Retrieve any parameters provided in the request body. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, this method MUST + * return the contents of $_POST. + * + * Otherwise, this method may return any results of deserializing + * the request body content; as parsing returns structured content, the + * potential types MUST be arrays or objects only. A null value indicates + * the absence of body content. + * + * @return null|array|object The deserialized body parameters, if any. + * These will typically be an array or object. + */ + public function getParsedBody(); + + /** + * Return an instance with the specified body parameters. + * + * These MAY be injected during instantiation. + * + * If the request Content-Type is either application/x-www-form-urlencoded + * or multipart/form-data, and the request method is POST, use this method + * ONLY to inject the contents of $_POST. + * + * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of + * deserializing the request body content. Deserialization/parsing returns + * structured data, and, as such, this method ONLY accepts arrays or objects, + * or a null value if nothing was available to parse. + * + * As an example, if content negotiation determines that the request data + * is a JSON payload, this method could be used to create a request + * instance with the deserialized parameters. + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated body parameters. + * + * @param null|array|object $data The deserialized body data. This will + * typically be in an array or object. + * @return static + * @throws \InvalidArgumentException if an unsupported argument type is + * provided. + */ + public function withParsedBody($data); + + /** + * Retrieve attributes derived from the request. + * + * The request "attributes" may be used to allow injection of any + * parameters derived from the request: e.g., the results of path + * match operations; the results of decrypting cookies; the results of + * deserializing non-form-encoded message bodies; etc. Attributes + * will be application and request specific, and CAN be mutable. + * + * @return array Attributes derived from the request. + */ + public function getAttributes(); + + /** + * Retrieve a single derived request attribute. + * + * Retrieves a single derived request attribute as described in + * getAttributes(). If the attribute has not been previously set, returns + * the default value as provided. + * + * This method obviates the need for a hasAttribute() method, as it allows + * specifying a default value to return if the attribute is not found. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $default Default value to return if the attribute does not exist. + * @return mixed + */ + public function getAttribute($name, $default = null); + + /** + * Return an instance with the specified derived request attribute. + * + * This method allows setting a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that has the + * updated attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @param mixed $value The value of the attribute. + * @return static + */ + public function withAttribute($name, $value); + + /** + * Return an instance that removes the specified derived request attribute. + * + * This method allows removing a single derived request attribute as + * described in getAttributes(). + * + * This method MUST be implemented in such a way as to retain the + * immutability of the message, and MUST return an instance that removes + * the attribute. + * + * @see getAttributes() + * @param string $name The attribute name. + * @return static + */ + public function withoutAttribute($name); +} diff --git a/videodb/vendor/psr/http-message/src/StreamInterface.php b/videodb/vendor/psr/http-message/src/StreamInterface.php new file mode 100644 index 0000000..f68f391 --- /dev/null +++ b/videodb/vendor/psr/http-message/src/StreamInterface.php @@ -0,0 +1,158 @@ + + * [user-info@]host[:port] + * + * + * If the port component is not set or is the standard port for the current + * scheme, it SHOULD NOT be included. + * + * @see https://tools.ietf.org/html/rfc3986#section-3.2 + * @return string The URI authority, in "[user-info@]host[:port]" format. + */ + public function getAuthority(); + + /** + * Retrieve the user information component of the URI. + * + * If no user information is present, this method MUST return an empty + * string. + * + * If a user is present in the URI, this will return that value; + * additionally, if the password is also present, it will be appended to the + * user value, with a colon (":") separating the values. + * + * The trailing "@" character is not part of the user information and MUST + * NOT be added. + * + * @return string The URI user information, in "username[:password]" format. + */ + public function getUserInfo(); + + /** + * Retrieve the host component of the URI. + * + * If no host is present, this method MUST return an empty string. + * + * The value returned MUST be normalized to lowercase, per RFC 3986 + * Section 3.2.2. + * + * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 + * @return string The URI host. + */ + public function getHost(); + + /** + * Retrieve the port component of the URI. + * + * If a port is present, and it is non-standard for the current scheme, + * this method MUST return it as an integer. If the port is the standard port + * used with the current scheme, this method SHOULD return null. + * + * If no port is present, and no scheme is present, this method MUST return + * a null value. + * + * If no port is present, but a scheme is present, this method MAY return + * the standard port for that scheme, but SHOULD return null. + * + * @return null|int The URI port. + */ + public function getPort(); + + /** + * Retrieve the path component of the URI. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * Normally, the empty path "" and absolute path "/" are considered equal as + * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically + * do this normalization because in contexts with a trimmed base path, e.g. + * the front controller, this difference becomes significant. It's the task + * of the user to handle both "" and "/". + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.3. + * + * As an example, if the value should include a slash ("/") not intended as + * delimiter between path segments, that value MUST be passed in encoded + * form (e.g., "%2F") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.3 + * @return string The URI path. + */ + public function getPath(); + + /** + * Retrieve the query string of the URI. + * + * If no query string is present, this method MUST return an empty string. + * + * The leading "?" character is not part of the query and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.4. + * + * As an example, if a value in a key/value pair of the query string should + * include an ampersand ("&") not intended as a delimiter between values, + * that value MUST be passed in encoded form (e.g., "%26") to the instance. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.4 + * @return string The URI query string. + */ + public function getQuery(); + + /** + * Retrieve the fragment component of the URI. + * + * If no fragment is present, this method MUST return an empty string. + * + * The leading "#" character is not part of the fragment and MUST NOT be + * added. + * + * The value returned MUST be percent-encoded, but MUST NOT double-encode + * any characters. To determine what characters to encode, please refer to + * RFC 3986, Sections 2 and 3.5. + * + * @see https://tools.ietf.org/html/rfc3986#section-2 + * @see https://tools.ietf.org/html/rfc3986#section-3.5 + * @return string The URI fragment. + */ + public function getFragment(); + + /** + * Return an instance with the specified scheme. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified scheme. + * + * Implementations MUST support the schemes "http" and "https" case + * insensitively, and MAY accommodate other schemes if required. + * + * An empty scheme is equivalent to removing the scheme. + * + * @param string $scheme The scheme to use with the new instance. + * @return static A new instance with the specified scheme. + * @throws \InvalidArgumentException for invalid or unsupported schemes. + */ + public function withScheme($scheme); + + /** + * Return an instance with the specified user information. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified user information. + * + * Password is optional, but the user information MUST include the + * user; an empty string for the user is equivalent to removing user + * information. + * + * @param string $user The user name to use for authority. + * @param null|string $password The password associated with $user. + * @return static A new instance with the specified user information. + */ + public function withUserInfo($user, $password = null); + + /** + * Return an instance with the specified host. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified host. + * + * An empty host value is equivalent to removing the host. + * + * @param string $host The hostname to use with the new instance. + * @return static A new instance with the specified host. + * @throws \InvalidArgumentException for invalid hostnames. + */ + public function withHost($host); + + /** + * Return an instance with the specified port. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified port. + * + * Implementations MUST raise an exception for ports outside the + * established TCP and UDP port ranges. + * + * A null value provided for the port is equivalent to removing the port + * information. + * + * @param null|int $port The port to use with the new instance; a null value + * removes the port information. + * @return static A new instance with the specified port. + * @throws \InvalidArgumentException for invalid ports. + */ + public function withPort($port); + + /** + * Return an instance with the specified path. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified path. + * + * The path can either be empty or absolute (starting with a slash) or + * rootless (not starting with a slash). Implementations MUST support all + * three syntaxes. + * + * If the path is intended to be domain-relative rather than path relative then + * it must begin with a slash ("/"). Paths not starting with a slash ("/") + * are assumed to be relative to some base path known to the application or + * consumer. + * + * Users can provide both encoded and decoded path characters. + * Implementations ensure the correct encoding as outlined in getPath(). + * + * @param string $path The path to use with the new instance. + * @return static A new instance with the specified path. + * @throws \InvalidArgumentException for invalid paths. + */ + public function withPath($path); + + /** + * Return an instance with the specified query string. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified query string. + * + * Users can provide both encoded and decoded query characters. + * Implementations ensure the correct encoding as outlined in getQuery(). + * + * An empty query string value is equivalent to removing the query string. + * + * @param string $query The query string to use with the new instance. + * @return static A new instance with the specified query string. + * @throws \InvalidArgumentException for invalid query strings. + */ + public function withQuery($query); + + /** + * Return an instance with the specified URI fragment. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified URI fragment. + * + * Users can provide both encoded and decoded fragment characters. + * Implementations ensure the correct encoding as outlined in getFragment(). + * + * An empty fragment value is equivalent to removing the fragment. + * + * @param string $fragment The fragment to use with the new instance. + * @return static A new instance with the specified fragment. + */ + public function withFragment($fragment); + + /** + * Return the string representation as a URI reference. + * + * Depending on which components of the URI are present, the resulting + * string is either a full URI or relative reference according to RFC 3986, + * Section 4.1. The method concatenates the various components of the URI, + * using the appropriate delimiters: + * + * - If a scheme is present, it MUST be suffixed by ":". + * - If an authority is present, it MUST be prefixed by "//". + * - The path can be concatenated without delimiters. But there are two + * cases where the path has to be adjusted to make the URI reference + * valid as PHP does not allow to throw an exception in __toString(): + * - If the path is rootless and an authority is present, the path MUST + * be prefixed by "/". + * - If the path is starting with more than one "/" and no authority is + * present, the starting slashes MUST be reduced to one. + * - If a query is present, it MUST be prefixed by "?". + * - If a fragment is present, it MUST be prefixed by "#". + * + * @see http://tools.ietf.org/html/rfc3986#section-4.1 + * @return string + */ + public function __toString(); +} diff --git a/videodb/vendor/ralouphie/getallheaders/LICENSE b/videodb/vendor/ralouphie/getallheaders/LICENSE new file mode 100644 index 0000000..be5540c --- /dev/null +++ b/videodb/vendor/ralouphie/getallheaders/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Ralph Khattar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/videodb/vendor/ralouphie/getallheaders/README.md b/videodb/vendor/ralouphie/getallheaders/README.md new file mode 100644 index 0000000..9430d76 --- /dev/null +++ b/videodb/vendor/ralouphie/getallheaders/README.md @@ -0,0 +1,27 @@ +getallheaders +============= + +PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3. + +[![Build Status](https://travis-ci.org/ralouphie/getallheaders.svg?branch=master)](https://travis-ci.org/ralouphie/getallheaders) +[![Coverage Status](https://coveralls.io/repos/ralouphie/getallheaders/badge.png?branch=master)](https://coveralls.io/r/ralouphie/getallheaders?branch=master) +[![Latest Stable Version](https://poser.pugx.org/ralouphie/getallheaders/v/stable.png)](https://packagist.org/packages/ralouphie/getallheaders) +[![Latest Unstable Version](https://poser.pugx.org/ralouphie/getallheaders/v/unstable.png)](https://packagist.org/packages/ralouphie/getallheaders) +[![License](https://poser.pugx.org/ralouphie/getallheaders/license.png)](https://packagist.org/packages/ralouphie/getallheaders) + + +This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php). + +## Install + +For PHP version **`>= 5.6`**: + +``` +composer require ralouphie/getallheaders +``` + +For PHP version **`< 5.6`**: + +``` +composer require ralouphie/getallheaders "^2" +``` diff --git a/videodb/vendor/ralouphie/getallheaders/composer.json b/videodb/vendor/ralouphie/getallheaders/composer.json new file mode 100644 index 0000000..de8ce62 --- /dev/null +++ b/videodb/vendor/ralouphie/getallheaders/composer.json @@ -0,0 +1,26 @@ +{ + "name": "ralouphie/getallheaders", + "description": "A polyfill for getallheaders.", + "license": "MIT", + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "require": { + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "^5 || ^6.5", + "php-coveralls/php-coveralls": "^2.1" + }, + "autoload": { + "files": ["src/getallheaders.php"] + }, + "autoload-dev": { + "psr-4": { + "getallheaders\\Tests\\": "tests/" + } + } +} diff --git a/videodb/vendor/ralouphie/getallheaders/src/getallheaders.php b/videodb/vendor/ralouphie/getallheaders/src/getallheaders.php new file mode 100644 index 0000000..c7285a5 --- /dev/null +++ b/videodb/vendor/ralouphie/getallheaders/src/getallheaders.php @@ -0,0 +1,46 @@ + 'Content-Type', + 'CONTENT_LENGTH' => 'Content-Length', + 'CONTENT_MD5' => 'Content-Md5', + ); + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) === 'HTTP_') { + $key = substr($key, 5); + if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) { + $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key)))); + $headers[$key] = $value; + } + } elseif (isset($copy_server[$key])) { + $headers[$copy_server[$key]] = $value; + } + } + + if (!isset($headers['Authorization'])) { + if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { + $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; + } elseif (isset($_SERVER['PHP_AUTH_USER'])) { + $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass); + } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) { + $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST']; + } + } + + return $headers; + } + +} diff --git a/videodb/vendor/setasign/fpdf/FAQ.htm b/videodb/vendor/setasign/fpdf/FAQ.htm new file mode 100644 index 0000000..6379a4b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/FAQ.htm @@ -0,0 +1,270 @@ + + + + +FAQ + + + + +

        FAQ

        +
        + +
          +
        • +

          1. What's exactly the license of FPDF? Are there any usage restrictions?

          +FPDF is released under a permissive license: there is no usage restriction. You may embed it +freely in your application (commercial or not), with or without modifications. +
        • + +
        • +

          2. I get the following error when I try to generate a PDF: Some data has already been output, can't send PDF file

          +You must send nothing to the browser except the PDF itself: no HTML, no space, no carriage return. A common +case is having extra blank at the end of an included script file.
          +
          +The message may be followed by this indication:
          +
          +(output started at script.php:X)
          +
          +which gives you exactly the script and line number responsible for the output. If you don't see it, +try adding this line at the very beginning of your script: +
          +
          ob_end_clean();
          +
          +
        • + +
        • +

          3. Accented letters are replaced with some strange characters like é.

          +Don't use UTF-8 with the standard fonts; they expect text encoded in windows-1252. +You can perform a conversion with iconv: +
          +
          $str = iconv('UTF-8', 'windows-1252', $str);
          +
          +Or with mbstring: +
          +
          $str = mb_convert_encoding($str, 'windows-1252', 'UTF-8');
          +
          +In case you need characters outside windows-1252, take a look at tutorial #7 or +tFPDF. +
        • + +
        • +

          4. I try to display the Euro symbol but it doesn't work.

          +The standard fonts have the Euro character at position 128. You can define a constant like this +for convenience: +
          +
          define('EURO', chr(128));
          +
          +
        • + +
        • +

          5. I try to display a variable in the Header method but nothing prints.

          +You have to use the global keyword to access global variables, for example: +
          +
          function Header()
          +{
          +    global $title;
          +
          +    $this->SetFont('Arial', 'B', 15);
          +    $this->Cell(0, 10, $title, 1, 1, 'C');
          +}
          +
          +$title = 'My title';
          +
          +Alternatively, you can use an object property: +
          +
          function Header()
          +{
          +    $this->SetFont('Arial', 'B', 15);
          +    $this->Cell(0, 10, $this->title, 1, 1, 'C');
          +}
          +
          +$pdf->title = 'My title';
          +
          +
        • + +
        • +

          6. I have defined the Header and Footer methods in my PDF class but nothing shows.

          +You have to create an object from the PDF class, not FPDF: +
          +
          $pdf = new PDF();
          +
          +
        • + +
        • +

          7. I can't make line breaks work. I put \n in the string printed by MultiCell but it doesn't work.

          +You have to enclose your string with double quotes, not single ones. +
        • + +
        • +

          8. I use jQuery to generate the PDF but it doesn't show.

          +Don't use an AJAX request to retrieve the PDF. +
        • + +
        • +

          9. I draw a frame with very precise dimensions, but when printed I notice some differences.

          +To respect dimensions, select "None" for the Page Scaling setting instead of "Shrink to Printable Area" in the print dialog box. +
        • + +
        • +

          10. I'd like to use the whole surface of the page, but when printed I always have some margins. How can I get rid of them?

          +Printers have physical margins (different depending on the models); it is therefore impossible to remove +them and print on the whole surface of the paper. +
        • + +
        • +

          11. How can I put a background in my PDF?

          +For a picture, call Image() in the Header() method, before any other output. To set a background color, use Rect(). +
        • + +
        • +

          12. How can I set a specific header or footer on the first page?

          +Just test the page number: +
          +
          function Header()
          +{
          +    if($this->PageNo()==1)
          +    {
          +        //First page
          +        ...
          +    }
          +    else
          +    {
          +        //Other pages
          +        ...
          +    }
          +}
          +
          +
        • + +
        • +

          13. I'd like to use extensions provided by different scripts. How can I combine them?

          +Use an inheritance chain. If you have two classes, say A in a.php: +
          +
          require('fpdf.php');
          +
          +class A extends FPDF
          +{
          +...
          +}
          +
          +and B in b.php: +
          +
          require('fpdf.php');
          +
          +class B extends FPDF
          +{
          +...
          +}
          +
          +then make B extend A: +
          +
          require('a.php');
          +
          +class B extends A
          +{
          +...
          +}
          +
          +and make your own class extend B: +
          +
          require('b.php');
          +
          +class PDF extends B
          +{
          +...
          +}
          +
          +$pdf = new PDF();
          +
          +
        • + +
        • +

          14. How can I open the PDF in a new tab?

          +Just do the same as you would for an HTML page or anything else: add a target="_blank" to your link or form. +
        • + +
        • +

          15. How can I send the PDF by email?

          +As for any other file, but an easy way is to use PHPMailer and +its in-memory attachment: +
          +
          $mail = new PHPMailer();
          +...
          +$doc = $pdf->Output('S');
          +$mail->AddStringAttachment($doc, 'doc.pdf', 'base64', 'application/pdf');
          +$mail->Send();
          +
          +
        • + +
        • +

          16. What's the limit of the file sizes I can generate with FPDF?

          +There is no particular limit. There are some constraints, however: +
          +
          +- There is usually a maximum memory size allocated to PHP scripts. For very big documents, +especially with images, the limit may be reached (the file being built in memory). The +parameter is configured in the php.ini file. +
          +
          +- The maximum execution time allocated to scripts defaults to 30 seconds. This limit can of course +be easily reached. It is configured in php.ini and may be altered dynamically with set_time_limit(). +
          +
          +You can work around the memory limit with this script. +
        • + +
        • +

          17. Can I modify a PDF with FPDF?

          +It's possible to import pages from an existing PDF document thanks to the +FPDI extension. +Then you can add some content to them. +
        • + +
        • +

          18. I'd like to make a search engine in PHP and index PDF files. Can I do it with FPDF?

          +No. But a GPL C utility does exist, pdftotext, which is able to extract the textual content from a PDF. +It's provided with the Xpdf package. +
        • + +
        • +

          19. Can I convert an HTML page to PDF with FPDF?

          +Not real-world pages. But a GPL C utility does exist, HTMLDOC, +which allows to do it and gives good results. +
        • + +
        • +

          20. Can I concatenate PDF files with FPDF?

          +Not directly, but it's possible to use FPDI +to perform that task. Some free command-line tools also exist: +pdftk and +mbtPdfAsm. +
        • +
        + + diff --git a/videodb/vendor/setasign/fpdf/README.md b/videodb/vendor/setasign/fpdf/README.md new file mode 100644 index 0000000..195029e --- /dev/null +++ b/videodb/vendor/setasign/fpdf/README.md @@ -0,0 +1,21 @@ +# FPDF +**This repository is only made for cloning official FPDF releases which are available at: http://www.fpdf.org** +**THERE WILL BE NO DEVELOPMENT IN THIS REPOSITORY!** + +FPDF is a PHP class which allows to generate PDF files with pure PHP. F from FPDF stands for Free: you may use it for any kind of usage and modify it to suit your needs. + +## Installation with [Composer](https://packagist.org/packages/setasign/fpdf) + +If you're using Composer to manage dependencies, you can use + + $ composer require setasign/fpdf:^1.8 + +or you can include the following in your composer.json file: + +```json +{ + "require": { + "setasign/fpdf": "^1.8" + } +} +``` diff --git a/videodb/vendor/setasign/fpdf/changelog.htm b/videodb/vendor/setasign/fpdf/changelog.htm new file mode 100644 index 0000000..b296def --- /dev/null +++ b/videodb/vendor/setasign/fpdf/changelog.htm @@ -0,0 +1,183 @@ + + + + +Changelog + + + + +

        Changelog

        +
        +
        v1.85 (2022-11-10)
        +
        +- Removed deprecation notices on PHP 8.2.
        +- Removed notices when passing null values instead of strings.
        +- The FPDF_VERSION constant was replaced by a class constant.
        +- The creation date of the PDF now includes the timezone.
        +- The content-type is now always application/pdf, even for downloads.
        +
        +
        v1.84 (2021-08-28)
        +
        +- Fixed an issue related to annotations.
        +
        +
        v1.83 (2021-04-18)
        +
        +- Fixed an issue related to annotations.
        +
        +
        v1.82 (2019-12-07)
        +
        +- Removed a deprecation notice on PHP 7.4.
        +
        +
        v1.81 (2015-12-20)
        +
        +- Added GetPageWidth() and GetPageHeight().
        +- Fixed a bug in SetXY().
        +
        +
        v1.8 (2015-11-29)
        +
        +- PHP 5.1.0 or higher is now required.
        +- The MakeFont utility now subsets fonts, which can greatly reduce font sizes.
        +- Added ToUnicode CMaps to improve text extraction.
        +- Added a parameter to AddPage() to rotate the page.
        +- Added a parameter to SetY() to indicate whether the x position should be reset or not.
        +- Added a parameter to Output() to specify the encoding of the name, and special characters are now properly encoded. Additionally the order of the first two parameters was reversed to be more logical (however the old order is still supported for compatibility).
        +- The Error() method now throws an exception.
        +- Adding contents before the first AddPage() or after Close() now raises an error.
        +- Outputting text with no font selected now raises an error.
        +
        +
        v1.7 (2011-06-18)
        +
        +- The MakeFont utility has been completely rewritten and doesn't depend on ttf2pt1 anymore.
        +- Alpha channel is now supported for PNGs.
        +- When inserting an image, it's now possible to specify its resolution.
        +- Default resolution for images was increased from 72 to 96 dpi.
        +- When inserting a GIF image, no temporary file is used anymore if the PHP version is 5.1 or higher.
        +- When output buffering is enabled and the PDF is about to be sent, the buffer is now cleared if it contains only a UTF-8 BOM and/or whitespace (instead of throwing an error).
        +- Symbol and ZapfDingbats fonts now support underline style.
        +- Custom page sizes are now checked to ensure that width is smaller than height.
        +- Standard font files were changed to use the same format as user fonts.
        +- A bug in the embedding of Type1 fonts was fixed.
        +- A bug related to SetDisplayMode() and the current locale was fixed.
        +- A display issue occurring with the Adobe Reader X plug-in was fixed.
        +- An issue related to transparency with some versions of Adobe Reader was fixed.
        +- The Content-Length header was removed because it caused an issue when the HTTP server applies compression.
        +
        +
        v1.6 (2008-08-03)
        +
        +- PHP 4.3.10 or higher is now required.
        +- GIF image support.
        +- Images can now trigger page breaks.
        +- Possibility to have different page formats in a single document.
        +- Document properties (author, creator, keywords, subject and title) can now be specified in UTF-8.
        +- Fixed a bug: when a PNG was inserted through a URL, an error sometimes occurred.
        +- An automatic page break in Header() doesn't cause an infinite loop any more.
        +- Removed some warning messages appearing with recent PHP versions.
        +- Added HTTP headers to reduce problems with IE.
        +
        +
        v1.53 (2004-12-31)
        +
        +- When the font subdirectory is in the same directory as fpdf.php, it's no longer necessary to define the FPDF_FONTPATH constant.
        +- The array $HTTP_SERVER_VARS is no longer used. It could cause trouble on PHP5-based configurations with the register_long_arrays option disabled.
        +- Fixed a problem related to Type1 font embedding which caused trouble to some PDF processors.
        +- The file name sent to the browser could not contain a space character.
        +- The Cell() method could not print the number 0 (you had to pass the string '0').
        +
        +
        v1.52 (2003-12-30)
        +
        +- Image() now displays the image at 72 dpi if no dimension is given.
        +- Output() takes a string as second parameter to indicate destination.
        +- Open() is now called automatically by AddPage().
        +- Inserting remote JPEG images doesn't generate an error any longer.
        +- Decimal separator is forced to dot in the constructor.
        +- Added several encodings (Turkish, Thai, Hebrew, Ukrainian and Vietnamese).
        +- The last line of a right-aligned MultiCell() was not correctly aligned if it was terminated by a carriage return.
        +- No more error message about already sent headers when outputting the PDF to the standard output from the command line.
        +- The underlining was going too far for text containing characters \, ( or ).
        +- $HTTP_ENV_VARS has been replaced by $HTTP_SERVER_VARS.
        +
        +
        v1.51 (2002-08-03)
        +
        +- Type1 font support.
        +- Added Baltic encoding.
        +- The class now works internally in points with the origin at the bottom in order to avoid two bugs occurring with Acrobat 5:
          * The line thickness was too large when printed on Windows 98 SE and ME.
          * TrueType fonts didn't appear immediately inside the plug-in (a substitution font was used), one had to cause a window refresh to make them show up.
        +- It's no longer necessary to set the decimal separator as dot to produce valid documents.
        +- The clickable area in a cell was always on the left independently from the text alignment.
        +- JPEG images in CMYK mode appeared in inverted colors.
        +- Transparent PNG images in grayscale or true color mode were incorrectly handled.
        +- Adding new fonts now works correctly even with the magic_quotes_runtime option set to on.
        +
        +
        v1.5 (2002-05-28)
        +
        +- TrueType font (AddFont()) and encoding support (Western and Eastern Europe, Cyrillic and Greek).
        +- Added Write() method.
        +- Added underlined style.
        +- Internal and external link support (AddLink(), SetLink(), Link()).
        +- Added right margin management and methods SetRightMargin(), SetTopMargin().
        +- Modification of SetDisplayMode() to select page layout.
        +- The border parameter of MultiCell() now lets choose borders to draw as Cell().
        +- When a document contains no page, Close() now calls AddPage() instead of causing a fatal error.
        +
        +
        v1.41 (2002-03-13)
        +
        +- Fixed SetDisplayMode() which no longer worked (the PDF viewer used its default display).
        +
        +
        v1.4 (2002-03-02)
        +
        +- PHP3 is no longer supported.
        +- Page compression (SetCompression()).
        +- Choice of page format and possibility to change orientation inside document.
        +- Added AcceptPageBreak() method.
        +- Ability to print the total number of pages (AliasNbPages()).
        +- Choice of cell borders to draw.
        +- New mode for Cell(): the current position can now move under the cell.
        +- Ability to include an image by specifying height only (width is calculated automatically).
        +- Fixed a bug: when a justified line triggered a page break, the footer inherited the corresponding word spacing.
        +
        +
        v1.31 (2002-01-12)
        +
        +- Fixed a bug in drawing frame with MultiCell(): the last line always started from the left margin.
        +- Removed Expires HTTP header (gives trouble in some situations).
        +- Added Content-disposition HTTP header (seems to help in some situations).
        +
        +
        v1.3 (2001-12-03)
        +
        +- Line break and text justification support (MultiCell()).
        +- Color support (SetDrawColor(), SetFillColor(), SetTextColor()). Possibility to draw filled rectangles and paint cell background.
        +- A cell whose width is declared null extends up to the right margin of the page.
        +- Line width is now retained from page to page and defaults to 0.2 mm.
        +- Added SetXY() method.
        +- Fixed a passing by reference done in a deprecated manner for PHP4.
        +
        +
        v1.2 (2001-11-11)
        +
        +- Added font metric files and GetStringWidth() method.
        +- Centering and right-aligning text in cells.
        +- Display mode control (SetDisplayMode()).
        +- Added methods to set document properties (SetAuthor(), SetCreator(), SetKeywords(), SetSubject(), SetTitle()).
        +- Possibility to force PDF download by browser.
        +- Added SetX() and GetX() methods.
        +- During automatic page break, current abscissa is now retained.
        +
        +
        v1.11 (2001-10-20)
        +
        +- PNG support doesn't require PHP4/zlib any more. Data are now put directly into PDF without any decompression/recompression stage.
        +- Image insertion now works correctly even with magic_quotes_runtime option set to on.
        +
        +
        v1.1 (2001-10-07)
        +
        +- JPEG and PNG image support.
        +
        +
        v1.01 (2001-10-03)
        +
        +- Fixed a bug involving page break: in case when Header() doesn't specify a font, the one from previous page was not restored and produced an incorrect document.
        +
        +
        v1.0 (2001-09-17)
        +
        +- First version.
        +
        +
        + + diff --git a/videodb/vendor/setasign/fpdf/composer.json b/videodb/vendor/setasign/fpdf/composer.json new file mode 100644 index 0000000..6a88bd4 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/composer.json @@ -0,0 +1,24 @@ +{ + "name": "setasign/fpdf", + "homepage": "http://www.fpdf.org", + "description": "FPDF is a PHP class which allows to generate PDF files with pure PHP. F from FPDF stands for Free: you may use it for any kind of usage and modify it to suit your needs.", + "type": "library", + "keywords": ["pdf", "fpdf"], + "license": "MIT", + "authors": [ + { + "name": "Olivier Plathey", + "email": "oliver@fpdf.org", + "homepage": "http://fpdf.org/" + } + ], + "autoload": { + "classmap": [ + "fpdf.php" + ] + }, + "require": { + "ext-zlib": "*", + "ext-gd": "*" + } +} diff --git a/videodb/vendor/setasign/fpdf/doc/__construct.htm b/videodb/vendor/setasign/fpdf/doc/__construct.htm new file mode 100644 index 0000000..f0f3520 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/__construct.htm @@ -0,0 +1,63 @@ + + + + +__construct + + + +

        __construct

        +__construct([string orientation [, string unit [, mixed size]]]) +

        Description

        +This is the class constructor. It allows to set up the page size, the orientation and the +unit of measure used in all methods (except for font sizes). +

        Parameters

        +
        +
        orientation
        +
        +Default page orientation. Possible values are (case insensitive): +
          +
        • P or Portrait
        • +
        • L or Landscape
        • +
        +Default value is P. +
        +
        unit
        +
        +User unit. Possible values are: +
          +
        • pt: point
        • +
        • mm: millimeter
        • +
        • cm: centimeter
        • +
        • in: inch
        • +
        +A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This +is a very common unit in typography; font sizes are expressed in that unit. +
        +
        +Default value is mm. +
        +
        size
        +
        +The size used for pages. It can be either one of the following values (case insensitive): +
          +
        • A3
        • +
        • A4
        • +
        • A5
        • +
        • Letter
        • +
        • Legal
        • +
        +or an array containing the width and the height (expressed in the unit given by unit).
        +
        +Default value is A4. +
        +
        +

        Example

        +Document with a custom 100x150 mm page size: +
        +
        $pdf = new FPDF('P', 'mm', array(100,150));
        +
        +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/acceptpagebreak.htm b/videodb/vendor/setasign/fpdf/doc/acceptpagebreak.htm new file mode 100644 index 0000000..bb1aaa1 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/acceptpagebreak.htm @@ -0,0 +1,63 @@ + + + + +AcceptPageBreak + + + +

        AcceptPageBreak

        +boolean AcceptPageBreak() +

        Description

        +Whenever a page break condition is met, the method is called, and the break is issued or not +depending on the returned value. The default implementation returns a value according to the +mode selected by SetAutoPageBreak(). +
        +This method is called automatically and should not be called directly by the application. +

        Example

        +The method is overriden in an inherited class in order to obtain a 3 column layout: +
        +
        class PDF extends FPDF
        +{
        +    protected $col = 0;
        +
        +    function SetCol($col)
        +    {
        +        // Move position to a column
        +        $this->col = $col;
        +        $x = 10 + $col*65;
        +        $this->SetLeftMargin($x);
        +        $this->SetX($x);
        +    }
        +
        +    function AcceptPageBreak()
        +    {
        +        if($this->col<2)
        +        {
        +            // Go to next column
        +            $this->SetCol($this->col+1);
        +            $this->SetY(10);
        +            return false;
        +        }
        +        else
        +        {
        +            // Go back to first column and issue page break
        +            $this->SetCol(0);
        +            return true;
        +        }
        +    }
        +}
        +
        +$pdf = new PDF();
        +$pdf->AddPage();
        +$pdf->SetFont('Arial', '', 12);
        +for($i=1;$i<=300;$i++)
        +    $pdf->Cell(0, 5, "Line $i", 0, 1);
        +$pdf->Output();
        +
        +

        See also

        +SetAutoPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/addfont.htm b/videodb/vendor/setasign/fpdf/doc/addfont.htm new file mode 100644 index 0000000..128e59c --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/addfont.htm @@ -0,0 +1,55 @@ + + + + +AddFont + + + +

        AddFont

        +AddFont(string family [, string style [, string file]]) +

        Description

        +Imports a TrueType, OpenType or Type1 font and makes it available. It is necessary to generate a font +definition file first with the MakeFont utility. +
        +The definition file (and the font file itself when embedding) must be present in the font directory. +If it is not found, the error "Could not include font definition file" is raised. +

        Parameters

        +
        +
        family
        +
        +Font family. The name can be chosen arbitrarily. If it is a standard family name, it will +override the corresponding font. +
        +
        style
        +
        +Font style. Possible values are (case insensitive): +
          +
        • empty string: regular
        • +
        • B: bold
        • +
        • I: italic
        • +
        • BI or IB: bold italic
        • +
        +The default value is regular. +
        +
        file
        +
        +The font definition file. +
        +By default, the name is built from the family and style, in lower case with no space. +
        +
        +

        Example

        +
        +
        $pdf->AddFont('Comic', 'I');
        +
        +is equivalent to: +
        +
        $pdf->AddFont('Comic', 'I', 'comici.php');
        +
        +

        See also

        +SetFont +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/addlink.htm b/videodb/vendor/setasign/fpdf/doc/addlink.htm new file mode 100644 index 0000000..9bc3276 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/addlink.htm @@ -0,0 +1,26 @@ + + + + +AddLink + + + +

        AddLink

        +int AddLink() +

        Description

        +Creates a new internal link and returns its identifier. An internal link is a clickable area +which directs to another place within the document. +
        +The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is +defined with SetLink(). +

        See also

        +Cell, +Write, +Image, +Link, +SetLink +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/addpage.htm b/videodb/vendor/setasign/fpdf/doc/addpage.htm new file mode 100644 index 0000000..7d9ebe1 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/addpage.htm @@ -0,0 +1,61 @@ + + + + +AddPage + + + +

        AddPage

        +AddPage([string orientation [, mixed size [, int rotation]]]) +

        Description

        +Adds a new page to the document. If a page is already present, the Footer() method is called +first to output the footer. Then the page is added, the current position set to the top-left +corner according to the left and top margins, and Header() is called to display the header. +
        +The font which was set before calling is automatically restored. There is no need to call +SetFont() again if you want to continue with the same font. The same is true for colors and +line width. +
        +The origin of the coordinate system is at the top-left corner and increasing ordinates go +downwards. +

        Parameters

        +
        +
        orientation
        +
        +Page orientation. Possible values are (case insensitive): +
          +
        • P or Portrait
        • +
        • L or Landscape
        • +
        +The default value is the one passed to the constructor. +
        +
        size
        +
        +Page size. It can be either one of the following values (case insensitive): +
          +
        • A3
        • +
        • A4
        • +
        • A5
        • +
        • Letter
        • +
        • Legal
        • +
        +or an array containing the width and the height (expressed in user unit).
        +
        +The default value is the one passed to the constructor. +
        +
        rotation
        +
        +Angle by which to rotate the page. It must be a multiple of 90; positive values +mean clockwise rotation. The default value is 0. +
        +
        +

        See also

        +__construct, +Header, +Footer, +SetMargins +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/aliasnbpages.htm b/videodb/vendor/setasign/fpdf/doc/aliasnbpages.htm new file mode 100644 index 0000000..d675efb --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/aliasnbpages.htm @@ -0,0 +1,45 @@ + + + + +AliasNbPages + + + +

        AliasNbPages

        +AliasNbPages([string alias]) +

        Description

        +Defines an alias for the total number of pages. It will be substituted as the document is +closed. +

        Parameters

        +
        +
        alias
        +
        +The alias. Default value: {nb}. +
        +
        +

        Example

        +
        +
        class PDF extends FPDF
        +{
        +    function Footer()
        +    {
        +        // Go to 1.5 cm from bottom
        +        $this->SetY(-15);
        +        // Select Arial italic 8
        +        $this->SetFont('Arial', 'I', 8);
        +        // Print current and total page numbers
        +        $this->Cell(0, 10, 'Page '.$this->PageNo().'/{nb}', 0, 0, 'C');
        +    }
        +}
        +
        +$pdf = new PDF();
        +$pdf->AliasNbPages();
        +
        +

        See also

        +PageNo, +Footer +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/cell.htm b/videodb/vendor/setasign/fpdf/doc/cell.htm new file mode 100644 index 0000000..8b69ba5 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/cell.htm @@ -0,0 +1,104 @@ + + + + +Cell + + + +

        Cell

        +Cell(float w [, float h [, string txt [, mixed border [, int ln [, string align [, boolean fill [, mixed link]]]]]]]) +

        Description

        +Prints a cell (rectangular area) with optional borders, background color and character string. +The upper-left corner of the cell corresponds to the current position. The text can be aligned +or centered. After the call, the current position moves to the right or to the next line. It is +possible to put a link on the text. +
        +If automatic page breaking is enabled and the cell goes beyond the limit, a page break is +done before outputting. +

        Parameters

        +
        +
        w
        +
        +Cell width. If 0, the cell extends up to the right margin. +
        +
        h
        +
        +Cell height. +Default value: 0. +
        +
        txt
        +
        +String to print. +Default value: empty string. +
        +
        border
        +
        +Indicates if borders must be drawn around the cell. The value can be either a number: +
          +
        • 0: no border
        • +
        • 1: frame
        • +
        +or a string containing some or all of the following characters (in any order): +
          +
        • L: left
        • +
        • T: top
        • +
        • R: right
        • +
        • B: bottom
        • +
        +Default value: 0. +
        +
        ln
        +
        +Indicates where the current position should go after the call. Possible values are: +
          +
        • 0: to the right
        • +
        • 1: to the beginning of the next line
        • +
        • 2: below
        • +
        +Putting 1 is equivalent to putting 0 and calling Ln() just after. +Default value: 0. +
        +
        align
        +
        +Allows to center or align the text. Possible values are: +
          +
        • L or empty string: left align (default value)
        • +
        • C: center
        • +
        • R: right align
        • +
        +
        +
        fill
        +
        +Indicates if the cell background must be painted (true) or transparent (false). +Default value: false. +
        +
        link
        +
        +URL or identifier returned by AddLink(). +
        +
        +

        Example

        +
        +
        // Set font
        +$pdf->SetFont('Arial', 'B', 16);
        +// Move to 8 cm to the right
        +$pdf->Cell(80);
        +// Centered text in a framed 20*10 mm cell and line break
        +$pdf->Cell(20, 10, 'Title', 1, 1, 'C');
        +
        +

        See also

        +SetFont, +SetDrawColor, +SetFillColor, +SetTextColor, +SetLineWidth, +AddLink, +Ln, +MultiCell, +Write, +SetAutoPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/close.htm b/videodb/vendor/setasign/fpdf/doc/close.htm new file mode 100644 index 0000000..933eaba --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/close.htm @@ -0,0 +1,21 @@ + + + + +Close + + + +

        Close

        +Close() +

        Description

        +Terminates the PDF document. It is not necessary to call this method explicitly because Output() +does it automatically. +
        +If the document contains no page, AddPage() is called to prevent from getting an invalid document. +

        See also

        +Output +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/error.htm b/videodb/vendor/setasign/fpdf/doc/error.htm new file mode 100644 index 0000000..95b02c6 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/error.htm @@ -0,0 +1,26 @@ + + + + +Error + + + +

        Error

        +Error(string msg) +

        Description

        +This method is automatically called in case of a fatal error; it simply throws an exception +with the provided message.
        +An inherited class may override it to customize the error handling but the method should +never return, otherwise the resulting document would probably be invalid. +

        Parameters

        +
        +
        msg
        +
        +The error message. +
        +
        +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/footer.htm b/videodb/vendor/setasign/fpdf/doc/footer.htm new file mode 100644 index 0000000..4398bd8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/footer.htm @@ -0,0 +1,35 @@ + + + + +Footer + + + +

        Footer

        +Footer() +

        Description

        +This method is used to render the page footer. It is automatically called by AddPage() and +Close() and should not be called directly by the application. The implementation in FPDF is +empty, so you have to subclass it and override the method if you want a specific processing. +

        Example

        +
        +
        class PDF extends FPDF
        +{
        +    function Footer()
        +    {
        +        // Go to 1.5 cm from bottom
        +        $this->SetY(-15);
        +        // Select Arial italic 8
        +        $this->SetFont('Arial', 'I', 8);
        +        // Print centered page number
        +        $this->Cell(0, 10, 'Page '.$this->PageNo(), 0, 0, 'C');
        +    }
        +}
        +
        +

        See also

        +Header +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/getpageheight.htm b/videodb/vendor/setasign/fpdf/doc/getpageheight.htm new file mode 100644 index 0000000..5150072 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/getpageheight.htm @@ -0,0 +1,18 @@ + + + + +GetPageHeight + + + +

        GetPageHeight

        +float GetPageHeight() +

        Description

        +Returns the current page height. +

        See also

        +GetPageWidth +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/getpagewidth.htm b/videodb/vendor/setasign/fpdf/doc/getpagewidth.htm new file mode 100644 index 0000000..2ae306b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/getpagewidth.htm @@ -0,0 +1,18 @@ + + + + +GetPageWidth + + + +

        GetPageWidth

        +float GetPageWidth() +

        Description

        +Returns the current page width. +

        See also

        +GetPageHeight +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/getstringwidth.htm b/videodb/vendor/setasign/fpdf/doc/getstringwidth.htm new file mode 100644 index 0000000..38b4875 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/getstringwidth.htm @@ -0,0 +1,23 @@ + + + + +GetStringWidth + + + +

        GetStringWidth

        +float GetStringWidth(string s) +

        Description

        +Returns the length of a string in user unit. A font must be selected. +

        Parameters

        +
        +
        s
        +
        +The string whose length is to be computed. +
        +
        +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/getx.htm b/videodb/vendor/setasign/fpdf/doc/getx.htm new file mode 100644 index 0000000..86550a3 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/getx.htm @@ -0,0 +1,20 @@ + + + + +GetX + + + +

        GetX

        +float GetX() +

        Description

        +Returns the abscissa of the current position. +

        See also

        +SetX, +GetY, +SetY +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/gety.htm b/videodb/vendor/setasign/fpdf/doc/gety.htm new file mode 100644 index 0000000..e1b387a --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/gety.htm @@ -0,0 +1,20 @@ + + + + +GetY + + + +

        GetY

        +float GetY() +

        Description

        +Returns the ordinate of the current position. +

        See also

        +SetY, +GetX, +SetX +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/header.htm b/videodb/vendor/setasign/fpdf/doc/header.htm new file mode 100644 index 0000000..984fc6a --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/header.htm @@ -0,0 +1,37 @@ + + + + +Header + + + +

        Header

        +Header() +

        Description

        +This method is used to render the page header. It is automatically called by AddPage() and +should not be called directly by the application. The implementation in FPDF is empty, so +you have to subclass it and override the method if you want a specific processing. +

        Example

        +
        +
        class PDF extends FPDF
        +{
        +    function Header()
        +    {
        +        // Select Arial bold 15
        +        $this->SetFont('Arial', 'B', 15);
        +        // Move to the right
        +        $this->Cell(80);
        +        // Framed title
        +        $this->Cell(30, 10, 'Title', 1, 0, 'C');
        +        // Line break
        +        $this->Ln(20);
        +    }
        +}
        +
        +

        See also

        +Footer +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/image.htm b/videodb/vendor/setasign/fpdf/doc/image.htm new file mode 100644 index 0000000..aad4775 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/image.htm @@ -0,0 +1,99 @@ + + + + +Image + + + +

        Image

        +Image(string file [, float x [, float y [, float w [, float h [, string type [, mixed link]]]]]]) +

        Description

        +Puts an image. The size it will take on the page can be specified in different ways: +
          +
        • explicit width and height (expressed in user unit or dpi)
        • +
        • one explicit dimension, the other being calculated automatically in order to keep the original proportions
        • +
        • no explicit dimension, in which case the image is put at 96 dpi
        • +
        +Supported formats are JPEG, PNG and GIF. The GD extension is required for GIF. +
        +
        +For JPEGs, all flavors are allowed: +
          +
        • gray scales
        • +
        • true colors (24 bits)
        • +
        • CMYK (32 bits)
        • +
        +For PNGs, are allowed: +
          +
        • gray scales on at most 8 bits (256 levels)
        • +
        • indexed colors
        • +
        • true colors (24 bits)
        • +
        +For GIFs: in case of an animated GIF, only the first frame is displayed.
        +
        +Transparency is supported.
        +
        +The format can be specified explicitly or inferred from the file extension.
        +
        +It is possible to put a link on the image.
        +
        +Remark: if an image is used several times, only one copy is embedded in the file. +

        Parameters

        +
        +
        file
        +
        +Path or URL of the image. +
        +
        x
        +
        +Abscissa of the upper-left corner. If not specified or equal to null, the current abscissa +is used. +
        +
        y
        +
        +Ordinate of the upper-left corner. If not specified or equal to null, the current ordinate +is used; moreover, a page break is triggered first if necessary (in case automatic page breaking is enabled) +and, after the call, the current ordinate is moved to the bottom of the image. +
        +
        w
        +
        +Width of the image in the page. There are three cases: +
          +
        • If the value is positive, it represents the width in user unit
        • +
        • If the value is negative, the absolute value represents the horizontal resolution in dpi
        • +
        • If the value is not specified or equal to zero, it is automatically calculated
        • +
        +
        +
        h
        +
        +Height of the image in the page. There are three cases: +
          +
        • If the value is positive, it represents the height in user unit
        • +
        • If the value is negative, the absolute value represents the vertical resolution in dpi
        • +
        • If the value is not specified or equal to zero, it is automatically calculated
        • +
        +
        +
        type
        +
        +Image format. Possible values are (case insensitive): JPG, JPEG, PNG and GIF. +If not specified, the type is inferred from the file extension. +
        +
        link
        +
        +URL or identifier returned by AddLink(). +
        +
        +

        Example

        +
        +
        // Insert a logo in the top-left corner at 300 dpi
        +$pdf->Image('logo.png', 10, 10, -300);
        +// Insert a dynamic image from a URL
        +$pdf->Image('http://chart.googleapis.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World', 60, 30, 90, 0, 'PNG');
        +
        +

        See also

        +AddLink +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/index.htm b/videodb/vendor/setasign/fpdf/doc/index.htm new file mode 100644 index 0000000..211032b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/index.htm @@ -0,0 +1,59 @@ + + + + +FPDF 1.85 Reference Manual + + + +

        FPDF 1.85 Reference Manual

        +__construct - constructor
        +AcceptPageBreak - accept or not automatic page break
        +AddFont - add a new font
        +AddLink - create an internal link
        +AddPage - add a new page
        +AliasNbPages - define an alias for number of pages
        +Cell - print a cell
        +Close - terminate the document
        +Error - fatal error
        +Footer - page footer
        +GetPageHeight - get current page height
        +GetPageWidth - get current page width
        +GetStringWidth - compute string length
        +GetX - get current x position
        +GetY - get current y position
        +Header - page header
        +Image - output an image
        +Line - draw a line
        +Link - put a link
        +Ln - line break
        +MultiCell - print text with line breaks
        +Output - save or send the document
        +PageNo - page number
        +Rect - draw a rectangle
        +SetAuthor - set the document author
        +SetAutoPageBreak - set the automatic page breaking mode
        +SetCompression - turn compression on or off
        +SetCreator - set document creator
        +SetDisplayMode - set display mode
        +SetDrawColor - set drawing color
        +SetFillColor - set filling color
        +SetFont - set font
        +SetFontSize - set font size
        +SetKeywords - associate keywords with document
        +SetLeftMargin - set left margin
        +SetLineWidth - set line width
        +SetLink - set internal link destination
        +SetMargins - set margins
        +SetRightMargin - set right margin
        +SetSubject - set document subject
        +SetTextColor - set text color
        +SetTitle - set document title
        +SetTopMargin - set top margin
        +SetX - set current x position
        +SetXY - set current x and y positions
        +SetY - set current y position and optionally reset x
        +Text - print a string
        +Write - print flowing text
        + + diff --git a/videodb/vendor/setasign/fpdf/doc/line.htm b/videodb/vendor/setasign/fpdf/doc/line.htm new file mode 100644 index 0000000..e232b4e --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/line.htm @@ -0,0 +1,38 @@ + + + + +Line + + + +

        Line

        +Line(float x1, float y1, float x2, float y2) +

        Description

        +Draws a line between two points. +

        Parameters

        +
        +
        x1
        +
        +Abscissa of first point. +
        +
        y1
        +
        +Ordinate of first point. +
        +
        x2
        +
        +Abscissa of second point. +
        +
        y2
        +
        +Ordinate of second point. +
        +
        +

        See also

        +SetLineWidth, +SetDrawColor +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/link.htm b/videodb/vendor/setasign/fpdf/doc/link.htm new file mode 100644 index 0000000..f3df2fb --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/link.htm @@ -0,0 +1,46 @@ + + + + +Link + + + +

        Link

        +Link(float x, float y, float w, float h, mixed link) +

        Description

        +Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), +Write() or Image(), but this method can be useful for instance to define a clickable area inside +an image. +

        Parameters

        +
        +
        x
        +
        +Abscissa of the upper-left corner of the rectangle. +
        +
        y
        +
        +Ordinate of the upper-left corner of the rectangle. +
        +
        w
        +
        +Width of the rectangle. +
        +
        h
        +
        +Height of the rectangle. +
        +
        link
        +
        +URL or identifier returned by AddLink(). +
        +
        +

        See also

        +AddLink, +Cell, +Write, +Image +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/ln.htm b/videodb/vendor/setasign/fpdf/doc/ln.htm new file mode 100644 index 0000000..04dbe37 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/ln.htm @@ -0,0 +1,28 @@ + + + + +Ln + + + +

        Ln

        +Ln([float h]) +

        Description

        +Performs a line break. The current abscissa goes back to the left margin and the ordinate +increases by the amount passed in parameter. +

        Parameters

        +
        +
        h
        +
        +The height of the break. +
        +By default, the value equals the height of the last printed cell. +
        +
        +

        See also

        +Cell +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/multicell.htm b/videodb/vendor/setasign/fpdf/doc/multicell.htm new file mode 100644 index 0000000..9f136ae --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/multicell.htm @@ -0,0 +1,76 @@ + + + + +MultiCell + + + +

        MultiCell

        +MultiCell(float w, float h, string txt [, mixed border [, string align [, boolean fill]]]) +

        Description

        +This method allows printing text with line breaks. They can be automatic (as soon as the +text reaches the right border of the cell) or explicit (via the \n character). As many cells +as necessary are output, one below the other. +
        +Text can be aligned, centered or justified. The cell block can be framed and the background +painted. +

        Parameters

        +
        +
        w
        +
        +Width of cells. If 0, they extend up to the right margin of the page. +
        +
        h
        +
        +Height of cells. +
        +
        txt
        +
        +String to print. +
        +
        border
        +
        +Indicates if borders must be drawn around the cell block. The value can be either a number: +
          +
        • 0: no border
        • +
        • 1: frame
        • +
        +or a string containing some or all of the following characters (in any order): +
          +
        • L: left
        • +
        • T: top
        • +
        • R: right
        • +
        • B: bottom
        • +
        +Default value: 0. +
        +
        align
        +
        +Sets the text alignment. Possible values are: +
          +
        • L: left alignment
        • +
        • C: center
        • +
        • R: right alignment
        • +
        • J: justification (default value)
        • +
        +
        +
        fill
        +
        +Indicates if the cell background must be painted (true) or transparent (false). +Default value: false. +
        +
        +

        See also

        +SetFont, +SetDrawColor, +SetFillColor, +SetTextColor, +SetLineWidth, +Cell, +Write, +SetAutoPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/output.htm b/videodb/vendor/setasign/fpdf/doc/output.htm new file mode 100644 index 0000000..8992f93 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/output.htm @@ -0,0 +1,55 @@ + + + + +Output + + + +

        Output

        +string Output([string dest [, string name [, boolean isUTF8]]]) +

        Description

        +Send the document to a given destination: browser, file or string. In the case of a browser, the +PDF viewer may be used or a download may be forced. +
        +The method first calls Close() if necessary to terminate the document. +

        Parameters

        +
        +
        dest
        +
        +Destination where to send the document. It can be one of the following: +
          +
        • I: send the file inline to the browser. The PDF viewer is used if available.
        • +
        • D: send to the browser and force a file download with the name given by name.
        • +
        • F: save to a local file with the name given by name (may include a path).
        • +
        • S: return the document as a string.
        • +
        +The default value is I. +
        +
        name
        +
        +The name of the file. It is ignored in case of destination S.
        +The default value is doc.pdf. +
        +
        isUTF8
        +
        +Indicates if name is encoded in ISO-8859-1 (false) or UTF-8 (true). +Only used for destinations I and D.
        +The default value is false. +
        +
        +

        Example

        +Save the document to a local directory: +
        +
        $pdf->Output('F', 'reports/report.pdf');
        +
        +Force a download: +
        +
        $pdf->Output('D', 'report.pdf');
        +
        +

        See also

        +Close +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/pageno.htm b/videodb/vendor/setasign/fpdf/doc/pageno.htm new file mode 100644 index 0000000..95400be --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/pageno.htm @@ -0,0 +1,18 @@ + + + + +PageNo + + + +

        PageNo

        +int PageNo() +

        Description

        +Returns the current page number. +

        See also

        +AliasNbPages +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/rect.htm b/videodb/vendor/setasign/fpdf/doc/rect.htm new file mode 100644 index 0000000..febc9ae --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/rect.htm @@ -0,0 +1,48 @@ + + + + +Rect + + + +

        Rect

        +Rect(float x, float y, float w, float h [, string style]) +

        Description

        +Outputs a rectangle. It can be drawn (border only), filled (with no border) or both. +

        Parameters

        +
        +
        x
        +
        +Abscissa of upper-left corner. +
        +
        y
        +
        +Ordinate of upper-left corner. +
        +
        w
        +
        +Width. +
        +
        h
        +
        +Height. +
        +
        style
        +
        +Style of rendering. Possible values are: +
          +
        • D or empty string: draw. This is the default value.
        • +
        • F: fill
        • +
        • DF or FD: draw and fill
        • +
        +
        +
        +

        See also

        +SetLineWidth, +SetDrawColor, +SetFillColor +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setauthor.htm b/videodb/vendor/setasign/fpdf/doc/setauthor.htm new file mode 100644 index 0000000..374134b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setauthor.htm @@ -0,0 +1,33 @@ + + + + +SetAuthor + + + +

        SetAuthor

        +SetAuthor(string author [, boolean isUTF8]) +

        Description

        +Defines the author of the document. +

        Parameters

        +
        +
        author
        +
        +The name of the author. +
        +
        isUTF8
        +
        +Indicates if the string is encoded in ISO-8859-1 (false) or UTF-8 (true).
        +Default value: false. +
        +
        +

        See also

        +SetCreator, +SetKeywords, +SetSubject, +SetTitle +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setautopagebreak.htm b/videodb/vendor/setasign/fpdf/doc/setautopagebreak.htm new file mode 100644 index 0000000..8c0c6f2 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setautopagebreak.htm @@ -0,0 +1,33 @@ + + + + +SetAutoPageBreak + + + +

        SetAutoPageBreak

        +SetAutoPageBreak(boolean auto [, float margin]) +

        Description

        +Enables or disables the automatic page breaking mode. When enabling, the second parameter is +the distance from the bottom of the page that defines the triggering limit. By default, the +mode is on and the margin is 2 cm. +

        Parameters

        +
        +
        auto
        +
        +Boolean indicating if mode should be on or off. +
        +
        margin
        +
        +Distance from the bottom of the page. +
        +
        +

        See also

        +Cell, +MultiCell, +AcceptPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setcompression.htm b/videodb/vendor/setasign/fpdf/doc/setcompression.htm new file mode 100644 index 0000000..ec506d9 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setcompression.htm @@ -0,0 +1,31 @@ + + + + +SetCompression + + + +

        SetCompression

        +SetCompression(boolean compress) +

        Description

        +Activates or deactivates page compression. When activated, the internal representation of +each page is compressed, which leads to a compression ratio of about 2 for the resulting +document. +
        +Compression is on by default. +
        +
        +Note: the Zlib extension is required for this feature. If not present, compression +will be turned off. +

        Parameters

        +
        +
        compress
        +
        +Boolean indicating if compression must be enabled. +
        +
        +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setcreator.htm b/videodb/vendor/setasign/fpdf/doc/setcreator.htm new file mode 100644 index 0000000..1cf2ed8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setcreator.htm @@ -0,0 +1,34 @@ + + + + +SetCreator + + + +

        SetCreator

        +SetCreator(string creator [, boolean isUTF8]) +

        Description

        +Defines the creator of the document. This is typically the name of the application that +generates the PDF. +

        Parameters

        +
        +
        creator
        +
        +The name of the creator. +
        +
        isUTF8
        +
        +Indicates if the string is encoded in ISO-8859-1 (false) or UTF-8 (true).
        +Default value: false. +
        +
        +

        See also

        +SetAuthor, +SetKeywords, +SetSubject, +SetTitle +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setdisplaymode.htm b/videodb/vendor/setasign/fpdf/doc/setdisplaymode.htm new file mode 100644 index 0000000..e9deb56 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setdisplaymode.htm @@ -0,0 +1,45 @@ + + + + +SetDisplayMode + + + +

        SetDisplayMode

        +SetDisplayMode(mixed zoom [, string layout]) +

        Description

        +Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be +displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a +specific zooming factor or use viewer default (configured in the Preferences menu of Adobe Reader). +The page layout can be specified too: single at once, continuous display, two columns or viewer +default. +

        Parameters

        +
        +
        zoom
        +
        +The zoom to use. It can be one of the following string values: +
          +
        • fullpage: displays the entire page on screen
        • +
        • fullwidth: uses maximum width of window
        • +
        • real: uses real size (equivalent to 100% zoom)
        • +
        • default: uses viewer default mode
        • +
        +or a number indicating the zooming factor to use. +
        +
        layout
        +
        +The page layout. Possible values are: +
          +
        • single: displays one page at once
        • +
        • continuous: displays pages continuously
        • +
        • two: displays two pages on two columns
        • +
        • default: uses viewer default mode
        • +
        +Default value is default. +
        +
        +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setdrawcolor.htm b/videodb/vendor/setasign/fpdf/doc/setdrawcolor.htm new file mode 100644 index 0000000..fc5a093 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setdrawcolor.htm @@ -0,0 +1,41 @@ + + + + +SetDrawColor + + + +

        SetDrawColor

        +SetDrawColor(int r [, int g, int b]) +

        Description

        +Defines the color used for all drawing operations (lines, rectangles and cell borders). It +can be expressed in RGB components or gray scale. The method can be called before the first +page is created and the value is retained from page to page. +

        Parameters

        +
        +
        r
        +
        +If g et b are given, red component; if not, indicates the gray level. +Value between 0 and 255. +
        +
        g
        +
        +Green component (between 0 and 255). +
        +
        b
        +
        +Blue component (between 0 and 255). +
        +
        +

        See also

        +SetFillColor, +SetTextColor, +Line, +Rect, +Cell, +MultiCell +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setfillcolor.htm b/videodb/vendor/setasign/fpdf/doc/setfillcolor.htm new file mode 100644 index 0000000..9e18343 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setfillcolor.htm @@ -0,0 +1,40 @@ + + + + +SetFillColor + + + +

        SetFillColor

        +SetFillColor(int r [, int g, int b]) +

        Description

        +Defines the color used for all filling operations (filled rectangles and cell backgrounds). +It can be expressed in RGB components or gray scale. The method can be called before the first +page is created and the value is retained from page to page. +

        Parameters

        +
        +
        r
        +
        +If g and b are given, red component; if not, indicates the gray level. +Value between 0 and 255. +
        +
        g
        +
        +Green component (between 0 and 255). +
        +
        b
        +
        +Blue component (between 0 and 255). +
        +
        +

        See also

        +SetDrawColor, +SetTextColor, +Rect, +Cell, +MultiCell +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setfont.htm b/videodb/vendor/setasign/fpdf/doc/setfont.htm new file mode 100644 index 0000000..072de22 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setfont.htm @@ -0,0 +1,91 @@ + + + + +SetFont + + + +

        SetFont

        +SetFont(string family [, string style [, float size]]) +

        Description

        +Sets the font used to print character strings. It is mandatory to call this method +at least once before printing text or the resulting document would not be valid. +
        +The font can be either a standard one or a font added via the AddFont() method. Standard fonts +use the Windows encoding cp1252 (Western Europe). +
        +The method can be called before the first page is created and the font is kept from page +to page. +
        +If you just wish to change the current font size, it is simpler to call SetFontSize(). +
        +
        +Note: the font definition files must be accessible. They are searched successively in: +
          +
        • The directory defined by the FPDF_FONTPATH constant (if that constant is defined)
        • +
        • The font directory located in the same directory as fpdf.php (if it exists)
        • +
        +Example using FPDF_FONTPATH: +
        +
        define('FPDF_FONTPATH', '/home/www/font');
        +require('fpdf.php');
        +
        +If the file corresponding to the requested font is not found, the error "Could not include font +definition file" is raised. +

        Parameters

        +
        +
        family
        +
        +Family font. It can be either a name defined by AddFont() or one of the standard families (case +insensitive): +
          +
        • Courier (fixed-width)
        • +
        • Helvetica or Arial (synonymous; sans serif)
        • +
        • Times (serif)
        • +
        • Symbol (symbolic)
        • +
        • ZapfDingbats (symbolic)
        • +
        +It is also possible to pass an empty string. In that case, the current family is kept. +
        +
        style
        +
        +Font style. Possible values are (case insensitive): +
          +
        • empty string: regular
        • +
        • B: bold
        • +
        • I: italic
        • +
        • U: underline
        • +
        +or any combination. The default value is regular. +Bold and italic styles do not apply to Symbol and ZapfDingbats. +
        +
        size
        +
        +Font size in points. +
        +The default value is the current size. If no size has been specified since the beginning of +the document, the value is 12. +
        +
        +

        Example

        +
        +
        // Times regular 12
        +$pdf->SetFont('Times');
        +// Arial bold 14
        +$pdf->SetFont('Arial', 'B', 14);
        +// Removes bold
        +$pdf->SetFont('');
        +// Times bold, italic and underlined 14
        +$pdf->SetFont('Times', 'BIU');
        +
        +

        See also

        +AddFont, +SetFontSize, +Cell, +MultiCell, +Write +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setfontsize.htm b/videodb/vendor/setasign/fpdf/doc/setfontsize.htm new file mode 100644 index 0000000..70d002d --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setfontsize.htm @@ -0,0 +1,25 @@ + + + + +SetFontSize + + + +

        SetFontSize

        +SetFontSize(float size) +

        Description

        +Defines the size of the current font. +

        Parameters

        +
        +
        size
        +
        +The size (in points). +
        +
        +

        See also

        +SetFont +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setkeywords.htm b/videodb/vendor/setasign/fpdf/doc/setkeywords.htm new file mode 100644 index 0000000..4952cc6 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setkeywords.htm @@ -0,0 +1,33 @@ + + + + +SetKeywords + + + +

        SetKeywords

        +SetKeywords(string keywords [, boolean isUTF8]) +

        Description

        +Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. +

        Parameters

        +
        +
        keywords
        +
        +The list of keywords. +
        +
        isUTF8
        +
        +Indicates if the string is encoded in ISO-8859-1 (false) or UTF-8 (true).
        +Default value: false. +
        +
        +

        See also

        +SetAuthor, +SetCreator, +SetSubject, +SetTitle +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setleftmargin.htm b/videodb/vendor/setasign/fpdf/doc/setleftmargin.htm new file mode 100644 index 0000000..efa9c4e --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setleftmargin.htm @@ -0,0 +1,30 @@ + + + + +SetLeftMargin + + + +

        SetLeftMargin

        +SetLeftMargin(float margin) +

        Description

        +Defines the left margin. The method can be called before creating the first page. +
        +If the current abscissa gets out of page, it is brought back to the margin. +

        Parameters

        +
        +
        margin
        +
        +The margin. +
        +
        +

        See also

        +SetTopMargin, +SetRightMargin, +SetAutoPageBreak, +SetMargins +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setlinewidth.htm b/videodb/vendor/setasign/fpdf/doc/setlinewidth.htm new file mode 100644 index 0000000..b60b3b9 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setlinewidth.htm @@ -0,0 +1,29 @@ + + + + +SetLineWidth + + + +

        SetLineWidth

        +SetLineWidth(float width) +

        Description

        +Defines the line width. By default, the value equals 0.2 mm. The method can be called before +the first page is created and the value is retained from page to page. +

        Parameters

        +
        +
        width
        +
        +The width. +
        +
        +

        See also

        +Line, +Rect, +Cell, +MultiCell +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setlink.htm b/videodb/vendor/setasign/fpdf/doc/setlink.htm new file mode 100644 index 0000000..6160dc8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setlink.htm @@ -0,0 +1,34 @@ + + + + +SetLink + + + +

        SetLink

        +SetLink(int link [, float y [, int page]]) +

        Description

        +Defines the page and position a link points to. +

        Parameters

        +
        +
        link
        +
        +The link identifier returned by AddLink(). +
        +
        y
        +
        +Ordinate of target position; -1 indicates the current position. +The default value is 0 (top of page). +
        +
        page
        +
        +Number of target page; -1 indicates the current page. This is the default value. +
        +
        +

        See also

        +AddLink +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setmargins.htm b/videodb/vendor/setasign/fpdf/doc/setmargins.htm new file mode 100644 index 0000000..01e32f7 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setmargins.htm @@ -0,0 +1,37 @@ + + + + +SetMargins + + + +

        SetMargins

        +SetMargins(float left, float top [, float right]) +

        Description

        +Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change +them. +

        Parameters

        +
        +
        left
        +
        +Left margin. +
        +
        top
        +
        +Top margin. +
        +
        right
        +
        +Right margin. Default value is the left one. +
        +
        +

        See also

        +SetLeftMargin, +SetTopMargin, +SetRightMargin, +SetAutoPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setrightmargin.htm b/videodb/vendor/setasign/fpdf/doc/setrightmargin.htm new file mode 100644 index 0000000..b55941b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setrightmargin.htm @@ -0,0 +1,28 @@ + + + + +SetRightMargin + + + +

        SetRightMargin

        +SetRightMargin(float margin) +

        Description

        +Defines the right margin. The method can be called before creating the first page. +

        Parameters

        +
        +
        margin
        +
        +The margin. +
        +
        +

        See also

        +SetLeftMargin, +SetTopMargin, +SetAutoPageBreak, +SetMargins +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setsubject.htm b/videodb/vendor/setasign/fpdf/doc/setsubject.htm new file mode 100644 index 0000000..5df7fd4 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setsubject.htm @@ -0,0 +1,33 @@ + + + + +SetSubject + + + +

        SetSubject

        +SetSubject(string subject [, boolean isUTF8]) +

        Description

        +Defines the subject of the document. +

        Parameters

        +
        +
        subject
        +
        +The subject. +
        +
        isUTF8
        +
        +Indicates if the string is encoded in ISO-8859-1 (false) or UTF-8 (true).
        +Default value: false. +
        +
        +

        See also

        +SetAuthor, +SetCreator, +SetKeywords, +SetTitle +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/settextcolor.htm b/videodb/vendor/setasign/fpdf/doc/settextcolor.htm new file mode 100644 index 0000000..2db3b0d --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/settextcolor.htm @@ -0,0 +1,40 @@ + + + + +SetTextColor + + + +

        SetTextColor

        +SetTextColor(int r [, int g, int b]) +

        Description

        +Defines the color used for text. It can be expressed in RGB components or gray scale. The +method can be called before the first page is created and the value is retained from page to +page. +

        Parameters

        +
        +
        r
        +
        +If g et b are given, red component; if not, indicates the gray level. +Value between 0 and 255. +
        +
        g
        +
        +Green component (between 0 and 255). +
        +
        b
        +
        +Blue component (between 0 and 255). +
        +
        +

        See also

        +SetDrawColor, +SetFillColor, +Text, +Cell, +MultiCell +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/settitle.htm b/videodb/vendor/setasign/fpdf/doc/settitle.htm new file mode 100644 index 0000000..75cb9aa --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/settitle.htm @@ -0,0 +1,33 @@ + + + + +SetTitle + + + +

        SetTitle

        +SetTitle(string title [, boolean isUTF8]) +

        Description

        +Defines the title of the document. +

        Parameters

        +
        +
        title
        +
        +The title. +
        +
        isUTF8
        +
        +Indicates if the string is encoded in ISO-8859-1 (false) or UTF-8 (true).
        +Default value: false. +
        +
        +

        See also

        +SetAuthor, +SetCreator, +SetKeywords, +SetSubject +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/settopmargin.htm b/videodb/vendor/setasign/fpdf/doc/settopmargin.htm new file mode 100644 index 0000000..5ea6ebc --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/settopmargin.htm @@ -0,0 +1,28 @@ + + + + +SetTopMargin + + + +

        SetTopMargin

        +SetTopMargin(float margin) +

        Description

        +Defines the top margin. The method can be called before creating the first page. +

        Parameters

        +
        +
        margin
        +
        +The margin. +
        +
        +

        See also

        +SetLeftMargin, +SetRightMargin, +SetAutoPageBreak, +SetMargins +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setx.htm b/videodb/vendor/setasign/fpdf/doc/setx.htm new file mode 100644 index 0000000..39d3796 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setx.htm @@ -0,0 +1,29 @@ + + + + +SetX + + + +

        SetX

        +SetX(float x) +

        Description

        +Defines the abscissa of the current position. If the passed value is negative, it is relative +to the right of the page. +

        Parameters

        +
        +
        x
        +
        +The value of the abscissa. +
        +
        +

        See also

        +GetX, +GetY, +SetY, +SetXY +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/setxy.htm b/videodb/vendor/setasign/fpdf/doc/setxy.htm new file mode 100644 index 0000000..005acd4 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/setxy.htm @@ -0,0 +1,31 @@ + + + + +SetXY + + + +

        SetXY

        +SetXY(float x, float y) +

        Description

        +Defines the abscissa and ordinate of the current position. If the passed values are negative, +they are relative respectively to the right and bottom of the page. +

        Parameters

        +
        +
        x
        +
        +The value of the abscissa. +
        +
        y
        +
        +The value of the ordinate. +
        +
        +

        See also

        +SetX, +SetY +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/sety.htm b/videodb/vendor/setasign/fpdf/doc/sety.htm new file mode 100644 index 0000000..7ee94ea --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/sety.htm @@ -0,0 +1,33 @@ + + + + +SetY + + + +

        SetY

        +SetY(float y [, boolean resetX]) +

        Description

        +Sets the ordinate and optionally moves the current abscissa back to the left margin. If the value +is negative, it is relative to the bottom of the page. +

        Parameters

        +
        +
        y
        +
        +The value of the ordinate. +
        +
        resetX
        +
        +Whether to reset the abscissa. Default value: true. +
        +
        +

        See also

        +GetX, +GetY, +SetX, +SetXY +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/text.htm b/videodb/vendor/setasign/fpdf/doc/text.htm new file mode 100644 index 0000000..0ef6187 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/text.htm @@ -0,0 +1,39 @@ + + + + +Text + + + +

        Text

        +Text(float x, float y, string txt) +

        Description

        +Prints a character string. The origin is on the left of the first character, on the baseline. +This method allows to place a string precisely on the page, but it is usually easier to use +Cell(), MultiCell() or Write() which are the standard methods to print text. +

        Parameters

        +
        +
        x
        +
        +Abscissa of the origin. +
        +
        y
        +
        +Ordinate of the origin. +
        +
        txt
        +
        +String to print. +
        +
        +

        See also

        +SetFont, +SetTextColor, +Cell, +MultiCell, +Write +
        + + + diff --git a/videodb/vendor/setasign/fpdf/doc/write.htm b/videodb/vendor/setasign/fpdf/doc/write.htm new file mode 100644 index 0000000..42b1c99 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/doc/write.htm @@ -0,0 +1,51 @@ + + + + +Write + + + +

        Write

        +Write(float h, string txt [, mixed link]) +

        Description

        +This method prints text from the current position. When the right margin is reached (or the \n +character is met) a line break occurs and text continues from the left margin. Upon method exit, +the current position is left just at the end of the text. +
        +It is possible to put a link on the text. +

        Parameters

        +
        +
        h
        +
        +Line height. +
        +
        txt
        +
        +String to print. +
        +
        link
        +
        +URL or identifier returned by AddLink(). +
        +
        +

        Example

        +
        +
        // Begin with regular font
        +$pdf->SetFont('Arial', '', 14);
        +$pdf->Write(5, 'Visit ');
        +// Then put a blue underlined link
        +$pdf->SetTextColor(0, 0, 255);
        +$pdf->SetFont('', 'U');
        +$pdf->Write(5, 'www.fpdf.org', 'http://www.fpdf.org');
        +
        +

        See also

        +SetFont, +SetTextColor, +AddLink, +MultiCell, +SetAutoPageBreak +
        + + + diff --git a/videodb/vendor/setasign/fpdf/font/courier.php b/videodb/vendor/setasign/fpdf/font/courier.php new file mode 100644 index 0000000..bc8478e --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/courier.php @@ -0,0 +1,10 @@ +array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/courierb.php b/videodb/vendor/setasign/fpdf/font/courierb.php new file mode 100644 index 0000000..97ecd70 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/courierb.php @@ -0,0 +1,10 @@ +array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/courierbi.php b/videodb/vendor/setasign/fpdf/font/courierbi.php new file mode 100644 index 0000000..c4bfff8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/courierbi.php @@ -0,0 +1,10 @@ +array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/courieri.php b/videodb/vendor/setasign/fpdf/font/courieri.php new file mode 100644 index 0000000..015a15a --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/courieri.php @@ -0,0 +1,10 @@ +array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/helvetica.php b/videodb/vendor/setasign/fpdf/font/helvetica.php new file mode 100644 index 0000000..927759b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/helvetica.php @@ -0,0 +1,21 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/helveticab.php b/videodb/vendor/setasign/fpdf/font/helveticab.php new file mode 100644 index 0000000..bcd7367 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/helveticab.php @@ -0,0 +1,21 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/helveticabi.php b/videodb/vendor/setasign/fpdf/font/helveticabi.php new file mode 100644 index 0000000..0243cde --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/helveticabi.php @@ -0,0 +1,21 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, + 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, + 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, + chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, + chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/helveticai.php b/videodb/vendor/setasign/fpdf/font/helveticai.php new file mode 100644 index 0000000..06ec735 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/helveticai.php @@ -0,0 +1,21 @@ +278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, + chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, + ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, + 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, + 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, + chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, + chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, + chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/symbol.php b/videodb/vendor/setasign/fpdf/font/symbol.php new file mode 100644 index 0000000..f8f0c33 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/symbol.php @@ -0,0 +1,20 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>713,'#'=>500,'$'=>549,'%'=>833,'&'=>778,'\''=>439,'('=>333,')'=>333,'*'=>500,'+'=>549, + ','=>250,'-'=>549,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>549,'='=>549,'>'=>549,'?'=>444,'@'=>549,'A'=>722, + 'B'=>667,'C'=>722,'D'=>612,'E'=>611,'F'=>763,'G'=>603,'H'=>722,'I'=>333,'J'=>631,'K'=>722,'L'=>686,'M'=>889,'N'=>722,'O'=>722,'P'=>768,'Q'=>741,'R'=>556,'S'=>592,'T'=>611,'U'=>690,'V'=>439,'W'=>768, + 'X'=>645,'Y'=>795,'Z'=>611,'['=>333,'\\'=>863,']'=>333,'^'=>658,'_'=>500,'`'=>500,'a'=>631,'b'=>549,'c'=>549,'d'=>494,'e'=>439,'f'=>521,'g'=>411,'h'=>603,'i'=>329,'j'=>603,'k'=>549,'l'=>549,'m'=>576, + 'n'=>521,'o'=>549,'p'=>549,'q'=>521,'r'=>549,'s'=>603,'t'=>439,'u'=>576,'v'=>713,'w'=>686,'x'=>493,'y'=>686,'z'=>494,'{'=>480,'|'=>200,'}'=>480,'~'=>549,chr(127)=>0,chr(128)=>0,chr(129)=>0,chr(130)=>0,chr(131)=>0, + chr(132)=>0,chr(133)=>0,chr(134)=>0,chr(135)=>0,chr(136)=>0,chr(137)=>0,chr(138)=>0,chr(139)=>0,chr(140)=>0,chr(141)=>0,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>750,chr(161)=>620,chr(162)=>247,chr(163)=>549,chr(164)=>167,chr(165)=>713,chr(166)=>500,chr(167)=>753,chr(168)=>753,chr(169)=>753,chr(170)=>753,chr(171)=>1042,chr(172)=>987,chr(173)=>603,chr(174)=>987,chr(175)=>603, + chr(176)=>400,chr(177)=>549,chr(178)=>411,chr(179)=>549,chr(180)=>549,chr(181)=>713,chr(182)=>494,chr(183)=>460,chr(184)=>549,chr(185)=>549,chr(186)=>549,chr(187)=>549,chr(188)=>1000,chr(189)=>603,chr(190)=>1000,chr(191)=>658,chr(192)=>823,chr(193)=>686,chr(194)=>795,chr(195)=>987,chr(196)=>768,chr(197)=>768, + chr(198)=>823,chr(199)=>768,chr(200)=>768,chr(201)=>713,chr(202)=>713,chr(203)=>713,chr(204)=>713,chr(205)=>713,chr(206)=>713,chr(207)=>713,chr(208)=>768,chr(209)=>713,chr(210)=>790,chr(211)=>790,chr(212)=>890,chr(213)=>823,chr(214)=>549,chr(215)=>250,chr(216)=>713,chr(217)=>603,chr(218)=>603,chr(219)=>1042, + chr(220)=>987,chr(221)=>603,chr(222)=>987,chr(223)=>603,chr(224)=>494,chr(225)=>329,chr(226)=>790,chr(227)=>790,chr(228)=>786,chr(229)=>713,chr(230)=>384,chr(231)=>384,chr(232)=>384,chr(233)=>384,chr(234)=>384,chr(235)=>384,chr(236)=>494,chr(237)=>494,chr(238)=>494,chr(239)=>494,chr(240)=>0,chr(241)=>329, + chr(242)=>274,chr(243)=>686,chr(244)=>686,chr(245)=>686,chr(246)=>384,chr(247)=>384,chr(248)=>384,chr(249)=>384,chr(250)=>384,chr(251)=>384,chr(252)=>494,chr(253)=>494,chr(254)=>494,chr(255)=>0); +$uv = array(32=>160,33=>33,34=>8704,35=>35,36=>8707,37=>array(37,2),39=>8715,40=>array(40,2),42=>8727,43=>array(43,2),45=>8722,46=>array(46,18),64=>8773,65=>array(913,2),67=>935,68=>array(916,2),70=>934,71=>915,72=>919,73=>921,74=>977,75=>array(922,4),79=>array(927,2),81=>920,82=>929,83=>array(931,3),86=>962,87=>937,88=>926,89=>936,90=>918,91=>91,92=>8756,93=>93,94=>8869,95=>95,96=>63717,97=>array(945,2),99=>967,100=>array(948,2),102=>966,103=>947,104=>951,105=>953,106=>981,107=>array(954,4),111=>array(959,2),113=>952,114=>961,115=>array(963,3),118=>982,119=>969,120=>958,121=>968,122=>950,123=>array(123,3),126=>8764,160=>8364,161=>978,162=>8242,163=>8804,164=>8725,165=>8734,166=>402,167=>9827,168=>9830,169=>9829,170=>9824,171=>8596,172=>array(8592,4),176=>array(176,2),178=>8243,179=>8805,180=>215,181=>8733,182=>8706,183=>8226,184=>247,185=>array(8800,2),187=>8776,188=>8230,189=>array(63718,2),191=>8629,192=>8501,193=>8465,194=>8476,195=>8472,196=>8855,197=>8853,198=>8709,199=>array(8745,2),201=>8835,202=>8839,203=>8836,204=>8834,205=>8838,206=>array(8712,2),208=>8736,209=>8711,210=>63194,211=>63193,212=>63195,213=>8719,214=>8730,215=>8901,216=>172,217=>array(8743,2),219=>8660,220=>array(8656,4),224=>9674,225=>9001,226=>array(63720,3),229=>8721,230=>array(63723,10),241=>9002,242=>8747,243=>8992,244=>63733,245=>8993,246=>array(63734,9)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/times.php b/videodb/vendor/setasign/fpdf/font/times.php new file mode 100644 index 0000000..81f2a8b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/times.php @@ -0,0 +1,21 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>408,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>180,'('=>333,')'=>333,'*'=>500,'+'=>564, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>564,'='=>564,'>'=>564,'?'=>444,'@'=>921,'A'=>722, + 'B'=>667,'C'=>667,'D'=>722,'E'=>611,'F'=>556,'G'=>722,'H'=>722,'I'=>333,'J'=>389,'K'=>722,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>556,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>722,'W'=>944, + 'X'=>722,'Y'=>722,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>469,'_'=>500,'`'=>333,'a'=>444,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>333,'s'=>389,'t'=>278,'u'=>500,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>480,'|'=>200,'}'=>480,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>444,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>889,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>444,chr(148)=>444,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>200,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>564,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>564,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>453,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>444,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>564,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>556,chr(223)=>500,chr(224)=>444,chr(225)=>444,chr(226)=>444,chr(227)=>444,chr(228)=>444,chr(229)=>444,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>564,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>500,chr(254)=>500,chr(255)=>500); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/timesb.php b/videodb/vendor/setasign/fpdf/font/timesb.php new file mode 100644 index 0000000..7db704f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/timesb.php @@ -0,0 +1,21 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>555,'#'=>500,'$'=>500,'%'=>1000,'&'=>833,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>930,'A'=>722, + 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>778,'I'=>389,'J'=>500,'K'=>778,'L'=>667,'M'=>944,'N'=>722,'O'=>778,'P'=>611,'Q'=>778,'R'=>722,'S'=>556,'T'=>667,'U'=>722,'V'=>722,'W'=>1000, + 'X'=>722,'Y'=>722,'Z'=>667,'['=>333,'\\'=>278,']'=>333,'^'=>581,'_'=>500,'`'=>333,'a'=>500,'b'=>556,'c'=>444,'d'=>556,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>333,'k'=>556,'l'=>278,'m'=>833, + 'n'=>556,'o'=>500,'p'=>556,'q'=>556,'r'=>444,'s'=>389,'t'=>333,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>394,'|'=>220,'}'=>394,'~'=>520,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>667,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>300,chr(171)=>500,chr(172)=>570,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>556,chr(182)=>540,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>330,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, + chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>570,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>722,chr(222)=>611,chr(223)=>556,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/timesbi.php b/videodb/vendor/setasign/fpdf/font/timesbi.php new file mode 100644 index 0000000..089f21a --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/timesbi.php @@ -0,0 +1,21 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>389,'"'=>555,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>832,'A'=>667, + 'B'=>667,'C'=>667,'D'=>722,'E'=>667,'F'=>667,'G'=>722,'H'=>778,'I'=>389,'J'=>500,'K'=>667,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>611,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>667,'W'=>889, + 'X'=>667,'Y'=>611,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>570,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, + 'n'=>556,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>556,'v'=>444,'w'=>667,'x'=>500,'y'=>444,'z'=>389,'{'=>348,'|'=>220,'}'=>348,'~'=>570,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, + chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>389,chr(159)=>611,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>266,chr(171)=>500,chr(172)=>606,chr(173)=>333,chr(174)=>747,chr(175)=>333, + chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>576,chr(182)=>500,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>300,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, + chr(198)=>944,chr(199)=>667,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>570,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>611,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>444,chr(254)=>500,chr(255)=>444); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/timesi.php b/videodb/vendor/setasign/fpdf/font/timesi.php new file mode 100644 index 0000000..f958b5b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/timesi.php @@ -0,0 +1,21 @@ +250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, + chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>420,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>214,'('=>333,')'=>333,'*'=>500,'+'=>675, + ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>675,'='=>675,'>'=>675,'?'=>500,'@'=>920,'A'=>611, + 'B'=>611,'C'=>667,'D'=>722,'E'=>611,'F'=>611,'G'=>722,'H'=>722,'I'=>333,'J'=>444,'K'=>667,'L'=>556,'M'=>833,'N'=>667,'O'=>722,'P'=>611,'Q'=>722,'R'=>611,'S'=>500,'T'=>556,'U'=>722,'V'=>611,'W'=>833, + 'X'=>611,'Y'=>556,'Z'=>556,'['=>389,'\\'=>278,']'=>389,'^'=>422,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>278,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>444,'l'=>278,'m'=>722, + 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>500,'v'=>444,'w'=>667,'x'=>444,'y'=>444,'z'=>389,'{'=>400,'|'=>275,'}'=>400,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, + chr(132)=>556,chr(133)=>889,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>500,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>556,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>556,chr(148)=>556,chr(149)=>350,chr(150)=>500,chr(151)=>889,chr(152)=>333,chr(153)=>980, + chr(154)=>389,chr(155)=>333,chr(156)=>667,chr(157)=>350,chr(158)=>389,chr(159)=>556,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>275,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>675,chr(173)=>333,chr(174)=>760,chr(175)=>333, + chr(176)=>400,chr(177)=>675,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>523,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>611,chr(193)=>611,chr(194)=>611,chr(195)=>611,chr(196)=>611,chr(197)=>611, + chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>667,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>675,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, + chr(220)=>722,chr(221)=>556,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, + chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>675,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>444,chr(254)=>500,chr(255)=>444); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +?> diff --git a/videodb/vendor/setasign/fpdf/font/zapfdingbats.php b/videodb/vendor/setasign/fpdf/font/zapfdingbats.php new file mode 100644 index 0000000..7c2cb5e --- /dev/null +++ b/videodb/vendor/setasign/fpdf/font/zapfdingbats.php @@ -0,0 +1,20 @@ +0,chr(1)=>0,chr(2)=>0,chr(3)=>0,chr(4)=>0,chr(5)=>0,chr(6)=>0,chr(7)=>0,chr(8)=>0,chr(9)=>0,chr(10)=>0,chr(11)=>0,chr(12)=>0,chr(13)=>0,chr(14)=>0,chr(15)=>0,chr(16)=>0,chr(17)=>0,chr(18)=>0,chr(19)=>0,chr(20)=>0,chr(21)=>0, + chr(22)=>0,chr(23)=>0,chr(24)=>0,chr(25)=>0,chr(26)=>0,chr(27)=>0,chr(28)=>0,chr(29)=>0,chr(30)=>0,chr(31)=>0,' '=>278,'!'=>974,'"'=>961,'#'=>974,'$'=>980,'%'=>719,'&'=>789,'\''=>790,'('=>791,')'=>690,'*'=>960,'+'=>939, + ','=>549,'-'=>855,'.'=>911,'/'=>933,'0'=>911,'1'=>945,'2'=>974,'3'=>755,'4'=>846,'5'=>762,'6'=>761,'7'=>571,'8'=>677,'9'=>763,':'=>760,';'=>759,'<'=>754,'='=>494,'>'=>552,'?'=>537,'@'=>577,'A'=>692, + 'B'=>786,'C'=>788,'D'=>788,'E'=>790,'F'=>793,'G'=>794,'H'=>816,'I'=>823,'J'=>789,'K'=>841,'L'=>823,'M'=>833,'N'=>816,'O'=>831,'P'=>923,'Q'=>744,'R'=>723,'S'=>749,'T'=>790,'U'=>792,'V'=>695,'W'=>776, + 'X'=>768,'Y'=>792,'Z'=>759,'['=>707,'\\'=>708,']'=>682,'^'=>701,'_'=>826,'`'=>815,'a'=>789,'b'=>789,'c'=>707,'d'=>687,'e'=>696,'f'=>689,'g'=>786,'h'=>787,'i'=>713,'j'=>791,'k'=>785,'l'=>791,'m'=>873, + 'n'=>761,'o'=>762,'p'=>762,'q'=>759,'r'=>759,'s'=>892,'t'=>892,'u'=>788,'v'=>784,'w'=>438,'x'=>138,'y'=>277,'z'=>415,'{'=>392,'|'=>392,'}'=>668,'~'=>668,chr(127)=>0,chr(128)=>390,chr(129)=>390,chr(130)=>317,chr(131)=>317, + chr(132)=>276,chr(133)=>276,chr(134)=>509,chr(135)=>509,chr(136)=>410,chr(137)=>410,chr(138)=>234,chr(139)=>234,chr(140)=>334,chr(141)=>334,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, + chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>0,chr(161)=>732,chr(162)=>544,chr(163)=>544,chr(164)=>910,chr(165)=>667,chr(166)=>760,chr(167)=>760,chr(168)=>776,chr(169)=>595,chr(170)=>694,chr(171)=>626,chr(172)=>788,chr(173)=>788,chr(174)=>788,chr(175)=>788, + chr(176)=>788,chr(177)=>788,chr(178)=>788,chr(179)=>788,chr(180)=>788,chr(181)=>788,chr(182)=>788,chr(183)=>788,chr(184)=>788,chr(185)=>788,chr(186)=>788,chr(187)=>788,chr(188)=>788,chr(189)=>788,chr(190)=>788,chr(191)=>788,chr(192)=>788,chr(193)=>788,chr(194)=>788,chr(195)=>788,chr(196)=>788,chr(197)=>788, + chr(198)=>788,chr(199)=>788,chr(200)=>788,chr(201)=>788,chr(202)=>788,chr(203)=>788,chr(204)=>788,chr(205)=>788,chr(206)=>788,chr(207)=>788,chr(208)=>788,chr(209)=>788,chr(210)=>788,chr(211)=>788,chr(212)=>894,chr(213)=>838,chr(214)=>1016,chr(215)=>458,chr(216)=>748,chr(217)=>924,chr(218)=>748,chr(219)=>918, + chr(220)=>927,chr(221)=>928,chr(222)=>928,chr(223)=>834,chr(224)=>873,chr(225)=>828,chr(226)=>924,chr(227)=>924,chr(228)=>917,chr(229)=>930,chr(230)=>931,chr(231)=>463,chr(232)=>883,chr(233)=>836,chr(234)=>836,chr(235)=>867,chr(236)=>867,chr(237)=>696,chr(238)=>696,chr(239)=>874,chr(240)=>0,chr(241)=>874, + chr(242)=>760,chr(243)=>946,chr(244)=>771,chr(245)=>865,chr(246)=>771,chr(247)=>888,chr(248)=>967,chr(249)=>888,chr(250)=>831,chr(251)=>873,chr(252)=>927,chr(253)=>970,chr(254)=>918,chr(255)=>0); +$uv = array(32=>32,33=>array(9985,4),37=>9742,38=>array(9990,4),42=>9755,43=>9758,44=>array(9996,28),72=>9733,73=>array(10025,35),108=>9679,109=>10061,110=>9632,111=>array(10063,4),115=>9650,116=>9660,117=>9670,118=>10070,119=>9687,120=>array(10072,7),128=>array(10088,14),161=>array(10081,7),168=>9827,169=>9830,170=>9829,171=>9824,172=>array(9312,10),182=>array(10102,31),213=>8594,214=>array(8596,2),216=>array(10136,24),241=>array(10161,14)); +?> diff --git a/videodb/vendor/setasign/fpdf/fpdf.css b/videodb/vendor/setasign/fpdf/fpdf.css new file mode 100644 index 0000000..8cfa33d --- /dev/null +++ b/videodb/vendor/setasign/fpdf/fpdf.css @@ -0,0 +1,21 @@ +body {font-family:"Times New Roman",serif} +h1 {font:bold 135% Arial,sans-serif; color:#4000A0; margin-bottom:0.9em} +h2 {font:bold 95% Arial,sans-serif; color:#900000; margin-top:1.5em; margin-bottom:1em} +dl.param dt {text-decoration:underline} +dl.param dd {margin-top:1em; margin-bottom:1em} +dl.param ul {margin-top:1em; margin-bottom:1em} +tt, code, kbd {font-family:"Courier New",Courier,monospace; font-size:82%} +div.source {margin-top:1.4em; margin-bottom:1.3em} +div.source pre {display:table; border:1px solid #24246A; width:100%; margin:0em; font-family:inherit; font-size:100%} +div.source code {display:block; border:1px solid #C5C5EC; background-color:#F0F5FF; padding:6px; color:#000000} +div.doc-source {margin-top:1.4em; margin-bottom:1.3em} +div.doc-source pre {display:table; width:100%; margin:0em; font-family:inherit; font-size:100%} +div.doc-source code {display:block; background-color:#E0E0E0; padding:4px} +.kw {color:#000080; font-weight:bold} +.str {color:#CC0000} +.cmt {color:#008000} +p.demo {text-align:center; margin-top:-0.9em} +a.demo {text-decoration:none; font-weight:bold; color:#0000CC} +a.demo:link {text-decoration:none; font-weight:bold; color:#0000CC} +a.demo:hover {text-decoration:none; font-weight:bold; color:#0000FF} +a.demo:active {text-decoration:none; font-weight:bold; color:#0000FF} diff --git a/videodb/vendor/setasign/fpdf/fpdf.php b/videodb/vendor/setasign/fpdf/fpdf.php new file mode 100644 index 0000000..2af3176 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/fpdf.php @@ -0,0 +1,1935 @@ +state = 0; + $this->page = 0; + $this->n = 2; + $this->buffer = ''; + $this->pages = array(); + $this->PageInfo = array(); + $this->fonts = array(); + $this->FontFiles = array(); + $this->encodings = array(); + $this->cmaps = array(); + $this->images = array(); + $this->links = array(); + $this->InHeader = false; + $this->InFooter = false; + $this->lasth = 0; + $this->FontFamily = ''; + $this->FontStyle = ''; + $this->FontSizePt = 12; + $this->underline = false; + $this->DrawColor = '0 G'; + $this->FillColor = '0 g'; + $this->TextColor = '0 g'; + $this->ColorFlag = false; + $this->WithAlpha = false; + $this->ws = 0; + $this->iconv = function_exists('iconv'); + // Font path + if(defined('FPDF_FONTPATH')) + { + $this->fontpath = FPDF_FONTPATH; + if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\') + $this->fontpath .= '/'; + } + elseif(is_dir(dirname(__FILE__).'/font')) + $this->fontpath = dirname(__FILE__).'/font/'; + else + $this->fontpath = ''; + // Core fonts + $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); + // Scale factor + if($unit=='pt') + $this->k = 1; + elseif($unit=='mm') + $this->k = 72/25.4; + elseif($unit=='cm') + $this->k = 72/2.54; + elseif($unit=='in') + $this->k = 72; + else + $this->Error('Incorrect unit: '.$unit); + // Page sizes + $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28), + 'letter'=>array(612,792), 'legal'=>array(612,1008)); + $size = $this->_getpagesize($size); + $this->DefPageSize = $size; + $this->CurPageSize = $size; + // Page orientation + $orientation = strtolower($orientation); + if($orientation=='p' || $orientation=='portrait') + { + $this->DefOrientation = 'P'; + $this->w = $size[0]; + $this->h = $size[1]; + } + elseif($orientation=='l' || $orientation=='landscape') + { + $this->DefOrientation = 'L'; + $this->w = $size[1]; + $this->h = $size[0]; + } + else + $this->Error('Incorrect orientation: '.$orientation); + $this->CurOrientation = $this->DefOrientation; + $this->wPt = $this->w*$this->k; + $this->hPt = $this->h*$this->k; + // Page rotation + $this->CurRotation = 0; + // Page margins (1 cm) + $margin = 28.35/$this->k; + $this->SetMargins($margin,$margin); + // Interior cell margin (1 mm) + $this->cMargin = $margin/10; + // Line width (0.2 mm) + $this->LineWidth = .567/$this->k; + // Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + // Default display mode + $this->SetDisplayMode('default'); + // Enable compression + $this->SetCompression(true); + // Metadata + $this->metadata = array('Producer'=>'FPDF '.self::VERSION); + // Set default PDF version number + $this->PDFVersion = '1.3'; +} + +function SetMargins($left, $top, $right=null) +{ + // Set left, top and right margins + $this->lMargin = $left; + $this->tMargin = $top; + if($right===null) + $right = $left; + $this->rMargin = $right; +} + +function SetLeftMargin($margin) +{ + // Set left margin + $this->lMargin = $margin; + if($this->page>0 && $this->x<$margin) + $this->x = $margin; +} + +function SetTopMargin($margin) +{ + // Set top margin + $this->tMargin = $margin; +} + +function SetRightMargin($margin) +{ + // Set right margin + $this->rMargin = $margin; +} + +function SetAutoPageBreak($auto, $margin=0) +{ + // Set auto page break mode and triggering margin + $this->AutoPageBreak = $auto; + $this->bMargin = $margin; + $this->PageBreakTrigger = $this->h-$margin; +} + +function SetDisplayMode($zoom, $layout='default') +{ + // Set display mode in viewer + if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom)) + $this->ZoomMode = $zoom; + else + $this->Error('Incorrect zoom display mode: '.$zoom); + if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default') + $this->LayoutMode = $layout; + else + $this->Error('Incorrect layout display mode: '.$layout); +} + +function SetCompression($compress) +{ + // Set page compression + if(function_exists('gzcompress')) + $this->compress = $compress; + else + $this->compress = false; +} + +function SetTitle($title, $isUTF8=false) +{ + // Title of document + $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title); +} + +function SetAuthor($author, $isUTF8=false) +{ + // Author of document + $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author); +} + +function SetSubject($subject, $isUTF8=false) +{ + // Subject of document + $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject); +} + +function SetKeywords($keywords, $isUTF8=false) +{ + // Keywords of document + $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords); +} + +function SetCreator($creator, $isUTF8=false) +{ + // Creator of document + $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator); +} + +function AliasNbPages($alias='{nb}') +{ + // Define an alias for total number of pages + $this->AliasNbPages = $alias; +} + +function Error($msg) +{ + // Fatal error + throw new Exception('FPDF error: '.$msg); +} + +function Close() +{ + // Terminate document + if($this->state==3) + return; + if($this->page==0) + $this->AddPage(); + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + // Close document + $this->_enddoc(); +} + +function AddPage($orientation='', $size='', $rotation=0) +{ + // Start a new page + if($this->state==3) + $this->Error('The document is closed'); + $family = $this->FontFamily; + $style = $this->FontStyle.($this->underline ? 'U' : ''); + $fontsize = $this->FontSizePt; + $lw = $this->LineWidth; + $dc = $this->DrawColor; + $fc = $this->FillColor; + $tc = $this->TextColor; + $cf = $this->ColorFlag; + if($this->page>0) + { + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + } + // Start new page + $this->_beginpage($orientation,$size,$rotation); + // Set line cap style to square + $this->_out('2 J'); + // Set line width + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + // Set font + if($family) + $this->SetFont($family,$style,$fontsize); + // Set colors + $this->DrawColor = $dc; + if($dc!='0 G') + $this->_out($dc); + $this->FillColor = $fc; + if($fc!='0 g') + $this->_out($fc); + $this->TextColor = $tc; + $this->ColorFlag = $cf; + // Page header + $this->InHeader = true; + $this->Header(); + $this->InHeader = false; + // Restore line width + if($this->LineWidth!=$lw) + { + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w',$lw*$this->k)); + } + // Restore font + if($family) + $this->SetFont($family,$style,$fontsize); + // Restore colors + if($this->DrawColor!=$dc) + { + $this->DrawColor = $dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) + { + $this->FillColor = $fc; + $this->_out($fc); + } + $this->TextColor = $tc; + $this->ColorFlag = $cf; +} + +function Header() +{ + // To be implemented in your own inherited class +} + +function Footer() +{ + // To be implemented in your own inherited class +} + +function PageNo() +{ + // Get current page number + return $this->page; +} + +function SetDrawColor($r, $g=null, $b=null) +{ + // Set color for all stroking operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->DrawColor = sprintf('%.3F G',$r/255); + else + $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255); + if($this->page>0) + $this->_out($this->DrawColor); +} + +function SetFillColor($r, $g=null, $b=null) +{ + // Set color for all filling operations + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->FillColor = sprintf('%.3F g',$r/255); + else + $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag = ($this->FillColor!=$this->TextColor); + if($this->page>0) + $this->_out($this->FillColor); +} + +function SetTextColor($r, $g=null, $b=null) +{ + // Set color for text + if(($r==0 && $g==0 && $b==0) || $g===null) + $this->TextColor = sprintf('%.3F g',$r/255); + else + $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255); + $this->ColorFlag = ($this->FillColor!=$this->TextColor); +} + +function GetStringWidth($s) +{ + // Get width of a string in the current font + $cw = $this->CurrentFont['cw']; + $w = 0; + $s = (string)$s; + $l = strlen($s); + for($i=0;$i<$l;$i++) + $w += $cw[$s[$i]]; + return $w*$this->FontSize/1000; +} + +function SetLineWidth($width) +{ + // Set line width + $this->LineWidth = $width; + if($this->page>0) + $this->_out(sprintf('%.2F w',$width*$this->k)); +} + +function Line($x1, $y1, $x2, $y2) +{ + // Draw a line + $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k)); +} + +function Rect($x, $y, $w, $h, $style='') +{ + // Draw a rectangle + if($style=='F') + $op = 'f'; + elseif($style=='FD' || $style=='DF') + $op = 'B'; + else + $op = 'S'; + $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); +} + +function AddFont($family, $style='', $file='') +{ + // Add a TrueType, OpenType or Type1 font + $family = strtolower($family); + if($file=='') + $file = str_replace(' ','',$family).strtolower($style).'.php'; + $style = strtoupper($style); + if($style=='IB') + $style = 'BI'; + $fontkey = $family.$style; + if(isset($this->fonts[$fontkey])) + return; + $info = $this->_loadfont($file); + $info['i'] = count($this->fonts)+1; + if(!empty($info['file'])) + { + // Embedded font + if($info['type']=='TrueType') + $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']); + else + $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']); + } + $this->fonts[$fontkey] = $info; +} + +function SetFont($family, $style='', $size=0) +{ + // Select a font; size given in points + if($family=='') + $family = $this->FontFamily; + else + $family = strtolower($family); + $style = strtoupper($style); + if(strpos($style,'U')!==false) + { + $this->underline = true; + $style = str_replace('U','',$style); + } + else + $this->underline = false; + if($style=='IB') + $style = 'BI'; + if($size==0) + $size = $this->FontSizePt; + // Test if font is already selected + if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size) + return; + // Test if font is already loaded + $fontkey = $family.$style; + if(!isset($this->fonts[$fontkey])) + { + // Test if one of the core fonts + if($family=='arial') + $family = 'helvetica'; + if(in_array($family,$this->CoreFonts)) + { + if($family=='symbol' || $family=='zapfdingbats') + $style = ''; + $fontkey = $family.$style; + if(!isset($this->fonts[$fontkey])) + $this->AddFont($family,$style); + } + else + $this->Error('Undefined font: '.$family.' '.$style); + } + // Select it + $this->FontFamily = $family; + $this->FontStyle = $style; + $this->FontSizePt = $size; + $this->FontSize = $size/$this->k; + $this->CurrentFont = $this->fonts[$fontkey]; + if($this->page>0) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function SetFontSize($size) +{ + // Set font size in points + if($this->FontSizePt==$size) + return; + $this->FontSizePt = $size; + $this->FontSize = $size/$this->k; + if($this->page>0 && isset($this->CurrentFont)) + $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt)); +} + +function AddLink() +{ + // Create a new internal link + $n = count($this->links)+1; + $this->links[$n] = array(0, 0); + return $n; +} + +function SetLink($link, $y=0, $page=-1) +{ + // Set destination of internal link + if($y==-1) + $y = $this->y; + if($page==-1) + $page = $this->page; + $this->links[$link] = array($page, $y); +} + +function Link($x, $y, $w, $h, $link) +{ + // Put a link on the page + $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link); +} + +function Text($x, $y, $txt) +{ + // Output a string + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + $txt = (string)$txt; + $s = sprintf('BT %.2F %.2F Td (%s) Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escape($txt)); + if($this->underline && $txt!=='') + $s .= ' '.$this->_dounderline($x,$y,$txt); + if($this->ColorFlag) + $s = 'q '.$this->TextColor.' '.$s.' Q'; + $this->_out($s); +} + +function AcceptPageBreak() +{ + // Accept automatic page break or not + return $this->AutoPageBreak; +} + +function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='') +{ + // Output a cell + $k = $this->k; + if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) + { + // Automatic page break + $x = $this->x; + $ws = $this->ws; + if($ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); + $this->x = $x; + if($ws>0) + { + $this->ws = $ws; + $this->_out(sprintf('%.3F Tw',$ws*$k)); + } + } + if($w==0) + $w = $this->w-$this->rMargin-$this->x; + $s = ''; + if($fill || $border==1) + { + if($fill) + $op = ($border==1) ? 'B' : 'f'; + else + $op = 'S'; + $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op); + } + if(is_string($border)) + { + $x = $this->x; + $y = $this->y; + if(strpos($border,'L')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k); + if(strpos($border,'T')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k); + if(strpos($border,'R')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + if(strpos($border,'B')!==false) + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k); + } + $txt = (string)$txt; + if($txt!=='') + { + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + if($align=='R') + $dx = $w-$this->cMargin-$this->GetStringWidth($txt); + elseif($align=='C') + $dx = ($w-$this->GetStringWidth($txt))/2; + else + $dx = $this->cMargin; + if($this->ColorFlag) + $s .= 'q '.$this->TextColor.' '; + $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$this->_escape($txt)); + if($this->underline) + $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt); + if($this->ColorFlag) + $s .= ' Q'; + if($link) + $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link); + } + if($s) + $this->_out($s); + $this->lasth = $h; + if($ln>0) + { + // Go to next line + $this->y += $h; + if($ln==1) + $this->x = $this->lMargin; + } + else + $this->x += $w; +} + +function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false) +{ + // Output text with automatic or explicit line breaks + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + $cw = $this->CurrentFont['cw']; + if($w==0) + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + $s = str_replace("\r",'',(string)$txt); + $nb = strlen($s); + if($nb>0 && $s[$nb-1]=="\n") + $nb--; + $b = 0; + if($border) + { + if($border==1) + { + $border = 'LTRB'; + $b = 'LRT'; + $b2 = 'LR'; + } + else + { + $b2 = ''; + if(strpos($border,'L')!==false) + $b2 .= 'L'; + if(strpos($border,'R')!==false) + $b2 .= 'R'; + $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2; + } + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $ns = 0; + $nl = 1; + while($i<$nb) + { + // Get next character + $c = $s[$i]; + if($c=="\n") + { + // Explicit line break + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + $i++; + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if($border && $nl==2) + $b = $b2; + continue; + } + if($c==' ') + { + $sep = $i; + $ls = $l; + $ns++; + } + $l += $cw[$c]; + if($l>$wmax) + { + // Automatic line break + if($sep==-1) + { + if($i==$j) + $i++; + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + } + else + { + if($align=='J') + { + $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; + $this->_out(sprintf('%.3F Tw',$this->ws*$this->k)); + } + $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill); + $i = $sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if($border && $nl==2) + $b = $b2; + } + else + $i++; + } + // Last chunk + if($this->ws>0) + { + $this->ws = 0; + $this->_out('0 Tw'); + } + if($border && strpos($border,'B')!==false) + $b .= 'B'; + $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill); + $this->x = $this->lMargin; +} + +function Write($h, $txt, $link='') +{ + // Output text in flowing mode + if(!isset($this->CurrentFont)) + $this->Error('No font has been set'); + $cw = $this->CurrentFont['cw']; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + $s = str_replace("\r",'',(string)$txt); + $nb = strlen($s); + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while($i<$nb) + { + // Get next character + $c = $s[$i]; + if($c=="\n") + { + // Explicit line break + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); + $i++; + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) + { + $this->x = $this->lMargin; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + } + $nl++; + continue; + } + if($c==' ') + $sep = $i; + $l += $cw[$c]; + if($l>$wmax) + { + // Automatic line break + if($sep==-1) + { + if($this->x>$this->lMargin) + { + // Move to next line + $this->x = $this->lMargin; + $this->y += $h; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + $i++; + $nl++; + continue; + } + if($i==$j) + $i++; + $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',false,$link); + } + else + { + $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',false,$link); + $i = $sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) + { + $this->x = $this->lMargin; + $w = $this->w-$this->rMargin-$this->x; + $wmax = ($w-2*$this->cMargin)*1000/$this->FontSize; + } + $nl++; + } + else + $i++; + } + // Last chunk + if($i!=$j) + $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j),0,0,'',false,$link); +} + +function Ln($h=null) +{ + // Line feed; default value is the last cell height + $this->x = $this->lMargin; + if($h===null) + $this->y += $this->lasth; + else + $this->y += $h; +} + +function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='') +{ + // Put an image on the page + if($file=='') + $this->Error('Image file name is empty'); + if(!isset($this->images[$file])) + { + // First use of this image, get info + if($type=='') + { + $pos = strrpos($file,'.'); + if(!$pos) + $this->Error('Image file has no extension and no type was specified: '.$file); + $type = substr($file,$pos+1); + } + $type = strtolower($type); + if($type=='jpeg') + $type = 'jpg'; + $mtd = '_parse'.$type; + if(!method_exists($this,$mtd)) + $this->Error('Unsupported image type: '.$type); + $info = $this->$mtd($file); + $info['i'] = count($this->images)+1; + $this->images[$file] = $info; + } + else + $info = $this->images[$file]; + + // Automatic width and height calculation if needed + if($w==0 && $h==0) + { + // Put image at 96 dpi + $w = -96; + $h = -96; + } + if($w<0) + $w = -$info['w']*72/$w/$this->k; + if($h<0) + $h = -$info['h']*72/$h/$this->k; + if($w==0) + $w = $h*$info['w']/$info['h']; + if($h==0) + $h = $w*$info['h']/$info['w']; + + // Flowing mode + if($y===null) + { + if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) + { + // Automatic page break + $x2 = $this->x; + $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation); + $this->x = $x2; + } + $y = $this->y; + $this->y += $h; + } + + if($x===null) + $x = $this->x; + $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i'])); + if($link) + $this->Link($x,$y,$w,$h,$link); +} + +function GetPageWidth() +{ + // Get current page width + return $this->w; +} + +function GetPageHeight() +{ + // Get current page height + return $this->h; +} + +function GetX() +{ + // Get x position + return $this->x; +} + +function SetX($x) +{ + // Set x position + if($x>=0) + $this->x = $x; + else + $this->x = $this->w+$x; +} + +function GetY() +{ + // Get y position + return $this->y; +} + +function SetY($y, $resetX=true) +{ + // Set y position and optionally reset x + if($y>=0) + $this->y = $y; + else + $this->y = $this->h+$y; + if($resetX) + $this->x = $this->lMargin; +} + +function SetXY($x, $y) +{ + // Set x and y positions + $this->SetX($x); + $this->SetY($y,false); +} + +function Output($dest='', $name='', $isUTF8=false) +{ + // Output PDF to some destination + $this->Close(); + if(strlen($name)==1 && strlen($dest)!=1) + { + // Fix parameter order + $tmp = $dest; + $dest = $name; + $name = $tmp; + } + if($dest=='') + $dest = 'I'; + if($name=='') + $name = 'doc.pdf'; + switch(strtoupper($dest)) + { + case 'I': + // Send to standard output + $this->_checkoutput(); + if(PHP_SAPI!='cli') + { + // We send to a browser + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; '.$this->_httpencode('filename',$name,$isUTF8)); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + } + echo $this->buffer; + break; + case 'D': + // Download file + $this->_checkoutput(); + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; '.$this->_httpencode('filename',$name,$isUTF8)); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + echo $this->buffer; + break; + case 'F': + // Save to local file + if(!file_put_contents($name,$this->buffer)) + $this->Error('Unable to create output file: '.$name); + break; + case 'S': + // Return as a string + return $this->buffer; + default: + $this->Error('Incorrect output destination: '.$dest); + } + return ''; +} + +/******************************************************************************* +* Protected methods * +*******************************************************************************/ + +protected function _checkoutput() +{ + if(PHP_SAPI!='cli') + { + if(headers_sent($file,$line)) + $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); + } + if(ob_get_length()) + { + // The output buffer is not empty + if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents())) + { + // It contains only a UTF-8 BOM and/or whitespace, let's clean it + ob_clean(); + } + else + $this->Error("Some data has already been output, can't send PDF file"); + } +} + +protected function _getpagesize($size) +{ + if(is_string($size)) + { + $size = strtolower($size); + if(!isset($this->StdPageSizes[$size])) + $this->Error('Unknown page size: '.$size); + $a = $this->StdPageSizes[$size]; + return array($a[0]/$this->k, $a[1]/$this->k); + } + else + { + if($size[0]>$size[1]) + return array($size[1], $size[0]); + else + return $size; + } +} + +protected function _beginpage($orientation, $size, $rotation) +{ + $this->page++; + $this->pages[$this->page] = ''; + $this->PageLinks[$this->page] = array(); + $this->state = 2; + $this->x = $this->lMargin; + $this->y = $this->tMargin; + $this->FontFamily = ''; + // Check page size and orientation + if($orientation=='') + $orientation = $this->DefOrientation; + else + $orientation = strtoupper($orientation[0]); + if($size=='') + $size = $this->DefPageSize; + else + $size = $this->_getpagesize($size); + if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1]) + { + // New size or orientation + if($orientation=='P') + { + $this->w = $size[0]; + $this->h = $size[1]; + } + else + { + $this->w = $size[1]; + $this->h = $size[0]; + } + $this->wPt = $this->w*$this->k; + $this->hPt = $this->h*$this->k; + $this->PageBreakTrigger = $this->h-$this->bMargin; + $this->CurOrientation = $orientation; + $this->CurPageSize = $size; + } + if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1]) + $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); + if($rotation!=0) + { + if($rotation%90!=0) + $this->Error('Incorrect rotation value: '.$rotation); + $this->PageInfo[$this->page]['rotation'] = $rotation; + } + $this->CurRotation = $rotation; +} + +protected function _endpage() +{ + $this->state = 1; +} + +protected function _loadfont($font) +{ + // Load a font definition file from the font directory + if(strpos($font,'/')!==false || strpos($font,"\\")!==false) + $this->Error('Incorrect font definition file name: '.$font); + include($this->fontpath.$font); + if(!isset($name)) + $this->Error('Could not include font definition file'); + if(isset($enc)) + $enc = strtolower($enc); + if(!isset($subsetted)) + $subsetted = false; + return get_defined_vars(); +} + +protected function _isascii($s) +{ + // Test if string is ASCII + $nb = strlen($s); + for($i=0;$i<$nb;$i++) + { + if(ord($s[$i])>127) + return false; + } + return true; +} + +protected function _httpencode($param, $value, $isUTF8) +{ + // Encode HTTP header field parameter + if($this->_isascii($value)) + return $param.'="'.$value.'"'; + if(!$isUTF8) + $value = $this->_UTF8encode($value); + return $param."*=UTF-8''".rawurlencode($value); +} + +protected function _UTF8encode($s) +{ + // Convert ISO-8859-1 to UTF-8 + if($this->iconv) + return iconv('ISO-8859-1','UTF-8',$s); + $res = ''; + $nb = strlen($s); + for($i=0;$i<$nb;$i++) + { + $c = $s[$i]; + $v = ord($c); + if($v>=128) + { + $res .= chr(0xC0 | ($v >> 6)); + $res .= chr(0x80 | ($v & 0x3F)); + } + else + $res .= $c; + } + return $res; +} + +protected function _UTF8toUTF16($s) +{ + // Convert UTF-8 to UTF-16BE with BOM + $res = "\xFE\xFF"; + if($this->iconv) + return $res.iconv('UTF-8','UTF-16BE',$s); + $nb = strlen($s); + $i = 0; + while($i<$nb) + { + $c1 = ord($s[$i++]); + if($c1>=224) + { + // 3-byte character + $c2 = ord($s[$i++]); + $c3 = ord($s[$i++]); + $res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2)); + $res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F)); + } + elseif($c1>=192) + { + // 2-byte character + $c2 = ord($s[$i++]); + $res .= chr(($c1 & 0x1C)>>2); + $res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F)); + } + else + { + // Single-byte character + $res .= "\0".chr($c1); + } + } + return $res; +} + +protected function _escape($s) +{ + // Escape special characters + if(strpos($s,'(')!==false || strpos($s,')')!==false || strpos($s,'\\')!==false || strpos($s,"\r")!==false) + return str_replace(array('\\','(',')',"\r"), array('\\\\','\\(','\\)','\\r'), $s); + else + return $s; +} + +protected function _textstring($s) +{ + // Format a text string + if(!$this->_isascii($s)) + $s = $this->_UTF8toUTF16($s); + return '('.$this->_escape($s).')'; +} + +protected function _dounderline($x, $y, $txt) +{ + // Underline text + $up = $this->CurrentFont['up']; + $ut = $this->CurrentFont['ut']; + $w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' '); + return sprintf('%.2F %.2F %.2F %.2F re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt); +} + +protected function _parsejpg($file) +{ + // Extract info from a JPEG file + $a = getimagesize($file); + if(!$a) + $this->Error('Missing or incorrect image file: '.$file); + if($a[2]!=2) + $this->Error('Not a JPEG file: '.$file); + if(!isset($a['channels']) || $a['channels']==3) + $colspace = 'DeviceRGB'; + elseif($a['channels']==4) + $colspace = 'DeviceCMYK'; + else + $colspace = 'DeviceGray'; + $bpc = isset($a['bits']) ? $a['bits'] : 8; + $data = file_get_contents($file); + return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data); +} + +protected function _parsepng($file) +{ + // Extract info from a PNG file + $f = fopen($file,'rb'); + if(!$f) + $this->Error('Can\'t open image file: '.$file); + $info = $this->_parsepngstream($f,$file); + fclose($f); + return $info; +} + +protected function _parsepngstream($f, $file) +{ + // Check signature + if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) + $this->Error('Not a PNG file: '.$file); + + // Read header chunk + $this->_readstream($f,4); + if($this->_readstream($f,4)!='IHDR') + $this->Error('Incorrect PNG file: '.$file); + $w = $this->_readint($f); + $h = $this->_readint($f); + $bpc = ord($this->_readstream($f,1)); + if($bpc>8) + $this->Error('16-bit depth not supported: '.$file); + $ct = ord($this->_readstream($f,1)); + if($ct==0 || $ct==4) + $colspace = 'DeviceGray'; + elseif($ct==2 || $ct==6) + $colspace = 'DeviceRGB'; + elseif($ct==3) + $colspace = 'Indexed'; + else + $this->Error('Unknown color type: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown compression method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Unknown filter method: '.$file); + if(ord($this->_readstream($f,1))!=0) + $this->Error('Interlacing not supported: '.$file); + $this->_readstream($f,4); + $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w; + + // Scan chunks looking for palette, transparency and image data + $pal = ''; + $trns = ''; + $data = ''; + do + { + $n = $this->_readint($f); + $type = $this->_readstream($f,4); + if($type=='PLTE') + { + // Read palette + $pal = $this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='tRNS') + { + // Read transparency info + $t = $this->_readstream($f,$n); + if($ct==0) + $trns = array(ord(substr($t,1,1))); + elseif($ct==2) + $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1))); + else + { + $pos = strpos($t,chr(0)); + if($pos!==false) + $trns = array($pos); + } + $this->_readstream($f,4); + } + elseif($type=='IDAT') + { + // Read image data block + $data .= $this->_readstream($f,$n); + $this->_readstream($f,4); + } + elseif($type=='IEND') + break; + else + $this->_readstream($f,$n+4); + } + while($n); + + if($colspace=='Indexed' && empty($pal)) + $this->Error('Missing palette in '.$file); + $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns); + if($ct>=4) + { + // Extract alpha channel + if(!function_exists('gzuncompress')) + $this->Error('Zlib not available, can\'t handle alpha channel: '.$file); + $data = gzuncompress($data); + $color = ''; + $alpha = ''; + if($ct==4) + { + // Gray image + $len = 2*$w; + for($i=0;$i<$h;$i++) + { + $pos = (1+$len)*$i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data,$pos+1,$len); + $color .= preg_replace('/(.)./s','$1',$line); + $alpha .= preg_replace('/.(.)/s','$1',$line); + } + } + else + { + // RGB image + $len = 4*$w; + for($i=0;$i<$h;$i++) + { + $pos = (1+$len)*$i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data,$pos+1,$len); + $color .= preg_replace('/(.{3})./s','$1',$line); + $alpha .= preg_replace('/.{3}(.)/s','$1',$line); + } + } + unset($data); + $data = gzcompress($color); + $info['smask'] = gzcompress($alpha); + $this->WithAlpha = true; + if($this->PDFVersion<'1.4') + $this->PDFVersion = '1.4'; + } + $info['data'] = $data; + return $info; +} + +protected function _readstream($f, $n) +{ + // Read n bytes from stream + $res = ''; + while($n>0 && !feof($f)) + { + $s = fread($f,$n); + if($s===false) + $this->Error('Error while reading stream'); + $n -= strlen($s); + $res .= $s; + } + if($n>0) + $this->Error('Unexpected end of stream'); + return $res; +} + +protected function _readint($f) +{ + // Read a 4-byte integer from stream + $a = unpack('Ni',$this->_readstream($f,4)); + return $a['i']; +} + +protected function _parsegif($file) +{ + // Extract info from a GIF file (via PNG conversion) + if(!function_exists('imagepng')) + $this->Error('GD extension is required for GIF support'); + if(!function_exists('imagecreatefromgif')) + $this->Error('GD has no GIF read support'); + $im = imagecreatefromgif($file); + if(!$im) + $this->Error('Missing or incorrect image file: '.$file); + imageinterlace($im,0); + ob_start(); + imagepng($im); + $data = ob_get_clean(); + imagedestroy($im); + $f = fopen('php://temp','rb+'); + if(!$f) + $this->Error('Unable to create memory stream'); + fwrite($f,$data); + rewind($f); + $info = $this->_parsepngstream($f,$file); + fclose($f); + return $info; +} + +protected function _out($s) +{ + // Add a line to the current page + if($this->state==2) + $this->pages[$this->page] .= $s."\n"; + elseif($this->state==0) + $this->Error('No page has been added yet'); + elseif($this->state==1) + $this->Error('Invalid call'); + elseif($this->state==3) + $this->Error('The document is closed'); +} + +protected function _put($s) +{ + // Add a line to the document + $this->buffer .= $s."\n"; +} + +protected function _getoffset() +{ + return strlen($this->buffer); +} + +protected function _newobj($n=null) +{ + // Begin a new object + if($n===null) + $n = ++$this->n; + $this->offsets[$n] = $this->_getoffset(); + $this->_put($n.' 0 obj'); +} + +protected function _putstream($data) +{ + $this->_put('stream'); + $this->_put($data); + $this->_put('endstream'); +} + +protected function _putstreamobject($data) +{ + if($this->compress) + { + $entries = '/Filter /FlateDecode '; + $data = gzcompress($data); + } + else + $entries = ''; + $entries .= '/Length '.strlen($data); + $this->_newobj(); + $this->_put('<<'.$entries.'>>'); + $this->_putstream($data); + $this->_put('endobj'); +} + +protected function _putlinks($n) +{ + foreach($this->PageLinks[$n] as $pl) + { + $this->_newobj(); + $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $s = '<_textstring($pl[4]).'>>>>'; + else + { + $l = $this->links[$pl[4]]; + if(isset($this->PageInfo[$l[0]]['size'])) + $h = $this->PageInfo[$l[0]]['size'][1]; + else + $h = ($this->DefOrientation=='P') ? $this->DefPageSize[1]*$this->k : $this->DefPageSize[0]*$this->k; + $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',$this->PageInfo[$l[0]]['n'],$h-$l[1]*$this->k); + } + $this->_put($s); + $this->_put('endobj'); + } +} + +protected function _putpage($n) +{ + $this->_newobj(); + $this->_put('<_put('/Parent 1 0 R'); + if(isset($this->PageInfo[$n]['size'])) + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageInfo[$n]['size'][0],$this->PageInfo[$n]['size'][1])); + if(isset($this->PageInfo[$n]['rotation'])) + $this->_put('/Rotate '.$this->PageInfo[$n]['rotation']); + $this->_put('/Resources 2 0 R'); + if(!empty($this->PageLinks[$n])) + { + $s = '/Annots ['; + foreach($this->PageLinks[$n] as $pl) + $s .= $pl[5].' 0 R '; + $s .= ']'; + $this->_put($s); + } + if($this->WithAlpha) + $this->_put('/Group <>'); + $this->_put('/Contents '.($this->n+1).' 0 R>>'); + $this->_put('endobj'); + // Page content + if(!empty($this->AliasNbPages)) + $this->pages[$n] = str_replace($this->AliasNbPages,$this->page,$this->pages[$n]); + $this->_putstreamobject($this->pages[$n]); + // Link annotations + $this->_putlinks($n); +} + +protected function _putpages() +{ + $nb = $this->page; + $n = $this->n; + for($i=1;$i<=$nb;$i++) + { + $this->PageInfo[$i]['n'] = ++$n; + $n++; + foreach($this->PageLinks[$i] as &$pl) + $pl[5] = ++$n; + unset($pl); + } + for($i=1;$i<=$nb;$i++) + $this->_putpage($i); + // Pages root + $this->_newobj(1); + $this->_put('<PageInfo[$i]['n'].' 0 R '; + $kids .= ']'; + $this->_put($kids); + $this->_put('/Count '.$nb); + if($this->DefOrientation=='P') + { + $w = $this->DefPageSize[0]; + $h = $this->DefPageSize[1]; + } + else + { + $w = $this->DefPageSize[1]; + $h = $this->DefPageSize[0]; + } + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]',$w*$this->k,$h*$this->k)); + $this->_put('>>'); + $this->_put('endobj'); +} + +protected function _putfonts() +{ + foreach($this->FontFiles as $file=>$info) + { + // Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n'] = $this->n; + $font = file_get_contents($this->fontpath.$file,true); + if(!$font) + $this->Error('Font file not found: '.$file); + $compressed = (substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) + $font = substr($font,6,$info['length1']).substr($font,6+$info['length1']+6,$info['length2']); + $this->_put('<_put('/Filter /FlateDecode'); + $this->_put('/Length1 '.$info['length1']); + if(isset($info['length2'])) + $this->_put('/Length2 '.$info['length2'].' /Length3 0'); + $this->_put('>>'); + $this->_putstream($font); + $this->_put('endobj'); + } + foreach($this->fonts as $k=>$font) + { + // Encoding + if(isset($font['diff'])) + { + if(!isset($this->encodings[$font['enc']])) + { + $this->_newobj(); + $this->_put('<>'); + $this->_put('endobj'); + $this->encodings[$font['enc']] = $this->n; + } + } + // ToUnicode CMap + if(isset($font['uv'])) + { + if(isset($font['enc'])) + $cmapkey = $font['enc']; + else + $cmapkey = $font['name']; + if(!isset($this->cmaps[$cmapkey])) + { + $cmap = $this->_tounicodecmap($font['uv']); + $this->_putstreamobject($cmap); + $this->cmaps[$cmapkey] = $this->n; + } + } + // Font object + $this->fonts[$k]['n'] = $this->n+1; + $type = $font['type']; + $name = $font['name']; + if($font['subsetted']) + $name = 'AAAAAA+'.$name; + if($type=='Core') + { + // Core font + $this->_newobj(); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') + $this->_put('/Encoding /WinAnsiEncoding'); + if(isset($font['uv'])) + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); + } + elseif($type=='Type1' || $type=='TrueType') + { + // Additional Type1 or TrueType/OpenType font + $this->_newobj(); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /'.$type); + $this->_put('/FirstChar 32 /LastChar 255'); + $this->_put('/Widths '.($this->n+1).' 0 R'); + $this->_put('/FontDescriptor '.($this->n+2).' 0 R'); + if(isset($font['diff'])) + $this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R'); + else + $this->_put('/Encoding /WinAnsiEncoding'); + if(isset($font['uv'])) + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + $this->_put('>>'); + $this->_put('endobj'); + // Widths + $this->_newobj(); + $cw = $font['cw']; + $s = '['; + for($i=32;$i<=255;$i++) + $s .= $cw[chr($i)].' '; + $this->_put($s.']'); + $this->_put('endobj'); + // Descriptor + $this->_newobj(); + $s = '<$v) + $s .= ' /'.$k.' '.$v; + if(!empty($font['file'])) + $s .= ' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; + $this->_put($s.'>>'); + $this->_put('endobj'); + } + else + { + // Allow for additional types + $mtd = '_put'.strtolower($type); + if(!method_exists($this,$mtd)) + $this->Error('Unsupported font type: '.$type); + $this->$mtd($font); + } + } +} + +protected function _tounicodecmap($uv) +{ + $ranges = ''; + $nbr = 0; + $chars = ''; + $nbc = 0; + foreach($uv as $c=>$v) + { + if(is_array($v)) + { + $ranges .= sprintf("<%02X> <%02X> <%04X>\n",$c,$c+$v[1]-1,$v[0]); + $nbr++; + } + else + { + $chars .= sprintf("<%02X> <%04X>\n",$c,$v); + $nbc++; + } + } + $s = "/CIDInit /ProcSet findresource begin\n"; + $s .= "12 dict begin\n"; + $s .= "begincmap\n"; + $s .= "/CIDSystemInfo\n"; + $s .= "<0) + { + $s .= "$nbr beginbfrange\n"; + $s .= $ranges; + $s .= "endbfrange\n"; + } + if($nbc>0) + { + $s .= "$nbc beginbfchar\n"; + $s .= $chars; + $s .= "endbfchar\n"; + } + $s .= "endcmap\n"; + $s .= "CMapName currentdict /CMap defineresource pop\n"; + $s .= "end\n"; + $s .= "end"; + return $s; +} + +protected function _putimages() +{ + foreach(array_keys($this->images) as $file) + { + $this->_putimage($this->images[$file]); + unset($this->images[$file]['data']); + unset($this->images[$file]['smask']); + } +} + +protected function _putimage(&$info) +{ + $this->_newobj(); + $info['n'] = $this->n; + $this->_put('<_put('/Subtype /Image'); + $this->_put('/Width '.$info['w']); + $this->_put('/Height '.$info['h']); + if($info['cs']=='Indexed') + $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + else + { + $this->_put('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') + $this->_put('/Decode [1 0 1 0 1 0 1 0]'); + } + $this->_put('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) + $this->_put('/Filter /'.$info['f']); + if(isset($info['dp'])) + $this->_put('/DecodeParms <<'.$info['dp'].'>>'); + if(isset($info['trns']) && is_array($info['trns'])) + { + $trns = ''; + for($i=0;$i_put('/Mask ['.$trns.']'); + } + if(isset($info['smask'])) + $this->_put('/SMask '.($this->n+1).' 0 R'); + $this->_put('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + $this->_put('endobj'); + // Soft mask + if(isset($info['smask'])) + { + $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w']; + $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']); + $this->_putimage($smask); + } + // Palette + if($info['cs']=='Indexed') + $this->_putstreamobject($info['pal']); +} + +protected function _putxobjectdict() +{ + foreach($this->images as $image) + $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R'); +} + +protected function _putresourcedict() +{ + $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_put('/Font <<'); + foreach($this->fonts as $font) + $this->_put('/F'.$font['i'].' '.$font['n'].' 0 R'); + $this->_put('>>'); + $this->_put('/XObject <<'); + $this->_putxobjectdict(); + $this->_put('>>'); +} + +protected function _putresources() +{ + $this->_putfonts(); + $this->_putimages(); + // Resource dictionary + $this->_newobj(2); + $this->_put('<<'); + $this->_putresourcedict(); + $this->_put('>>'); + $this->_put('endobj'); +} + +protected function _putinfo() +{ + $date = @date('YmdHisO', $this->CreationDate); + $this->metadata['CreationDate'] = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'"; + foreach($this->metadata as $key=>$value) + $this->_put('/'.$key.' '.$this->_textstring($value)); +} + +protected function _putcatalog() +{ + $n = $this->PageInfo[1]['n']; + $this->_put('/Type /Catalog'); + $this->_put('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') + $this->_put('/OpenAction ['.$n.' 0 R /Fit]'); + elseif($this->ZoomMode=='fullwidth') + $this->_put('/OpenAction ['.$n.' 0 R /FitH null]'); + elseif($this->ZoomMode=='real') + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]'); + elseif(!is_string($this->ZoomMode)) + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']'); + if($this->LayoutMode=='single') + $this->_put('/PageLayout /SinglePage'); + elseif($this->LayoutMode=='continuous') + $this->_put('/PageLayout /OneColumn'); + elseif($this->LayoutMode=='two') + $this->_put('/PageLayout /TwoColumnLeft'); +} + +protected function _putheader() +{ + $this->_put('%PDF-'.$this->PDFVersion); +} + +protected function _puttrailer() +{ + $this->_put('/Size '.($this->n+1)); + $this->_put('/Root '.$this->n.' 0 R'); + $this->_put('/Info '.($this->n-1).' 0 R'); +} + +protected function _enddoc() +{ + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + // Info + $this->_newobj(); + $this->_put('<<'); + $this->_putinfo(); + $this->_put('>>'); + $this->_put('endobj'); + // Catalog + $this->_newobj(); + $this->_put('<<'); + $this->_putcatalog(); + $this->_put('>>'); + $this->_put('endobj'); + // Cross-ref + $offset = $this->_getoffset(); + $this->_put('xref'); + $this->_put('0 '.($this->n+1)); + $this->_put('0000000000 65535 f '); + for($i=1;$i<=$this->n;$i++) + $this->_put(sprintf('%010d 00000 n ',$this->offsets[$i])); + // Trailer + $this->_put('trailer'); + $this->_put('<<'); + $this->_puttrailer(); + $this->_put('>>'); + $this->_put('startxref'); + $this->_put($offset); + $this->_put('%%EOF'); + $this->state = 3; + $this->CreationDate = time(); +} +} +?> diff --git a/videodb/vendor/setasign/fpdf/install.txt b/videodb/vendor/setasign/fpdf/install.txt new file mode 100644 index 0000000..62d25e6 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/install.txt @@ -0,0 +1,15 @@ +The FPDF library is made up of the following elements: + +- the main file, fpdf.php, which contains the class +- the font definition files located in the font directory + +The font definition files are necessary as soon as you want to output some text in a document. +If they are not accessible, the SetFont() method will produce the following error: + +FPDF error: Could not include font definition file + + +Remarks: + +- Only the files corresponding to the fonts actually used are necessary +- The tutorials provided in this package are ready to be executed diff --git a/videodb/vendor/setasign/fpdf/license.txt b/videodb/vendor/setasign/fpdf/license.txt new file mode 100644 index 0000000..6107ee4 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/license.txt @@ -0,0 +1,6 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software to use, copy, modify, distribute, sublicense, and/or sell +copies of the software, and to permit persons to whom the software is furnished +to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. \ No newline at end of file diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1250.map b/videodb/vendor/setasign/fpdf/makefont/cp1250.map new file mode 100644 index 0000000..ec110af --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1250.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+015A Sacute +!8D U+0164 Tcaron +!8E U+017D Zcaron +!8F U+0179 Zacute +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+015B sacute +!9D U+0165 tcaron +!9E U+017E zcaron +!9F U+017A zacute +!A0 U+00A0 space +!A1 U+02C7 caron +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+0104 Aogonek +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+015E Scedilla +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+0105 aogonek +!BA U+015F scedilla +!BB U+00BB guillemotright +!BC U+013D Lcaron +!BD U+02DD hungarumlaut +!BE U+013E lcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1251.map b/videodb/vendor/setasign/fpdf/makefont/cp1251.map new file mode 100644 index 0000000..de6a198 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1251.map @@ -0,0 +1,255 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0402 afii10051 +!81 U+0403 afii10052 +!82 U+201A quotesinglbase +!83 U+0453 afii10100 +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+20AC Euro +!89 U+2030 perthousand +!8A U+0409 afii10058 +!8B U+2039 guilsinglleft +!8C U+040A afii10059 +!8D U+040C afii10061 +!8E U+040B afii10060 +!8F U+040F afii10145 +!90 U+0452 afii10099 +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9A U+0459 afii10106 +!9B U+203A guilsinglright +!9C U+045A afii10107 +!9D U+045C afii10109 +!9E U+045B afii10108 +!9F U+045F afii10193 +!A0 U+00A0 space +!A1 U+040E afii10062 +!A2 U+045E afii10110 +!A3 U+0408 afii10057 +!A4 U+00A4 currency +!A5 U+0490 afii10050 +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+0401 afii10023 +!A9 U+00A9 copyright +!AA U+0404 afii10053 +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+0407 afii10056 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+0406 afii10055 +!B3 U+0456 afii10103 +!B4 U+0491 afii10098 +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0451 afii10071 +!B9 U+2116 afii61352 +!BA U+0454 afii10101 +!BB U+00BB guillemotright +!BC U+0458 afii10105 +!BD U+0405 afii10054 +!BE U+0455 afii10102 +!BF U+0457 afii10104 +!C0 U+0410 afii10017 +!C1 U+0411 afii10018 +!C2 U+0412 afii10019 +!C3 U+0413 afii10020 +!C4 U+0414 afii10021 +!C5 U+0415 afii10022 +!C6 U+0416 afii10024 +!C7 U+0417 afii10025 +!C8 U+0418 afii10026 +!C9 U+0419 afii10027 +!CA U+041A afii10028 +!CB U+041B afii10029 +!CC U+041C afii10030 +!CD U+041D afii10031 +!CE U+041E afii10032 +!CF U+041F afii10033 +!D0 U+0420 afii10034 +!D1 U+0421 afii10035 +!D2 U+0422 afii10036 +!D3 U+0423 afii10037 +!D4 U+0424 afii10038 +!D5 U+0425 afii10039 +!D6 U+0426 afii10040 +!D7 U+0427 afii10041 +!D8 U+0428 afii10042 +!D9 U+0429 afii10043 +!DA U+042A afii10044 +!DB U+042B afii10045 +!DC U+042C afii10046 +!DD U+042D afii10047 +!DE U+042E afii10048 +!DF U+042F afii10049 +!E0 U+0430 afii10065 +!E1 U+0431 afii10066 +!E2 U+0432 afii10067 +!E3 U+0433 afii10068 +!E4 U+0434 afii10069 +!E5 U+0435 afii10070 +!E6 U+0436 afii10072 +!E7 U+0437 afii10073 +!E8 U+0438 afii10074 +!E9 U+0439 afii10075 +!EA U+043A afii10076 +!EB U+043B afii10077 +!EC U+043C afii10078 +!ED U+043D afii10079 +!EE U+043E afii10080 +!EF U+043F afii10081 +!F0 U+0440 afii10082 +!F1 U+0441 afii10083 +!F2 U+0442 afii10084 +!F3 U+0443 afii10085 +!F4 U+0444 afii10086 +!F5 U+0445 afii10087 +!F6 U+0446 afii10088 +!F7 U+0447 afii10089 +!F8 U+0448 afii10090 +!F9 U+0449 afii10091 +!FA U+044A afii10092 +!FB U+044B afii10093 +!FC U+044C afii10094 +!FD U+044D afii10095 +!FE U+044E afii10096 +!FF U+044F afii10097 diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1252.map b/videodb/vendor/setasign/fpdf/makefont/cp1252.map new file mode 100644 index 0000000..dd490e5 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1252.map @@ -0,0 +1,251 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!8E U+017D Zcaron +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9E U+017E zcaron +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1253.map b/videodb/vendor/setasign/fpdf/makefont/cp1253.map new file mode 100644 index 0000000..4bd826f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1253.map @@ -0,0 +1,239 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+0385 dieresistonos +!A2 U+0386 Alphatonos +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1254.map b/videodb/vendor/setasign/fpdf/makefont/cp1254.map new file mode 100644 index 0000000..829473b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1254.map @@ -0,0 +1,249 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8A U+0160 Scaron +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9A U+0161 scaron +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1255.map b/videodb/vendor/setasign/fpdf/makefont/cp1255.map new file mode 100644 index 0000000..079e10c --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1255.map @@ -0,0 +1,233 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AA afii57636 +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00D7 multiply +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD sfthyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 middot +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00F7 divide +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+05B0 afii57799 +!C1 U+05B1 afii57801 +!C2 U+05B2 afii57800 +!C3 U+05B3 afii57802 +!C4 U+05B4 afii57793 +!C5 U+05B5 afii57794 +!C6 U+05B6 afii57795 +!C7 U+05B7 afii57798 +!C8 U+05B8 afii57797 +!C9 U+05B9 afii57806 +!CB U+05BB afii57796 +!CC U+05BC afii57807 +!CD U+05BD afii57839 +!CE U+05BE afii57645 +!CF U+05BF afii57841 +!D0 U+05C0 afii57842 +!D1 U+05C1 afii57804 +!D2 U+05C2 afii57803 +!D3 U+05C3 afii57658 +!D4 U+05F0 afii57716 +!D5 U+05F1 afii57717 +!D6 U+05F2 afii57718 +!D7 U+05F3 gereshhebrew +!D8 U+05F4 gershayimhebrew +!E0 U+05D0 afii57664 +!E1 U+05D1 afii57665 +!E2 U+05D2 afii57666 +!E3 U+05D3 afii57667 +!E4 U+05D4 afii57668 +!E5 U+05D5 afii57669 +!E6 U+05D6 afii57670 +!E7 U+05D7 afii57671 +!E8 U+05D8 afii57672 +!E9 U+05D9 afii57673 +!EA U+05DA afii57674 +!EB U+05DB afii57675 +!EC U+05DC afii57676 +!ED U+05DD afii57677 +!EE U+05DE afii57678 +!EF U+05DF afii57679 +!F0 U+05E0 afii57680 +!F1 U+05E1 afii57681 +!F2 U+05E2 afii57682 +!F3 U+05E3 afii57683 +!F4 U+05E4 afii57684 +!F5 U+05E5 afii57685 +!F6 U+05E6 afii57686 +!F7 U+05E7 afii57687 +!F8 U+05E8 afii57688 +!F9 U+05E9 afii57689 +!FA U+05EA afii57690 +!FD U+200E afii299 +!FE U+200F afii300 diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1257.map b/videodb/vendor/setasign/fpdf/makefont/cp1257.map new file mode 100644 index 0000000..2f2ecfa --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1257.map @@ -0,0 +1,244 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8D U+00A8 dieresis +!8E U+02C7 caron +!8F U+00B8 cedilla +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!99 U+2122 trademark +!9B U+203A guilsinglright +!9D U+00AF macron +!9E U+02DB ogonek +!A0 U+00A0 space +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00D8 Oslash +!A9 U+00A9 copyright +!AA U+0156 Rcommaaccent +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00C6 AE +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00F8 oslash +!B9 U+00B9 onesuperior +!BA U+0157 rcommaaccent +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00E6 ae +!C0 U+0104 Aogonek +!C1 U+012E Iogonek +!C2 U+0100 Amacron +!C3 U+0106 Cacute +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+0118 Eogonek +!C7 U+0112 Emacron +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0179 Zacute +!CB U+0116 Edotaccent +!CC U+0122 Gcommaaccent +!CD U+0136 Kcommaaccent +!CE U+012A Imacron +!CF U+013B Lcommaaccent +!D0 U+0160 Scaron +!D1 U+0143 Nacute +!D2 U+0145 Ncommaaccent +!D3 U+00D3 Oacute +!D4 U+014C Omacron +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0172 Uogonek +!D9 U+0141 Lslash +!DA U+015A Sacute +!DB U+016A Umacron +!DC U+00DC Udieresis +!DD U+017B Zdotaccent +!DE U+017D Zcaron +!DF U+00DF germandbls +!E0 U+0105 aogonek +!E1 U+012F iogonek +!E2 U+0101 amacron +!E3 U+0107 cacute +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+0119 eogonek +!E7 U+0113 emacron +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+017A zacute +!EB U+0117 edotaccent +!EC U+0123 gcommaaccent +!ED U+0137 kcommaaccent +!EE U+012B imacron +!EF U+013C lcommaaccent +!F0 U+0161 scaron +!F1 U+0144 nacute +!F2 U+0146 ncommaaccent +!F3 U+00F3 oacute +!F4 U+014D omacron +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0173 uogonek +!F9 U+0142 lslash +!FA U+015B sacute +!FB U+016B umacron +!FC U+00FC udieresis +!FD U+017C zdotaccent +!FE U+017E zcaron +!FF U+02D9 dotaccent diff --git a/videodb/vendor/setasign/fpdf/makefont/cp1258.map b/videodb/vendor/setasign/fpdf/makefont/cp1258.map new file mode 100644 index 0000000..fed915f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp1258.map @@ -0,0 +1,247 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!82 U+201A quotesinglbase +!83 U+0192 florin +!84 U+201E quotedblbase +!85 U+2026 ellipsis +!86 U+2020 dagger +!87 U+2021 daggerdbl +!88 U+02C6 circumflex +!89 U+2030 perthousand +!8B U+2039 guilsinglleft +!8C U+0152 OE +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!98 U+02DC tilde +!99 U+2122 trademark +!9B U+203A guilsinglright +!9C U+0153 oe +!9F U+0178 Ydieresis +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+0300 gravecomb +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+00D1 Ntilde +!D2 U+0309 hookabovecomb +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+01A0 Ohorn +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+01AF Uhorn +!DE U+0303 tildecomb +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+0301 acutecomb +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+00F1 ntilde +!F2 U+0323 dotbelowcomb +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+01A1 ohorn +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+01B0 uhorn +!FE U+20AB dong +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/cp874.map b/videodb/vendor/setasign/fpdf/makefont/cp874.map new file mode 100644 index 0000000..1006e6b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/cp874.map @@ -0,0 +1,225 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+20AC Euro +!85 U+2026 ellipsis +!91 U+2018 quoteleft +!92 U+2019 quoteright +!93 U+201C quotedblleft +!94 U+201D quotedblright +!95 U+2022 bullet +!96 U+2013 endash +!97 U+2014 emdash +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-1.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-1.map new file mode 100644 index 0000000..61740a3 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-1.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-11.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-11.map new file mode 100644 index 0000000..9168812 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-11.map @@ -0,0 +1,248 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0E01 kokaithai +!A2 U+0E02 khokhaithai +!A3 U+0E03 khokhuatthai +!A4 U+0E04 khokhwaithai +!A5 U+0E05 khokhonthai +!A6 U+0E06 khorakhangthai +!A7 U+0E07 ngonguthai +!A8 U+0E08 chochanthai +!A9 U+0E09 chochingthai +!AA U+0E0A chochangthai +!AB U+0E0B sosothai +!AC U+0E0C chochoethai +!AD U+0E0D yoyingthai +!AE U+0E0E dochadathai +!AF U+0E0F topatakthai +!B0 U+0E10 thothanthai +!B1 U+0E11 thonangmonthothai +!B2 U+0E12 thophuthaothai +!B3 U+0E13 nonenthai +!B4 U+0E14 dodekthai +!B5 U+0E15 totaothai +!B6 U+0E16 thothungthai +!B7 U+0E17 thothahanthai +!B8 U+0E18 thothongthai +!B9 U+0E19 nonuthai +!BA U+0E1A bobaimaithai +!BB U+0E1B poplathai +!BC U+0E1C phophungthai +!BD U+0E1D fofathai +!BE U+0E1E phophanthai +!BF U+0E1F fofanthai +!C0 U+0E20 phosamphaothai +!C1 U+0E21 momathai +!C2 U+0E22 yoyakthai +!C3 U+0E23 roruathai +!C4 U+0E24 ruthai +!C5 U+0E25 lolingthai +!C6 U+0E26 luthai +!C7 U+0E27 wowaenthai +!C8 U+0E28 sosalathai +!C9 U+0E29 sorusithai +!CA U+0E2A sosuathai +!CB U+0E2B hohipthai +!CC U+0E2C lochulathai +!CD U+0E2D oangthai +!CE U+0E2E honokhukthai +!CF U+0E2F paiyannoithai +!D0 U+0E30 saraathai +!D1 U+0E31 maihanakatthai +!D2 U+0E32 saraaathai +!D3 U+0E33 saraamthai +!D4 U+0E34 saraithai +!D5 U+0E35 saraiithai +!D6 U+0E36 sarauethai +!D7 U+0E37 saraueethai +!D8 U+0E38 sarauthai +!D9 U+0E39 sarauuthai +!DA U+0E3A phinthuthai +!DF U+0E3F bahtthai +!E0 U+0E40 saraethai +!E1 U+0E41 saraaethai +!E2 U+0E42 saraothai +!E3 U+0E43 saraaimaimuanthai +!E4 U+0E44 saraaimaimalaithai +!E5 U+0E45 lakkhangyaothai +!E6 U+0E46 maiyamokthai +!E7 U+0E47 maitaikhuthai +!E8 U+0E48 maiekthai +!E9 U+0E49 maithothai +!EA U+0E4A maitrithai +!EB U+0E4B maichattawathai +!EC U+0E4C thanthakhatthai +!ED U+0E4D nikhahitthai +!EE U+0E4E yamakkanthai +!EF U+0E4F fongmanthai +!F0 U+0E50 zerothai +!F1 U+0E51 onethai +!F2 U+0E52 twothai +!F3 U+0E53 threethai +!F4 U+0E54 fourthai +!F5 U+0E55 fivethai +!F6 U+0E56 sixthai +!F7 U+0E57 seventhai +!F8 U+0E58 eightthai +!F9 U+0E59 ninethai +!FA U+0E5A angkhankhuthai +!FB U+0E5B khomutthai diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-15.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-15.map new file mode 100644 index 0000000..6c2b571 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-15.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+20AC Euro +!A5 U+00A5 yen +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+017D Zcaron +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+00D0 Eth +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+00DE Thorn +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+00F0 eth +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+00FE thorn +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-16.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-16.map new file mode 100644 index 0000000..202c8fe --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-16.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0105 aogonek +!A3 U+0141 Lslash +!A4 U+20AC Euro +!A5 U+201E quotedblbase +!A6 U+0160 Scaron +!A7 U+00A7 section +!A8 U+0161 scaron +!A9 U+00A9 copyright +!AA U+0218 Scommaaccent +!AB U+00AB guillemotleft +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017A zacute +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+010C Ccaron +!B3 U+0142 lslash +!B4 U+017D Zcaron +!B5 U+201D quotedblright +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+017E zcaron +!B9 U+010D ccaron +!BA U+0219 scommaaccent +!BB U+00BB guillemotright +!BC U+0152 OE +!BD U+0153 oe +!BE U+0178 Ydieresis +!BF U+017C zdotaccent +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0106 Cacute +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+015A Sacute +!D8 U+0170 Uhungarumlaut +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0118 Eogonek +!DE U+021A Tcommaaccent +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+0107 cacute +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+015B sacute +!F8 U+0171 uhungarumlaut +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0119 eogonek +!FE U+021B tcommaaccent +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-2.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-2.map new file mode 100644 index 0000000..65ae09f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-2.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+02D8 breve +!A3 U+0141 Lslash +!A4 U+00A4 currency +!A5 U+013D Lcaron +!A6 U+015A Sacute +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+015E Scedilla +!AB U+0164 Tcaron +!AC U+0179 Zacute +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+017B Zdotaccent +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0142 lslash +!B4 U+00B4 acute +!B5 U+013E lcaron +!B6 U+015B sacute +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+015F scedilla +!BB U+0165 tcaron +!BC U+017A zacute +!BD U+02DD hungarumlaut +!BE U+017E zcaron +!BF U+017C zdotaccent +!C0 U+0154 Racute +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+0102 Abreve +!C4 U+00C4 Adieresis +!C5 U+0139 Lacute +!C6 U+0106 Cacute +!C7 U+00C7 Ccedilla +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+011A Ecaron +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+010E Dcaron +!D0 U+0110 Dcroat +!D1 U+0143 Nacute +!D2 U+0147 Ncaron +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+0150 Ohungarumlaut +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+0158 Rcaron +!D9 U+016E Uring +!DA U+00DA Uacute +!DB U+0170 Uhungarumlaut +!DC U+00DC Udieresis +!DD U+00DD Yacute +!DE U+0162 Tcommaaccent +!DF U+00DF germandbls +!E0 U+0155 racute +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+0103 abreve +!E4 U+00E4 adieresis +!E5 U+013A lacute +!E6 U+0107 cacute +!E7 U+00E7 ccedilla +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+011B ecaron +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+010F dcaron +!F0 U+0111 dcroat +!F1 U+0144 nacute +!F2 U+0148 ncaron +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+0151 ohungarumlaut +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+0159 rcaron +!F9 U+016F uring +!FA U+00FA uacute +!FB U+0171 uhungarumlaut +!FC U+00FC udieresis +!FD U+00FD yacute +!FE U+0163 tcommaaccent +!FF U+02D9 dotaccent diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-4.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-4.map new file mode 100644 index 0000000..a7d87bf --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-4.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0104 Aogonek +!A2 U+0138 kgreenlandic +!A3 U+0156 Rcommaaccent +!A4 U+00A4 currency +!A5 U+0128 Itilde +!A6 U+013B Lcommaaccent +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+0160 Scaron +!AA U+0112 Emacron +!AB U+0122 Gcommaaccent +!AC U+0166 Tbar +!AD U+00AD hyphen +!AE U+017D Zcaron +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+0105 aogonek +!B2 U+02DB ogonek +!B3 U+0157 rcommaaccent +!B4 U+00B4 acute +!B5 U+0129 itilde +!B6 U+013C lcommaaccent +!B7 U+02C7 caron +!B8 U+00B8 cedilla +!B9 U+0161 scaron +!BA U+0113 emacron +!BB U+0123 gcommaaccent +!BC U+0167 tbar +!BD U+014A Eng +!BE U+017E zcaron +!BF U+014B eng +!C0 U+0100 Amacron +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+012E Iogonek +!C8 U+010C Ccaron +!C9 U+00C9 Eacute +!CA U+0118 Eogonek +!CB U+00CB Edieresis +!CC U+0116 Edotaccent +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+012A Imacron +!D0 U+0110 Dcroat +!D1 U+0145 Ncommaaccent +!D2 U+014C Omacron +!D3 U+0136 Kcommaaccent +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+0172 Uogonek +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0168 Utilde +!DE U+016A Umacron +!DF U+00DF germandbls +!E0 U+0101 amacron +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+012F iogonek +!E8 U+010D ccaron +!E9 U+00E9 eacute +!EA U+0119 eogonek +!EB U+00EB edieresis +!EC U+0117 edotaccent +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+012B imacron +!F0 U+0111 dcroat +!F1 U+0146 ncommaaccent +!F2 U+014D omacron +!F3 U+0137 kcommaaccent +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+0173 uogonek +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0169 utilde +!FE U+016B umacron +!FF U+02D9 dotaccent diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-5.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-5.map new file mode 100644 index 0000000..f9cd4ed --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-5.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+0401 afii10023 +!A2 U+0402 afii10051 +!A3 U+0403 afii10052 +!A4 U+0404 afii10053 +!A5 U+0405 afii10054 +!A6 U+0406 afii10055 +!A7 U+0407 afii10056 +!A8 U+0408 afii10057 +!A9 U+0409 afii10058 +!AA U+040A afii10059 +!AB U+040B afii10060 +!AC U+040C afii10061 +!AD U+00AD hyphen +!AE U+040E afii10062 +!AF U+040F afii10145 +!B0 U+0410 afii10017 +!B1 U+0411 afii10018 +!B2 U+0412 afii10019 +!B3 U+0413 afii10020 +!B4 U+0414 afii10021 +!B5 U+0415 afii10022 +!B6 U+0416 afii10024 +!B7 U+0417 afii10025 +!B8 U+0418 afii10026 +!B9 U+0419 afii10027 +!BA U+041A afii10028 +!BB U+041B afii10029 +!BC U+041C afii10030 +!BD U+041D afii10031 +!BE U+041E afii10032 +!BF U+041F afii10033 +!C0 U+0420 afii10034 +!C1 U+0421 afii10035 +!C2 U+0422 afii10036 +!C3 U+0423 afii10037 +!C4 U+0424 afii10038 +!C5 U+0425 afii10039 +!C6 U+0426 afii10040 +!C7 U+0427 afii10041 +!C8 U+0428 afii10042 +!C9 U+0429 afii10043 +!CA U+042A afii10044 +!CB U+042B afii10045 +!CC U+042C afii10046 +!CD U+042D afii10047 +!CE U+042E afii10048 +!CF U+042F afii10049 +!D0 U+0430 afii10065 +!D1 U+0431 afii10066 +!D2 U+0432 afii10067 +!D3 U+0433 afii10068 +!D4 U+0434 afii10069 +!D5 U+0435 afii10070 +!D6 U+0436 afii10072 +!D7 U+0437 afii10073 +!D8 U+0438 afii10074 +!D9 U+0439 afii10075 +!DA U+043A afii10076 +!DB U+043B afii10077 +!DC U+043C afii10078 +!DD U+043D afii10079 +!DE U+043E afii10080 +!DF U+043F afii10081 +!E0 U+0440 afii10082 +!E1 U+0441 afii10083 +!E2 U+0442 afii10084 +!E3 U+0443 afii10085 +!E4 U+0444 afii10086 +!E5 U+0445 afii10087 +!E6 U+0446 afii10088 +!E7 U+0447 afii10089 +!E8 U+0448 afii10090 +!E9 U+0449 afii10091 +!EA U+044A afii10092 +!EB U+044B afii10093 +!EC U+044C afii10094 +!ED U+044D afii10095 +!EE U+044E afii10096 +!EF U+044F afii10097 +!F0 U+2116 afii61352 +!F1 U+0451 afii10071 +!F2 U+0452 afii10099 +!F3 U+0453 afii10100 +!F4 U+0454 afii10101 +!F5 U+0455 afii10102 +!F6 U+0456 afii10103 +!F7 U+0457 afii10104 +!F8 U+0458 afii10105 +!F9 U+0459 afii10106 +!FA U+045A afii10107 +!FB U+045B afii10108 +!FC U+045C afii10109 +!FD U+00A7 section +!FE U+045E afii10110 +!FF U+045F afii10193 diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-7.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-7.map new file mode 100644 index 0000000..e163796 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-7.map @@ -0,0 +1,250 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+2018 quoteleft +!A2 U+2019 quoteright +!A3 U+00A3 sterling +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AF U+2015 afii00208 +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+0384 tonos +!B5 U+0385 dieresistonos +!B6 U+0386 Alphatonos +!B7 U+00B7 periodcentered +!B8 U+0388 Epsilontonos +!B9 U+0389 Etatonos +!BA U+038A Iotatonos +!BB U+00BB guillemotright +!BC U+038C Omicrontonos +!BD U+00BD onehalf +!BE U+038E Upsilontonos +!BF U+038F Omegatonos +!C0 U+0390 iotadieresistonos +!C1 U+0391 Alpha +!C2 U+0392 Beta +!C3 U+0393 Gamma +!C4 U+0394 Delta +!C5 U+0395 Epsilon +!C6 U+0396 Zeta +!C7 U+0397 Eta +!C8 U+0398 Theta +!C9 U+0399 Iota +!CA U+039A Kappa +!CB U+039B Lambda +!CC U+039C Mu +!CD U+039D Nu +!CE U+039E Xi +!CF U+039F Omicron +!D0 U+03A0 Pi +!D1 U+03A1 Rho +!D3 U+03A3 Sigma +!D4 U+03A4 Tau +!D5 U+03A5 Upsilon +!D6 U+03A6 Phi +!D7 U+03A7 Chi +!D8 U+03A8 Psi +!D9 U+03A9 Omega +!DA U+03AA Iotadieresis +!DB U+03AB Upsilondieresis +!DC U+03AC alphatonos +!DD U+03AD epsilontonos +!DE U+03AE etatonos +!DF U+03AF iotatonos +!E0 U+03B0 upsilondieresistonos +!E1 U+03B1 alpha +!E2 U+03B2 beta +!E3 U+03B3 gamma +!E4 U+03B4 delta +!E5 U+03B5 epsilon +!E6 U+03B6 zeta +!E7 U+03B7 eta +!E8 U+03B8 theta +!E9 U+03B9 iota +!EA U+03BA kappa +!EB U+03BB lambda +!EC U+03BC mu +!ED U+03BD nu +!EE U+03BE xi +!EF U+03BF omicron +!F0 U+03C0 pi +!F1 U+03C1 rho +!F2 U+03C2 sigma1 +!F3 U+03C3 sigma +!F4 U+03C4 tau +!F5 U+03C5 upsilon +!F6 U+03C6 phi +!F7 U+03C7 chi +!F8 U+03C8 psi +!F9 U+03C9 omega +!FA U+03CA iotadieresis +!FB U+03CB upsilondieresis +!FC U+03CC omicrontonos +!FD U+03CD upsilontonos +!FE U+03CE omegatonos diff --git a/videodb/vendor/setasign/fpdf/makefont/iso-8859-9.map b/videodb/vendor/setasign/fpdf/makefont/iso-8859-9.map new file mode 100644 index 0000000..48c123a --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/iso-8859-9.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+0080 .notdef +!81 U+0081 .notdef +!82 U+0082 .notdef +!83 U+0083 .notdef +!84 U+0084 .notdef +!85 U+0085 .notdef +!86 U+0086 .notdef +!87 U+0087 .notdef +!88 U+0088 .notdef +!89 U+0089 .notdef +!8A U+008A .notdef +!8B U+008B .notdef +!8C U+008C .notdef +!8D U+008D .notdef +!8E U+008E .notdef +!8F U+008F .notdef +!90 U+0090 .notdef +!91 U+0091 .notdef +!92 U+0092 .notdef +!93 U+0093 .notdef +!94 U+0094 .notdef +!95 U+0095 .notdef +!96 U+0096 .notdef +!97 U+0097 .notdef +!98 U+0098 .notdef +!99 U+0099 .notdef +!9A U+009A .notdef +!9B U+009B .notdef +!9C U+009C .notdef +!9D U+009D .notdef +!9E U+009E .notdef +!9F U+009F .notdef +!A0 U+00A0 space +!A1 U+00A1 exclamdown +!A2 U+00A2 cent +!A3 U+00A3 sterling +!A4 U+00A4 currency +!A5 U+00A5 yen +!A6 U+00A6 brokenbar +!A7 U+00A7 section +!A8 U+00A8 dieresis +!A9 U+00A9 copyright +!AA U+00AA ordfeminine +!AB U+00AB guillemotleft +!AC U+00AC logicalnot +!AD U+00AD hyphen +!AE U+00AE registered +!AF U+00AF macron +!B0 U+00B0 degree +!B1 U+00B1 plusminus +!B2 U+00B2 twosuperior +!B3 U+00B3 threesuperior +!B4 U+00B4 acute +!B5 U+00B5 mu +!B6 U+00B6 paragraph +!B7 U+00B7 periodcentered +!B8 U+00B8 cedilla +!B9 U+00B9 onesuperior +!BA U+00BA ordmasculine +!BB U+00BB guillemotright +!BC U+00BC onequarter +!BD U+00BD onehalf +!BE U+00BE threequarters +!BF U+00BF questiondown +!C0 U+00C0 Agrave +!C1 U+00C1 Aacute +!C2 U+00C2 Acircumflex +!C3 U+00C3 Atilde +!C4 U+00C4 Adieresis +!C5 U+00C5 Aring +!C6 U+00C6 AE +!C7 U+00C7 Ccedilla +!C8 U+00C8 Egrave +!C9 U+00C9 Eacute +!CA U+00CA Ecircumflex +!CB U+00CB Edieresis +!CC U+00CC Igrave +!CD U+00CD Iacute +!CE U+00CE Icircumflex +!CF U+00CF Idieresis +!D0 U+011E Gbreve +!D1 U+00D1 Ntilde +!D2 U+00D2 Ograve +!D3 U+00D3 Oacute +!D4 U+00D4 Ocircumflex +!D5 U+00D5 Otilde +!D6 U+00D6 Odieresis +!D7 U+00D7 multiply +!D8 U+00D8 Oslash +!D9 U+00D9 Ugrave +!DA U+00DA Uacute +!DB U+00DB Ucircumflex +!DC U+00DC Udieresis +!DD U+0130 Idotaccent +!DE U+015E Scedilla +!DF U+00DF germandbls +!E0 U+00E0 agrave +!E1 U+00E1 aacute +!E2 U+00E2 acircumflex +!E3 U+00E3 atilde +!E4 U+00E4 adieresis +!E5 U+00E5 aring +!E6 U+00E6 ae +!E7 U+00E7 ccedilla +!E8 U+00E8 egrave +!E9 U+00E9 eacute +!EA U+00EA ecircumflex +!EB U+00EB edieresis +!EC U+00EC igrave +!ED U+00ED iacute +!EE U+00EE icircumflex +!EF U+00EF idieresis +!F0 U+011F gbreve +!F1 U+00F1 ntilde +!F2 U+00F2 ograve +!F3 U+00F3 oacute +!F4 U+00F4 ocircumflex +!F5 U+00F5 otilde +!F6 U+00F6 odieresis +!F7 U+00F7 divide +!F8 U+00F8 oslash +!F9 U+00F9 ugrave +!FA U+00FA uacute +!FB U+00FB ucircumflex +!FC U+00FC udieresis +!FD U+0131 dotlessi +!FE U+015F scedilla +!FF U+00FF ydieresis diff --git a/videodb/vendor/setasign/fpdf/makefont/koi8-r.map b/videodb/vendor/setasign/fpdf/makefont/koi8-r.map new file mode 100644 index 0000000..6ad5d05 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/koi8-r.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2219 periodcentered +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+2553 SF520000 +!A5 U+2554 SF390000 +!A6 U+2555 SF220000 +!A7 U+2556 SF210000 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+255C SF270000 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+2562 SF200000 +!B5 U+2563 SF230000 +!B6 U+2564 SF470000 +!B7 U+2565 SF480000 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+256B SF530000 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/videodb/vendor/setasign/fpdf/makefont/koi8-u.map b/videodb/vendor/setasign/fpdf/makefont/koi8-u.map new file mode 100644 index 0000000..40a7e4f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/koi8-u.map @@ -0,0 +1,256 @@ +!00 U+0000 .notdef +!01 U+0001 .notdef +!02 U+0002 .notdef +!03 U+0003 .notdef +!04 U+0004 .notdef +!05 U+0005 .notdef +!06 U+0006 .notdef +!07 U+0007 .notdef +!08 U+0008 .notdef +!09 U+0009 .notdef +!0A U+000A .notdef +!0B U+000B .notdef +!0C U+000C .notdef +!0D U+000D .notdef +!0E U+000E .notdef +!0F U+000F .notdef +!10 U+0010 .notdef +!11 U+0011 .notdef +!12 U+0012 .notdef +!13 U+0013 .notdef +!14 U+0014 .notdef +!15 U+0015 .notdef +!16 U+0016 .notdef +!17 U+0017 .notdef +!18 U+0018 .notdef +!19 U+0019 .notdef +!1A U+001A .notdef +!1B U+001B .notdef +!1C U+001C .notdef +!1D U+001D .notdef +!1E U+001E .notdef +!1F U+001F .notdef +!20 U+0020 space +!21 U+0021 exclam +!22 U+0022 quotedbl +!23 U+0023 numbersign +!24 U+0024 dollar +!25 U+0025 percent +!26 U+0026 ampersand +!27 U+0027 quotesingle +!28 U+0028 parenleft +!29 U+0029 parenright +!2A U+002A asterisk +!2B U+002B plus +!2C U+002C comma +!2D U+002D hyphen +!2E U+002E period +!2F U+002F slash +!30 U+0030 zero +!31 U+0031 one +!32 U+0032 two +!33 U+0033 three +!34 U+0034 four +!35 U+0035 five +!36 U+0036 six +!37 U+0037 seven +!38 U+0038 eight +!39 U+0039 nine +!3A U+003A colon +!3B U+003B semicolon +!3C U+003C less +!3D U+003D equal +!3E U+003E greater +!3F U+003F question +!40 U+0040 at +!41 U+0041 A +!42 U+0042 B +!43 U+0043 C +!44 U+0044 D +!45 U+0045 E +!46 U+0046 F +!47 U+0047 G +!48 U+0048 H +!49 U+0049 I +!4A U+004A J +!4B U+004B K +!4C U+004C L +!4D U+004D M +!4E U+004E N +!4F U+004F O +!50 U+0050 P +!51 U+0051 Q +!52 U+0052 R +!53 U+0053 S +!54 U+0054 T +!55 U+0055 U +!56 U+0056 V +!57 U+0057 W +!58 U+0058 X +!59 U+0059 Y +!5A U+005A Z +!5B U+005B bracketleft +!5C U+005C backslash +!5D U+005D bracketright +!5E U+005E asciicircum +!5F U+005F underscore +!60 U+0060 grave +!61 U+0061 a +!62 U+0062 b +!63 U+0063 c +!64 U+0064 d +!65 U+0065 e +!66 U+0066 f +!67 U+0067 g +!68 U+0068 h +!69 U+0069 i +!6A U+006A j +!6B U+006B k +!6C U+006C l +!6D U+006D m +!6E U+006E n +!6F U+006F o +!70 U+0070 p +!71 U+0071 q +!72 U+0072 r +!73 U+0073 s +!74 U+0074 t +!75 U+0075 u +!76 U+0076 v +!77 U+0077 w +!78 U+0078 x +!79 U+0079 y +!7A U+007A z +!7B U+007B braceleft +!7C U+007C bar +!7D U+007D braceright +!7E U+007E asciitilde +!7F U+007F .notdef +!80 U+2500 SF100000 +!81 U+2502 SF110000 +!82 U+250C SF010000 +!83 U+2510 SF030000 +!84 U+2514 SF020000 +!85 U+2518 SF040000 +!86 U+251C SF080000 +!87 U+2524 SF090000 +!88 U+252C SF060000 +!89 U+2534 SF070000 +!8A U+253C SF050000 +!8B U+2580 upblock +!8C U+2584 dnblock +!8D U+2588 block +!8E U+258C lfblock +!8F U+2590 rtblock +!90 U+2591 ltshade +!91 U+2592 shade +!92 U+2593 dkshade +!93 U+2320 integraltp +!94 U+25A0 filledbox +!95 U+2022 bullet +!96 U+221A radical +!97 U+2248 approxequal +!98 U+2264 lessequal +!99 U+2265 greaterequal +!9A U+00A0 space +!9B U+2321 integralbt +!9C U+00B0 degree +!9D U+00B2 twosuperior +!9E U+00B7 periodcentered +!9F U+00F7 divide +!A0 U+2550 SF430000 +!A1 U+2551 SF240000 +!A2 U+2552 SF510000 +!A3 U+0451 afii10071 +!A4 U+0454 afii10101 +!A5 U+2554 SF390000 +!A6 U+0456 afii10103 +!A7 U+0457 afii10104 +!A8 U+2557 SF250000 +!A9 U+2558 SF500000 +!AA U+2559 SF490000 +!AB U+255A SF380000 +!AC U+255B SF280000 +!AD U+0491 afii10098 +!AE U+255D SF260000 +!AF U+255E SF360000 +!B0 U+255F SF370000 +!B1 U+2560 SF420000 +!B2 U+2561 SF190000 +!B3 U+0401 afii10023 +!B4 U+0404 afii10053 +!B5 U+2563 SF230000 +!B6 U+0406 afii10055 +!B7 U+0407 afii10056 +!B8 U+2566 SF410000 +!B9 U+2567 SF450000 +!BA U+2568 SF460000 +!BB U+2569 SF400000 +!BC U+256A SF540000 +!BD U+0490 afii10050 +!BE U+256C SF440000 +!BF U+00A9 copyright +!C0 U+044E afii10096 +!C1 U+0430 afii10065 +!C2 U+0431 afii10066 +!C3 U+0446 afii10088 +!C4 U+0434 afii10069 +!C5 U+0435 afii10070 +!C6 U+0444 afii10086 +!C7 U+0433 afii10068 +!C8 U+0445 afii10087 +!C9 U+0438 afii10074 +!CA U+0439 afii10075 +!CB U+043A afii10076 +!CC U+043B afii10077 +!CD U+043C afii10078 +!CE U+043D afii10079 +!CF U+043E afii10080 +!D0 U+043F afii10081 +!D1 U+044F afii10097 +!D2 U+0440 afii10082 +!D3 U+0441 afii10083 +!D4 U+0442 afii10084 +!D5 U+0443 afii10085 +!D6 U+0436 afii10072 +!D7 U+0432 afii10067 +!D8 U+044C afii10094 +!D9 U+044B afii10093 +!DA U+0437 afii10073 +!DB U+0448 afii10090 +!DC U+044D afii10095 +!DD U+0449 afii10091 +!DE U+0447 afii10089 +!DF U+044A afii10092 +!E0 U+042E afii10048 +!E1 U+0410 afii10017 +!E2 U+0411 afii10018 +!E3 U+0426 afii10040 +!E4 U+0414 afii10021 +!E5 U+0415 afii10022 +!E6 U+0424 afii10038 +!E7 U+0413 afii10020 +!E8 U+0425 afii10039 +!E9 U+0418 afii10026 +!EA U+0419 afii10027 +!EB U+041A afii10028 +!EC U+041B afii10029 +!ED U+041C afii10030 +!EE U+041D afii10031 +!EF U+041E afii10032 +!F0 U+041F afii10033 +!F1 U+042F afii10049 +!F2 U+0420 afii10034 +!F3 U+0421 afii10035 +!F4 U+0422 afii10036 +!F5 U+0423 afii10037 +!F6 U+0416 afii10024 +!F7 U+0412 afii10019 +!F8 U+042C afii10046 +!F9 U+042B afii10045 +!FA U+0417 afii10025 +!FB U+0428 afii10042 +!FC U+042D afii10047 +!FD U+0429 afii10043 +!FE U+0427 afii10041 +!FF U+042A afii10044 diff --git a/videodb/vendor/setasign/fpdf/makefont/makefont.php b/videodb/vendor/setasign/fpdf/makefont/makefont.php new file mode 100644 index 0000000..2c147ac --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/makefont.php @@ -0,0 +1,447 @@ +$severity
        : "; + echo "$txt
        "; + } +} + +function Notice($txt) +{ + Message($txt, 'Notice'); +} + +function Warning($txt) +{ + Message($txt, 'Warning'); +} + +function Error($txt) +{ + Message($txt, 'Error'); + exit; +} + +function LoadMap($enc) +{ + $file = dirname(__FILE__).'/'.strtolower($enc).'.map'; + $a = file($file); + if(empty($a)) + Error('Encoding not found: '.$enc); + $map = array_fill(0, 256, array('uv'=>-1, 'name'=>'.notdef')); + foreach($a as $line) + { + $e = explode(' ', rtrim($line)); + $c = hexdec(substr($e[0],1)); + $uv = hexdec(substr($e[1],2)); + $name = $e[2]; + $map[$c] = array('uv'=>$uv, 'name'=>$name); + } + return $map; +} + +function GetInfoFromTrueType($file, $embed, $subset, $map) +{ + // Return information from a TrueType font + try + { + $ttf = new TTFParser($file); + $ttf->Parse(); + } + catch(Exception $e) + { + Error($e->getMessage()); + } + if($embed) + { + if(!$ttf->embeddable) + Error('Font license does not allow embedding'); + if($subset) + { + $chars = array(); + foreach($map as $v) + { + if($v['name']!='.notdef') + $chars[] = $v['uv']; + } + $ttf->Subset($chars); + $info['Data'] = $ttf->Build(); + } + else + $info['Data'] = file_get_contents($file); + $info['OriginalSize'] = strlen($info['Data']); + } + $k = 1000/$ttf->unitsPerEm; + $info['FontName'] = $ttf->postScriptName; + $info['Bold'] = $ttf->bold; + $info['ItalicAngle'] = $ttf->italicAngle; + $info['IsFixedPitch'] = $ttf->isFixedPitch; + $info['Ascender'] = round($k*$ttf->typoAscender); + $info['Descender'] = round($k*$ttf->typoDescender); + $info['UnderlineThickness'] = round($k*$ttf->underlineThickness); + $info['UnderlinePosition'] = round($k*$ttf->underlinePosition); + $info['FontBBox'] = array(round($k*$ttf->xMin), round($k*$ttf->yMin), round($k*$ttf->xMax), round($k*$ttf->yMax)); + $info['CapHeight'] = round($k*$ttf->capHeight); + $info['MissingWidth'] = round($k*$ttf->glyphs[0]['w']); + $widths = array_fill(0, 256, $info['MissingWidth']); + foreach($map as $c=>$v) + { + if($v['name']!='.notdef') + { + if(isset($ttf->chars[$v['uv']])) + { + $id = $ttf->chars[$v['uv']]; + $w = $ttf->glyphs[$id]['w']; + $widths[$c] = round($k*$w); + } + else + Warning('Character '.$v['name'].' is missing'); + } + } + $info['Widths'] = $widths; + return $info; +} + +function GetInfoFromType1($file, $embed, $map) +{ + // Return information from a Type1 font + if($embed) + { + $f = fopen($file, 'rb'); + if(!$f) + Error('Can\'t open font file'); + // Read first segment + $a = unpack('Cmarker/Ctype/Vsize', fread($f,6)); + if($a['marker']!=128) + Error('Font file is not a valid binary Type1'); + $size1 = $a['size']; + $data = fread($f, $size1); + // Read second segment + $a = unpack('Cmarker/Ctype/Vsize', fread($f,6)); + if($a['marker']!=128) + Error('Font file is not a valid binary Type1'); + $size2 = $a['size']; + $data .= fread($f, $size2); + fclose($f); + $info['Data'] = $data; + $info['Size1'] = $size1; + $info['Size2'] = $size2; + } + + $afm = substr($file, 0, -3).'afm'; + if(!file_exists($afm)) + Error('AFM font file not found: '.$afm); + $a = file($afm); + if(empty($a)) + Error('AFM file empty or not readable'); + foreach($a as $line) + { + $e = explode(' ', rtrim($line)); + if(count($e)<2) + continue; + $entry = $e[0]; + if($entry=='C') + { + $w = $e[4]; + $name = $e[7]; + $cw[$name] = $w; + } + elseif($entry=='FontName') + $info['FontName'] = $e[1]; + elseif($entry=='Weight') + $info['Weight'] = $e[1]; + elseif($entry=='ItalicAngle') + $info['ItalicAngle'] = (int)$e[1]; + elseif($entry=='Ascender') + $info['Ascender'] = (int)$e[1]; + elseif($entry=='Descender') + $info['Descender'] = (int)$e[1]; + elseif($entry=='UnderlineThickness') + $info['UnderlineThickness'] = (int)$e[1]; + elseif($entry=='UnderlinePosition') + $info['UnderlinePosition'] = (int)$e[1]; + elseif($entry=='IsFixedPitch') + $info['IsFixedPitch'] = ($e[1]=='true'); + elseif($entry=='FontBBox') + $info['FontBBox'] = array((int)$e[1], (int)$e[2], (int)$e[3], (int)$e[4]); + elseif($entry=='CapHeight') + $info['CapHeight'] = (int)$e[1]; + elseif($entry=='StdVW') + $info['StdVW'] = (int)$e[1]; + } + + if(!isset($info['FontName'])) + Error('FontName missing in AFM file'); + if(!isset($info['Ascender'])) + $info['Ascender'] = $info['FontBBox'][3]; + if(!isset($info['Descender'])) + $info['Descender'] = $info['FontBBox'][1]; + $info['Bold'] = isset($info['Weight']) && preg_match('/bold|black/i', $info['Weight']); + if(isset($cw['.notdef'])) + $info['MissingWidth'] = $cw['.notdef']; + else + $info['MissingWidth'] = 0; + $widths = array_fill(0, 256, $info['MissingWidth']); + foreach($map as $c=>$v) + { + if($v['name']!='.notdef') + { + if(isset($cw[$v['name']])) + $widths[$c] = $cw[$v['name']]; + else + Warning('Character '.$v['name'].' is missing'); + } + } + $info['Widths'] = $widths; + return $info; +} + +function MakeFontDescriptor($info) +{ + // Ascent + $fd = "array('Ascent'=>".$info['Ascender']; + // Descent + $fd .= ",'Descent'=>".$info['Descender']; + // CapHeight + if(!empty($info['CapHeight'])) + $fd .= ",'CapHeight'=>".$info['CapHeight']; + else + $fd .= ",'CapHeight'=>".$info['Ascender']; + // Flags + $flags = 0; + if($info['IsFixedPitch']) + $flags += 1<<0; + $flags += 1<<5; + if($info['ItalicAngle']!=0) + $flags += 1<<6; + $fd .= ",'Flags'=>".$flags; + // FontBBox + $fbb = $info['FontBBox']; + $fd .= ",'FontBBox'=>'[".$fbb[0].' '.$fbb[1].' '.$fbb[2].' '.$fbb[3]."]'"; + // ItalicAngle + $fd .= ",'ItalicAngle'=>".$info['ItalicAngle']; + // StemV + if(isset($info['StdVW'])) + $stemv = $info['StdVW']; + elseif($info['Bold']) + $stemv = 120; + else + $stemv = 70; + $fd .= ",'StemV'=>".$stemv; + // MissingWidth + $fd .= ",'MissingWidth'=>".$info['MissingWidth'].')'; + return $fd; +} + +function MakeWidthArray($widths) +{ + $s = "array(\n\t"; + for($c=0;$c<=255;$c++) + { + if(chr($c)=="'") + $s .= "'\\''"; + elseif(chr($c)=="\\") + $s .= "'\\\\'"; + elseif($c>=32 && $c<=126) + $s .= "'".chr($c)."'"; + else + $s .= "chr($c)"; + $s .= '=>'.$widths[$c]; + if($c<255) + $s .= ','; + if(($c+1)%22==0) + $s .= "\n\t"; + } + $s .= ')'; + return $s; +} + +function MakeFontEncoding($map) +{ + // Build differences from reference encoding + $ref = LoadMap('cp1252'); + $s = ''; + $last = 0; + for($c=32;$c<=255;$c++) + { + if($map[$c]['name']!=$ref[$c]['name']) + { + if($c!=$last+1) + $s .= $c.' '; + $last = $c; + $s .= '/'.$map[$c]['name'].' '; + } + } + return rtrim($s); +} + +function MakeUnicodeArray($map) +{ + // Build mapping to Unicode values + $ranges = array(); + foreach($map as $c=>$v) + { + $uv = $v['uv']; + if($uv!=-1) + { + if(isset($range)) + { + if($c==$range[1]+1 && $uv==$range[3]+1) + { + $range[1]++; + $range[3]++; + } + else + { + $ranges[] = $range; + $range = array($c, $c, $uv, $uv); + } + } + else + $range = array($c, $c, $uv, $uv); + } + } + $ranges[] = $range; + + foreach($ranges as $range) + { + if(isset($s)) + $s .= ','; + else + $s = 'array('; + $s .= $range[0].'=>'; + $nb = $range[1]-$range[0]+1; + if($nb>1) + $s .= 'array('.$range[2].','.$nb.')'; + else + $s .= $range[2]; + } + $s .= ')'; + return $s; +} + +function SaveToFile($file, $s, $mode) +{ + $f = fopen($file, 'w'.$mode); + if(!$f) + Error('Can\'t write to file '.$file); + fwrite($f, $s); + fclose($f); +} + +function MakeDefinitionFile($file, $type, $enc, $embed, $subset, $map, $info) +{ + $s = "\n"; + SaveToFile($file, $s, 't'); +} + +function MakeFont($fontfile, $enc='cp1252', $embed=true, $subset=true) +{ + // Generate a font definition file + if(!file_exists($fontfile)) + Error('Font file not found: '.$fontfile); + $ext = strtolower(substr($fontfile,-3)); + if($ext=='ttf' || $ext=='otf') + $type = 'TrueType'; + elseif($ext=='pfb') + $type = 'Type1'; + else + Error('Unrecognized font file extension: '.$ext); + + $map = LoadMap($enc); + + if($type=='TrueType') + $info = GetInfoFromTrueType($fontfile, $embed, $subset, $map); + else + $info = GetInfoFromType1($fontfile, $embed, $map); + + $basename = substr(basename($fontfile), 0, -4); + if($embed) + { + if(function_exists('gzcompress')) + { + $file = $basename.'.z'; + SaveToFile($file, gzcompress($info['Data']), 'b'); + $info['File'] = $file; + Message('Font file compressed: '.$file); + } + else + { + $info['File'] = basename($fontfile); + $subset = false; + Notice('Font file could not be compressed (zlib extension not available)'); + } + } + + MakeDefinitionFile($basename.'.php', $type, $enc, $embed, $subset, $map, $info); + Message('Font definition file generated: '.$basename.'.php'); +} + +if(PHP_SAPI=='cli') +{ + // Command-line interface + ini_set('log_errors', '0'); + if($argc==1) + die("Usage: php makefont.php fontfile [encoding] [embed] [subset]\n"); + $fontfile = $argv[1]; + if($argc>=3) + $enc = $argv[2]; + else + $enc = 'cp1252'; + if($argc>=4) + $embed = ($argv[3]=='true' || $argv[3]=='1'); + else + $embed = true; + if($argc>=5) + $subset = ($argv[4]=='true' || $argv[4]=='1'); + else + $subset = true; + MakeFont($fontfile, $enc, $embed, $subset); +} +?> diff --git a/videodb/vendor/setasign/fpdf/makefont/ttfparser.php b/videodb/vendor/setasign/fpdf/makefont/ttfparser.php new file mode 100644 index 0000000..1ec1276 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/makefont/ttfparser.php @@ -0,0 +1,714 @@ +f = fopen($file, 'rb'); + if(!$this->f) + $this->Error('Can\'t open file: '.$file); + } + + function __destruct() + { + if(is_resource($this->f)) + fclose($this->f); + } + + function Parse() + { + $this->ParseOffsetTable(); + $this->ParseHead(); + $this->ParseHhea(); + $this->ParseMaxp(); + $this->ParseHmtx(); + $this->ParseLoca(); + $this->ParseGlyf(); + $this->ParseCmap(); + $this->ParseName(); + $this->ParseOS2(); + $this->ParsePost(); + } + + function ParseOffsetTable() + { + $version = $this->Read(4); + if($version=='OTTO') + $this->Error('OpenType fonts based on PostScript outlines are not supported'); + if($version!="\x00\x01\x00\x00") + $this->Error('Unrecognized file format'); + $numTables = $this->ReadUShort(); + $this->Skip(3*2); // searchRange, entrySelector, rangeShift + $this->tables = array(); + for($i=0;$i<$numTables;$i++) + { + $tag = $this->Read(4); + $checkSum = $this->Read(4); + $offset = $this->ReadULong(); + $length = $this->ReadULong(); + $this->tables[$tag] = array('offset'=>$offset, 'length'=>$length, 'checkSum'=>$checkSum); + } + } + + function ParseHead() + { + $this->Seek('head'); + $this->Skip(3*4); // version, fontRevision, checkSumAdjustment + $magicNumber = $this->ReadULong(); + if($magicNumber!=0x5F0F3CF5) + $this->Error('Incorrect magic number'); + $this->Skip(2); // flags + $this->unitsPerEm = $this->ReadUShort(); + $this->Skip(2*8); // created, modified + $this->xMin = $this->ReadShort(); + $this->yMin = $this->ReadShort(); + $this->xMax = $this->ReadShort(); + $this->yMax = $this->ReadShort(); + $this->Skip(3*2); // macStyle, lowestRecPPEM, fontDirectionHint + $this->indexToLocFormat = $this->ReadShort(); + } + + function ParseHhea() + { + $this->Seek('hhea'); + $this->Skip(4+15*2); + $this->numberOfHMetrics = $this->ReadUShort(); + } + + function ParseMaxp() + { + $this->Seek('maxp'); + $this->Skip(4); + $this->numGlyphs = $this->ReadUShort(); + } + + function ParseHmtx() + { + $this->Seek('hmtx'); + $this->glyphs = array(); + for($i=0;$i<$this->numberOfHMetrics;$i++) + { + $advanceWidth = $this->ReadUShort(); + $lsb = $this->ReadShort(); + $this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb); + } + for($i=$this->numberOfHMetrics;$i<$this->numGlyphs;$i++) + { + $lsb = $this->ReadShort(); + $this->glyphs[$i] = array('w'=>$advanceWidth, 'lsb'=>$lsb); + } + } + + function ParseLoca() + { + $this->Seek('loca'); + $offsets = array(); + if($this->indexToLocFormat==0) + { + // Short format + for($i=0;$i<=$this->numGlyphs;$i++) + $offsets[] = 2*$this->ReadUShort(); + } + else + { + // Long format + for($i=0;$i<=$this->numGlyphs;$i++) + $offsets[] = $this->ReadULong(); + } + for($i=0;$i<$this->numGlyphs;$i++) + { + $this->glyphs[$i]['offset'] = $offsets[$i]; + $this->glyphs[$i]['length'] = $offsets[$i+1] - $offsets[$i]; + } + } + + function ParseGlyf() + { + $tableOffset = $this->tables['glyf']['offset']; + foreach($this->glyphs as &$glyph) + { + if($glyph['length']>0) + { + fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET); + if($this->ReadShort()<0) + { + // Composite glyph + $this->Skip(4*2); // xMin, yMin, xMax, yMax + $offset = 5*2; + $a = array(); + do + { + $flags = $this->ReadUShort(); + $index = $this->ReadUShort(); + $a[$offset+2] = $index; + if($flags & 1) // ARG_1_AND_2_ARE_WORDS + $skip = 2*2; + else + $skip = 2; + if($flags & 8) // WE_HAVE_A_SCALE + $skip += 2; + elseif($flags & 64) // WE_HAVE_AN_X_AND_Y_SCALE + $skip += 2*2; + elseif($flags & 128) // WE_HAVE_A_TWO_BY_TWO + $skip += 4*2; + $this->Skip($skip); + $offset += 2*2 + $skip; + } + while($flags & 32); // MORE_COMPONENTS + $glyph['components'] = $a; + } + } + } + } + + function ParseCmap() + { + $this->Seek('cmap'); + $this->Skip(2); // version + $numTables = $this->ReadUShort(); + $offset31 = 0; + for($i=0;$i<$numTables;$i++) + { + $platformID = $this->ReadUShort(); + $encodingID = $this->ReadUShort(); + $offset = $this->ReadULong(); + if($platformID==3 && $encodingID==1) + $offset31 = $offset; + } + if($offset31==0) + $this->Error('No Unicode encoding found'); + + $startCount = array(); + $endCount = array(); + $idDelta = array(); + $idRangeOffset = array(); + $this->chars = array(); + fseek($this->f, $this->tables['cmap']['offset']+$offset31, SEEK_SET); + $format = $this->ReadUShort(); + if($format!=4) + $this->Error('Unexpected subtable format: '.$format); + $this->Skip(2*2); // length, language + $segCount = $this->ReadUShort()/2; + $this->Skip(3*2); // searchRange, entrySelector, rangeShift + for($i=0;$i<$segCount;$i++) + $endCount[$i] = $this->ReadUShort(); + $this->Skip(2); // reservedPad + for($i=0;$i<$segCount;$i++) + $startCount[$i] = $this->ReadUShort(); + for($i=0;$i<$segCount;$i++) + $idDelta[$i] = $this->ReadShort(); + $offset = ftell($this->f); + for($i=0;$i<$segCount;$i++) + $idRangeOffset[$i] = $this->ReadUShort(); + + for($i=0;$i<$segCount;$i++) + { + $c1 = $startCount[$i]; + $c2 = $endCount[$i]; + $d = $idDelta[$i]; + $ro = $idRangeOffset[$i]; + if($ro>0) + fseek($this->f, $offset+2*$i+$ro, SEEK_SET); + for($c=$c1;$c<=$c2;$c++) + { + if($c==0xFFFF) + break; + if($ro>0) + { + $gid = $this->ReadUShort(); + if($gid>0) + $gid += $d; + } + else + $gid = $c+$d; + if($gid>=65536) + $gid -= 65536; + if($gid>0) + $this->chars[$c] = $gid; + } + } + } + + function ParseName() + { + $this->Seek('name'); + $tableOffset = $this->tables['name']['offset']; + $this->postScriptName = ''; + $this->Skip(2); // format + $count = $this->ReadUShort(); + $stringOffset = $this->ReadUShort(); + for($i=0;$i<$count;$i++) + { + $this->Skip(3*2); // platformID, encodingID, languageID + $nameID = $this->ReadUShort(); + $length = $this->ReadUShort(); + $offset = $this->ReadUShort(); + if($nameID==6) + { + // PostScript name + fseek($this->f, $tableOffset+$stringOffset+$offset, SEEK_SET); + $s = $this->Read($length); + $s = str_replace(chr(0), '', $s); + $s = preg_replace('|[ \[\](){}<>/%]|', '', $s); + $this->postScriptName = $s; + break; + } + } + if($this->postScriptName=='') + $this->Error('PostScript name not found'); + } + + function ParseOS2() + { + $this->Seek('OS/2'); + $version = $this->ReadUShort(); + $this->Skip(3*2); // xAvgCharWidth, usWeightClass, usWidthClass + $fsType = $this->ReadUShort(); + $this->embeddable = ($fsType!=2) && ($fsType & 0x200)==0; + $this->Skip(11*2+10+4*4+4); + $fsSelection = $this->ReadUShort(); + $this->bold = ($fsSelection & 32)!=0; + $this->Skip(2*2); // usFirstCharIndex, usLastCharIndex + $this->typoAscender = $this->ReadShort(); + $this->typoDescender = $this->ReadShort(); + if($version>=2) + { + $this->Skip(3*2+2*4+2); + $this->capHeight = $this->ReadShort(); + } + else + $this->capHeight = 0; + } + + function ParsePost() + { + $this->Seek('post'); + $version = $this->ReadULong(); + $this->italicAngle = $this->ReadShort(); + $this->Skip(2); // Skip decimal part + $this->underlinePosition = $this->ReadShort(); + $this->underlineThickness = $this->ReadShort(); + $this->isFixedPitch = ($this->ReadULong()!=0); + if($version==0x20000) + { + // Extract glyph names + $this->Skip(4*4); // min/max usage + $this->Skip(2); // numberOfGlyphs + $glyphNameIndex = array(); + $names = array(); + $numNames = 0; + for($i=0;$i<$this->numGlyphs;$i++) + { + $index = $this->ReadUShort(); + $glyphNameIndex[] = $index; + if($index>=258 && $index-257>$numNames) + $numNames = $index-257; + } + for($i=0;$i<$numNames;$i++) + { + $len = ord($this->Read(1)); + $names[] = $this->Read($len); + } + foreach($glyphNameIndex as $i=>$index) + { + if($index>=258) + $this->glyphs[$i]['name'] = $names[$index-258]; + else + $this->glyphs[$i]['name'] = $index; + } + $this->glyphNames = true; + } + else + $this->glyphNames = false; + } + + function Subset($chars) + { + $this->subsettedGlyphs = array(); + $this->AddGlyph(0); + $this->subsettedChars = array(); + foreach($chars as $char) + { + if(isset($this->chars[$char])) + { + $this->subsettedChars[] = $char; + $this->AddGlyph($this->chars[$char]); + } + } + } + + function AddGlyph($id) + { + if(!isset($this->glyphs[$id]['ssid'])) + { + $this->glyphs[$id]['ssid'] = count($this->subsettedGlyphs); + $this->subsettedGlyphs[] = $id; + if(isset($this->glyphs[$id]['components'])) + { + foreach($this->glyphs[$id]['components'] as $cid) + $this->AddGlyph($cid); + } + } + } + + function Build() + { + $this->BuildCmap(); + $this->BuildHhea(); + $this->BuildHmtx(); + $this->BuildLoca(); + $this->BuildGlyf(); + $this->BuildMaxp(); + $this->BuildPost(); + return $this->BuildFont(); + } + + function BuildCmap() + { + if(!isset($this->subsettedChars)) + return; + + // Divide charset in contiguous segments + $chars = $this->subsettedChars; + sort($chars); + $segments = array(); + $segment = array($chars[0], $chars[0]); + for($i=1;$i$segment[1]+1) + { + $segments[] = $segment; + $segment = array($chars[$i], $chars[$i]); + } + else + $segment[1]++; + } + $segments[] = $segment; + $segments[] = array(0xFFFF, 0xFFFF); + $segCount = count($segments); + + // Build a Format 4 subtable + $startCount = array(); + $endCount = array(); + $idDelta = array(); + $idRangeOffset = array(); + $glyphIdArray = ''; + for($i=0;$i<$segCount;$i++) + { + list($start, $end) = $segments[$i]; + $startCount[] = $start; + $endCount[] = $end; + if($start!=$end) + { + // Segment with multiple chars + $idDelta[] = 0; + $idRangeOffset[] = strlen($glyphIdArray) + ($segCount-$i)*2; + for($c=$start;$c<=$end;$c++) + { + $ssid = $this->glyphs[$this->chars[$c]]['ssid']; + $glyphIdArray .= pack('n', $ssid); + } + } + else + { + // Segment with a single char + if($start<0xFFFF) + $ssid = $this->glyphs[$this->chars[$start]]['ssid']; + else + $ssid = 0; + $idDelta[] = $ssid - $start; + $idRangeOffset[] = 0; + } + } + $entrySelector = 0; + $n = $segCount; + while($n!=1) + { + $n = $n>>1; + $entrySelector++; + } + $searchRange = (1<<$entrySelector)*2; + $rangeShift = 2*$segCount - $searchRange; + $cmap = pack('nnnn', 2*$segCount, $searchRange, $entrySelector, $rangeShift); + foreach($endCount as $val) + $cmap .= pack('n', $val); + $cmap .= pack('n', 0); // reservedPad + foreach($startCount as $val) + $cmap .= pack('n', $val); + foreach($idDelta as $val) + $cmap .= pack('n', $val); + foreach($idRangeOffset as $val) + $cmap .= pack('n', $val); + $cmap .= $glyphIdArray; + + $data = pack('nn', 0, 1); // version, numTables + $data .= pack('nnN', 3, 1, 12); // platformID, encodingID, offset + $data .= pack('nnn', 4, 6+strlen($cmap), 0); // format, length, language + $data .= $cmap; + $this->SetTable('cmap', $data); + } + + function BuildHhea() + { + $this->LoadTable('hhea'); + $numberOfHMetrics = count($this->subsettedGlyphs); + $data = substr_replace($this->tables['hhea']['data'], pack('n',$numberOfHMetrics), 4+15*2, 2); + $this->SetTable('hhea', $data); + } + + function BuildHmtx() + { + $data = ''; + foreach($this->subsettedGlyphs as $id) + { + $glyph = $this->glyphs[$id]; + $data .= pack('nn', $glyph['w'], $glyph['lsb']); + } + $this->SetTable('hmtx', $data); + } + + function BuildLoca() + { + $data = ''; + $offset = 0; + foreach($this->subsettedGlyphs as $id) + { + if($this->indexToLocFormat==0) + $data .= pack('n', $offset/2); + else + $data .= pack('N', $offset); + $offset += $this->glyphs[$id]['length']; + } + if($this->indexToLocFormat==0) + $data .= pack('n', $offset/2); + else + $data .= pack('N', $offset); + $this->SetTable('loca', $data); + } + + function BuildGlyf() + { + $tableOffset = $this->tables['glyf']['offset']; + $data = ''; + foreach($this->subsettedGlyphs as $id) + { + $glyph = $this->glyphs[$id]; + fseek($this->f, $tableOffset+$glyph['offset'], SEEK_SET); + $glyph_data = $this->Read($glyph['length']); + if(isset($glyph['components'])) + { + // Composite glyph + foreach($glyph['components'] as $offset=>$cid) + { + $ssid = $this->glyphs[$cid]['ssid']; + $glyph_data = substr_replace($glyph_data, pack('n',$ssid), $offset, 2); + } + } + $data .= $glyph_data; + } + $this->SetTable('glyf', $data); + } + + function BuildMaxp() + { + $this->LoadTable('maxp'); + $numGlyphs = count($this->subsettedGlyphs); + $data = substr_replace($this->tables['maxp']['data'], pack('n',$numGlyphs), 4, 2); + $this->SetTable('maxp', $data); + } + + function BuildPost() + { + $this->Seek('post'); + if($this->glyphNames) + { + // Version 2.0 + $numberOfGlyphs = count($this->subsettedGlyphs); + $numNames = 0; + $names = ''; + $data = $this->Read(2*4+2*2+5*4); + $data .= pack('n', $numberOfGlyphs); + foreach($this->subsettedGlyphs as $id) + { + $name = $this->glyphs[$id]['name']; + if(is_string($name)) + { + $data .= pack('n', 258+$numNames); + $names .= chr(strlen($name)).$name; + $numNames++; + } + else + $data .= pack('n', $name); + } + $data .= $names; + } + else + { + // Version 3.0 + $this->Skip(4); + $data = "\x00\x03\x00\x00"; + $data .= $this->Read(4+2*2+5*4); + } + $this->SetTable('post', $data); + } + + function BuildFont() + { + $tags = array(); + foreach(array('cmap', 'cvt ', 'fpgm', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'prep') as $tag) + { + if(isset($this->tables[$tag])) + $tags[] = $tag; + } + $numTables = count($tags); + $offset = 12 + 16*$numTables; + foreach($tags as $tag) + { + if(!isset($this->tables[$tag]['data'])) + $this->LoadTable($tag); + $this->tables[$tag]['offset'] = $offset; + $offset += strlen($this->tables[$tag]['data']); + } + + // Build offset table + $entrySelector = 0; + $n = $numTables; + while($n!=1) + { + $n = $n>>1; + $entrySelector++; + } + $searchRange = 16*(1<<$entrySelector); + $rangeShift = 16*$numTables - $searchRange; + $offsetTable = pack('nnnnnn', 1, 0, $numTables, $searchRange, $entrySelector, $rangeShift); + foreach($tags as $tag) + { + $table = $this->tables[$tag]; + $offsetTable .= $tag.$table['checkSum'].pack('NN', $table['offset'], $table['length']); + } + + // Compute checkSumAdjustment (0xB1B0AFBA - font checkSum) + $s = $this->CheckSum($offsetTable); + foreach($tags as $tag) + $s .= $this->tables[$tag]['checkSum']; + $a = unpack('n2', $this->CheckSum($s)); + $high = 0xB1B0 + ($a[1]^0xFFFF); + $low = 0xAFBA + ($a[2]^0xFFFF) + 1; + $checkSumAdjustment = pack('nn', $high+($low>>16), $low); + $this->tables['head']['data'] = substr_replace($this->tables['head']['data'], $checkSumAdjustment, 8, 4); + + $font = $offsetTable; + foreach($tags as $tag) + $font .= $this->tables[$tag]['data']; + + return $font; + } + + function LoadTable($tag) + { + $this->Seek($tag); + $length = $this->tables[$tag]['length']; + $n = $length % 4; + if($n>0) + $length += 4 - $n; + $this->tables[$tag]['data'] = $this->Read($length); + } + + function SetTable($tag, $data) + { + $length = strlen($data); + $n = $length % 4; + if($n>0) + $data = str_pad($data, $length+4-$n, "\x00"); + $this->tables[$tag]['data'] = $data; + $this->tables[$tag]['length'] = $length; + $this->tables[$tag]['checkSum'] = $this->CheckSum($data); + } + + function Seek($tag) + { + if(!isset($this->tables[$tag])) + $this->Error('Table not found: '.$tag); + fseek($this->f, $this->tables[$tag]['offset'], SEEK_SET); + } + + function Skip($n) + { + fseek($this->f, $n, SEEK_CUR); + } + + function Read($n) + { + return $n>0 ? fread($this->f, $n) : ''; + } + + function ReadUShort() + { + $a = unpack('nn', fread($this->f,2)); + return $a['n']; + } + + function ReadShort() + { + $a = unpack('nn', fread($this->f,2)); + $v = $a['n']; + if($v>=0x8000) + $v -= 65536; + return $v; + } + + function ReadULong() + { + $a = unpack('NN', fread($this->f,4)); + return $a['N']; + } + + function CheckSum($s) + { + $n = strlen($s); + $high = 0; + $low = 0; + for($i=0;$i<$n;$i+=4) + { + $high += (ord($s[$i])<<8) + ord($s[$i+1]); + $low += (ord($s[$i+2])<<8) + ord($s[$i+3]); + } + return pack('nn', $high+($low>>16), $low); + } + + function Error($msg) + { + throw new Exception($msg); + } +} +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/20k_c1.txt b/videodb/vendor/setasign/fpdf/tutorial/20k_c1.txt new file mode 100644 index 0000000..6d5b295 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/20k_c1.txt @@ -0,0 +1,10 @@ +The year 1866 was marked by a bizarre development, an unexplained and downright inexplicable phenomenon that surely no one has forgotten. Without getting into those rumors that upset civilians in the seaports and deranged the public mind even far inland, it must be said that professional seamen were especially alarmed. Traders, shipowners, captains of vessels, skippers, and master mariners from Europe and America, naval officers from every country, and at their heels the various national governments on these two continents, were all extremely disturbed by the business. +In essence, over a period of time several ships had encountered "an enormous thing" at sea, a long spindle-shaped object, sometimes giving off a phosphorescent glow, infinitely bigger and faster than any whale. +The relevant data on this apparition, as recorded in various logbooks, agreed pretty closely as to the structure of the object or creature in question, its unprecedented speed of movement, its startling locomotive power, and the unique vitality with which it seemed to be gifted. If it was a cetacean, it exceeded in bulk any whale previously classified by science. No naturalist, neither Cuvier nor Lacpde, neither Professor Dumeril nor Professor de Quatrefages, would have accepted the existence of such a monster sight unseen -- specifically, unseen by their own scientific eyes. +Striking an average of observations taken at different times -- rejecting those timid estimates that gave the object a length of 200 feet, and ignoring those exaggerated views that saw it as a mile wide and three long--you could still assert that this phenomenal creature greatly exceeded the dimensions of anything then known to ichthyologists, if it existed at all. +Now then, it did exist, this was an undeniable fact; and since the human mind dotes on objects of wonder, you can understand the worldwide excitement caused by this unearthly apparition. As for relegating it to the realm of fiction, that charge had to be dropped. +In essence, on July 20, 1866, the steamer Governor Higginson, from the Calcutta & Burnach Steam Navigation Co., encountered this moving mass five miles off the eastern shores of Australia. Captain Baker at first thought he was in the presence of an unknown reef; he was even about to fix its exact position when two waterspouts shot out of this inexplicable object and sprang hissing into the air some 150 feet. So, unless this reef was subject to the intermittent eruptions of a geyser, the Governor Higginson had fair and honest dealings with some aquatic mammal, until then unknown, that could spurt from its blowholes waterspouts mixed with air and steam. +Similar events were likewise observed in Pacific seas, on July 23 of the same year, by the Christopher Columbus from the West India & Pacific Steam Navigation Co. Consequently, this extraordinary cetacean could transfer itself from one locality to another with startling swiftness, since within an interval of just three days, the Governor Higginson and the Christopher Columbus had observed it at two positions on the charts separated by a distance of more than 700 nautical leagues. +Fifteen days later and 2,000 leagues farther, the Helvetia from the Compagnie Nationale and the Shannon from the Royal Mail line, running on opposite tacks in that part of the Atlantic lying between the United States and Europe, respectively signaled each other that the monster had been sighted in latitude 42 degrees 15' north and longitude 60 degrees 35' west of the meridian of Greenwich. From their simultaneous observations, they were able to estimate the mammal's minimum length at more than 350 English feet; this was because both the Shannon and the Helvetia were of smaller dimensions, although each measured 100 meters stem to stern. Now then, the biggest whales, those rorqual whales that frequent the waterways of the Aleutian Islands, have never exceeded a length of 56 meters--if they reach even that. +One after another, reports arrived that would profoundly affect public opinion: new observations taken by the transatlantic liner Pereire, the Inman line's Etna running afoul of the monster, an official report drawn up by officers on the French frigate Normandy, dead-earnest reckonings obtained by the general staff of Commodore Fitz-James aboard the Lord Clyde. In lighthearted countries, people joked about this phenomenon, but such serious, practical countries as England, America, and Germany were deeply concerned. +In every big city the monster was the latest rage; they sang about it in the coffee houses, they ridiculed it in the newspapers, they dramatized it in the theaters. The tabloids found it a fine opportunity for hatching all sorts of hoaxes. In those newspapers short of copy, you saw the reappearance of every gigantic imaginary creature, from "Moby Dick," that dreadful white whale from the High Arctic regions, to the stupendous kraken whose tentacles could entwine a 500-ton craft and drag it into the ocean depths. They even reprinted reports from ancient times: the views of Aristotle and Pliny accepting the existence of such monsters, then the Norwegian stories of Bishop Pontoppidan, the narratives of Paul Egede, and finally the reports of Captain Harrington -- whose good faith is above suspicion--in which he claims he saw, while aboard the Castilian in 1857, one of those enormous serpents that, until then, had frequented only the seas of France's old extremist newspaper, The Constitutionalist. diff --git a/videodb/vendor/setasign/fpdf/tutorial/20k_c2.txt b/videodb/vendor/setasign/fpdf/tutorial/20k_c2.txt new file mode 100644 index 0000000..7b5c565 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/20k_c2.txt @@ -0,0 +1,23 @@ +During the period in which these developments were occurring, I had returned from a scientific undertaking organized to explore the Nebraska badlands in the United States. In my capacity as Assistant Professor at the Paris Museum of Natural History, I had been attached to this expedition by the French government. After spending six months in Nebraska, I arrived in New York laden with valuable collections near the end of March. My departure for France was set for early May. In the meantime, then, I was busy classifying my mineralogical, botanical, and zoological treasures when that incident took place with the Scotia. +I was perfectly abreast of this question, which was the big news of the day, and how could I not have been? I had read and reread every American and European newspaper without being any farther along. This mystery puzzled me. Finding it impossible to form any views, I drifted from one extreme to the other. Something was out there, that much was certain, and any doubting Thomas was invited to place his finger on the Scotia's wound. +When I arrived in New York, the question was at the boiling point. The hypothesis of a drifting islet or an elusive reef, put forward by people not quite in their right minds, was completely eliminated. And indeed, unless this reef had an engine in its belly, how could it move about with such prodigious speed? +Also discredited was the idea of a floating hull or some other enormous wreckage, and again because of this speed of movement. +So only two possible solutions to the question were left, creating two very distinct groups of supporters: on one side, those favoring a monster of colossal strength; on the other, those favoring an "underwater boat" of tremendous motor power. +Now then, although the latter hypothesis was completely admissible, it couldn't stand up to inquiries conducted in both the New World and the Old. That a private individual had such a mechanism at his disposal was less than probable. Where and when had he built it, and how could he have built it in secret? +Only some government could own such an engine of destruction, and in these disaster-filled times, when men tax their ingenuity to build increasingly powerful aggressive weapons, it was possible that, unknown to the rest of the world, some nation could have been testing such a fearsome machine. The Chassepot rifle led to the torpedo, and the torpedo has led to this underwater battering ram, which in turn will lead to the world putting its foot down. At least I hope it will. +But this hypothesis of a war machine collapsed in the face of formal denials from the various governments. Since the public interest was at stake and transoceanic travel was suffering, the sincerity of these governments could not be doubted. Besides, how could the assembly of this underwater boat have escaped public notice? Keeping a secret under such circumstances would be difficult enough for an individual, and certainly impossible for a nation whose every move is under constant surveillance by rival powers. +So, after inquiries conducted in England, France, Russia, Prussia, Spain, Italy, America, and even Turkey, the hypothesis of an underwater Monitor was ultimately rejected. +After I arrived in New York, several people did me the honor of consulting me on the phenomenon in question. In France I had published a two-volume work, in quarto, entitled The Mysteries of the Great Ocean Depths. Well received in scholarly circles, this book had established me as a specialist in this pretty obscure field of natural history. My views were in demand. As long as I could deny the reality of the business, I confined myself to a flat "no comment." But soon, pinned to the wall, I had to explain myself straight out. And in this vein, "the honorable Pierre Aronnax, Professor at the Paris Museum," was summoned by The New York Herald to formulate his views no matter what. +I complied. Since I could no longer hold my tongue, I let it wag. I discussed the question in its every aspect, both political and scientific, and this is an excerpt from the well-padded article I published in the issue of April 30. + +"Therefore," I wrote, "after examining these different hypotheses one by one, we are forced, every other supposition having been refuted, to accept the existence of an extremely powerful marine animal. +"The deepest parts of the ocean are totally unknown to us. No soundings have been able to reach them. What goes on in those distant depths? What creatures inhabit, or could inhabit, those regions twelve or fifteen miles beneath the surface of the water? What is the constitution of these animals? It's almost beyond conjecture. +"However, the solution to this problem submitted to me can take the form of a choice between two alternatives. +"Either we know every variety of creature populating our planet, or we do not. +"If we do not know every one of them, if nature still keeps ichthyological secrets from us, nothing is more admissible than to accept the existence of fish or cetaceans of new species or even new genera, animals with a basically 'cast-iron' constitution that inhabit strata beyond the reach of our soundings, and which some development or other, an urge or a whim if you prefer, can bring to the upper level of the ocean for long intervals. +"If, on the other hand, we do know every living species, we must look for the animal in question among those marine creatures already cataloged, and in this event I would be inclined to accept the existence of a giant narwhale. +"The common narwhale, or sea unicorn, often reaches a length of sixty feet. Increase its dimensions fivefold or even tenfold, then give this cetacean a strength in proportion to its size while enlarging its offensive weapons, and you have the animal we're looking for. It would have the proportions determined by the officers of the Shannon, the instrument needed to perforate the Scotia, and the power to pierce a steamer's hull. +"In essence, the narwhale is armed with a sort of ivory sword, or lance, as certain naturalists have expressed it. It's a king-sized tooth as hard as steel. Some of these teeth have been found buried in the bodies of baleen whales, which the narwhale attacks with invariable success. Others have been wrenched, not without difficulty, from the undersides of vessels that narwhales have pierced clean through, as a gimlet pierces a wine barrel. The museum at the Faculty of Medicine in Paris owns one of these tusks with a length of 2.25 meters and a width at its base of forty-eight centimeters! +"All right then! Imagine this weapon to be ten times stronger and the animal ten times more powerful, launch it at a speed of twenty miles per hour, multiply its mass times its velocity, and you get just the collision we need to cause the specified catastrophe. +"So, until information becomes more abundant, I plump for a sea unicorn of colossal dimensions, no longer armed with a mere lance but with an actual spur, like ironclad frigates or those warships called 'rams,' whose mass and motor power it would possess simultaneously. +"This inexplicable phenomenon is thus explained away--unless it's something else entirely, which, despite everything that has been sighted, studied, explored and experienced, is still possible!" diff --git a/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular-Licence.txt b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular-Licence.txt new file mode 100644 index 0000000..e4eabd8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular-Licence.txt @@ -0,0 +1,94 @@ +Copyright (c) 2011 by LatinoType Limitada (luciano@latinotype.com), +with Reserved Font Names "Cecivhe" and "Ceviche One" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.php b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.php new file mode 100644 index 0000000..ca5c411 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.php @@ -0,0 +1,25 @@ +806,'Descent'=>-237,'CapHeight'=>425,'Flags'=>32,'FontBBox'=>'[-42 -237 1427 806]','ItalicAngle'=>0,'StemV'=>70,'MissingWidth'=>294); +$up = -75; +$ut = 50; +$cw = array( + chr(0)=>294,chr(1)=>294,chr(2)=>294,chr(3)=>294,chr(4)=>294,chr(5)=>294,chr(6)=>294,chr(7)=>294,chr(8)=>294,chr(9)=>294,chr(10)=>294,chr(11)=>294,chr(12)=>294,chr(13)=>294,chr(14)=>294,chr(15)=>294,chr(16)=>294,chr(17)=>294,chr(18)=>294,chr(19)=>294,chr(20)=>294,chr(21)=>294, + chr(22)=>294,chr(23)=>294,chr(24)=>294,chr(25)=>294,chr(26)=>294,chr(27)=>294,chr(28)=>294,chr(29)=>294,chr(30)=>294,chr(31)=>294,' '=>130,'!'=>254,'"'=>334,'#'=>496,'$'=>469,'%'=>765,'&'=>761,'\''=>148,'('=>268,')'=>269,'*'=>435,'+'=>442, + ','=>235,'-'=>339,'.'=>233,'/'=>301,'0'=>512,'1'=>252,'2'=>501,'3'=>471,'4'=>537,'5'=>471,'6'=>501,'7'=>424,'8'=>506,'9'=>502,':'=>292,';'=>299,'<'=>463,'='=>483,'>'=>459,'?'=>453,'@'=>672,'A'=>511, + 'B'=>573,'C'=>480,'D'=>541,'E'=>507,'F'=>490,'G'=>515,'H'=>517,'I'=>250,'J'=>270,'K'=>570,'L'=>368,'M'=>620,'N'=>548,'O'=>507,'P'=>540,'Q'=>532,'R'=>552,'S'=>490,'T'=>434,'U'=>527,'V'=>514,'W'=>670, + 'X'=>541,'Y'=>497,'Z'=>499,'['=>288,'\\'=>472,']'=>288,'^'=>448,'_'=>350,'`'=>449,'a'=>446,'b'=>454,'c'=>394,'d'=>462,'e'=>404,'f'=>332,'g'=>458,'h'=>446,'i'=>234,'j'=>232,'k'=>457,'l'=>231,'m'=>659, + 'n'=>450,'o'=>412,'p'=>436,'q'=>468,'r'=>389,'s'=>394,'t'=>318,'u'=>458,'v'=>421,'w'=>612,'x'=>450,'y'=>429,'z'=>423,'{'=>312,'|'=>231,'}'=>312,'~'=>504,chr(127)=>294,chr(128)=>586,chr(129)=>294,chr(130)=>176,chr(131)=>377, + chr(132)=>361,chr(133)=>761,chr(134)=>422,chr(135)=>425,chr(136)=>448,chr(137)=>1050,chr(138)=>490,chr(139)=>377,chr(140)=>787,chr(141)=>294,chr(142)=>499,chr(143)=>294,chr(144)=>294,chr(145)=>171,chr(146)=>148,chr(147)=>357,chr(148)=>334,chr(149)=>266,chr(150)=>444,chr(151)=>554,chr(152)=>416,chr(153)=>608, + chr(154)=>394,chr(155)=>378,chr(156)=>674,chr(157)=>294,chr(158)=>423,chr(159)=>497,chr(160)=>130,chr(161)=>253,chr(162)=>424,chr(163)=>510,chr(164)=>665,chr(165)=>551,chr(166)=>252,chr(167)=>413,chr(168)=>505,chr(169)=>675,chr(170)=>361,chr(171)=>668,chr(172)=>548,chr(173)=>440,chr(174)=>676,chr(175)=>413, + chr(176)=>278,chr(177)=>438,chr(178)=>328,chr(179)=>318,chr(180)=>449,chr(181)=>463,chr(182)=>535,chr(183)=>234,chr(184)=>535,chr(185)=>176,chr(186)=>322,chr(187)=>669,chr(188)=>761,chr(189)=>710,chr(190)=>904,chr(191)=>452,chr(192)=>511,chr(193)=>511,chr(194)=>514,chr(195)=>511,chr(196)=>511,chr(197)=>511, + chr(198)=>787,chr(199)=>480,chr(200)=>507,chr(201)=>507,chr(202)=>507,chr(203)=>507,chr(204)=>250,chr(205)=>250,chr(206)=>271,chr(207)=>272,chr(208)=>542,chr(209)=>548,chr(210)=>507,chr(211)=>507,chr(212)=>507,chr(213)=>507,chr(214)=>507,chr(215)=>496,chr(216)=>507,chr(217)=>527,chr(218)=>527,chr(219)=>526, + chr(220)=>527,chr(221)=>497,chr(222)=>522,chr(223)=>457,chr(224)=>446,chr(225)=>446,chr(226)=>446,chr(227)=>446,chr(228)=>446,chr(229)=>446,chr(230)=>635,chr(231)=>394,chr(232)=>404,chr(233)=>404,chr(234)=>412,chr(235)=>401,chr(236)=>235,chr(237)=>235,chr(238)=>256,chr(239)=>249,chr(240)=>475,chr(241)=>450, + chr(242)=>412,chr(243)=>412,chr(244)=>412,chr(245)=>413,chr(246)=>412,chr(247)=>444,chr(248)=>412,chr(249)=>458,chr(250)=>458,chr(251)=>458,chr(252)=>458,chr(253)=>429,chr(254)=>452,chr(255)=>429); +$enc = 'cp1252'; +$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); +$file = 'CevicheOne-Regular.z'; +$originalsize = 25916; +$subsetted = true; +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.ttf b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.ttf new file mode 100644 index 0000000..0bf3b6f Binary files /dev/null and b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.ttf differ diff --git a/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.z b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.z new file mode 100644 index 0000000..a2c2325 Binary files /dev/null and b/videodb/vendor/setasign/fpdf/tutorial/CevicheOne-Regular.z differ diff --git a/videodb/vendor/setasign/fpdf/tutorial/countries.txt b/videodb/vendor/setasign/fpdf/tutorial/countries.txt new file mode 100644 index 0000000..5a48a42 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/countries.txt @@ -0,0 +1,15 @@ +Austria;Vienna;83859;8075 +Belgium;Brussels;30518;10192 +Denmark;Copenhagen;43094;5295 +Finland;Helsinki;304529;5147 +France;Paris;543965;58728 +Germany;Berlin;357022;82057 +Greece;Athens;131625;10511 +Ireland;Dublin;70723;3694 +Italy;Roma;301316;57563 +Luxembourg;Luxembourg;2586;424 +Netherlands;Amsterdam;41526;15654 +Portugal;Lisbon;91906;9957 +Spain;Madrid;504790;39348 +Sweden;Stockholm;410934;8839 +United Kingdom;London;243820;58862 diff --git a/videodb/vendor/setasign/fpdf/tutorial/index.htm b/videodb/vendor/setasign/fpdf/tutorial/index.htm new file mode 100644 index 0000000..d1f8fb6 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/index.htm @@ -0,0 +1,20 @@ + + + + +Tutorials + + + +

        Tutorials

        + + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/logo.png b/videodb/vendor/setasign/fpdf/tutorial/logo.png new file mode 100644 index 0000000..284a007 Binary files /dev/null and b/videodb/vendor/setasign/fpdf/tutorial/logo.png differ diff --git a/videodb/vendor/setasign/fpdf/tutorial/makefont.php b/videodb/vendor/setasign/fpdf/tutorial/makefont.php new file mode 100644 index 0000000..7a09201 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/makefont.php @@ -0,0 +1,6 @@ + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto1.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto1.htm new file mode 100644 index 0000000..0b95c93 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto1.htm @@ -0,0 +1,76 @@ + + + + +Minimal example + + + +

        Minimal example

        +Let's start with the classic example: +
        +
        <?php
        +require('fpdf.php');
        +
        +$pdf = new FPDF();
        +$pdf->AddPage();
        +$pdf->SetFont('Arial','B',16);
        +$pdf->Cell(40,10,'Hello World!');
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +After including the library file, we create an FPDF object. +The constructor is used here with the default values: pages are in A4 portrait and +the unit of measure is millimeter. It could have been specified explicitly with: +
        +
        $pdf = new FPDF('P','mm','A4');
        +
        +
        +It's possible to use landscape (L), other page sizes (such as Letter and +Legal) and units (pt, cm, in). +
        +
        +There's no page at the moment, so we have to add one with AddPage(). The origin +is at the upper-left corner and the current position is by default set at 1 cm from the +borders; the margins can be changed with SetMargins(). +
        +
        +Before we can print text, it's mandatory to select a font with SetFont(). +We choose Arial bold 16: +
        +
        $pdf->SetFont('Arial','B',16);
        +
        +
        +We could have specified italics with I, underlined with U or a regular font with an empty string +(or any combination). Note that the font size is given in points, not millimeters (or another user +unit); it's the only exception. The other standard fonts are Times, Courier, Symbol and ZapfDingbats. +
        +
        +We can now print a cell with Cell(). A cell is a rectangular area, possibly framed, +which contains a line of text. It is output at the current position. We specify its dimensions, +its text (centered or aligned), if borders should be drawn, and where the current position +moves after it (to the right, below or to the beginning of the next line). To add a frame, we would do this: +
        +
        $pdf->Cell(40,10,'Hello World !',1);
        +
        +
        +To add a new cell next to it with centered text and go to the next line, we would do: +
        +
        $pdf->Cell(60,10,'Powered by FPDF.',0,1,'C');
        +
        +
        +Remark: the line break can also be done with Ln(). This method additionnaly allows to specify +the height of the break. +
        +
        +Finally, the document is closed and sent to the browser with Output(). We could have saved +it to a file by passing the appropriate parameters. +
        +
        +Caution: in case when the PDF is sent to the browser, nothing else must be output by the +script, neither before nor after (no HTML, not even a space or a carriage return). If you send something +before, you will get the error message: "Some data has already been output, can't send PDF file". If you +send something after, the document might not display. + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto1.php b/videodb/vendor/setasign/fpdf/tutorial/tuto1.php new file mode 100644 index 0000000..3ab55a1 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto1.php @@ -0,0 +1,9 @@ +AddPage(); +$pdf->SetFont('Arial','B',16); +$pdf->Cell(40,10,'Hello World!'); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto2.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto2.htm new file mode 100644 index 0000000..c402cf4 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto2.htm @@ -0,0 +1,80 @@ + + + + +Header, footer, page break and image + + + +

        Header, footer, page break and image

        +Here's a two page example with header, footer and logo: +
        +
        <?php
        +require('fpdf.php');
        +
        +class PDF extends FPDF
        +{
        +// Page header
        +function Header()
        +{
        +    // Logo
        +    $this->Image('logo.png',10,6,30);
        +    // Arial bold 15
        +    $this->SetFont('Arial','B',15);
        +    // Move to the right
        +    $this->Cell(80);
        +    // Title
        +    $this->Cell(30,10,'Title',1,0,'C');
        +    // Line break
        +    $this->Ln(20);
        +}
        +
        +// Page footer
        +function Footer()
        +{
        +    // Position at 1.5 cm from bottom
        +    $this->SetY(-15);
        +    // Arial italic 8
        +    $this->SetFont('Arial','I',8);
        +    // Page number
        +    $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C');
        +}
        +}
        +
        +// Instanciation of inherited class
        +$pdf = new PDF();
        +$pdf->AliasNbPages();
        +$pdf->AddPage();
        +$pdf->SetFont('Times','',12);
        +for($i=1;$i<=40;$i++)
        +    $pdf->Cell(0,10,'Printing line number '.$i,0,1);
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +This example makes use of the Header() and Footer() methods to process page headers and +footers. They are called automatically. They already exist in the FPDF class but do nothing, +therefore we have to extend the class and override them. +
        +
        +The logo is printed with the Image() method by specifying its upper-left corner and +its width. The height is calculated automatically to respect the image proportions. +
        +
        +To print the page number, a null value is passed as the cell width. It means that the cell +should extend up to the right margin of the page; this is handy to center text. The current page +number is returned by the PageNo() method; as for the total number of pages, it's obtained +via the special value {nb} which is substituted when the document is finished +(provided you first called AliasNbPages()). +
        +Note the use of the SetY() method which allows to set position at an absolute location in +the page, starting from the top or the bottom. +
        +
        +Another interesting feature is used here: the automatic page breaking. As soon as a cell would +cross a limit in the page (at 2 centimeters from the bottom by default), a break is issued +and the font restored. Although the header and footer select their own font (Arial), the body +continues with Times. This mechanism of automatic restoration also applies to colors and line +width. The limit which triggers page breaks can be set with SetAutoPageBreak(). + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto2.php b/videodb/vendor/setasign/fpdf/tutorial/tuto2.php new file mode 100644 index 0000000..6a1b4f8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto2.php @@ -0,0 +1,41 @@ +Image('logo.png',10,6,30); + // Arial bold 15 + $this->SetFont('Arial','B',15); + // Move to the right + $this->Cell(80); + // Title + $this->Cell(30,10,'Title',1,0,'C'); + // Line break + $this->Ln(20); +} + +// Page footer +function Footer() +{ + // Position at 1.5 cm from bottom + $this->SetY(-15); + // Arial italic 8 + $this->SetFont('Arial','I',8); + // Page number + $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); +} +} + +// Instanciation of inherited class +$pdf = new PDF(); +$pdf->AliasNbPages(); +$pdf->AddPage(); +$pdf->SetFont('Times','',12); +for($i=1;$i<=40;$i++) + $pdf->Cell(0,10,'Printing line number '.$i,0,1); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto3.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto3.htm new file mode 100644 index 0000000..5d8363f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto3.htm @@ -0,0 +1,115 @@ + + + + +Line breaks and colors + + + +

        Line breaks and colors

        +Let's continue with an example which prints justified paragraphs. It also illustrates the use +of colors. +
        +
        <?php
        +require('fpdf.php');
        +
        +class PDF extends FPDF
        +{
        +function Header()
        +{
        +    global $title;
        +
        +    // Arial bold 15
        +    $this->SetFont('Arial','B',15);
        +    // Calculate width of title and position
        +    $w = $this->GetStringWidth($title)+6;
        +    $this->SetX((210-$w)/2);
        +    // Colors of frame, background and text
        +    $this->SetDrawColor(0,80,180);
        +    $this->SetFillColor(230,230,0);
        +    $this->SetTextColor(220,50,50);
        +    // Thickness of frame (1 mm)
        +    $this->SetLineWidth(1);
        +    // Title
        +    $this->Cell($w,9,$title,1,1,'C',true);
        +    // Line break
        +    $this->Ln(10);
        +}
        +
        +function Footer()
        +{
        +    // Position at 1.5 cm from bottom
        +    $this->SetY(-15);
        +    // Arial italic 8
        +    $this->SetFont('Arial','I',8);
        +    // Text color in gray
        +    $this->SetTextColor(128);
        +    // Page number
        +    $this->Cell(0,10,'Page '.$this->PageNo(),0,0,'C');
        +}
        +
        +function ChapterTitle($num, $label)
        +{
        +    // Arial 12
        +    $this->SetFont('Arial','',12);
        +    // Background color
        +    $this->SetFillColor(200,220,255);
        +    // Title
        +    $this->Cell(0,6,"Chapter $num : $label",0,1,'L',true);
        +    // Line break
        +    $this->Ln(4);
        +}
        +
        +function ChapterBody($file)
        +{
        +    // Read text file
        +    $txt = file_get_contents($file);
        +    // Times 12
        +    $this->SetFont('Times','',12);
        +    // Output justified text
        +    $this->MultiCell(0,5,$txt);
        +    // Line break
        +    $this->Ln();
        +    // Mention in italics
        +    $this->SetFont('','I');
        +    $this->Cell(0,5,'(end of excerpt)');
        +}
        +
        +function PrintChapter($num, $title, $file)
        +{
        +    $this->AddPage();
        +    $this->ChapterTitle($num,$title);
        +    $this->ChapterBody($file);
        +}
        +}
        +
        +$pdf = new PDF();
        +$title = '20000 Leagues Under the Seas';
        +$pdf->SetTitle($title);
        +$pdf->SetAuthor('Jules Verne');
        +$pdf->PrintChapter(1,'A RUNAWAY REEF','20k_c1.txt');
        +$pdf->PrintChapter(2,'THE PROS AND CONS','20k_c2.txt');
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +The GetStringWidth() method allows to determine the length of a string in the current font, +which is used here to calculate the position and the width of the frame surrounding the title. +Then colors are set (via SetDrawColor(), SetFillColor() and SetTextColor()) and the +thickness of the line is set to 1 mm (instead of 0.2 by default) with SetLineWidth(). Finally, +we output the cell (the last parameter true indicates that the background must +be filled). +
        +
        +The method used to print the paragraphs is MultiCell(). Each time a line reaches the +right extremity of the cell or a carriage return character is met, a line break is issued +and a new cell automatically created under the current one. Text is justified by default. +
        +
        +Two document properties are defined: the title (SetTitle()) and the author (SetAuthor()). +There are several ways to view them in Adobe Reader. The first one is to open the file directly with +the reader, go to the File menu and choose the Properties option. The second one, also available from +the plug-in, is to right-click and select Document Properties. The third method is to type the Ctrl+D +key combination. + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto3.php b/videodb/vendor/setasign/fpdf/tutorial/tuto3.php new file mode 100644 index 0000000..3316ddb --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto3.php @@ -0,0 +1,81 @@ +SetFont('Arial','B',15); + // Calculate width of title and position + $w = $this->GetStringWidth($title)+6; + $this->SetX((210-$w)/2); + // Colors of frame, background and text + $this->SetDrawColor(0,80,180); + $this->SetFillColor(230,230,0); + $this->SetTextColor(220,50,50); + // Thickness of frame (1 mm) + $this->SetLineWidth(1); + // Title + $this->Cell($w,9,$title,1,1,'C',true); + // Line break + $this->Ln(10); +} + +function Footer() +{ + // Position at 1.5 cm from bottom + $this->SetY(-15); + // Arial italic 8 + $this->SetFont('Arial','I',8); + // Text color in gray + $this->SetTextColor(128); + // Page number + $this->Cell(0,10,'Page '.$this->PageNo(),0,0,'C'); +} + +function ChapterTitle($num, $label) +{ + // Arial 12 + $this->SetFont('Arial','',12); + // Background color + $this->SetFillColor(200,220,255); + // Title + $this->Cell(0,6,"Chapter $num : $label",0,1,'L',true); + // Line break + $this->Ln(4); +} + +function ChapterBody($file) +{ + // Read text file + $txt = file_get_contents($file); + // Times 12 + $this->SetFont('Times','',12); + // Output justified text + $this->MultiCell(0,5,$txt); + // Line break + $this->Ln(); + // Mention in italics + $this->SetFont('','I'); + $this->Cell(0,5,'(end of excerpt)'); +} + +function PrintChapter($num, $title, $file) +{ + $this->AddPage(); + $this->ChapterTitle($num,$title); + $this->ChapterBody($file); +} +} + +$pdf = new PDF(); +$title = '20000 Leagues Under the Seas'; +$pdf->SetTitle($title); +$pdf->SetAuthor('Jules Verne'); +$pdf->PrintChapter(1,'A RUNAWAY REEF','20k_c1.txt'); +$pdf->PrintChapter(2,'THE PROS AND CONS','20k_c2.txt'); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto4.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto4.htm new file mode 100644 index 0000000..05ccde2 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto4.htm @@ -0,0 +1,132 @@ + + + + +Multi-columns + + + +

        Multi-columns

        +This example is a variant of the previous one showing how to lay the text across multiple +columns. +
        +
        <?php
        +require('fpdf.php');
        +
        +class PDF extends FPDF
        +{
        +protected $col = 0; // Current column
        +protected $y0;      // Ordinate of column start
        +
        +function Header()
        +{
        +    // Page header
        +    global $title;
        +
        +    $this->SetFont('Arial','B',15);
        +    $w = $this->GetStringWidth($title)+6;
        +    $this->SetX((210-$w)/2);
        +    $this->SetDrawColor(0,80,180);
        +    $this->SetFillColor(230,230,0);
        +    $this->SetTextColor(220,50,50);
        +    $this->SetLineWidth(1);
        +    $this->Cell($w,9,$title,1,1,'C',true);
        +    $this->Ln(10);
        +    // Save ordinate
        +    $this->y0 = $this->GetY();
        +}
        +
        +function Footer()
        +{
        +    // Page footer
        +    $this->SetY(-15);
        +    $this->SetFont('Arial','I',8);
        +    $this->SetTextColor(128);
        +    $this->Cell(0,10,'Page '.$this->PageNo(),0,0,'C');
        +}
        +
        +function SetCol($col)
        +{
        +    // Set position at a given column
        +    $this->col = $col;
        +    $x = 10+$col*65;
        +    $this->SetLeftMargin($x);
        +    $this->SetX($x);
        +}
        +
        +function AcceptPageBreak()
        +{
        +    // Method accepting or not automatic page break
        +    if($this->col<2)
        +    {
        +        // Go to next column
        +        $this->SetCol($this->col+1);
        +        // Set ordinate to top
        +        $this->SetY($this->y0);
        +        // Keep on page
        +        return false;
        +    }
        +    else
        +    {
        +        // Go back to first column
        +        $this->SetCol(0);
        +        // Page break
        +        return true;
        +    }
        +}
        +
        +function ChapterTitle($num, $label)
        +{
        +    // Title
        +    $this->SetFont('Arial','',12);
        +    $this->SetFillColor(200,220,255);
        +    $this->Cell(0,6,"Chapter $num : $label",0,1,'L',true);
        +    $this->Ln(4);
        +    // Save ordinate
        +    $this->y0 = $this->GetY();
        +}
        +
        +function ChapterBody($file)
        +{
        +    // Read text file
        +    $txt = file_get_contents($file);
        +    // Font
        +    $this->SetFont('Times','',12);
        +    // Output text in a 6 cm width column
        +    $this->MultiCell(60,5,$txt);
        +    $this->Ln();
        +    // Mention
        +    $this->SetFont('','I');
        +    $this->Cell(0,5,'(end of excerpt)');
        +    // Go back to first column
        +    $this->SetCol(0);
        +}
        +
        +function PrintChapter($num, $title, $file)
        +{
        +    // Add chapter
        +    $this->AddPage();
        +    $this->ChapterTitle($num,$title);
        +    $this->ChapterBody($file);
        +}
        +}
        +
        +$pdf = new PDF();
        +$title = '20000 Leagues Under the Seas';
        +$pdf->SetTitle($title);
        +$pdf->SetAuthor('Jules Verne');
        +$pdf->PrintChapter(1,'A RUNAWAY REEF','20k_c1.txt');
        +$pdf->PrintChapter(2,'THE PROS AND CONS','20k_c2.txt');
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +The key method used is AcceptPageBreak(). It allows to accept or not an automatic page +break. By refusing it and altering the margin and current position, the desired column layout +is achieved. +
        +For the rest, not many changes; two properties have been added to the class to save the current +column number and the position where columns begin, and the MultiCell() call specifies a +6 centimeter width. + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto4.php b/videodb/vendor/setasign/fpdf/tutorial/tuto4.php new file mode 100644 index 0000000..c39b42c --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto4.php @@ -0,0 +1,109 @@ +SetFont('Arial','B',15); + $w = $this->GetStringWidth($title)+6; + $this->SetX((210-$w)/2); + $this->SetDrawColor(0,80,180); + $this->SetFillColor(230,230,0); + $this->SetTextColor(220,50,50); + $this->SetLineWidth(1); + $this->Cell($w,9,$title,1,1,'C',true); + $this->Ln(10); + // Save ordinate + $this->y0 = $this->GetY(); +} + +function Footer() +{ + // Page footer + $this->SetY(-15); + $this->SetFont('Arial','I',8); + $this->SetTextColor(128); + $this->Cell(0,10,'Page '.$this->PageNo(),0,0,'C'); +} + +function SetCol($col) +{ + // Set position at a given column + $this->col = $col; + $x = 10+$col*65; + $this->SetLeftMargin($x); + $this->SetX($x); +} + +function AcceptPageBreak() +{ + // Method accepting or not automatic page break + if($this->col<2) + { + // Go to next column + $this->SetCol($this->col+1); + // Set ordinate to top + $this->SetY($this->y0); + // Keep on page + return false; + } + else + { + // Go back to first column + $this->SetCol(0); + // Page break + return true; + } +} + +function ChapterTitle($num, $label) +{ + // Title + $this->SetFont('Arial','',12); + $this->SetFillColor(200,220,255); + $this->Cell(0,6,"Chapter $num : $label",0,1,'L',true); + $this->Ln(4); + // Save ordinate + $this->y0 = $this->GetY(); +} + +function ChapterBody($file) +{ + // Read text file + $txt = file_get_contents($file); + // Font + $this->SetFont('Times','',12); + // Output text in a 6 cm width column + $this->MultiCell(60,5,$txt); + $this->Ln(); + // Mention + $this->SetFont('','I'); + $this->Cell(0,5,'(end of excerpt)'); + // Go back to first column + $this->SetCol(0); +} + +function PrintChapter($num, $title, $file) +{ + // Add chapter + $this->AddPage(); + $this->ChapterTitle($num,$title); + $this->ChapterBody($file); +} +} + +$pdf = new PDF(); +$title = '20000 Leagues Under the Seas'; +$pdf->SetTitle($title); +$pdf->SetAuthor('Jules Verne'); +$pdf->PrintChapter(1,'A RUNAWAY REEF','20k_c1.txt'); +$pdf->PrintChapter(2,'THE PROS AND CONS','20k_c2.txt'); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto5.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto5.htm new file mode 100644 index 0000000..f90102b --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto5.htm @@ -0,0 +1,134 @@ + + + + +Tables + + + +

        Tables

        +This tutorial shows different ways to make tables. +
        +
        <?php
        +require('fpdf.php');
        +
        +class PDF extends FPDF
        +{
        +// Load data
        +function LoadData($file)
        +{
        +    // Read file lines
        +    $lines = file($file);
        +    $data = array();
        +    foreach($lines as $line)
        +        $data[] = explode(';',trim($line));
        +    return $data;
        +}
        +
        +// Simple table
        +function BasicTable($header, $data)
        +{
        +    // Header
        +    foreach($header as $col)
        +        $this->Cell(40,7,$col,1);
        +    $this->Ln();
        +    // Data
        +    foreach($data as $row)
        +    {
        +        foreach($row as $col)
        +            $this->Cell(40,6,$col,1);
        +        $this->Ln();
        +    }
        +}
        +
        +// Better table
        +function ImprovedTable($header, $data)
        +{
        +    // Column widths
        +    $w = array(40, 35, 40, 45);
        +    // Header
        +    for($i=0;$i<count($header);$i++)
        +        $this->Cell($w[$i],7,$header[$i],1,0,'C');
        +    $this->Ln();
        +    // Data
        +    foreach($data as $row)
        +    {
        +        $this->Cell($w[0],6,$row[0],'LR');
        +        $this->Cell($w[1],6,$row[1],'LR');
        +        $this->Cell($w[2],6,number_format($row[2]),'LR',0,'R');
        +        $this->Cell($w[3],6,number_format($row[3]),'LR',0,'R');
        +        $this->Ln();
        +    }
        +    // Closing line
        +    $this->Cell(array_sum($w),0,'','T');
        +}
        +
        +// Colored table
        +function FancyTable($header, $data)
        +{
        +    // Colors, line width and bold font
        +    $this->SetFillColor(255,0,0);
        +    $this->SetTextColor(255);
        +    $this->SetDrawColor(128,0,0);
        +    $this->SetLineWidth(.3);
        +    $this->SetFont('','B');
        +    // Header
        +    $w = array(40, 35, 40, 45);
        +    for($i=0;$i<count($header);$i++)
        +        $this->Cell($w[$i],7,$header[$i],1,0,'C',true);
        +    $this->Ln();
        +    // Color and font restoration
        +    $this->SetFillColor(224,235,255);
        +    $this->SetTextColor(0);
        +    $this->SetFont('');
        +    // Data
        +    $fill = false;
        +    foreach($data as $row)
        +    {
        +        $this->Cell($w[0],6,$row[0],'LR',0,'L',$fill);
        +        $this->Cell($w[1],6,$row[1],'LR',0,'L',$fill);
        +        $this->Cell($w[2],6,number_format($row[2]),'LR',0,'R',$fill);
        +        $this->Cell($w[3],6,number_format($row[3]),'LR',0,'R',$fill);
        +        $this->Ln();
        +        $fill = !$fill;
        +    }
        +    // Closing line
        +    $this->Cell(array_sum($w),0,'','T');
        +}
        +}
        +
        +$pdf = new PDF();
        +// Column headings
        +$header = array('Country', 'Capital', 'Area (sq km)', 'Pop. (thousands)');
        +// Data loading
        +$data = $pdf->LoadData('countries.txt');
        +$pdf->SetFont('Arial','',14);
        +$pdf->AddPage();
        +$pdf->BasicTable($header,$data);
        +$pdf->AddPage();
        +$pdf->ImprovedTable($header,$data);
        +$pdf->AddPage();
        +$pdf->FancyTable($header,$data);
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +A table being just a collection of cells, it's natural to build one from them. The first +example is achieved in the most basic way possible: simple framed cells, all of the same size +and left aligned. The result is rudimentary but very quick to obtain. +
        +
        +The second table brings some improvements: each column has its own width, headings are centered, +and numbers right aligned. Moreover, horizontal lines have been removed. This is done by means +of the border parameter of the Cell() method, which specifies which sides of the +cell must be drawn. Here we want the left (L) and right (R) ones. It remains +the problem of the horizontal line to finish the table. There are two possibilities: either +check for the last line in the loop, in which case we use LRB for the border +parameter; or, as done here, add the line once the loop is over. +
        +
        +The third table is similar to the second one but uses colors. Fill, text and line colors are +simply specified. Alternate coloring for rows is obtained by using alternatively transparent +and filled cells. + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto5.php b/videodb/vendor/setasign/fpdf/tutorial/tuto5.php new file mode 100644 index 0000000..252b70f --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto5.php @@ -0,0 +1,102 @@ +Cell(40,7,$col,1); + $this->Ln(); + // Data + foreach($data as $row) + { + foreach($row as $col) + $this->Cell(40,6,$col,1); + $this->Ln(); + } +} + +// Better table +function ImprovedTable($header, $data) +{ + // Column widths + $w = array(40, 35, 40, 45); + // Header + for($i=0;$iCell($w[$i],7,$header[$i],1,0,'C'); + $this->Ln(); + // Data + foreach($data as $row) + { + $this->Cell($w[0],6,$row[0],'LR'); + $this->Cell($w[1],6,$row[1],'LR'); + $this->Cell($w[2],6,number_format($row[2]),'LR',0,'R'); + $this->Cell($w[3],6,number_format($row[3]),'LR',0,'R'); + $this->Ln(); + } + // Closing line + $this->Cell(array_sum($w),0,'','T'); +} + +// Colored table +function FancyTable($header, $data) +{ + // Colors, line width and bold font + $this->SetFillColor(255,0,0); + $this->SetTextColor(255); + $this->SetDrawColor(128,0,0); + $this->SetLineWidth(.3); + $this->SetFont('','B'); + // Header + $w = array(40, 35, 40, 45); + for($i=0;$iCell($w[$i],7,$header[$i],1,0,'C',true); + $this->Ln(); + // Color and font restoration + $this->SetFillColor(224,235,255); + $this->SetTextColor(0); + $this->SetFont(''); + // Data + $fill = false; + foreach($data as $row) + { + $this->Cell($w[0],6,$row[0],'LR',0,'L',$fill); + $this->Cell($w[1],6,$row[1],'LR',0,'L',$fill); + $this->Cell($w[2],6,number_format($row[2]),'LR',0,'R',$fill); + $this->Cell($w[3],6,number_format($row[3]),'LR',0,'R',$fill); + $this->Ln(); + $fill = !$fill; + } + // Closing line + $this->Cell(array_sum($w),0,'','T'); +} +} + +$pdf = new PDF(); +// Column headings +$header = array('Country', 'Capital', 'Area (sq km)', 'Pop. (thousands)'); +// Data loading +$data = $pdf->LoadData('countries.txt'); +$pdf->SetFont('Arial','',14); +$pdf->AddPage(); +$pdf->BasicTable($header,$data); +$pdf->AddPage(); +$pdf->ImprovedTable($header,$data); +$pdf->AddPage(); +$pdf->FancyTable($header,$data); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto6.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto6.htm new file mode 100644 index 0000000..602a119 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto6.htm @@ -0,0 +1,154 @@ + + + + +Links and flowing text + + + +

        Links and flowing text

        +This tutorial explains how to insert links (internal and external) and shows a new text writing +mode. It also contains a basic HTML parser. +
        +
        <?php
        +require('fpdf.php');
        +
        +class PDF extends FPDF
        +{
        +protected $B = 0;
        +protected $I = 0;
        +protected $U = 0;
        +protected $HREF = '';
        +
        +function WriteHTML($html)
        +{
        +    // HTML parser
        +    $html = str_replace("\n",' ',$html);
        +    $a = preg_split('/<(.*)>/U',$html,-1,PREG_SPLIT_DELIM_CAPTURE);
        +    foreach($a as $i=>$e)
        +    {
        +        if($i%2==0)
        +        {
        +            // Text
        +            if($this->HREF)
        +                $this->PutLink($this->HREF,$e);
        +            else
        +                $this->Write(5,$e);
        +        }
        +        else
        +        {
        +            // Tag
        +            if($e[0]=='/')
        +                $this->CloseTag(strtoupper(substr($e,1)));
        +            else
        +            {
        +                // Extract attributes
        +                $a2 = explode(' ',$e);
        +                $tag = strtoupper(array_shift($a2));
        +                $attr = array();
        +                foreach($a2 as $v)
        +                {
        +                    if(preg_match('/([^=]*)=["\']?([^"\']*)/',$v,$a3))
        +                        $attr[strtoupper($a3[1])] = $a3[2];
        +                }
        +                $this->OpenTag($tag,$attr);
        +            }
        +        }
        +    }
        +}
        +
        +function OpenTag($tag, $attr)
        +{
        +    // Opening tag
        +    if($tag=='B' || $tag=='I' || $tag=='U')
        +        $this->SetStyle($tag,true);
        +    if($tag=='A')
        +        $this->HREF = $attr['HREF'];
        +    if($tag=='BR')
        +        $this->Ln(5);
        +}
        +
        +function CloseTag($tag)
        +{
        +    // Closing tag
        +    if($tag=='B' || $tag=='I' || $tag=='U')
        +        $this->SetStyle($tag,false);
        +    if($tag=='A')
        +        $this->HREF = '';
        +}
        +
        +function SetStyle($tag, $enable)
        +{
        +    // Modify style and select corresponding font
        +    $this->$tag += ($enable ? 1 : -1);
        +    $style = '';
        +    foreach(array('B', 'I', 'U') as $s)
        +    {
        +        if($this->$s>0)
        +            $style .= $s;
        +    }
        +    $this->SetFont('',$style);
        +}
        +
        +function PutLink($URL, $txt)
        +{
        +    // Put a hyperlink
        +    $this->SetTextColor(0,0,255);
        +    $this->SetStyle('U',true);
        +    $this->Write(5,$txt,$URL);
        +    $this->SetStyle('U',false);
        +    $this->SetTextColor(0);
        +}
        +}
        +
        +$html = 'You can now easily print text mixing different styles: <b>bold</b>, <i>italic</i>,
        +<u>underlined</u>, or <b><i><u>all at once</u></i></b>!<br><br>You can also insert links on
        +text, such as <a href="http://www.fpdf.org">www.fpdf.org</a>, or on an image: click on the logo.';
        +
        +$pdf = new PDF();
        +// First page
        +$pdf->AddPage();
        +$pdf->SetFont('Arial','',20);
        +$pdf->Write(5,"To find out what's new in this tutorial, click ");
        +$pdf->SetFont('','U');
        +$link = $pdf->AddLink();
        +$pdf->Write(5,'here',$link);
        +$pdf->SetFont('');
        +// Second page
        +$pdf->AddPage();
        +$pdf->SetLink($link);
        +$pdf->Image('logo.png',10,12,30,0,'','http://www.fpdf.org');
        +$pdf->SetLeftMargin(45);
        +$pdf->SetFontSize(14);
        +$pdf->WriteHTML($html);
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        +The new method to print text is Write(). It's very close to MultiCell(); the differences are: +
          +
        • The end of line is at the right margin and the next line begins at the left one
        • +
        • The current position moves at the end of the text
        • +
        +So it allows to write a chunk of text, alter the font style, then continue from the exact +place we left it. On the other hand, you cannot justify it. +
        +
        +The method is used on the first page to put a link pointing to the second one. The beginning of +the sentence is written in regular style, then we switch to underline and finish it. The link +is created with AddLink(), which returns a link identifier. The identifier is +passed as third parameter of Write(). Once the second page is created, we use SetLink() to +make the link point to the beginning of the current page. +
        +
        +Then we put an image with an external link on it. An external link is just a URL. It's passed as +last parameter of Image(). +
        +
        +Finally, the left margin is moved after the image with SetLeftMargin() and some text in +HTML format is output. A very simple HTML parser is used for this, based on regular expressions. +Recognized tags are <b>, <i>, <u>, <a> and <br>; the others are +ignored. The parser also makes use of the Write() method. An external link is put the same way as +an internal one (third parameter of Write()). Note that Cell() also allows to put links. + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto6.php b/videodb/vendor/setasign/fpdf/tutorial/tuto6.php new file mode 100644 index 0000000..427e4d3 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto6.php @@ -0,0 +1,113 @@ +/U',$html,-1,PREG_SPLIT_DELIM_CAPTURE); + foreach($a as $i=>$e) + { + if($i%2==0) + { + // Text + if($this->HREF) + $this->PutLink($this->HREF,$e); + else + $this->Write(5,$e); + } + else + { + // Tag + if($e[0]=='/') + $this->CloseTag(strtoupper(substr($e,1))); + else + { + // Extract attributes + $a2 = explode(' ',$e); + $tag = strtoupper(array_shift($a2)); + $attr = array(); + foreach($a2 as $v) + { + if(preg_match('/([^=]*)=["\']?([^"\']*)/',$v,$a3)) + $attr[strtoupper($a3[1])] = $a3[2]; + } + $this->OpenTag($tag,$attr); + } + } + } +} + +function OpenTag($tag, $attr) +{ + // Opening tag + if($tag=='B' || $tag=='I' || $tag=='U') + $this->SetStyle($tag,true); + if($tag=='A') + $this->HREF = $attr['HREF']; + if($tag=='BR') + $this->Ln(5); +} + +function CloseTag($tag) +{ + // Closing tag + if($tag=='B' || $tag=='I' || $tag=='U') + $this->SetStyle($tag,false); + if($tag=='A') + $this->HREF = ''; +} + +function SetStyle($tag, $enable) +{ + // Modify style and select corresponding font + $this->$tag += ($enable ? 1 : -1); + $style = ''; + foreach(array('B', 'I', 'U') as $s) + { + if($this->$s>0) + $style .= $s; + } + $this->SetFont('',$style); +} + +function PutLink($URL, $txt) +{ + // Put a hyperlink + $this->SetTextColor(0,0,255); + $this->SetStyle('U',true); + $this->Write(5,$txt,$URL); + $this->SetStyle('U',false); + $this->SetTextColor(0); +} +} + +$html = 'You can now easily print text mixing different styles: bold, italic, +underlined, or all at once!

        You can also insert links on +text, such as www.fpdf.org, or on an image: click on the logo.'; + +$pdf = new PDF(); +// First page +$pdf->AddPage(); +$pdf->SetFont('Arial','',20); +$pdf->Write(5,"To find out what's new in this tutorial, click "); +$pdf->SetFont('','U'); +$link = $pdf->AddLink(); +$pdf->Write(5,'here',$link); +$pdf->SetFont(''); +// Second page +$pdf->AddPage(); +$pdf->SetLink($link); +$pdf->Image('logo.png',10,12,30,0,'','http://www.fpdf.org'); +$pdf->SetLeftMargin(45); +$pdf->SetFontSize(14); +$pdf->WriteHTML($html); +$pdf->Output(); +?> diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto7.htm b/videodb/vendor/setasign/fpdf/tutorial/tuto7.htm new file mode 100644 index 0000000..6cd3534 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto7.htm @@ -0,0 +1,182 @@ + + + + +Adding new fonts and encodings + + + +

        Adding new fonts and encodings

        +This tutorial explains how to use TrueType, OpenType and Type1 fonts so that you are not limited to the +standard fonts anymore. The other benefit is that you can choose the text encoding, which allows you to +use other languages than the Western ones (the standard fonts support only cp1252 aka windows-1252). +
        +
        +For OpenType, only the format based on TrueType is supported (not the one based on Type1).
        +For Type1, you will need the corresponding AFM file (it is usually provided with the font). +
        +
        +Adding a new font requires two steps: +
          +
        • Generation of the font definition file
        • +
        • Declaration of the font in the script
        • +
        + +

        Generation of the font definition file

        +The first step consists in generating a PHP file containing all the information needed by FPDF; +in addition, the font file is compressed. To do this, a helper script is provided in the makefont +directory of the package: makefont.php. It contains the following function: +
        +
        +MakeFont(string fontfile [, string enc [, boolean embed [, boolean subset]]]) +
        +
        fontfile
        +
        +

        Path to the .ttf, .otf or .pfb file.

        +
        +
        enc
        +
        +

        Name of the encoding to use. Default value: cp1252.

        +
        +
        embed
        +
        +

        Whether to embed the font or not. Default value: true.

        +
        +
        subset
        +
        +

        Whether to subset the font or not. Default value: true.

        +
        +
        +The first parameter is the name of the font file. The extension must be either .ttf, .otf or .pfb and +determines the font type. If your Type1 font is in ASCII format (.pfa), you can convert it to binary +(.pfb) with the help of t1utils. +
        +
        +For Type1 fonts, the corresponding .afm file must be present in the same directory. +
        +
        +The encoding defines the association between a code (from 0 to 255) and a character. The first 128 are +always the same and correspond to ASCII; the following are variable. Encodings are stored in .map +files. The available ones are: +
          +
        • cp1250 (Central Europe)
        • +
        • cp1251 (Cyrillic)
        • +
        • cp1252 (Western Europe)
        • +
        • cp1253 (Greek)
        • +
        • cp1254 (Turkish)
        • +
        • cp1255 (Hebrew)
        • +
        • cp1257 (Baltic)
        • +
        • cp1258 (Vietnamese)
        • +
        • cp874 (Thai)
        • +
        • ISO-8859-1 (Western Europe)
        • +
        • ISO-8859-2 (Central Europe)
        • +
        • ISO-8859-4 (Baltic)
        • +
        • ISO-8859-5 (Cyrillic)
        • +
        • ISO-8859-7 (Greek)
        • +
        • ISO-8859-9 (Turkish)
        • +
        • ISO-8859-11 (Thai)
        • +
        • ISO-8859-15 (Western Europe)
        • +
        • ISO-8859-16 (Central Europe)
        • +
        • KOI8-R (Russian)
        • +
        • KOI8-U (Ukrainian)
        • +
        +Of course, the font must contain the characters corresponding to the selected encoding. +
        +
        +The third parameter indicates whether the font should be embedded in the PDF or not. When a font is +not embedded, it is searched in the system. The advantage is that the PDF file is smaller; on the +other hand, if it is not available, then a substitution font is used. So you should ensure that the +needed font is installed on the client systems. Embedding is the recommended option to guarantee a +correct rendering. +
        +
        +The last parameter indicates whether subsetting should be used, that is to say, whether only +the characters from the selected encoding should be kept in the embedded font. As a result, +the size of the PDF file can be greatly reduced, especially if the original font was big. +
        +
        +After you have called the function (create a new file for this and include makefont.php), a .php file +is created, with the same name as the font file. You may rename it if you wish. If the case of embedding, +the font file is compressed and gives a second file with .z as extension (except if the compression +function is not available, it requires Zlib). You may rename it too, but in this case you have to change +the variable $file in the .php file accordingly. +
        +
        +Example: +
        +
        <?php
        +require('makefont/makefont.php');
        +
        +MakeFont('C:\\Windows\\Fonts\\comic.ttf','cp1252');
        +?>
        +
        +which gives the files comic.php and comic.z. +
        +
        +Then copy the generated files to the font directory. If the font file could not be compressed, copy +it directly instead of the .z version. +
        +
        +Another way to call MakeFont() is through the command line: +
        +
        +php makefont\makefont.php C:\Windows\Fonts\comic.ttf cp1252 +
        +
        +Finally, for TrueType and OpenType fonts, you can also generate the files +online instead of doing it manually. + +

        Declaration of the font in the script

        +The second step is simple. You just need to call the AddFont() method: +
        +
        $pdf->AddFont('Comic','','comic.php');
        +
        +
        +And the font is now available (in regular and underlined styles), usable like the others. If we +had worked with Comic Sans MS Bold (comicbd.ttf), we would have written: +
        +
        $pdf->AddFont('Comic','B','comicbd.php');
        +
        +
        + +

        Example

        +Now let's see a complete example. We will use the Ceviche One font. +The first step is the generation of the font files: +
        +
        <?php
        +require('makefont/makefont.php');
        +
        +MakeFont('CevicheOne-Regular.ttf','cp1252');
        +?>
        +
        +The script produces the following output: +
        +
        +Font file compressed: CevicheOne-Regular.z
        +Font definition file generated: CevicheOne-Regular.php
        +
        +Alternatively we could have used the command line: +
        +
        +php makefont\makefont.php CevicheOne-Regular.ttf cp1252 +
        +
        +or used the online generator. +
        +
        +We can now copy the two generated files to the font directory and write the script: +
        +
        <?php
        +require('fpdf.php');
        +
        +$pdf = new FPDF();
        +$pdf->AddFont('CevicheOne','','CevicheOne-Regular.php');
        +$pdf->AddPage();
        +$pdf->SetFont('CevicheOne','',35);
        +$pdf->Write(10,'Enjoy new fonts with FPDF!');
        +$pdf->Output();
        +?>
        +
        +

        [Demo]

        + + diff --git a/videodb/vendor/setasign/fpdf/tutorial/tuto7.php b/videodb/vendor/setasign/fpdf/tutorial/tuto7.php new file mode 100644 index 0000000..d25e0f8 --- /dev/null +++ b/videodb/vendor/setasign/fpdf/tutorial/tuto7.php @@ -0,0 +1,11 @@ +AddFont('CevicheOne','','CevicheOne-Regular.php'); +$pdf->AddPage(); +$pdf->SetFont('CevicheOne','',45); +$pdf->Cell(0,10,'Enjoy new fonts with FPDF!'); +$pdf->Output(); +?> diff --git a/videodb/vendor/smarty/smarty/CHANGELOG.md b/videodb/vendor/smarty/smarty/CHANGELOG.md new file mode 100644 index 0000000..bb99834 --- /dev/null +++ b/videodb/vendor/smarty/smarty/CHANGELOG.md @@ -0,0 +1,3517 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [3.1.47] - 2022-09-14 + +### Security +- Applied appropriate javascript and html escaping in mailto plugin to counter injection attacks [#454](https://github.com/smarty-php/smarty/issues/454) + +### Fixed +- Fixed use of `rand()` without a parameter in math function [#794](https://github.com/smarty-php/smarty/issues/794) +- Fixed unselected year/month/day not working in html_select_date [#395](https://github.com/smarty-php/smarty/issues/395) + +## [3.1.46] - 2022-08-01 + +### Fixed +- Fixed problems with smarty_mb_str_replace [#549](https://github.com/smarty-php/smarty/issues/549) +- Fixed second parameter of unescape modifier not working [#777](https://github.com/smarty-php/smarty/issues/777) + +## [3.1.45] - 2022-05-17 + +### Security +- Prevent PHP injection through malicious block name or include file name. This addresses CVE-2022-29221 + +### Fixed +- Math equation `max(x, y)` didn't work anymore [#721](https://github.com/smarty-php/smarty/issues/721) + +## [3.1.44] - 2022-01-18 + +### Fixed +- Fixed illegal characters bug in math function security check [#702](https://github.com/smarty-php/smarty/issues/702) + +## [3.1.43] - 2022-01-10 + +### Security +- Prevent evasion of the `static_classes` security policy. This addresses CVE-2021-21408 + +## [3.1.42] - 2022-01-10 + +### Security +- Prevent arbitrary PHP code execution through maliciously crafted expression for the math function. This addresses CVE-2021-29454 + +## [3.1.41] - 2022-01-09 + +### Security +- Rewrote the mailto function to not use `eval` when encoding with javascript + +## [3.1.40] - 2021-10-13 + +### Changed +- modifier escape now triggers a E_USER_NOTICE when an unsupported escape type is used https://github.com/smarty-php/smarty/pull/649 + +### Security +- More advanced javascript escaping to handle https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements thanks to m-haritonov + +## [3.1.39] - 2021-02-17 + +### Security +- Prevent access to `$smarty.template_object` in sandbox mode. This addresses CVE-2021-26119. +- Fixed code injection vulnerability by using illegal function names in `{function name='blah'}{/function}`. This addresses CVE-2021-26120. + +## [3.1.38] - 2021-01-08 + +### Fixed +- Smarty::SMARTY_VERSION wasn't updated https://github.com/smarty-php/smarty/issues/628 + +## [3.1.37] - 2021-01-07 + +### Changed +- Changed error handlers and handling of undefined constants for php8-compatibility (set $errcontext argument optional) https://github.com/smarty-php/smarty/issues/605 +- Changed expected error levels in unit tests for php8-compatibility +- Travis unit tests now run for all php versions >= 5.3, including php8 +- Travis runs on Xenial where possible + +### Fixed +- PHP5.3 compatibility fixes +- Brought lexer source functionally up-to-date with compiled version + +## [3.1.36] - 2020-04-14 + +### Fixed + - Smarty::SMARTY_VERSION wasn't updated in v3.1.35 https://github.com/smarty-php/smarty/issues/584 + +## [3.1.35] - 2020-04-14 + - remove whitespaces after comments https://github.com/smarty-php/smarty/issues/447 + - fix foreachelse on arrayiterators https://github.com/smarty-php/smarty/issues/506 + - fix files contained in git export archive for package maintainers https://github.com/smarty-php/smarty/issues/325 + - throw SmartyException when setting caching attributes for cacheable plugin https://github.com/smarty-php/smarty/issues/457 + - fix errors that occured where isset was replaced with null check such as https://github.com/smarty-php/smarty/issues/453 + - unit tests are now in the repository + +## 3.1.34 release - 05.11.2019 +13.01.2020 + - fix typo in exception message (JercSi) + - fix typehint warning with callable (bets4breakfast) + - add travis badge and compatability info to readme (matks) + - fix stdClass cast when compiling foreach (carpii) + - fix wrong set/get methods for memcached (IT-Experte) + - fix pborm assigning value to object variables in smarty_internal_compile_assign (Hunman) + - exclude error_reporting.ini from git export (glensc) + +## 3.1.34-dev-6 - +30.10.2018 + - bugfix a nested subblock in an inheritance child template was not replace by + outer level block with same name in same child template https://github.com/smarty-php/smarty/issues/500 + +29.10.2018 + - bugfix Smarty::$php_handling == PHP_PASSTHRU (default) did eat the "\n" (newline) character if it did directly followed + a PHP tag like "?>" or other https://github.com/smarty-php/smarty/issues/501 + +14.10.2018 + - bugfix autoloader exit shortcut https://github.com/smarty-php/smarty/issues/467 + +11.10.2018 + - bugfix {insert} not works when caching is enabled and included template is present + https://github.com/smarty-php/smarty/issues/496 + - bugfix in date-format modifier; NULL at date string or default_date did not produce correct output + https://github.com/smarty-php/smarty/pull/458 + +09.10.2018 + - bugfix fix of 26.8.2017 https://github.com/smarty-php/smarty/issues/327 + modifier is applied to sum expression https://github.com/smarty-php/smarty/issues/491 + - bugfix indexed arrays could not be defined "array(...)"" + +18.09.2018 + - bugfix large plain text template sections without a Smarty tag > 700kB could + could fail in version 3.1.32 and 3.1.33 because PHP preg_match() restrictions + https://github.com/smarty-php/smarty/issues/488 + +## 3.1.33 release - 12.09.2018 +## 3.1.33-dev-12 - +03.09.2018 + - bugfix {foreach} using new style property access like {$item@property} on + Smarty 2 style named foreach loop could produce errors https://github.com/smarty-php/smarty/issues/484 + +31.08.2018 + - bugfix some custom left and right delimiters like '{^' '^}' did not work + https://github.com/smarty-php/smarty/issues/450 https://github.com/smarty-php/smarty/pull/482 + + - reformating for PSR-2 coding standards https://github.com/smarty-php/smarty/pull/483 + + - bugfix on Windows absolute filepathes did fail if the drive letter was followed by a linux DIRECTORY_SEPARATOR + like C:/ at Smarty > 3.1.33-dev-5 https://github.com/smarty-php/smarty/issues/451 + + - PSR-2 code style fixes for config and template file Lexer/Parser generated with + the Smarty Lexer/Parser generator from https://github.com/smarty-php/smarty-lexer + https://github.com/smarty-php/smarty/pull/483 + +26.08.2018 + - bugfix/enhancement {capture} allow variable as capture block name in Smarty special variable + like $smarty.capture.$foo https://github.com/smarty-php/smarty/issues/478 https://github.com/smarty-php/smarty/pull/481 + +## 3.1.33-dev-6 - +19.08.2018 + - fix PSR-2 coding standards and PHPDoc blocks https://github.com/smarty-php/smarty/pull/452 + https://github.com/smarty-php/smarty/pull/475 + https://github.com/smarty-php/smarty/pull/473 + - bugfix PHP5.2 compatibility https://github.com/smarty-php/smarty/pull/472 + +## 3.1.33-dev-4 - +17.05.2018 + - bugfix strip-block produces different output in Smarty v3.1.32 https://github.com/smarty-php/smarty/issues/436 + - bugfix Smarty::compileAllTemplates ignores `$extension` parameter https://github.com/smarty-php/smarty/issues/437 + https://github.com/smarty-php/smarty/pull/438 + - improvement do not compute total property in {foreach} if not needed https://github.com/smarty-php/smarty/issues/443 + - bugfix plugins may not be loaded when setMergeCompiledIncludes is true https://github.com/smarty-php/smarty/issues/435 + +26.04.2018 + - bugfix regarding Security Vulnerability did not solve the problem under Linux. + Security issue CVE-2018-16831 + +## 3.1.32 - (24.04.2018) +24.04.2018 + - bugfix possible Security Vulnerability in Smarty_Security class. + +26.03.2018 + - bugfix plugins may not be loaded if {function} or {block} tags are executed in nocache mode + https://github.com/smarty-php/smarty/issues/371 + +26.03.2018 + - new feature {parent} = {$smarty.block.parent} {child} = {$smarty.block.child} + +23.03.2018 + - bugfix preg_replace could fail on large content resulting in a blank page https://github.com/smarty-php/smarty/issues/417 + +21.03.2018 + - bugfix {$smarty.section...} used outside {section}{/section} showed incorrect values if {section}{/section} was called inside + another loop https://github.com/smarty-php/smarty/issues/422 + - bugfix short form of {section} attributes did not work https://github.com/smarty-php/smarty/issues/428 + +17.03.2018 + - improvement Smarty::compileAllTemplates() exit with a non-zero status code if max errors is reached https://github.com/smarty-php/smarty/pull/402 + +16.03.2018 + - bugfix extends resource did not work with user defined left/right delimiter https://github.com/smarty-php/smarty/issues/419 + +22.11.2017 + - bugfix {break} and {continue} could fail if {foreach}{/foreach} did contain other + looping tags like {for}, {section} and {while} https://github.com/smarty-php/smarty/issues/323 + +20.11.2017 + - bugfix rework of newline spacing between tag code and template text. + now again identical with Smarty2 (forum topic 26878) + - replacement of " by ' + +05.11.2017 + - lexer/parser optimization + - code cleanup and optimizations + - bugfix {$smarty.section.name.loop} used together with {$smarty.section.name.total} could produce + wrong results (forum topic 27041) + +26.10.2017 + - bugfix Smarty version was not filled in header comment of compiled and cached files + - optimization replace internal Smarty::$ds property by DIRECTORY_SEPARATOR + - deprecate functions Smarty::muteExpectedErrors() and Smarty::unmuteExpectedErrors() + as Smarty does no longer use error suppression like @filemtime(). + for backward compatibility code is moved from Smarty class to an external class and still can be + called. + - correction of PHPDoc blocks + - minor code cleanup + +21.10.2017 + - bugfix custom delimiters could fail since modification of version 3.1.32-dev-23 + https://github.com/smarty-php/smarty/issues/394 + +18.10.2017 + - bugfix fix implementation of unclosed block tag in double quoted string of 12.10.2017 + https://github.com/smarty-php/smarty/issues/396 https://github.com/smarty-php/smarty/issues/397 + https://github.com/smarty-php/smarty/issues/391 https://github.com/smarty-php/smarty/issues/392 + +12.10.2017 + - bugfix $smarty.block.child and $smarty.block.parent could not be used like any + $smarty special variable https://github.com/smarty-php/smarty/issues/393 + - unclosed block tag in double quoted string must throw compiler exception. + https://github.com/smarty-php/smarty/issues/391 https://github.com/smarty-php/smarty/issues/392 + +07.10.2017 + - bugfix modification of 9.8.2017 did fail on some recursive + tag nesting. https://github.com/smarty-php/smarty/issues/389 + +26.8.2017 + - bugfix chained modifier failed when last modifier parameter is a signed value + https://github.com/smarty-php/smarty/issues/327 + - bugfix templates filepath with multibyte characters did not work + https://github.com/smarty-php/smarty/issues/385 + - bugfix {make_nocache} did display code if the template did not contain other nocache code + https://github.com/smarty-php/smarty/issues/369 + +09.8.2017 + - improvement repeated delimiter like {{ and }} will be treated as literal + https://groups.google.com/forum/#!topic/smarty-developers/h9r82Bx4KZw + +05.8.2017 + - bugfix wordwrap modifier could fail if used in nocache code. + converted plugin file shared.mb_wordwrap.php into modifier.mb_wordwrap.php + - cleanup of _getSmartyObj() + +31.7.2017 + - Call clearstatcache() after mkdir() failure https://github.com/smarty-php/smarty/pull/379 + +30.7.2017 + - rewrite mkdir() bugfix to retry automatically see https://github.com/smarty-php/smarty/pull/377 + https://github.com/smarty-php/smarty/pull/379 + +21.7.2017 + - security possible PHP code injection on custom resources at display() or fetch() + calls if the resource does not sanitize the template name + - bugfix fix 'mkdir(): File exists' error on create directory from parallel + processes https://github.com/smarty-php/smarty/pull/377 + - bugfix solve preg_match() hhvm parameter problem https://github.com/smarty-php/smarty/pull/372 + +27.5.2017 + - bugfix change compiled code for registered function and modifiers to called as callable to allow closures + https://github.com/smarty-php/smarty/pull/368, https://github.com/smarty-php/smarty/issues/273 + - bugfix https://github.com/smarty-php/smarty/pull/368 did break the default plugin handler + - improvement replace phpversion() by PHP_VERSION constant. + https://github.com/smarty-php/smarty/pull/363 + +21.5.2017 + - performance store flag for already required shared plugin functions in static variable or + Smarty's $_cache to improve performance when plugins are often called + https://github.com/smarty-php/smarty/commit/51e0d5cd405d764a4ea257d1bac1fb1205f74528#commitcomment-22280086 + - bugfix remove special treatment of classes implementing ArrayAccess in {foreach} + https://github.com/smarty-php/smarty/issues/332 + - bugfix remove deleted files by clear_cache() and clear_compiled_template() from + ACP cache if present, add some is_file() checks to avoid possible warnings on filemtime() + caused by above functions. + https://github.com/smarty-php/smarty/issues/341 + - bugfix version 3.1.31 did fail under PHP 5.2 + https://github.com/smarty-php/smarty/issues/365 + +19.5.2017 + - change properties $accessMap and $obsoleteProperties from private to protected + https://github.com/smarty-php/smarty/issues/351 + - new feature The named capture buffers can now be accessed also as array + See NEWS_FEATURES.txt https://github.com/smarty-php/smarty/issues/366 + - improvement check if ini_get() and ini_set() not disabled + https://github.com/smarty-php/smarty/pull/362 + +24.4.2017 + - fix spelling https://github.com/smarty-php/smarty/commit/e3eda8a5f5653d8abb960eb1bc47e3eca679b1b4#commitcomment-21803095 + +17.4.2017 + - correct generated code on empty() and isset() call, observe change PHP behaviour since PHP 5.5 + https://github.com/smarty-php/smarty/issues/347 + +14.4.2017 + - merge pull requests https://github.com/smarty-php/smarty/pull/349, https://github.com/smarty-php/smarty/pull/322 and https://github.com/smarty-php/smarty/pull/337 to fix spelling and annotation + +13.4.2017 + - bugfix array_merge() parameter should be checked https://github.com/smarty-php/smarty/issues/350 + +## 3.1.31 - (14.12.2016) + 23.11.2016 + - move template object cache into static variables + + 19.11.2016 + - bugfix inheritance root child templates containing nested {block}{/block} could call sub-bock content from parent + template https://github.com/smarty-php/smarty/issues/317 + - change version checking + + 11.11.2016 + - bugfix when Smarty is using a cached template object on Smarty::fetch() or Smarty::isCached() the inheritance data + must be removed https://github.com/smarty-php/smarty/issues/312 + - smaller speed optimization + + 08.11.2016 + - add bootstrap file to load and register Smarty_Autoloader. Change composer.json to make it known to composer + + 07.11.2016 + - optimization of lexer speed https://github.com/smarty-php/smarty/issues/311 + + 27.10.2016 + - bugfix template function definitions array has not been cached between Smarty::fetch() and Smarty::display() calls + https://github.com/smarty-php/smarty/issues/301 + + 23.10.2016 + - improvement/bugfix when Smarty::fetch() is called on a template object the inheritance and tplFunctions property + should be copied to the called template object + + 21.10.2016 + - bugfix for compile locking touched timestamp of old compiled file was not restored on compilation error https://github.com/smarty-php/smarty/issues/308 + + 20.10.2016 + - bugfix nocache code was not removed in cache file when subtemplate did contain PHP short tags in text but no other + nocache code https://github.com/smarty-php/smarty/issues/300 + + 19.10.2016 + - bugfix {make_nocache $var} did fail when variable value did contain '\' https://github.com/smarty-php/smarty/issues/305 + - bugfix {make_nocache $var} remove spaces from variable value https://github.com/smarty-php/smarty/issues/304 + + 12.10.2016 + - bugfix {include} with template names including variable or constants could fail after bugfix from + 28.09.2016 https://github.com/smarty-php/smarty/issues/302 + + 08.10.2016 + - optimization move runtime extension for template functions into Smarty objects + + 29.09.2016 + - improvement new Smarty::$extends_recursion property to disable execution of {extends} in templates called by extends resource + https://github.com/smarty-php/smarty/issues/296 + + 28.09.2016 + - bugfix the generated code for calling a subtemplate must pass the template resource name in single quotes https://github.com/smarty-php/smarty/issues/299 + - bugfix nocache hash was not removed for tags in subtemplates https://github.com/smarty-php/smarty/issues/300 + + 27.09.2016 + - bugfix when Smarty does use an internally cached template object on Smarty::fetch() calls + the template and config variables must be cleared https://github.com/smarty-php/smarty/issues/297 + + 20.09.2016 + - bugfix some $smarty special template variables are no longer accessed as real variable. + using them on calls like {if isset($smarty.foo)} or {if empty($smarty.foo)} will fail + http://www.smarty.net/forums/viewtopic.php?t=26222 + - temporary fix for https://github.com/smarty-php/smarty/issues/293 main reason still under investigation + - improvement new tags {block_parent} {block_child} in template inheritance + + 19.09.2016 + - optimization clear compiled and cached folder completely on detected version change + - cleanup convert cache resource file method clear into runtime extension + + 15.09.2016 + - bugfix assigning a variable in if condition by function like {if $value = array_shift($array)} the function got called twice https://github.com/smarty-php/smarty/issues/291 + - bugfix function plugins called with assign attribute like {foo assign='bar'} did not output returned content because + because assumption was made that it was assigned to a variable https://github.com/smarty-php/smarty/issues/292 + - bugfix calling $smarty->isCached() on a not existing cache file with $smarty->cache_locking = true; could cause a 10 second delay http://www.smarty.net/forums/viewtopic.php?t=26282 + - improvement make Smarty::clearCompiledTemplate() on custom resource independent from changes of templateId computation + + 11.09.2016 + - improvement {math} misleading E_USER_WARNING messages when parameter value = null https://github.com/smarty-php/smarty/issues/288 + - improvement move often used code snippets into methods + - performance Smarty::configLoad() did load unneeded template source object + + 09.09.2016 + - bugfix/optimization {foreach} did not execute the {foreachelse} when iterating empty objects https://github.com/smarty-php/smarty/pull/287 + - bugfix {foreach} must keep the @properties when restoring a saved $item variable as the properties might be used outside {foreach} https://github.com/smarty-php/smarty/issues/267 + - improvement {foreach} observe {break n} and {continue n} nesting levels when restoring saved $item and $key variables + + 08.09.2016 + - bugfix implement wrapper for removed method getConfigVariable() https://github.com/smarty-php/smarty/issues/286 + + 07.09.2016 + - bugfix using nocache like attribute with value true like {plugin nocache=true} did not work https://github.com/smarty-php/smarty/issues/285 + - bugfix uppercase TRUE, FALSE and NULL did not work when security was enabled https://github.com/smarty-php/smarty/issues/282 + - bugfix when {foreach} was looping over an object the total property like {$item@total} did always return 1 https://github.com/smarty-php/smarty/issues/281 + - bugfix {capture}{/capture} did add in 3.1.30 unintended additional blank lines https://github.com/smarty-php/smarty/issues/268 + + 01.09.2016 + - performance require_once should be called only once for shared plugins https://github.com/smarty-php/smarty/issues/280 + + 26.08.2016 + - bugfix change of 23.08.2016 failed on linux when use_include_path = true + + 23.08.2016 + - bugfix remove constant DS as shortcut for DIRECTORY_SEPARATOR as the user may have defined it to something else https://github.com/smarty-php/smarty/issues/277 + + 20.08-2016 + - bugfix {config_load ... scope="global"} shall not throw an arror but fallback to scope="smarty" https://github.com/smarty-php/smarty/issues/274 + - bugfix {make_nocache} failed when using composer autoloader https://github.com/smarty-php/smarty/issues/275 + + 14.08.2016 + - bugfix $smarty_>debugging = true; did E_NOTICE messages when {eval} tag was used https://github.com/smarty-php/smarty/issues/266 + - bugfix Class 'Smarty_Internal_Runtime_ValidateCompiled' not found when upgrading from some older Smarty versions with existing + compiled or cached template files https://github.com/smarty-php/smarty/issues/269 + - optimization remove unneeded call to update acopes when {assign} scope and template scope was local (default) + +## 3.1.30 - (07.08.2016) + + 07.08.2016 + - bugfix update of 04.08.2016 was incomplete + + 05.08.2016 + - bugfix compiling of templates failed when the Smarty delimiter did contain '/' https://github.com/smarty-php/smarty/issues/264 + - updated error checking at template and config default handler + + 04.08.2016 + - improvement move template function source parameter into extension + + 26.07.2016 + - optimization unneeded loading of compiled resource + + 24.07.2016 + - regression this->addPluginsDir('/abs/path/to/dir') adding absolute path without trailing '/' did fail https://github.com/smarty-php/smarty/issues/260 + + 23.07.2016 + - bugfix setTemplateDir('/') and setTemplateDir('') did create wrong absolute filepath https://github.com/smarty-php/smarty/issues/245 + - optimization of filepath normalization + - improvement remove double function declaration in plugin shared.escape_special_cars.php https://github.com/smarty-php/smarty/issues/229 + + 19.07.2016 + - bugfix multiple {include} with relative filepath within {block}{/block} could fail https://github.com/smarty-php/smarty/issues/246 + - bugfix {math} shell injection vulnerability patch provided by Tim Weber + + 18.07.2016 + - bugfix {foreach} if key variable and item@key attribute have been used both the key variable was not updated https://github.com/smarty-php/smarty/issues/254 + - bugfix modifier on plugins like {plugin|modifier ... } did fail when the plugin does return an array https://github.com/smarty-php/smarty/issues/228 + - bugfix avoid opcache_invalidate to result in ErrorException when opcache.restrict_api is not empty https://github.com/smarty-php/smarty/pull/244 + - bugfix multiple {include} with relative filepath within {block}{/block} could fail https://github.com/smarty-php/smarty/issues/246 + + 14.07.2016 + - bugfix wrong parameter on compileAllTemplates() and compileAllConfig() https://github.com/smarty-php/smarty/issues/231 + + 13.07.2016 + - bugfix PHP 7 compatibility on registered compiler plugins https://github.com/smarty-php/smarty/issues/241 + - update testInstall() https://github.com/smarty-php/smarty/issues/248https://github.com/smarty-php/smarty/issues/248 + - bugfix enable debugging could fail when template objects did already exists https://github.com/smarty-php/smarty/issues/237 + - bugfix template function data should be merged when loading subtemplate https://github.com/smarty-php/smarty/issues/240 + - bugfix wrong parameter on compileAllTemplates() https://github.com/smarty-php/smarty/issues/231 + + 12.07.2016 + - bugfix {foreach} item variable must be created also on empty from array https://github.com/smarty-php/smarty/issues/238 and https://github.com/smarty-php/smarty/issues/239 + - bugfix enableSecurity() must init cache flags https://github.com/smarty-php/smarty/issues/247 + + 27.05.2016 + - bugfix/improvement of compileAlltemplates() follow symlinks in template folder (PHP >= 5.3.1) https://github.com/smarty-php/smarty/issues/224 + clear internal cache and expension handler for each template to avoid possible conflicts https://github.com/smarty-php/smarty/issues/231 + + 16.05.2016 + - optimization {foreach} compiler and processing + - broken PHP 5.3 and 5.4 compatibility + + 15.05.2016 + - optimization and cleanup of resource code + + 10.05.2016 + - optimization of inheritance processing + + 07.05.2016 + -bugfix Only variables should be assigned by reference https://github.com/smarty-php/smarty/issues/227 + + 02.05.2016 + - enhancement {block} tag names can now be variable https://github.com/smarty-php/smarty/issues/221 + + 01.05.2016 + - bugfix same relative filepath at {include} called from template in different folders could display wrong sub-template + + 29.04.2016 + - bugfix {strip} remove space on linebreak between html tags https://github.com/smarty-php/smarty/issues/213 + + 24.04.2016 + - bugfix nested {include} with relative file path could fail when called in {block} ... {/block} https://github.com/smarty-php/smarty/issues/218 + + 14.04.2016 + - bugfix special variable {$smarty.capture.name} was not case sensitive on name https://github.com/smarty-php/smarty/issues/210 + - bugfix the default template handler must calculate the source uid https://github.com/smarty-php/smarty/issues/205 + + 13.04.2016 + - bugfix template inheritance status must be saved when calling sub-templates https://github.com/smarty-php/smarty/issues/215 + + 27.03.2016 + - bugfix change of 11.03.2016 cause again {capture} data could not been seen in other templates with {$smarty.capture.name} https://github.com/smarty-php/smarty/issues/153 + + 11.03.2016 + - optimization of capture and security handling + - improvement $smarty->clearCompiledTemplate() should return on recompiled or uncompiled resources + + 10.03.2016 + - optimization of resource processing + + 09.03.2016 + - improvement rework of 'scope' attribute handling see see NEW_FEATURES.txt https://github.com/smarty-php/smarty/issues/194 + https://github.com/smarty-php/smarty/issues/186 https://github.com/smarty-php/smarty/issues/179 + - bugfix correct Autoloader update of 2.3.2014 https://github.com/smarty-php/smarty/issues/199 + + 04.03.2016 + - bugfix change from 01.03.2016 will cause $smarty->isCached(..) failure if called multiple time for same template + (forum topic 25935) + + 02.03.2016 + - revert autoloader optimizations because of unexplainable warning when using plugins https://github.com/smarty-php/smarty/issues/199 + + 01.03.2016 + - bugfix template objects must be cached on $smarty->fetch('foo.tpl) calls incase the template is fetched + multiple times (forum topic 25909) + + 25.02.2016 + - bugfix wrong _realpath with 4 or more parent-directories https://github.com/smarty-php/smarty/issues/190 + - optimization of _realpath + - bugfix instanceof expression in template code must be treated as value https://github.com/smarty-php/smarty/issues/191 + + 20.02.2016 + - bugfix {strip} must keep space between hmtl tags. Broken by changes of 10.2.2016 https://github.com/smarty-php/smarty/issues/184 + - new feature/bugfix {foreach}{section} add 'properties' attribute to force compilation of loop properties + see NEW_FEATURES.txt https://github.com/smarty-php/smarty/issues/189 + + 19.02.2016 + - revert output buffer flushing on display, echo content again because possible problems when PHP files had + characters (newline} after ?> at file end https://github.com/smarty-php/smarty/issues/187 + + 14.02.2016 + - new tag {make_nocache} read NEW_FEATURES.txt https://github.com/smarty-php/smarty/issues/110 + - optimization of sub-template processing + - bugfix using extendsall as default resource and {include} inside {block} tags could produce unexpected results https://github.com/smarty-php/smarty/issues/183 + - optimization of tag attribute compiling + - optimization make compiler tag object cache static for higher compilation speed + + 11.02.2016 + - improvement added KnockoutJS comments to trimwhitespace outputfilter https://github.com/smarty-php/smarty/issues/82 + https://github.com/smarty-php/smarty/pull/181 + + 10.02.2016 + - bugfix {strip} must keep space on output creating smarty tags within html tags https://github.com/smarty-php/smarty/issues/177 + - bugfix wrong precedence on special if conditions like '$foo is ... by $bar' could cause wrong code https://github.com/smarty-php/smarty/issues/178 + - improvement because of ambiguities the inline constant support has been removed from the $foo.bar syntax https://github.com/smarty-php/smarty/issues/149 + - bugfix other {strip} error with output tags between hmtl https://github.com/smarty-php/smarty/issues/180 + + 09.02.2016 + - move some code from parser into compiler + - reformat all code for unique style + - update/bugfix scope attribute handling reworked. Read the newfeatures.txt file + + 05.02.2016 + - improvement internal compiler changes + + 01.02.2016 + - bugfix {foreach} compilation failed when $smarty->merge_compiled_includes = true and pre-filters are used. + + 29.01.2016 + - bugfix implement replacement code for _tag_stack property https://github.com/smarty-php/smarty/issues/151 + + 28.01.2016 + - bugfix allow windows network filepath or wrapper (forum topic 25876) https://github.com/smarty-php/smarty/issues/170 + - bugfix if fetch('foo.tpl') is called on a template object the $parent parameter should default to the calling template object https://github.com/smarty-php/smarty/issues/152 + + 27.01.2016 + - revert bugfix compiling {section} did create warning + - bugfix {$smarty.section.customer.loop} did throw compiler error https://github.com/smarty-php/smarty/issues/161 + update of yesterdays fix + - bugfix string resource could inject code at {block} or inline subtemplates through PHP comments https://github.com/smarty-php/smarty/issues/157 + - bugfix output filters did not observe nocache code flhttps://github.com/smarty-php/smarty/issues/154g https://github.com/smarty-php/smarty/issues/160 + - bugfix {extends} with relative file path did not work https://github.com/smarty-php/smarty/issues/154 + https://github.com/smarty-php/smarty/issues/158 + - bugfix {capture} data could not been seen in other templates with {$smarty.capture.name} https://github.com/smarty-php/smarty/issues/153 + + 26.01.2016 + - improvement observe Smarty::$_CHARSET in debugging console https://github.com/smarty-php/smarty/issues/169 + - bugfix compiling {section} did create warning + - bugfix {$smarty.section.customer.loop} did throw compiler error https://github.com/smarty-php/smarty/issues/161 + + 02.01.2016 + - update scope handling + - optimize block plugin compiler + - improvement runtime checks if registered block plugins are callable + + 01.01.2016 + - remove Smarty::$resource_cache_mode property + + 31.12.2015 + - optimization of {assign}, {if} and {while} compiled code + + 30.12.2015 + - bugfix plugin names starting with "php" did not compile https://github.com/smarty-php/smarty/issues/147 + + 29.12.2015 + - bugfix Smarty::error_reporting was not observed when display() or fetch() was called on template objects https://github.com/smarty-php/smarty/issues/145 + + 28.12.2015 + - optimization of {foreach} code size and processing + + 27.12.2015 + - improve inheritance code + - update external methods + - code fixes + - PHPdoc updates + + 25.12.2015 + - compile {block} tag code and its processing into classes + - optimization replace hhvm extension by inline code + - new feature If ACP is enabled force an apc_compile_file() when compiled or cached template was updated + + 24.12.2015 + - new feature Compiler does now observe the template_dir setting and will create separate compiled files if required + - bugfix post filter did fail on template inheritance https://github.com/smarty-php/smarty/issues/144 + + 23.12.2015 + - optimization move internal method decodeProperties back into template object + - optimization move subtemplate processing back into template object + - new feature Caching does now observe the template_dir setting and will create separate cache files if required + + 22.12.2015 + - change $xxx_dir properties from private to protected in case Smarty class gets extended + - code optimizations + + 21.12.2015 + - bugfix a filepath starting with '/' or '\' on windows should normalize to the root dir + of current working drive https://github.com/smarty-php/smarty/issues/134 + - optimization of filepath normalization + - bugfix {strip} must remove all blanks between html tags https://github.com/smarty-php/smarty/issues/136 + + - 3.1.29 - (21.12.2015) + 21.12.2015 + - optimization improve speed of filetime checks on extends and extendsall resource + + 20.12.2015 + - bugfix failure when the default resource type was set to 'extendsall' https://github.com/smarty-php/smarty/issues/123 + - update compilation of Smarty special variables + - bugfix add addition check for OS type on normalization of file path https://github.com/smarty-php/smarty/issues/134 + - bugfix the source uid of the extendsall resource must contain $template_dir settings https://github.com/smarty-php/smarty/issues/123 + + 19.12.2015 + - bugfix using $smarty.capture.foo in expressions could fail https://github.com/smarty-php/smarty/pull/138 + - bugfix broken PHP 5.2 compatibility https://github.com/smarty-php/smarty/issues/139 + - remove no longer used code + - improvement make sure that compiled and cache templates never can contain a trailing '?>? + + 18.12.2015 + - bugfix regression when modifier parameter was followed by math https://github.com/smarty-php/smarty/issues/132 + + 17.12.2015 + - bugfix {$smarty.capture.nameFail} did lowercase capture name https://github.com/smarty-php/smarty/issues/135 + - bugfix using {block append/prepend} on same block in multiple levels of inheritance templates could fail (forum topic 25827) + - bugfix text content consisting of just a single '0' like in {if true}0{/if} was suppressed (forum topic 25834) + + 16.12.2015 + - bugfix {foreach} did fail if from atrribute is a Generator class https://github.com/smarty-php/smarty/issues/128 + - bugfix direct access $smarty->template_dir = 'foo'; should call Smarty::setTemplateDir() https://github.com/smarty-php/smarty/issues/121 + + 15.12.2015 + - bugfix {$smarty.cookies.foo} did return the $_COOKIE array not the 'foo' value https://github.com/smarty-php/smarty/issues/122 + - bugfix a call to clearAllCache() and other should clear all internal template object caches (forum topic 25828) + + 14.12.2015 + - bugfix {$smarty.config.foo} broken in 3.1.28 https://github.com/smarty-php/smarty/issues/120 + - bugfix multiple calls of {section} with same name droped E_NOTICE error https://github.com/smarty-php/smarty/issues/118 + + - 3.1.28 - (13.12.2015) + 13.12.2015 + - bugfix {foreach} and {section} with uppercase characters in name attribute did not work (forum topic 25819) + - bugfix $smarty->debugging_ctrl = 'URL' did not work (forum topic 25811) + - bugfix Debug Console could display incorrect data when using subtemplates + + 09.12.2015 + - bugfix Smarty did fail under PHP 7.0.0 with use_include_path = true; + + 09.12.2015 + - bugfix {strip} should exclude some html tags from stripping, related to fix for https://github.com/smarty-php/smarty/issues/111 + + 08.12.2015 + - bugfix internal template function data got stored in wrong compiled file https://github.com/smarty-php/smarty/issues/114 + + 05.12.2015 + -bugfix {strip} should insert a single space https://github.com/smarty-php/smarty/issues/111 + + 25.11.2015 + -bugfix a left delimter like '[%' did fail on [%$var_[%$variable%]%] (forum topic 25798) + + 02.11.2015 + - bugfix {include} with variable file name like {include file="foo_`$bar`.tpl"} did fail in 3.1.28-dev https://github.com/smarty-php/smarty/issues/102 + + 01.11.2015 + - update config file processing + + 31.10.2015 + - bugfix add missing $trusted_dir property to SmartyBC class (forum topic 25751) + + 29.10.2015 + - improve template scope handling + + 24.10.2015 + - more optimizations of template processing + - bugfix Error when using {include} within {capture} https://github.com/smarty-php/smarty/issues/100 + + 21.10.2015 + - move some code into runtime extensions + + 18.10.2015 + - optimize filepath normalization + - rework of template inheritance + - speed and size optimizations + - bugfix under HHVM temporary cache file must only be created when caches template was updated + - fix compiled code for new {block} assign attribute + - update code generated by template function call handler + + 18.09.2015 + - bugfix {if $foo instanceof $bar} failed to compile if 2nd value is a variable https://github.com/smarty-php/smarty/issues/92 + + 17.09.2015 + - bugfix {foreach} first attribute was not correctly reset since commit 05a8fa2 of 02.08.2015 https://github.com/smarty-php/smarty/issues/90 + + 16.09.2015 + - update compiler by moving no longer needed properties, code optimizations and other + + 14.09.2015 + - optimize autoloader + - optimize subtemplate handling + - update template inheritance processing + - move code of {call} processing back into Smarty_Internal_Template class + - improvement invalidate OPCACHE for cleared compiled and cached template files (forum topic 25557) + - bugfix unintended multiple debug windows (forum topic 25699) + + 30.08.2015 + - size optimization move some runtime functions into extension + - optimize inline template processing + - optimization merge inheritance child and parent templates into one compiled template file + + 29.08.2015 + - improvement convert template inheritance into runtime processing + - bugfix {$smarty.block.parent} did always reference the root parent block https://github.com/smarty-php/smarty/issues/68 + + 23.08.2015 + - introduce Smarty::$resource_cache_mode and cache template object of {include} inside loop + - load seldom used Smarty API methods dynamically to reduce memory footprint + - cache template object of {include} if same template is included several times + - convert debug console processing to object + - use output buffers for better performance and less memory usage + - optimize nocache hash processing + - remove not really needed properties + - optimize rendering + - move caching to Smarty::_cache + - remove properties with redundant content + - optimize Smarty::templateExists() + - optimize use_include_path processing + - relocate properties for size optimization + - remove redundant code + - bugfix compiling super globals like {$smarty.get.foo} did fail in the master branch https://github.com/smarty-php/smarty/issues/77 + + 06.08.2015 + - avoid possible circular object references caused by parser/lexer objects + - rewrite compileAll... utility methods + - commit several internal improvements + - bugfix Smarty failed when compile_id did contain "|" + + 03.08.2015 + - rework clear cache methods + - bugfix compileAllConfig() was broken since 3.1.22 because of the changes in config file processing + - improve getIncludePath() to return directory if no file was given + + 02.08.2015 + - optimization and code cleanup of {foreach} and {section} compiler + - rework {capture} compiler + + 01.08.2015 + - update DateTime object can be instance of DateTimeImmutable since PHP5.5 https://github.com/smarty-php/smarty/pull/75 + - improvement show resource type and start of template source instead of uid on eval: and string: resource (forum topic 25630) + + 31.07.2015 + - optimize {foreach} and {section} compiler + + 29.07.2015 + - optimize {section} compiler for speed and size of compiled code + + 28.07.2015 + - update for PHP 7 compatibility + + 26.07.2015 + - improvement impement workaround for HHVM PHP incompatibillity https://github.com/facebook/hhvm/issues/4797 + + 25.07.2015 + - bugfix parser did hang on text starting fetch('foo.tpl') https://github.com/smarty-php/smarty/issues/70 + - improvement Added $limit parameter to regex_replace modifier #71 + - new feature multiple indices on file: resource + + 06.07.2015 + - optimize {block} compilation + - optimization get rid of __get and __set in source object + + 01.07.2015 + - optimize compile check handling + - update {foreach} compiler + - bugfix debugging console did not display string values containing \n, \r or \t correctly https://github.com/smarty-php/smarty/issues/66 + - optimize source resources + + 28.06.2015 + - move $smarty->enableSecurity() into Smarty_Security class + - optimize security isTrustedResourceDir() + - move auto load filter methods into extension + - move $smarty->getTemplateVars() into extension + - move getStreamVariable() into extension + - move $smarty->append() and $smarty->appendByRef() into extension + - optimize autoloader + - optimize file path normalization + - bugfix PATH_SEPARATOR was replaced by mistake in autoloader + - remove redundant code + + 27.06.2015 + - bugfix resolve naming conflict between custom Smarty delimiter '<%' and PHP ASP tags https://github.com/smarty-php/smarty/issues/64 + - update $smarty->_realpath for relative path not starting with './' + - update Smarty security with new realpath handling + - update {include_php} with new realpath handling + - move $smarty->loadPlugin() into extension + - minor compiler optimizations + - bugfix allow function plugins with name ending with 'close' https://github.com/smarty-php/smarty/issues/52 + - rework of $smarty->clearCompiledTemplate() and move it to its own extension + + 19.06.2015 + - improvement allow closures as callback at $smarty->registerFilter() https://github.com/smarty-php/smarty/issues/59 + + - 3.1.27- (18.06.2015) + 18.06.2015 + - bugfix another update on file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + - 3.1.26- (18.06.2015) + 18.06.2015 + - bugfix file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + 17.06.2015 + - bugfix calling a plugin with nocache option but no other attributes like {foo nocache} caused call to undefined function https://github.com/smarty-php/smarty/issues/55 + + - 3.1.25- (15.06.2015) + 15.06.2015 + - optimization of smarty_cachereource_keyvaluestore.php code + + 14.06.2015 + - bugfix a relative sub template path could fail if template_dir path did contain /../ https://github.com/smarty-php/smarty/issues/50 + - optimization rework of path normalization + - bugfix an output tag with variable, modifier followed by an operator like {$foo|modifier+1} did fail https://github.com/smarty-php/smarty/issues/53 + + 13.06.2015 + - bugfix a custom cache resource using smarty_cachereource_keyvaluestore.php did fail if php.ini mbstring.func_overload = 2 (forum topic 25568) + + 11.06.2015 + - bugfix the lexer could hang on very large quoted strings (forum topic 25570) + + 08.06.2015 + - bugfix using {$foo} as array index like $bar.{$foo} or in double quoted string like "some {$foo} thing" failed https://github.com/smarty-php/smarty/issues/49 + + 04.06.2015 + - bugfix possible error message on unset() while compiling {block} tags https://github.com/smarty-php/smarty/issues/46 + + 01.06.2015 + - bugfix including template variables broken since 3.1.22 https://github.com/smarty-php/smarty/issues/47 + + 27.05.2015 + - bugfix {include} with variable file name must not create by default individual cache file (since 3.1.22) https://github.com/smarty-php/smarty/issues/43 + + 24.05.2015 + - bugfix if condition string 'neq' broken due to a typo https://github.com/smarty-php/smarty/issues/42 + + - 3.1.24- (23.05.2015) + 23.05.2015 + - improvement on php_handling to allow very large PHP sections, better error handling + - improvement allow extreme large comment sections (forum 25538) + + 21.05.2015 + - bugfix broken PHP 5.2 compatibility when compiling 1 did compile into wrong code https://github.com/smarty-php/smarty/issues/41 + + 19.05.2015 + - bugfix compiler did overwrite existing variable value when setting the nocache attribute https://github.com/smarty-php/smarty/issues/39 + - bugfix output filter trimwhitespace could run into the pcre.backtrack_limit on large output (code.google issue 220) + - bugfix compiler could run into the pcre.backtrack_limit on larger comment or {php} tag sections (forum 25538) + + 18.05.2015 + - improvement introduce shortcuts in lexer/parser rules for most frequent terms for higher + compilation speed + + 16.05.2015 + - bugfix {php}{/php} did work just for single lines https://github.com/smarty-php/smarty/issues/33 + - improvement remove not needed ?> handling from parser to new compiler module + + 05.05.2015 + - bugfix code could be messed up when {tags} are used in multiple attributes https://github.com/smarty-php/smarty/issues/23 + + 04.05.2015 + - bugfix Smarty_Resource::parseResourceName incompatible with Google AppEngine (https://github.com/smarty-php/smarty/issues/22) + - improvement use is_file() checks to avoid errors suppressed by @ which could still cause problems (https://github.com/smarty-php/smarty/issues/24) + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) 2nd fix + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) + + 23.04.2015 + - bugfix a nocache template variable used as parameter at {insert} was by mistake cached + + 20.04.2015 + - bugfix at a template function containing nocache code a parmeter could overwrite a template variable of same name + + 27.03.2015 + - bugfix Smarty_Security->allow_constants=false; did also disable true, false and null (change of 16.03.2015) + - improvement added a whitelist for trusted constants to security Smarty_Security::$trusted_constants (forum topic 25471) + + 20.03.2015 + - bugfix make sure that function properties get saved only in compiled files containing the fuction definition {forum topic 25452} + - bugfix correct update of global variable values on exit of template functions. (reported under Smarty Developers) + + 16.03.2015 + - bugfix problems with {function}{/function} and {call} tags in different subtemplate cache files {forum topic 25452} + - bugfix Smarty_Security->allow_constants=false; did not disallow direct usage of defined constants like {SMARTY_DIR} {forum topic 25457} + - bugfix {block}{/block} tags did not work inside double quoted strings https://github.com/smarty-php/smarty/issues/18 + + + 15.03.2015 + - bugfix $smarty->compile_check must be restored before rendering of a just updated cache file {forum 25452} + + 14.03.2015 + - bugfix {nocache} {/nocache} tags corrupted code when used within a nocache section caused by a nocache template variable. + + - bugfix template functions defined with {function} in an included subtemplate could not be called in nocache + mode with {call... nocache} if the subtemplate had it's own cache file {forum 25452} + + 10.03.2015 + - bugfix {include ... nocache} whith variable file or compile_id attribute was not executed in nocache mode. + + 12.02.2015 + - bugfix multiple Smarty::fetch() of same template when $smarty->merge_compiled_includes = true; could cause function already defined error + + 11.02.2015 + - bugfix recursive {includes} did create E_NOTICE message when $smarty->merge_compiled_includes = true; (github issue #16) + + 22.01.2015 + - new feature security can now control access to static methods and properties + see also NEW_FEATURES.txt + + 21.01.2015 + - bugfix clearCompiledTemplates(), clearAll() and clear() could try to delete whole drive at wrong path permissions because realpath() fail (forum 25397) + - bugfix 'self::' and 'parent::' was interpreted in template syntax as static class + + 04.01.2015 + - push last weeks changes to github + + - different optimizations + - improvement automatically create different versions of compiled templates and config files depending + on property settings. + - optimization restructure template processing by moving code into classes it better belongs to + - optimization restructure config file processing + + 31.12.2014 + - bugfix use function_exists('mb_get_info') for setting Smarty::$_MBSTRING. + Function mb_split could be overloaded depending on php.ini mbstring.func_overload + + + 29.12.2014 + - new feature security can now limit the template nesting level by property $max_template_nesting + see also NEW_FEATURES.txt (forum 25370) + + 29.12.2014 + - new feature security can now disable special $smarty variables listed in property $disabled_special_smarty_vars + see also NEW_FEATURES.txt (forum 25370) + + 27.12.2014 + - bugfix clear internal _is_file_cache when plugins_dir was modified + + 13.12.2014 + - improvement optimization of lexer and parser resulting in a up to 30% higher compiling speed + + 11.12.2014 + - bugfix resolve parser ambiguity between constant print tag {CONST} and other smarty tags after change of 09.12.2014 + + 09.12.2014 + - bugfix variables $null, $true and $false did not work after the change of 12.11.2014 (forum 25342) + - bugfix call of template function by a variable name did not work after latest changes (forum 25342) + + 23.11.2014 + - bugfix a plugin with attached modifier could fail if the tag was immediately followed by another Smarty tag (since 3.1.21) (forum 25326) + + 13.11.2014 + - improvement move autoload code into Autoloader.php. Use Composer autoloader when possible + + 12.11.2014 + - new feature added support of namespaces to template code + + 08.11.2014 - 10.11.2014 + - bugfix subtemplate called in nocache mode could be called with wrong compile_id when it did change on one of the calling templates + - improvement add code of template functions called in nocache mode dynamically to cache file (related to bugfix of 01.11.2014) + - bugfix Debug Console did not include all data from merged compiled subtemplates + + 04.11.2014 + - new feature $smarty->debugging = true; => overwrite existing Debug Console window (old behaviour) + $smarty->debugging = 2; => individual Debug Console window by template name + + 03.11.2014 + - bugfix Debug Console did not show included subtemplates since 3.1.17 (forum 25301) + - bugfix Modifier debug_print_var did not limit recursion or prevent recursive object display at Debug Console + (ATTENTION: parameter order has changed to be able to specify maximum recursion) + - bugfix Debug consol did not include subtemplate information with $smarty->merge_compiled_includes = true + - improvement The template variables are no longer displayed as objects on the Debug Console + - improvement $smarty->createData($parent = null, $name = null) new optional name parameter for display at Debug Console + - addition of some hooks for future extension of Debug Console + + 01.11.2014 + - bugfix and enhancement on subtemplate {include} and template {function} tags. + * Calling a template which has a nocache section could fail if it was called from a cached and a not cached subtemplate. + * Calling the same subtemplate cached and not cached with the $smarty->merge_compiled_includes enabled could cause problems + * Many smaller related changes + + 30.10.2014 + - bugfix access to class constant by object like {$object::CONST} or variable class name {$class::CONST} did not work (forum 25301) + + 26.10.2014 + - bugfix E_NOTICE message was created during compilation when ASP tags '<%' or '%>' are in template source text + - bugfix merge_compiled_includes option failed when caching enables and same subtemplate was included cached and not cached + + - 3.1.21 - (18.10.2014) + 18.10.2014 + - composer moved to github + + 17.10.2014 + - bugfix on $php_handling security and optimization of smarty_internal_parsetree (Thue Kristensen) + + 16.10.2014 + - bugfix composer.json update + + 15.10.2014 + - bugfix calling a new created cache file with fetch() and Smarty::CACHING_LIFETIME_SAVED multiple times did fail (forum 22350) + + 14.10.2014 + - bugfix any tag placed within " diff --git a/videodb/vendor/smarty/smarty/libs/plugins/block.textformat.php b/videodb/vendor/smarty/smarty/libs/plugins/block.textformat.php new file mode 100644 index 0000000..5e49463 --- /dev/null +++ b/videodb/vendor/smarty/smarty/libs/plugins/block.textformat.php @@ -0,0 +1,121 @@ + + * @throws \SmartyException + */ +function smarty_block_textformat($params, $content, Smarty_Internal_Template $template, &$repeat) +{ + if (is_null($content)) { + return; + } + if (Smarty::$_MBSTRING) { + $template->_checkPlugins( + array( + array( + 'function' => 'smarty_modifier_mb_wordwrap', + 'file' => SMARTY_PLUGINS_DIR . 'modifier.mb_wordwrap.php' + ) + ) + ); + } + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string)$_val; + break; + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int)$_val; + break; + case 'wrap_cut': + $$_key = (bool)$_val; + break; + default: + trigger_error("textformat: unknown attribute '{$_key}'"); + } + } + if ($style === 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = + preg_replace( + array( + '!\s+!' . Smarty::$_UTF8_MODIFIER, + '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER + ), + array( + ' ', + '' + ), + $_paragraph + ); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + if (Smarty::$_MBSTRING) { + $_paragraph = smarty_modifier_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } else { + $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } +} diff --git a/videodb/vendor/smarty/smarty/libs/plugins/function.counter.php b/videodb/vendor/smarty/smarty/libs/plugins/function.counter.php new file mode 100644 index 0000000..a4129e7 --- /dev/null +++ b/videodb/vendor/smarty/smarty/libs/plugins/function.counter.php @@ -0,0 +1,62 @@ + + * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_counter($params, $template) +{ + static $counters = array(); + $name = (isset($params[ 'name' ])) ? $params[ 'name' ] : 'default'; + if (!isset($counters[ $name ])) { + $counters[ $name ] = array('start' => 1, 'skip' => 1, 'direction' => 'up', 'count' => 1); + } + $counter =& $counters[ $name ]; + if (isset($params[ 'start' ])) { + $counter[ 'start' ] = $counter[ 'count' ] = (int)$params[ 'start' ]; + } + if (!empty($params[ 'assign' ])) { + $counter[ 'assign' ] = $params[ 'assign' ]; + } + if (isset($counter[ 'assign' ])) { + $template->assign($counter[ 'assign' ], $counter[ 'count' ]); + } + if (isset($params[ 'print' ])) { + $print = (bool)$params[ 'print' ]; + } else { + $print = empty($counter[ 'assign' ]); + } + if ($print) { + $retval = $counter[ 'count' ]; + } else { + $retval = null; + } + if (isset($params[ 'skip' ])) { + $counter[ 'skip' ] = $params[ 'skip' ]; + } + if (isset($params[ 'direction' ])) { + $counter[ 'direction' ] = $params[ 'direction' ]; + } + if ($counter[ 'direction' ] === 'down') { + $counter[ 'count' ] -= $counter[ 'skip' ]; + } else { + $counter[ 'count' ] += $counter[ 'skip' ]; + } + return $retval; +} diff --git a/videodb/vendor/smarty/smarty/libs/plugins/function.cycle.php b/videodb/vendor/smarty/smarty/libs/plugins/function.cycle.php new file mode 100644 index 0000000..07ffcc5 --- /dev/null +++ b/videodb/vendor/smarty/smarty/libs/plugins/function.cycle.php @@ -0,0 +1,92 @@ + + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_cycle($params, $template) +{ + static $cycle_vars; + $name = (empty($params[ 'name' ])) ? 'default' : $params[ 'name' ]; + $print = (isset($params[ 'print' ])) ? (bool)$params[ 'print' ] : true; + $advance = (isset($params[ 'advance' ])) ? (bool)$params[ 'advance' ] : true; + $reset = (isset($params[ 'reset' ])) ? (bool)$params[ 'reset' ] : false; + if (!isset($params[ 'values' ])) { + if (!isset($cycle_vars[ $name ][ 'values' ])) { + trigger_error('cycle: missing \'values\' parameter'); + return; + } + } else { + if (isset($cycle_vars[ $name ][ 'values' ]) && $cycle_vars[ $name ][ 'values' ] !== $params[ 'values' ]) { + $cycle_vars[ $name ][ 'index' ] = 0; + } + $cycle_vars[ $name ][ 'values' ] = $params[ 'values' ]; + } + if (isset($params[ 'delimiter' ])) { + $cycle_vars[ $name ][ 'delimiter' ] = $params[ 'delimiter' ]; + } elseif (!isset($cycle_vars[ $name ][ 'delimiter' ])) { + $cycle_vars[ $name ][ 'delimiter' ] = ','; + } + if (is_array($cycle_vars[ $name ][ 'values' ])) { + $cycle_array = $cycle_vars[ $name ][ 'values' ]; + } else { + $cycle_array = explode($cycle_vars[ $name ][ 'delimiter' ], $cycle_vars[ $name ][ 'values' ]); + } + if (!isset($cycle_vars[ $name ][ 'index' ]) || $reset) { + $cycle_vars[ $name ][ 'index' ] = 0; + } + if (isset($params[ 'assign' ])) { + $print = false; + $template->assign($params[ 'assign' ], $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]); + } + if ($print) { + $retval = $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]; + } else { + $retval = null; + } + if ($advance) { + if ($cycle_vars[ $name ][ 'index' ] >= count($cycle_array) - 1) { + $cycle_vars[ $name ][ 'index' ] = 0; + } else { + $cycle_vars[ $name ][ 'index' ]++; + } + } + return $retval; +} diff --git a/videodb/vendor/smarty/smarty/libs/plugins/function.fetch.php b/videodb/vendor/smarty/smarty/libs/plugins/function.fetch.php new file mode 100644 index 0000000..768761b --- /dev/null +++ b/videodb/vendor/smarty/smarty/libs/plugins/function.fetch.php @@ -0,0 +1,204 @@ + + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException + * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable + */ +function smarty_function_fetch($params, $template) +{ + if (empty($params[ 'file' ])) { + trigger_error('[plugin] fetch parameter \'file\' cannot be empty', E_USER_NOTICE); + return; + } + // strip file protocol + if (stripos($params[ 'file' ], 'file://') === 0) { + $params[ 'file' ] = substr($params[ 'file' ], 7); + } + $protocol = strpos($params[ 'file' ], '://'); + if ($protocol !== false) { + $protocol = strtolower(substr($params[ 'file' ], 0, $protocol)); + } + if (isset($template->smarty->security_policy)) { + if ($protocol) { + // remote resource (or php stream, …) + if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) { + return; + } + } else { + // local file + if (!$template->smarty->security_policy->isTrustedResourceDir($params[ 'file' ])) { + return; + } + } + } + $content = ''; + if ($protocol === 'http') { + // http fetch + if ($uri_parts = parse_url($params[ 'file' ])) { + // set defaults + $host = $server_name = $uri_parts[ 'host' ]; + $timeout = 30; + $accept = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*'; + $agent = 'Smarty Template Engine ' . Smarty::SMARTY_VERSION; + $referer = ''; + $uri = !empty($uri_parts[ 'path' ]) ? $uri_parts[ 'path' ] : '/'; + $uri .= !empty($uri_parts[ 'query' ]) ? '?' . $uri_parts[ 'query' ] : ''; + $_is_proxy = false; + if (empty($uri_parts[ 'port' ])) { + $port = 80; + } else { + $port = $uri_parts[ 'port' ]; + } + if (!empty($uri_parts[ 'user' ])) { + $user = $uri_parts[ 'user' ]; + } + if (!empty($uri_parts[ 'pass' ])) { + $pass = $uri_parts[ 'pass' ]; + } + // loop through parameters, setup headers + foreach ($params as $param_key => $param_value) { + switch ($param_key) { + case 'file': + case 'assign': + case 'assign_headers': + break; + case 'user': + if (!empty($param_value)) { + $user = $param_value; + } + break; + case 'pass': + if (!empty($param_value)) { + $pass = $param_value; + } + break; + case 'accept': + if (!empty($param_value)) { + $accept = $param_value; + } + break; + case 'header': + if (!empty($param_value)) { + if (!preg_match('![\w\d-]+: .+!', $param_value)) { + trigger_error("[plugin] invalid header format '{$param_value}'", E_USER_NOTICE); + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case 'proxy_host': + if (!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case 'proxy_port': + if (!preg_match('!\D!', $param_value)) { + $proxy_port = (int)$param_value; + } else { + trigger_error("[plugin] invalid value for attribute '{$param_key }'", E_USER_NOTICE); + return; + } + break; + case 'agent': + if (!empty($param_value)) { + $agent = $param_value; + } + break; + case 'referer': + if (!empty($param_value)) { + $referer = $param_value; + } + break; + case 'timeout': + if (!preg_match('!\D!', $param_value)) { + $timeout = (int)$param_value; + } else { + trigger_error("[plugin] invalid value for attribute '{$param_key}'", E_USER_NOTICE); + return; + } + break; + default: + trigger_error("[plugin] unrecognized attribute '{$param_key}'", E_USER_NOTICE); + return; + } + } + if (!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout); + } else { + $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout); + } + if (!$fp) { + trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE); + return; + } else { + if ($_is_proxy) { + fputs($fp, 'GET ' . $params[ 'file' ] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if (!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if (!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if (!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if (!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if (isset($extra_headers) && is_array($extra_headers)) { + foreach ($extra_headers as $curr_header) { + fputs($fp, $curr_header . "\r\n"); + } + } + if (!empty($user) && !empty($pass)) { + fputs($fp, 'Authorization: BASIC ' . base64_encode("$user:$pass") . "\r\n"); + } + fputs($fp, "\r\n"); + while (!feof($fp)) { + $content .= fgets($fp, 4096); + } + fclose($fp); + $csplit = preg_split("!\r\n\r\n!", $content, 2); + $content = $csplit[ 1 ]; + if (!empty($params[ 'assign_headers' ])) { + $template->assign($params[ 'assign_headers' ], preg_split("!\r\n!", $csplit[ 0 ])); + } + } + } else { + trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE); + return; + } + } else { + $content = @file_get_contents($params[ 'file' ]); + if ($content === false) { + throw new SmartyException("{fetch} cannot read resource '" . $params[ 'file' ] . "'"); + } + } + if (!empty($params[ 'assign' ])) { + $template->assign($params[ 'assign' ], $content); + } else { + return $content; + } +} diff --git a/videodb/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php b/videodb/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php new file mode 100644 index 0000000..302358e --- /dev/null +++ b/videodb/vendor/smarty/smarty/libs/plugins/function.html_checkboxes.php @@ -0,0 +1,286 @@ +' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
        ' output=$names} + * + * Params: + * + * - name (optional) - string default "checkbox" + * - values (required) - array + * - options (optional) - associative array + * - checked (optional) - array default not set + * - separator (optional) - ie
        or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * - escape (optional) - escape the content (not value), defaults to true + * + * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string + * @uses smarty_function_escape_special_chars() + * @throws \SmartyException + */ +function smarty_function_html_checkboxes($params, Smarty_Internal_Template $template) +{ + $template->_checkPlugins( + array( + array( + 'function' => 'smarty_function_escape_special_chars', + 'file' => SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php' + ) + ) + ); + $name = 'checkbox'; + $values = null; + $options = null; + $selected = array(); + $separator = ''; + $escape = true; + $labels = true; + $label_ids = false; + $output = null; + $extra = ''; + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'name': + case 'separator': + $$_key = (string)$_val; + break; + case 'escape': + case 'labels': + case 'label_ids': + $$_key = (bool)$_val; + break; + case 'options': + $$_key = (array)$_val; + break; + case 'values': + case 'output': + $$_key = array_values((array)$_val); + break; + case 'checked': + case 'selected': + if (is_array($_val)) { + $selected = array(); + foreach ($_val as $_sel) { + if (is_object($_sel)) { + if (method_exists($_sel, '__toString')) { + $_sel = smarty_function_escape_special_chars((string)$_sel->__toString()); + } else { + trigger_error( + 'html_checkboxes: selected attribute contains an object of class \'' . + get_class($_sel) . '\' without __toString() method', + E_USER_NOTICE + ); + continue; + } + } else { + $_sel = smarty_function_escape_special_chars((string)$_sel); + } + $selected[ $_sel ] = true; + } + } elseif (is_object($_val)) { + if (method_exists($_val, '__toString')) { + $selected = smarty_function_escape_special_chars((string)$_val->__toString()); + } else { + trigger_error( + 'html_checkboxes: selected attribute is an object of class \'' . get_class($_val) . + '\' without __toString() method', + E_USER_NOTICE + ); + } + } else { + $selected = smarty_function_escape_special_chars((string)$_val); + } + break; + case 'checkboxes': + trigger_error( + 'html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', + E_USER_WARNING + ); + $options = (array)$_val; + break; + case 'assign': + break; + case 'strict': + break; + case 'disabled': + case 'readonly': + if (!empty($params[ 'strict' ])) { + if (!is_scalar($_val)) { + trigger_error( + "html_options: {$_key} attribute must be a scalar, only boolean true or string '{$_key}' will actually add the attribute", + E_USER_NOTICE + ); + } + if ($_val === true || $_val === $_key) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"'; + } + break; + } + // omit break; to fall through! + // no break + default: + if (!is_array($_val)) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; + } else { + trigger_error("html_checkboxes: extra attribute '{$_key}' cannot be an array", E_USER_NOTICE); + } + break; + } + } + if (!isset($options) && !isset($values)) { + return ''; + } /* raise error here? */ + $_html_result = array(); + if (isset($options)) { + foreach ($options as $_key => $_val) { + $_html_result[] = + smarty_function_html_checkboxes_output( + $name, + $_key, + $_val, + $selected, + $extra, + $separator, + $labels, + $label_ids, + $escape + ); + } + } else { + foreach ($values as $_i => $_key) { + $_val = isset($output[ $_i ]) ? $output[ $_i ] : ''; + $_html_result[] = + smarty_function_html_checkboxes_output( + $name, + $_key, + $_val, + $selected, + $extra, + $separator, + $labels, + $label_ids, + $escape + ); + } + } + if (!empty($params[ 'assign' ])) { + $template->assign($params[ 'assign' ], $_html_result); + } else { + return implode("\n", $_html_result); + } +} + +/** + * @param $name + * @param $value + * @param $output + * @param $selected + * @param $extra + * @param $separator + * @param $labels + * @param $label_ids + * @param bool $escape + * + * @return string + */ +function smarty_function_html_checkboxes_output( + $name, + $value, + $output, + $selected, + $extra, + $separator, + $labels, + $label_ids, + $escape = true +) { + $_output = ''; + if (is_object($value)) { + if (method_exists($value, '__toString')) { + $value = (string)$value->__toString(); + } else { + trigger_error( + 'html_options: value is an object of class \'' . get_class($value) . + '\' without __toString() method', + E_USER_NOTICE + ); + return ''; + } + } else { + $value = (string)$value; + } + if (is_object($output)) { + if (method_exists($output, '__toString')) { + $output = (string)$output->__toString(); + } else { + trigger_error( + 'html_options: output is an object of class \'' . get_class($output) . + '\' without __toString() method', + E_USER_NOTICE + ); + return ''; + } + } else { + $output = (string)$output; + } + if ($labels) { + if ($label_ids) { + $_id = smarty_function_escape_special_chars( + preg_replace( + '![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, + '_', + $name . '_' . $value + ) + ); + $_output .= '