Merge branch 'dev' of https://github.com/redmatrix/hubzilla into dev_merge

This commit is contained in:
zotlabs 2017-11-22 15:43:48 -08:00
commit 9936670f44
40 changed files with 2561 additions and 11 deletions

View file

@ -17,11 +17,35 @@ class Thumbnail {
if(! $c)
return;
$attach = $c[0];
$preview_style = intval(get_config('system','thumbnail_security',0));
$preview_width = intval(get_config('system','thumbnail_width',300));
$preview_height = intval(get_config('system','thumbnail_height',300));
$attach = $c[0];
$p = [
'attach' => $attach,
'preview_style' => $preview_style,
'preview_width' => $preview_width,
'preview_height' => $preview_height,
'thumbnail' => null
];
/**
* @hooks thumbnail
* * \e array \b attach
* * \e int \b preview_style
* * \e int \b preview_width
* * \e int \b preview_height
* * \e string \b thumbnail
*/
call_hooks('thumbnail',$p);
if($p['thumbnail']) {
return;
}
$default_controller = null;
$files = glob('Zotlabs/Thumbs/*.php');
@ -45,7 +69,9 @@ class Thumbnail {
}
}
}
if(($default_controller) && (! file_exists(dbunescbin($attach['content']) . '.thumb'))) {
if(($default_controller)
&& ((! file_exists(dbunescbin($attach['content']) . '.thumb'))
|| (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) {
$default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height);
}
}

View file

@ -63,6 +63,7 @@ class Site {
$verify_email = ((x($_POST,'verify_email')) ? 1 : 0);
$techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0);
$imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : '');
$thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0);
$force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 300);
$techlevel = null;
@ -85,7 +86,7 @@ class Site {
set_config('system', 'from_email', $from_email);
set_config('system', 'from_email_name' , $from_email_name);
set_config('system', 'imagick_convert_path' , $imagick_path);
set_config('system', 'thumbnail_security' , $thumbnail_security);
set_config('system', 'techlevel_lock', $techlevel_lock);
@ -323,6 +324,7 @@ class Site {
'$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',300), t("Always defer immediate delivery if queue contains more than this number of entries.")),
'$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")),
'$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")),
'$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")),
'$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")),
'$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')),
'$form_security_token' => get_form_security_token("admin_site"),

View file

@ -0,0 +1,38 @@
<?php
namespace Zotlabs\Thumbs;
require_once('library/epub-meta/epub.php');
class Epubthumb {
function Match($type) {
return(($type === 'application/epub+zip') ? true : false );
}
function Thumb($attach,$preview_style,$height = 300, $width = 300) {
$photo = false;
$ep = new \Epub(dbunescbin($attach['content']));
$data = $ep->Cover();
if($data['found']) {
$photo = $data['data'];
}
if($photo) {
$image = imagecreatefromstring($photo);
$dest = imagecreatetruecolor( $width, $height );
$srcwidth = imagesx($image);
$srcheight = imagesy($image);
imagealphablending($dest, false);
imagesavealpha($dest, true);
imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight);
imagedestroy($image);
imagejpeg($dest,dbunescbin($attach['content']) . '.thumb');
}
}
}

49
Zotlabs/Thumbs/Pdf.php Normal file
View file

@ -0,0 +1,49 @@
<?php
namespace Zotlabs\Thumbs;
class Pdf {
function Match($type) {
return(($type === 'application/pdf') ? true : false );
}
function Thumb($attach,$preview_style,$height = 300, $width = 300) {
$photo = false;
$file = dbunescbin($attach['content']);
$tmpfile = $file . '.pdf';
$outfile = $file . '.jpg';
$istream = fopen($file,'rb');
$ostream = fopen($tmpfile,'wb');
if($istream && $ostream) {
pipe_streams($istream,$ostream);
fclose($istream);
fclose($ostream);
}
$imagick_path = get_config('system','imagick_convert_path');
if($imagick_path && @file_exists($imagick_path)) {
$cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile);
// logger('imagick thumbnail command: ' . $cmd);
for($x = 0; $x < 4; $x ++) {
exec($cmd);
if(! file_exists($outfile)) {
logger('imagick scale failed. Retrying.');
continue;
}
}
if(! file_exists($outfile)) {
logger('imagick scale failed.');
}
else {
@rename($outfile,$file . '.thumb');
}
}
@unlink($tmpfile);
}
}

53
Zotlabs/Thumbs/Video.php Normal file
View file

@ -0,0 +1,53 @@
<?php
namespace Zotlabs\Thumbs;
class Video {
function MatchDefault($type) {
return(($type === 'video') ? true : false );
}
function Thumb($attach,$preview_style,$height = 300, $width = 300) {
$photo = false;
$t = explode('/',$attach['filetype']);
if($t[1])
$extension = '.' . $t[1];
else
return;
$file = dbunescbin($attach['content']);
$tmpfile = $file . $extension;
$outfile = $file . '.jpg';
$istream = fopen($file,'rb');
$ostream = fopen($tmpfile,'wb');
if($istream && $ostream) {
pipe_streams($istream,$ostream);
fclose($istream);
fclose($ostream);
}
$imagick_path = get_config('system','imagick_convert_path');
if($imagick_path && @file_exists($imagick_path)) {
$cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile);
// logger('imagick thumbnail command: ' . $cmd);
exec($cmd);
if(! file_exists($outfile)) {
logger('imagick scale failed.');
}
else {
@rename($outfile,$file . '.thumb');
}
}
@unlink($tmpfile);
}
}

View file

@ -247,7 +247,6 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/gender_selector_min]gender_selector_min[/zrl]
called when creating the 'gender' drop down list (normal profile)
[zrl=[baseurl]/help/hook/generate_map]generate_map[/zrl]
called to generate the HTML for displaying a map location by coordinates
@ -578,6 +577,9 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/tagged]tagged[/zrl]
Called when a delivery is processed which results in you being tagged
[zrl=[baseurl]/help/hook/thumbnail]thumbnail[/zrl]
Called when generating thumbnails for cloud storage 'tile' view
[zrl=[baseurl]/help/hook/update_unseen]update_unseen[/zrl]
Called prior to automatically marking items seen which were loaded in the browser

View file

@ -1632,16 +1632,17 @@ function find_filename_by_hash($channel_id, $attachHash) {
}
/**
* @brief Pipes $in to $out in 16MB chunks.
* @brief Pipes $in to $out in 16KB chunks.
*
* @param resource $in File pointer of input
* @param resource $out File pointer of output
* @param int $bufsize size of chunk, default 16384
* @return number with the size
*/
function pipe_streams($in, $out) {
function pipe_streams($in, $out, $bufize = 16384) {
$size = 0;
while (!feof($in))
$size += fwrite($out, fread($in, 16384));
$size += fwrite($out, fread($in, $bufsize));
return $size;
}

View file

@ -129,7 +129,14 @@ abstract class photo_driver {
return $this->types[$this->getType()];
}
public function scaleImage($max) {
/**
* @brief scale image
* int $max maximum pixel size in either dimension
* boolean $float_height - if true allow height to float to any length on tall images,
* constraining only the width
*/
public function scaleImage($max, $float_height = true) {
if(!$this->is_valid())
return FALSE;
@ -146,7 +153,7 @@ abstract class photo_driver {
// very tall image (greater than 16:9)
// constrain the width - let the height float.
if((($height * 9) / 16) > $width) {
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
@ -173,7 +180,7 @@ abstract class photo_driver {
// very tall image (greater than 16:9)
// but width is OK - don't do anything
if((($height * 9) / 16) > $width) {
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $width;
$dest_height = $height;
}

19
library/epub-meta/LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2012 Andreas Gohr <andi@splitbrain.org>
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.

28
library/epub-meta/README Normal file
View file

@ -0,0 +1,28 @@
====== PHP EPub Meta ======
This project aims to create a PHP class for reading and writing metadata
included in the EPub ebook format.
It also includes a very basic web interface to edit book metadata.
Please see the issue tracker for what's missing.
Forks and pull requests welcome.
===== About the EPub Manager Web Interface =====
The manager expects your ebooks in a single flat directory (no subfolders). The
location of that directory has to be configured at the top of the index.php file.
All the epubs need to be read- and writable by the webserver.
The manager also makes some assumption on how the files should be named. The
format is: "<Author file-as>-<Title>.epub". Commas will be replaced by __ and
spaces are replaced by _.
Note that the manager will RENAME your files to that form when saving.
Using the "Lookup Book Data" link will open a dialog that searches the book at
Google Books you can use the found data using the "fill in" and "replace"
buttons. The former will only fill empty fields, while the latter will replace
all data. Author filling is missing currently.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

@ -0,0 +1,24 @@
.cleditorMain {border:1px solid #999; padding:0 1px 1px; background-color:white}
.cleditorMain iframe {border:none; margin:0; padding:0}
.cleditorMain textarea {border:none; margin:0; padding:0; overflow-y:scroll; font:10pt Arial,Verdana; resize:none; outline:none /* webkit grip focus */}
.cleditorToolbar {background: url('images/toolbar.gif') repeat}
.cleditorGroup {float:left; height:26px}
.cleditorButton {float:left; width:24px; height:24px; margin:1px 0 1px 0; background: url('images/buttons.gif')}
.cleditorDisabled {opacity:0.3; filter:alpha(opacity=30)}
.cleditorDivider {float:left; width:1px; height:23px; margin:1px 0 1px 0; background:#CCC}
.cleditorPopup {border:solid 1px #999; background-color:white; position:absolute; font:10pt Arial,Verdana; cursor:default; z-index:10000}
.cleditorList div {padding:2px 4px 2px 4px}
.cleditorList p,
.cleditorList h1,
.cleditorList h2,
.cleditorList h3,
.cleditorList h4,
.cleditorList h5,
.cleditorList h6,
.cleditorList font {padding:0; margin:0; background-color:Transparent}
.cleditorColor {width:150px; padding:1px 0 0 1px}
.cleditorColor div {float:left; width:14px; height:14px; margin:0 1px 1px 0}
.cleditorPrompt {background-color:#F6F7F9; padding:4px; font-size:8.5pt}
.cleditorPrompt input,
.cleditorPrompt textarea {font:8.5pt Arial,Verdana;}
.cleditorMsg {background-color:#FDFCEE; width:150px; padding:4px; font-size:8.5pt}

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,565 @@
/*
* jQuery UI CSS Framework 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
.ui-helper-clearfix:after { clear: both; }
.ui-helper-clearfix { zoom: 1; }
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
/* Interaction Cues
----------------------------------*/
.ui-state-disabled { cursor: default !important; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
/*
* jQuery UI CSS Framework 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
.ui-widget .ui-widget { font-size: 1em; }
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
.ui-widget-content a { color: #222222; }
.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
.ui-widget-header a { color: #222222; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
.ui-widget :active { outline: none; }
/* Interaction Cues
----------------------------------*/
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
/* Icons
----------------------------------*/
/* states and images */
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-off { background-position: -96px -144px; }
.ui-icon-radio-on { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
* jQuery UI Resizable 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizable#theming
*/
.ui-resizable { position: relative;}
.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
* jQuery UI Selectable 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Selectable#theming
*/
.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
/*
* jQuery UI Accordion 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Accordion#theming
*/
/* IE/Win - Fix animation bug - #4615 */
.ui-accordion { width: 100%; }
.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
.ui-accordion .ui-accordion-li-fix { display: inline; }
.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
.ui-accordion .ui-accordion-content-active { display: block; }
/*
* jQuery UI Autocomplete 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Autocomplete#theming
*/
.ui-autocomplete { position: absolute; cursor: default; }
/* workarounds */
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
/*
* jQuery UI Menu 1.8.18
*
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Menu#theming
*/
.ui-menu {
list-style:none;
padding: 2px;
margin: 0;
display:block;
float: left;
}
.ui-menu .ui-menu {
margin-top: -3px;
}
.ui-menu .ui-menu-item {
margin:0;
padding: 0;
zoom: 1;
float: left;
clear: left;
width: 100%;
}
.ui-menu .ui-menu-item a {
text-decoration:none;
display:block;
padding:.2em .4em;
line-height:1.5;
zoom:1;
}
.ui-menu .ui-menu-item a.ui-state-hover,
.ui-menu .ui-menu-item a.ui-state-active {
font-weight: normal;
margin: -1px;
}
/*
* jQuery UI Button 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Button#theming
*/
.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: hidden; *overflow: visible; } /* the overflow property removes extra width in IE */
.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
.ui-button-icons-only { width: 3.4em; }
button.ui-button-icons-only { width: 3.7em; }
/*button text element */
.ui-button .ui-button-text { display: block; line-height: 1.4; }
.ui-button-text-only .ui-button-text { padding: .4em 1em; }
.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
/* no icon support for input elements, provide padding by default */
input.ui-button { padding: .4em 1em; }
/*button icon element(s) */
.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
/*button sets*/
.ui-buttonset { margin-right: 7px; }
.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
/* workarounds */
button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
/*
* jQuery UI Dialog 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Dialog#theming
*/
.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
.ui-draggable .ui-dialog-titlebar { cursor: move; }
/*
* jQuery UI Slider 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Slider#theming
*/
.ui-slider { position: relative; text-align: left; }
.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
.ui-slider-horizontal { height: .8em; }
.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
.ui-slider-horizontal .ui-slider-range-min { left: 0; }
.ui-slider-horizontal .ui-slider-range-max { right: 0; }
.ui-slider-vertical { width: .8em; height: 100px; }
.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
* jQuery UI Tabs 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Tabs#theming
*/
.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
.ui-tabs .ui-tabs-hide { display: none !important; }
/*
* jQuery UI Datepicker 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Datepicker#theming
*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
.ui-datepicker-cover {
display: none; /*sorry for IE5*/
display/**/: block; /*sorry for IE5*/
position: absolute; /*must have*/
z-index: -1; /*must have*/
filter: mask(); /*must have*/
top: -4px; /*must have*/
left: -4px; /*must have*/
width: 200px; /*must have*/
height: 200px; /*must have*/
}/*
* jQuery UI Progressbar 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Progressbar#theming
*/
.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }

View file

@ -0,0 +1,180 @@
body, td, th, input, textarea {
font-family: "Arial", sans-serif;
color: #333;
font-size: 15px;
}
a {
color: #4183C4;
text-decoration: none;
}
a:hover,
a:active {
background-color: #efefef;
}
#wrapper {
height: 95%;
width: 920px;
margin: auto;
padding: 0;
border: 1px solid #ccc;
}
#booklist {
float: left;
width: 300px;
height: 100%;
overflow: auto;
list-style-type: none;
padding: 0;
margin: 0 25px 0 0;
font-size: 13px;
}
#booklist li {
padding: 5px;
}
#booklist li:hover {
background-color: #efefef;
}
#booklist li span {
display: block;
}
#booklist li span.author {
padding-left: 5px;
font-size: 11px;
}
#booklist li.active {
background-color: #4183C4;
}
#booklist li.active a,
#booklist li.active a:hover,
#booklist li.active a:active {
color: #efefef;
background-color: #4183C4;
}
#bookpanel {
float: left;
width: 590px;
height: 100%;
overflow: auto;
}
.center {
text-align: center;
}
table {
margin-bottom: 20px;
}
table th,
table td {
vertical-align: top;
padding: 0 0 5px 0;
}
table th {
text-align: right;
font-weight: bold;
padding: 3px;
padding-right: 20px;
}
table td textarea,
table td input {
width: 450px;
border: none;
padding: 3px;
margin: 0;
border-bottom: solid 1px #dfdfdf;
}
table td .cleditormain {
border: solid 1px #dfdfdf;
}
table td textarea {
height: 250px;
}
table td p {
margin: 0;
padding: 0;
}
table td p input {
width: 200px;
}
table td textarea:focus,
table td input:focus {
border: 1px solid #4183C4;
}
table th img {
margin-top: 20px;
}
a.addauthor {
margin-top: -20px;
float: right;
}
div.license {
font-size: 11px;
margin-top: 300px;
}
#bookapi-s {
float: left;
}
#bookapi-q {
width: 600px;
}
#bookapi {
font-size: 12px;
}
#bookapi div.head {
margin-bottom: 10px;
}
#bookapi div.result {
clear: both;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
min-height: 130px;
}
#bookapi div.result div {
margin-left: 70px;
}
#bookapi div.result div.buttons {
float: right;
text-align: right;
}
#bookapi img {
margin-top: 30px;
width: 60px;
float: left;
}
#bookapi h1.title {
font-size: 14px;
}
#bookapi span.subjects {
font-style: italic;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,31 @@
/*
CLEditor WYSIWYG HTML Editor v1.3.0
http://premiumsoftware.net/cleditor
requires jQuery v1.4.2 or later
Copyright 2010, Chris Landowski, Premium Software, LLC
Dual licensed under the MIT or GPL Version 2 licenses.
*/
(function(e){function aa(a){var b=this,c=a.target,d=e.data(c,x),h=s[d],f=h.popupName,i=p[f];if(!(b.disabled||e(c).attr(n)==n)){var g={editor:b,button:c,buttonName:d,popup:i,popupName:f,command:h.command,useCSS:b.options.useCSS};if(h.buttonClick&&h.buttonClick(a,g)===false)return false;if(d=="source"){if(t(b)){delete b.range;b.$area.hide();b.$frame.show();c.title=h.title}else{b.$frame.hide();b.$area.show();c.title="Show Rich Text"}setTimeout(function(){u(b)},100)}else if(!t(b))if(f){var j=e(i);if(f==
"url"){if(d=="link"&&M(b)===""){z(b,"A selection is required when inserting a link.",c);return false}j.children(":button").unbind(q).bind(q,function(){var k=j.find(":text"),o=e.trim(k.val());o!==""&&v(b,g.command,o,null,g.button);k.val("http://");r();w(b)})}else f=="pastetext"&&j.children(":button").unbind(q).bind(q,function(){var k=j.find("textarea"),o=k.val().replace(/\n/g,"<br />");o!==""&&v(b,g.command,o,null,g.button);k.val("");r();w(b)});if(c!==e.data(i,A)){N(b,i,c);return false}return}else if(d==
"print")b.$frame[0].contentWindow.print();else if(!v(b,g.command,g.value,g.useCSS,c))return false;w(b)}}function O(a){a=e(a.target).closest("div");a.css(H,a.data(x)?"#FFF":"#FFC")}function P(a){e(a.target).closest("div").css(H,"transparent")}function ba(a){var b=a.data.popup,c=a.target;if(!(b===p.msg||e(b).hasClass(B))){var d=e.data(b,A),h=e.data(d,x),f=s[h],i=f.command,g,j=this.options.useCSS;if(h=="font")g=c.style.fontFamily.replace(/"/g,"");else if(h=="size"){if(c.tagName=="DIV")c=c.children[0];
g=c.innerHTML}else if(h=="style")g="<"+c.tagName+">";else if(h=="color")g=Q(c.style.backgroundColor);else if(h=="highlight"){g=Q(c.style.backgroundColor);if(l)i="backcolor";else j=true}b={editor:this,button:d,buttonName:h,popup:b,popupName:f.popupName,command:i,value:g,useCSS:j};if(!(f.popupClick&&f.popupClick(a,b)===false)){if(b.command&&!v(this,b.command,b.value,b.useCSS,d))return false;r();w(this)}}}function C(a){for(var b=1,c=0,d=0;d<a.length;++d){b=(b+a.charCodeAt(d))%65521;c=(c+b)%65521}return c<<
16|b}function R(a,b,c,d,h){if(p[a])return p[a];var f=e(m).hide().addClass(ca).appendTo("body");if(d)f.html(d);else if(a=="color"){b=b.colors.split(" ");b.length<10&&f.width("auto");e.each(b,function(i,g){e(m).appendTo(f).css(H,"#"+g)});c=da}else if(a=="font")e.each(b.fonts.split(","),function(i,g){e(m).appendTo(f).css("fontFamily",g).html(g)});else if(a=="size")e.each(b.sizes.split(","),function(i,g){e(m).appendTo(f).html("<font size="+g+">"+g+"</font>")});else if(a=="style")e.each(b.styles,function(i,
g){e(m).appendTo(f).html(g[1]+g[0]+g[1].replace("<","</"))});else if(a=="url"){f.html('Enter URL:<br><input type=text value="http://" size=35><br><input type=button value="Submit">');c=B}else if(a=="pastetext"){f.html("Paste your content here and click submit.<br /><textarea cols=40 rows=3></textarea><br /><input type=button value=Submit>");c=B}if(!c&&!d)c=S;f.addClass(c);l&&f.attr(I,"on").find("div,font,p,h1,h2,h3,h4,h5,h6").attr(I,"on");if(f.hasClass(S)||h===true)f.children().hover(O,P);p[a]=f[0];
return f[0]}function T(a,b){if(b){a.$area.attr(n,n);a.disabled=true}else{a.$area.removeAttr(n);delete a.disabled}try{if(l)a.doc.body.contentEditable=!b;else a.doc.designMode=!b?"on":"off"}catch(c){}u(a)}function v(a,b,c,d,h){D(a);if(!l){if(d===undefined||d===null)d=a.options.useCSS;a.doc.execCommand("styleWithCSS",0,d.toString())}d=true;var f;if(l&&b.toLowerCase()=="inserthtml")y(a).pasteHTML(c);else{try{d=a.doc.execCommand(b,0,c||null)}catch(i){f=i.description;d=false}d||("cutcopypaste".indexOf(b)>
-1?z(a,"For security reasons, your browser does not support the "+b+" command. Try using the keyboard shortcut or context menu instead.",h):z(a,f?f:"Error executing the "+b+" command.",h))}u(a);return d}function w(a){setTimeout(function(){t(a)?a.$area.focus():a.$frame[0].contentWindow.focus();u(a)},0)}function y(a){if(l)return J(a).createRange();return J(a).getRangeAt(0)}function J(a){if(l)return a.doc.selection;return a.$frame[0].contentWindow.getSelection()}function Q(a){var b=/rgba?\((\d+), (\d+), (\d+)/.exec(a),
c=a.split("");if(b)for(a=(b[1]<<16|b[2]<<8|b[3]).toString(16);a.length<6;)a="0"+a;return"#"+(a.length==6?a:c[1]+c[1]+c[2]+c[2]+c[3]+c[3])}function r(){e.each(p,function(a,b){e(b).hide().unbind(q).removeData(A)})}function U(){var a=e("link[href$='jquery.cleditor.css']").attr("href");return a.substr(0,a.length-19)+"images/"}function K(a){var b=a.$main,c=a.options;a.$frame&&a.$frame.remove();var d=a.$frame=e('<iframe frameborder="0" src="javascript:true;">').hide().appendTo(b),h=d[0].contentWindow,f=
a.doc=h.document,i=e(f);f.open();f.write(c.docType+"<html>"+(c.docCSSFile===""?"":'<head><link rel="stylesheet" type="text/css" href="'+c.docCSSFile+'" /></head>')+'<body style="'+c.bodyStyle+'"></body></html>');f.close();l&&i.click(function(){w(a)});E(a);if(l){i.bind("beforedeactivate beforeactivate selectionchange keypress",function(g){if(g.type=="beforedeactivate")a.inactive=true;else if(g.type=="beforeactivate"){!a.inactive&&a.range&&a.range.length>1&&a.range.shift();delete a.inactive}else if(!a.inactive){if(!a.range)a.range=
[];for(a.range.unshift(y(a));a.range.length>2;)a.range.pop()}});d.focus(function(){D(a)})}(e.browser.mozilla?i:e(h)).blur(function(){V(a,true)});i.click(r).bind("keyup mouseup",function(){u(a)});L?a.$area.show():d.show();e(function(){var g=a.$toolbar,j=g.children("div:last"),k=b.width();j=j.offset().top+j.outerHeight()-g.offset().top+1;g.height(j);j=(/%/.test(""+c.height)?b.height():parseInt(c.height))-j;d.width(k).height(j);a.$area.width(k).height(ea?j-2:j);T(a,a.disabled);u(a)})}function u(a){if(!L&&
e.browser.webkit&&!a.focused){a.$frame[0].contentWindow.focus();window.focus();a.focused=true}var b=a.doc;if(l)b=y(a);var c=t(a);e.each(a.$toolbar.find("."+W),function(d,h){var f=e(h),i=e.cleditor.buttons[e.data(h,x)],g=i.command,j=true;if(a.disabled)j=false;else if(i.getEnabled){j=i.getEnabled({editor:a,button:h,buttonName:i.name,popup:p[i.popupName],popupName:i.popupName,command:i.command,useCSS:a.options.useCSS});if(j===undefined)j=true}else if((c||L)&&i.name!="source"||l&&(g=="undo"||g=="redo"))j=
false;else if(g&&g!="print"){if(l&&g=="hilitecolor")g="backcolor";if(!l||g!="inserthtml")try{j=b.queryCommandEnabled(g)}catch(k){j=false}}if(j){f.removeClass(X);f.removeAttr(n)}else{f.addClass(X);f.attr(n,n)}})}function D(a){l&&a.range&&a.range[0].select()}function M(a){D(a);if(l)return y(a).text;return J(a).toString()}function z(a,b,c){var d=R("msg",a.options,fa);d.innerHTML=b;N(a,d,c)}function N(a,b,c){var d,h,f=e(b);if(c){var i=e(c);d=i.offset();h=--d.left;d=d.top+i.height()}else{i=a.$toolbar;
d=i.offset();h=Math.floor((i.width()-f.width())/2)+d.left;d=d.top+i.height()-2}r();f.css({left:h,top:d}).show();if(c){e.data(b,A,c);f.bind(q,{popup:b},e.proxy(ba,a))}setTimeout(function(){f.find(":text,textarea").eq(0).focus().select()},100)}function t(a){return a.$area.is(":visible")}function E(a,b){var c=a.$area.val(),d=a.options,h=d.updateFrame,f=e(a.doc.body);if(h){var i=C(c);if(b&&a.areaChecksum==i)return;a.areaChecksum=i}c=h?h(c):c;c=c.replace(/<(?=\/?script)/ig,"&lt;");if(d.updateTextArea)a.frameChecksum=
C(c);if(c!=f.html()){f.html(c);e(a).triggerHandler(F)}}function V(a,b){var c=e(a.doc.body).html(),d=a.options,h=d.updateTextArea,f=a.$area;if(h){var i=C(c);if(b&&a.frameChecksum==i)return;a.frameChecksum=i}c=h?h(c):c;if(d.updateFrame)a.areaChecksum=C(c);if(c!=f.val()){f.val(c);e(a).triggerHandler(F)}}e.cleditor={defaultOptions:{width:500,height:250,controls:"bold italic underline strikethrough subscript superscript | font size style | color highlight removeformat | bullets numbering | outdent indent | alignleft center alignright justify | undo redo | rule image link unlink | cut copy paste pastetext | print source",
colors:"FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C 999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C 666 900 C60 C93 990 090 399 33F 60C 939 333 600 930 963 660 060 366 009 339 636 000 300 630 633 330 030 033 006 309 303",fonts:"Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond,Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",sizes:"1,2,3,4,5,6,7",styles:[["Paragraph","<p>"],["Header 1","<h1>"],["Header 2","<h2>"],
["Header 3","<h3>"],["Header 4","<h4>"],["Header 5","<h5>"],["Header 6","<h6>"]],useCSS:false,docType:'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',docCSSFile:"",bodyStyle:"margin:4px; font:10pt Arial,Verdana; cursor:text"},buttons:{init:"bold,,|italic,,|underline,,|strikethrough,,|subscript,,|superscript,,|font,,fontname,|size,Font Size,fontsize,|style,,formatblock,|color,Font Color,forecolor,|highlight,Text Highlight Color,hilitecolor,color|removeformat,Remove Formatting,|bullets,,insertunorderedlist|numbering,,insertorderedlist|outdent,,|indent,,|alignleft,Align Text Left,justifyleft|center,,justifycenter|alignright,Align Text Right,justifyright|justify,,justifyfull|undo,,|redo,,|rule,Insert Horizontal Rule,inserthorizontalrule|image,Insert Image,insertimage,url|link,Insert Hyperlink,createlink,url|unlink,Remove Hyperlink,|cut,,|copy,,|paste,,|pastetext,Paste as Text,inserthtml,|print,,|source,Show Source"},
imagesPath:function(){return U()}};e.fn.cleditor=function(a){var b=e([]);this.each(function(c,d){if(d.tagName=="TEXTAREA"){var h=e.data(d,Y);h||(h=new cleditor(d,a));b=b.add(h)}});return b};var H="backgroundColor",A="button",x="buttonName",F="change",Y="cleditor",q="click",n="disabled",m="<div>",I="unselectable",W="cleditorButton",X="cleditorDisabled",ca="cleditorPopup",S="cleditorList",da="cleditorColor",B="cleditorPrompt",fa="cleditorMsg",l=e.browser.msie,ea=/msie\s6/i.test(navigator.userAgent),
L=/iphone|ipad|ipod/i.test(navigator.userAgent),p={},Z,s=e.cleditor.buttons;e.each(s.init.split("|"),function(a,b){var c=b.split(","),d=c[0];s[d]={stripIndex:a,name:d,title:c[1]===""?d.charAt(0).toUpperCase()+d.substr(1):c[1],command:c[2]===""?d:c[2],popupName:c[3]===""?d:c[3]}});delete s.init;cleditor=function(a,b){var c=this;c.options=b=e.extend({},e.cleditor.defaultOptions,b);var d=c.$area=e(a).hide().data(Y,c).blur(function(){E(c,true)}),h=c.$main=e(m).addClass("cleditorMain").width(b.width).height(b.height),
f=c.$toolbar=e(m).addClass("cleditorToolbar").appendTo(h),i=e(m).addClass("cleditorGroup").appendTo(f);e.each(b.controls.split(" "),function(g,j){if(j==="")return true;if(j=="|"){e(m).addClass("cleditorDivider").appendTo(i);i=e(m).addClass("cleditorGroup").appendTo(f)}else{var k=s[j],o=e(m).data(x,k.name).addClass(W).attr("title",k.title).bind(q,e.proxy(aa,c)).appendTo(i).hover(O,P),G={};if(k.css)G=k.css;else if(k.image)G.backgroundImage="url("+U()+k.image+")";if(k.stripIndex)G.backgroundPosition=
k.stripIndex*-24;o.css(G);l&&o.attr(I,"on");k.popupName&&R(k.popupName,b,k.popupClass,k.popupContent,k.popupHover)}});h.insertBefore(d).append(d);if(!Z){e(document).click(function(g){g=e(g.target);g.add(g.parents()).is("."+B)||r()});Z=true}/auto|%/.test(""+b.width+b.height)&&e(window).resize(function(){K(c)});K(c)};var $=cleditor.prototype;e.each([["clear",function(a){a.$area.val("");E(a)}],["disable",T],["execCommand",v],["focus",w],["hidePopups",r],["sourceMode",t,true],["refresh",K],["select",
function(a){setTimeout(function(){t(a)?a.$area.select():v(a,"selectall")},0)}],["selectedHTML",function(a){D(a);a=y(a);if(l)return a.htmlText;var b=e("<layer>")[0];b.appendChild(a.cloneContents());return b.innerHTML},true],["selectedText",M,true],["showMessage",z],["updateFrame",E],["updateTextArea",V]],function(a,b){$[b[0]]=function(){for(var c=[this],d=0;d<arguments.length;d++)c.push(arguments[d]);c=b[1].apply(this,c);if(b[2])return c;return this}});$.change=function(a){var b=e(this);return a?b.bind(F,
a):b.trigger(F)}})(jQuery);

View file

@ -0,0 +1,194 @@
var bookapi = {
$dialog: null,
resulttpl:
'<div class="result">' +
' <img src="" />'+
' <div>' +
' <div class="buttons">' +
' <button class="btn-repl">replace</button><br />' +
' <button class="btn-fill">fill in</button>' +
' </div>' +
' <h1 class="title"></h1>' +
' <p class="authors"></p>' +
' <p class="description"></p>' +
' <p class="more">' +
' <span class="lang"></span>' +
' <span class="publisher"></span>' +
' <span class="subjects"></span>' +
' </p>' +
' </div>' +
'</div>',
init: function(){
$('body').append('<div id="bookapi"></div>');
bookapi.$dialog = $('#bookapi');
bookapi.$dialog.dialog(
{
autoOpen: false,
title: 'Lookup Book Data',
width: 800,
height: 500
}
);
bookapi.$dialog.append('<div class="head">Lookup: <input type="text" id="bookapi-q" /></div>')
.append('<div id="bookapi-out"></div>');
bookapi.$out = $('#bookapi-out');
$('#bookpanel').append('<a href="#" id="bookapi-s">Lookup Book Data</a>');
$('#bookapi-s').attr('title','Search this book at Google Books');
$('#bookapi-s').click(bookapi.open);
$('#bookapi-q').keypress(
function(event){
if(event.which == 13){
event.preventDefault();
bookapi.search();
}
});
},
open: function(){
bookapi.$dialog.dialog('open');
var query = $('#bookpanel input[name=title]').val();
$('#bookapi-q').val(query);
bookapi.search();
},
search: function(){
bookapi.$out.html('please wait...');
$.ajax({
type: 'GET',
data: {'api':$('#bookapi-q').val()},
success: bookapi.searchdone,
dataType: 'json'
});
},
searchdone: function(data){
if(data.totalItems == 0){
bookapi.$out.html('Found no results.<br />Try adjusting the query and retry.');
return;
}
bookapi.$out.html('');
for(i=0; i<data.items.length; i++){
$res = $(bookapi.resulttpl);
if(data.items[i].volumeInfo.title)
$res.find('.title').html(data.items[i].volumeInfo.title);
if(data.items[i].volumeInfo.authors)
$res.find('.authors').html(data.items[i].volumeInfo.authors.join(', '));
if(data.items[i].volumeInfo.description)
$res.find('.description').html(data.items[i].volumeInfo.description);
if(data.items[i].volumeInfo.language)
$res.find('.lang').html('['+data.items[i].volumeInfo.language+']');
if(data.items[i].volumeInfo.publisher)
$res.find('.publisher').html(data.items[i].volumeInfo.publisher);
if(data.items[i].volumeInfo.categories)
$res.find('.subjects').html(data.items[i].volumeInfo.categories.join(', '));
if(data.items[i].volumeInfo.imageLinks)
if(data.items[i].volumeInfo.imageLinks.thumbnail)
$res.find('img').attr('src',data.items[i].volumeInfo.imageLinks.thumbnail);
$res.find('.btn-repl').click(data.items[i].volumeInfo,bookapi.replace);
$res.find('.btn-fill').click(data.items[i].volumeInfo,bookapi.fillin);
bookapi.$out.append($res);
}
},
replace: function(event){
item = event.data;
if(item.title)
$('#bookpanel input[name=title]').val(item.title);
if(item.description)
$('#bookpanel textarea[name=description]').val(item.description);
$wysiwyg[0].updateFrame();
if(item.language)
$('#bookpanel input[name=language]').val(item.language);
if(item.publisher)
$('#bookpanel input[name=publisher]').val(item.publisher);
if(item.categories)
$('#bookpanel input[name=subjects]').val(item.categories.join(', '));
if(item.imageLinks){
$('#bookpanel input[name=coverurl]').val(item.imageLinks.thumbnail);
$('#cover').attr('src',item.imageLinks.thumbnail);
}
bookapi.$dialog.dialog('close');
},
fillin: function(event){
item = event.data;
if(item.title && $('#bookpanel input[name=title]').val() == '')
$('#bookpanel input[name=title]').val(item.title);
if(item.description && $('#bookpanel textarea[name=description]').val() == '')
$('#bookpanel textarea[name=description]').val(item.description);
$wysiwyg[0].updateFrame();
if(item.language && $('#bookpanel input[name=language]').val() == '')
$('#bookpanel input[name=language]').val(item.language);
if(item.publisher && $('#bookpanel input[name=publisher]').val() == '')
$('#bookpanel input[name=publisher]').val(item.publisher);
if(item.categories && $('#bookpanel input[name=subjects]').val() == '')
$('#bookpanel input[name=subjects]').val(item.categories.join(', '));
if(item.imageLinks && $('#cover').hasClass('noimg')){
$('#bookpanel input[name=coverurl]').val(item.imageLinks.thumbnail);
$('#cover').attr('src',item.imageLinks.thumbnail);
}
bookapi.$dialog.dialog('close');
}
};
var author = {
init: function(){
$button = $(document.createElement('a'));
$button.text('+').attr('href','#');
$button.attr('title','add another author line');
$button.click(author.add);
$button.addClass('addauthor');
$td = $('#authors');
$td.append($button);
},
add: function(){
$td = $('#authors');
$ps = $td.find('p');
$new = $ps.first().clone();
$new.find('input').first().attr('name','authorname['+$ps.length+']').val('');
$new.find('input').last().attr('name','authoras['+$ps.length+']').val('');
$ps.last().after($new);
}
};
var $wysiwg = null;
$(function(){
bookapi.init();
author.init();
// scroll to currently selected book
$current = $('#booklist li.active');
if($current.length){
$current[0].scrollIntoView();
}
// initialize the WYSIWYG editor
$wysiwyg = $('textarea').cleditor({
width: 450,
controls: // controls to add to the toolbar
"bold italic underline strikethrough | " +
"style removeformat | bullets numbering | " +
"alignleft center alignright justify | undo redo | " +
"link unlink | source",
styles: // styles in the style popup
[["Paragraph", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"],
["Header 3", "<h3>"], ["Header 4","<h4>"], ["Header 5","<h5>"]]
});
});

536
library/epub-meta/epub.php Normal file
View file

@ -0,0 +1,536 @@
<?php
/**
* PHP EPub Meta library
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
class EPub {
public $xml; //FIXME change to protected, later
protected $xpath;
protected $file;
protected $meta;
protected $namespaces;
protected $imagetoadd='';
/**
* Constructor
*
* @param string $file path to epub file to work on
* @throws Exception if metadata could not be loaded
*/
public function __construct($file){
// open file
$this->file = $file;
$zip = new ZipArchive();
if(!@$zip->open($this->file)){
throw new Exception('Failed to read epub file');
}
// read container data
$data = $zip->getFromName('META-INF/container.xml');
if($data == false){
throw new Exception('Failed to access epub container data');
}
$xml = new DOMDocument();
$xml->registerNodeClass('DOMElement','EPubDOMElement');
$xml->loadXML($data);
$xpath = new EPubDOMXPath($xml);
$nodes = $xpath->query('//n:rootfiles/n:rootfile[@media-type="application/oebps-package+xml"]');
$this->meta = $nodes->item(0)->attr('full-path');
// load metadata
$data = $zip->getFromName($this->meta);
if(!$data){
throw new Exception('Failed to access epub metadata');
}
$this->xml = new DOMDocument();
$this->xml->registerNodeClass('DOMElement','EPubDOMElement');
$this->xml->loadXML($data);
$this->xml->formatOutput = true;
$this->xpath = new EPubDOMXPath($this->xml);
$zip->close();
}
/**
* file name getter
*/
public function file(){
return $this->file;
}
/**
* Writes back all meta data changes
*/
public function save(){
$zip = new ZipArchive();
$res = @$zip->open($this->file, ZipArchive::CREATE);
if($res === false){
throw new Exception('Failed to write back metadata');
}
$zip->addFromString($this->meta,$this->xml->saveXML());
// add the cover image
if($this->imagetoadd){
$path = dirname('/'.$this->meta).'/php-epub-meta-cover.img'; // image path is relative to meta file
$path = ltrim($path,'/');
$zip->addFromString($path,file_get_contents($this->imagetoadd));
$this->imagetoadd='';
}
$zip->close();
}
/**
* Get or set the book author(s)
*
* Authors should be given with a "file-as" and a real name. The file as
* is used for sorting in e-readers.
*
* Example:
*
* array(
* 'Pratchett, Terry' => 'Terry Pratchett',
* 'Simpson, Jacqeline' => 'Jacqueline Simpson',
* )
*
* @params array $authors
*/
public function Authors($authors=false){
// set new data
if($authors !== false){
// Author where given as a comma separated list
if(is_string($authors)){
if($authors == ''){
$authors = array();
}else{
$authors = explode(',',$authors);
$authors = array_map('trim',$authors);
}
}
// delete existing nodes
$nodes = $this->xpath->query('//opf:metadata/dc:creator[@opf:role="aut"]');
foreach($nodes as $node) $node->delete();
// add new nodes
$parent = $this->xpath->query('//opf:metadata')->item(0);
foreach($authors as $as => $name){
if(is_int($as)) $as = $name; //numeric array given
$node = $parent->newChild('dc:creator',$name);
$node->attr('opf:role', 'aut');
$node->attr('opf:file-as', $as);
}
$this->reparse();
}
// read current data
$rolefix = false;
$authors = array();
$nodes = $this->xpath->query('//opf:metadata/dc:creator[@opf:role="aut"]');
if($nodes->length == 0){
// no nodes where found, let's try again without role
$nodes = $this->xpath->query('//opf:metadata/dc:creator');
$rolefix = true;
}
foreach($nodes as $node){
$name = $node->nodeValue;
$as = $node->attr('opf:file-as');
if(!$as){
$as = $name;
$node->attr('opf:file-as',$as);
}
if($rolefix){
$node->attr('opf:role','aut');
}
$authors[$as] = $name;
}
return $authors;
}
/**
* Set or get the book title
*
* @param string $title
*/
public function Title($title=false){
return $this->getset('dc:title',$title);
}
/**
* Set or get the book's language
*
* @param string $lang
*/
public function Language($lang=false){
return $this->getset('dc:language',$lang);
}
/**
* Set or get the book' publisher info
*
* @param string $publisher
*/
public function Publisher($publisher=false){
return $this->getset('dc:publisher',$publisher);
}
/**
* Set or get the book's copyright info
*
* @param string $rights
*/
public function Copyright($rights=false){
return $this->getset('dc:rights',$rights);
}
/**
* Set or get the book's description
*
* @param string $description
*/
public function Description($description=false){
return $this->getset('dc:description',$description);
}
/**
* Set or get the book's ISBN number
*
* @param string $isbn
*/
public function ISBN($isbn=false){
return $this->getset('dc:identifier',$isbn,'opf:scheme','ISBN');
}
/**
* Set or get the Google Books ID
*
* @param string $google
*/
public function Google($google=false){
return $this->getset('dc:identifier',$google,'opf:scheme','GOOGLE');
}
/**
* Set or get the Amazon ID of the book
*
* @param string $amazon
*/
public function Amazon($amazon=false){
return $this->getset('dc:identifier',$amazon,'opf:scheme','AMAZON');
}
/**
* Set or get the book's subjects (aka. tags)
*
* Subject should be given as array, but a comma separated string will also
* be accepted.
*
* @param array $subjects
*/
public function Subjects($subjects=false){
// setter
if($subjects !== false){
if(is_string($subjects)){
if($subjects === ''){
$subjects = array();
}else{
$subjects = explode(',',$subjects);
$subjects = array_map('trim',$subjects);
}
}
// delete previous
$nodes = $this->xpath->query('//opf:metadata/dc:subject');
foreach($nodes as $node){
$node->delete();
}
// add new ones
$parent = $this->xpath->query('//opf:metadata')->item(0);
foreach($subjects as $subj){
$node = $this->xml->createElement('dc:subject',htmlspecialchars($subj));
$node = $parent->appendChild($node);
}
$this->reparse();
}
//getter
$subjects = array();
$nodes = $this->xpath->query('//opf:metadata/dc:subject');
foreach($nodes as $node){
$subjects[] = $node->nodeValue;
}
return $subjects;
}
/**
* Read the cover data
*
* Returns an associative array with the following keys:
*
* mime - filetype (usually image/jpeg)
* data - the binary image data
* found - the internal path, or false if no image is set in epub
*
* When no image is set in the epub file, the binary data for a transparent
* GIF pixel is returned.
*
* When adding a new image this function return no or old data because the
* image contents are not in the epub file, yet. The image will be added when
* the save() method is called.
*
* @param string $path local filesystem path to a new cover image
* @param string $mime mime type of the given file
* @return array
*/
public function Cover($path=false, $mime=false){
// set cover
if($path !== false){
// remove current pointer
$nodes = $this->xpath->query('//opf:metadata/opf:meta[@name="cover"]');
foreach($nodes as $node) $node->delete();
// remove previous manifest entries if they where made by us
$nodes = $this->xpath->query('//opf:manifest/opf:item[@id="php-epub-meta-cover"]');
foreach($nodes as $node) $node->delete();
if($path){
// add pointer
$parent = $this->xpath->query('//opf:metadata')->item(0);
$node = $parent->newChild('opf:meta');
$node->attr('opf:name','cover');
$node->attr('opf:content','php-epub-meta-cover');
// add manifest
$parent = $this->xpath->query('//opf:manifest')->item(0);
$node = $parent->newChild('opf:item');
$node->attr('id','php-epub-meta-cover');
$node->attr('opf:href','php-epub-meta-cover.img');
$node->attr('opf:media-type',$mime);
// remember path for save action
$this->imagetoadd = $path;
}
$this->reparse();
}
// load cover
$nodes = $this->xpath->query('//opf:metadata/opf:meta[@name="cover"]');
if(!$nodes->length) return $this->no_cover();
$coverid = (String) $nodes->item(0)->attr('opf:content');
if(!$coverid) return $this->no_cover();
$nodes = $this->xpath->query('//opf:manifest/opf:item[@id="'.$coverid.'"]');
if(!$nodes->length) return $this->no_cover();
$mime = $nodes->item(0)->attr('opf:media-type');
$path = $nodes->item(0)->attr('opf:href');
$path = dirname('/'.$this->meta).'/'.$path; // image path is relative to meta file
$path = ltrim($path,'/');
$zip = new ZipArchive();
if(!@$zip->open($this->file)){
throw new Exception('Failed to read epub file');
}
$data = $zip->getFromName($path);
return array(
'mime' => $mime,
'data' => $data,
'found' => $path
);
}
/**
* A simple getter/setter for simple meta attributes
*
* It should only be used for attributes that are expected to be unique
*
* @param string $item XML node to set/get
* @param string $value New node value
* @param string $att Attribute name
* @param string $aval Attribute value
*/
protected function getset($item,$value=false,$att=false,$aval=false){
// construct xpath
$xpath = '//opf:metadata/'.$item;
if($att){
$xpath .= "[@$att=\"$aval\"]";
}
// set value
if($value !== false){
$value = htmlspecialchars($value);
$nodes = $this->xpath->query($xpath);
if($nodes->length == 1 ){
if($value === ''){
// the user want's to empty this value -> delete the node
$nodes->item(0)->delete();
}else{
// replace value
$nodes->item(0)->nodeValue = $value;
}
}else{
// if there are multiple matching nodes for some reason delete
// them. we'll replace them all with our own single one
foreach($nodes as $n) $n->delete();
// readd them
if($value){
$parent = $this->xpath->query('//opf:metadata')->item(0);
$node = $this->xml->createElement($item,$value);
$node = $parent->appendChild($node);
if($att) $node->attr($att,$aval);
}
}
$this->reparse();
}
// get value
$nodes = $this->xpath->query($xpath);
if($nodes->length){
return $nodes->item(0)->nodeValue;
}else{
return '';
}
}
/**
* Return a not found response for Cover()
*/
protected function no_cover(){
return array(
'data' => base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7'),
'mime' => 'image/gif',
'found' => false
);
}
/**
* Reparse the DOM tree
*
* I had to rely on this because otherwise xpath failed to find the newly
* added nodes
*/
protected function reparse() {
$this->xml->loadXML($this->xml->saveXML());
$this->xpath = new EPubDOMXPath($this->xml);
}
}
class EPubDOMXPath extends DOMXPath {
public function __construct(DOMDocument $doc){
parent::__construct($doc);
if(is_a($doc->documentElement, 'EPubDOMElement')){
foreach($doc->documentElement->namespaces as $ns => $url){
$this->registerNamespace($ns,$url);
}
}
}
}
class EPubDOMElement extends DOMElement {
public $namespaces = array(
'n' => 'urn:oasis:names:tc:opendocument:xmlns:container',
'opf' => 'http://www.idpf.org/2007/opf',
'dc' => 'http://purl.org/dc/elements/1.1/'
);
public function __construct($name, $value='', $namespaceURI=''){
list($ns,$name) = $this->splitns($name);
$value = htmlspecialchars($value);
if(!$namespaceURI && $ns){
$namespaceURI = $this->namespaces[$ns];
}
parent::__construct($name, $value, $namespaceURI);
}
/**
* Create and append a new child
*
* Works with our epub namespaces and omits default namespaces
*/
public function newChild($name, $value=''){
list($ns,$local) = $this->splitns($name);
if($ns){
$nsuri = $this->namespaces[$ns];
if($this->isDefaultNamespace($nsuri)){
$name = $local;
$nsuri = '';
}
}
// this doesn't call the construcor: $node = $this->ownerDocument->createElement($name,$value);
$node = new EPubDOMElement($name,$value,$nsuri);
return $this->appendChild($node);
}
/**
* Split given name in namespace prefix and local part
*
* @param string $name
* @return array (namespace, name)
*/
public function splitns($name){
$list = explode(':',$name,2);
if(count($list) < 2) array_unshift($list,'');
return $list;
}
/**
* Simple EPub namespace aware attribute accessor
*/
public function attr($attr,$value=null){
list($ns,$attr) = $this->splitns($attr);
$nsuri = '';
if($ns){
$nsuri = $this->namespaces[$ns];
if(!$this->namespaceURI){
if($this->isDefaultNamespace($nsuri)){
$nsuri = '';
}
}elseif($this->namespaceURI == $nsuri){
$nsuri = '';
}
}
if(!is_null($value)){
if($value === false){
// delete if false was given
if($nsuri){
$this->removeAttributeNS($nsuri,$attr);
}else{
$this->removeAttribute($attr);
}
}else{
// modify if value was given
if($nsuri){
$this->setAttributeNS($nsuri,$attr,$value);
}else{
$this->setAttribute($attr,$value);
}
}
}else{
// return value if none was given
if($nsuri){
return $this->getAttributeNS($nsuri,$attr);
}else{
return $this->getAttribute($attr);
}
}
}
/**
* Remove this node from the DOM
*/
public function delete(){
$this->parentNode->removeChild($this);
}
}

214
library/epub-meta/index.php Normal file
View file

@ -0,0 +1,214 @@
<?php
// modify this to point to your book directory
$bookdir = '/home/andi/Dropbox/ebooks/';
error_reporting(E_ALL ^ E_NOTICE);
// proxy google requests
if(isset($_GET['api'])){
header('application/json; charset=UTF-8');
echo file_get_contents('https://www.googleapis.com/books/v1/volumes?q='.rawurlencode($_GET['api']).'&maxResults=25&printType=books&projection=full');
exit;
}
require('util.php');
// load epub data
require('epub.php');
if(isset($_REQUEST['book'])){
try{
$book = $_REQUEST['book'];
$book = str_replace('..','',$book); // no upper dirs, lowers might be supported later
$epub = new EPub($bookdir.$book.'.epub');
}catch (Exception $e){
$error = $e->getMessage();
}
}
// return image data
if(isset($_REQUEST['img']) && isset($epub)){
$img = $epub->Cover();
header('Content-Type: '.$img['mime']);
echo $img['data'];
exit;
}
// save epub data
if($_REQUEST['save'] && isset($epub)){
$epub->Title($_POST['title']);
$epub->Description($_POST['description']);
$epub->Language($_POST['language']);
$epub->Publisher($_POST['publisher']);
$epub->Copyright($_POST['copyright']);
$epub->ISBN($_POST['isbn']);
$epub->Subjects($_POST['subjects']);
$authors = array();
foreach((array) $_POST['authorname'] as $num => $name){
if($name){
$as = $_POST['authoras'][$num];
if(!$as) $as = $name;
$authors[$as] = $name;
}
}
$epub->Authors($authors);
// handle image
$cover = '';
if(preg_match('/^https?:\/\//i',$_POST['coverurl'])){
$data = @file_get_contents($_POST['coverurl']);
if($data){
$cover = tempnam(sys_get_temp_dir(), 'epubcover');
file_put_contents($cover,$data);
unset($data);
}
}elseif(is_uploaded_file($_FILES['coverfile']['tmp_name'])){
$cover = $_FILES['coverfile']['tmp_name'];
}
if($cover){
$info = @getimagesize($cover);
if(preg_match('/^image\/(gif|jpe?g|png)$/',$info['mime'])){
$epub->Cover($cover,$info['meta']);
}else{
$error = "Not a valid image file".$cover;
}
}
// save the ebook
try{
$epub->save();
}catch(Exception $e){
$error = $e->getMessage();
}
// clean up temporary cover file
if($cover) @unlink($cover);
// rename
$author = array_shift(array_keys($epub->Authors()));
$title = $epub->Title();
$new = to_file($author.'-'.$title);
$new = $bookdir.$new.'.epub';
$old = $epub->file();
if(realpath($new) != realpath($old)){
if(!@rename($old,$new)) $new = $old; //rename failed, stay here
}
$go = basename($new,'.epub');
header('Location: ?book='.rawurlencode($go));
exit;
}
header('Content-Type: text/html; charset=utf-8');
?>
<html>
<head>
<title>EPub Manager</title>
<link rel="stylesheet" type="text/css" href="assets/css/smoothness/jquery-ui-1.8.18.custom.css" />
<link rel="stylesheet" type="text/css" href="assets/css/cleditor/jquery.cleditor.css" />
<link rel="stylesheet" type="text/css" href="assets/css/style.css" />
<script type="text/javascript">
<?php if($error) echo "alert('".htmlspecialchars($error)."');";?>
</script>
</head>
<body>
<div id="wrapper">
<ul id="booklist">
<?php
$list = glob($bookdir.'/*.epub');
foreach($list as $book){
$base = basename($book,'.epub');
$name = book_output($base);
echo '<li '.($base == $_REQUEST['book'] ? 'class="active"' : '' ).'>';
echo '<a href="?book='.htmlspecialchars($base).'">'.$name.'</a>';
echo '</li>';
}
?>
</ul>
<?php if($epub): ?>
<form action="" method="post" id="bookpanel" enctype="multipart/form-data">
<input type="hidden" name="book" value="<?php echo htmlspecialchars($_REQUEST['book'])?>" />
<table>
<tr>
<th>Title</th>
<td><input type="text" name="title" value="<?php echo htmlspecialchars($epub->Title())?>" /></td>
</tr>
<tr>
<th>Authors</th>
<td id="authors">
<?php
$count = 0;
foreach($epub->Authors() as $as => $name){
?>
<p>
<input type="text" name="authorname[<?php echo $count?>]" value="<?php echo htmlspecialchars($name)?>" />
(<input type="text" name="authoras[<?php echo $count?>]" value="<?php echo htmlspecialchars($as)?>" />)
</p>
<?php
$count++;
}
?>
</td>
</tr>
<tr>
<th>Description<br />
<img src="?book=<?php echo htmlspecialchars($_REQUEST['book'])?>&amp;img=1" id="cover" width="90"
class="<?php $c = $epub->Cover(); echo ($c['found']?'hasimg':'noimg')?>" />
</th>
<td><textarea name="description"><?php echo htmlspecialchars($epub->Description())?></textarea></td>
</tr>
<tr>
<th>Subjects</th>
<td><input type="text" name="subjects" value="<?php echo htmlspecialchars(join(', ',$epub->Subjects()))?>" /></td>
</tr>
<tr>
<th>Publisher</th>
<td><input type="text" name="publisher" value="<?php echo htmlspecialchars($epub->Publisher())?>" /></td>
</tr>
<tr>
<th>Copyright</th>
<td><input type="text" name="copyright" value="<?php echo htmlspecialchars($epub->Copyright())?>" /></td>
</tr>
<tr>
<th>Language</th>
<td><p><input type="text" name="language" value="<?php echo htmlspecialchars($epub->Language())?>" /></p></td>
</tr>
<tr>
<th>ISBN</th>
<td><p><input type="text" name="isbn" value="<?php echo htmlspecialchars($epub->ISBN())?>" /></p></td>
</tr>
<tr>
<th>Cover Image</th>
<td><p>
<input type="file" name="coverfile" />
URL: <input type="text" name="coverurl" value="" />
</p></td>
</table>
<div class="center">
<input name="save" type="submit" />
</div>
</form>
<?php else: ?>
<h1>EPub Manager</h1>
<p>View and edit epub books stored in <code><?php echo htmlspecialchars($bookdir)?></code>.</p>
<div class="license">
<p><?php echo str_replace("\n\n",'</p><p>',htmlspecialchars(file_get_contents('LICENSE'))) ?></p>
</div>
<?php endif; ?>
<!-- load at the end, for faster site load -->
<script type="text/javascript" src="assets/js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="assets/js/jquery-ui-1.8.18.custom.min.js"></script>
<script type="text/javascript" src="assets/js/jquery.cleditor.min.js"></script>
<script type="text/javascript" src="assets/js/script.js"></script>
</div>
</body>
</html>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

View file

@ -0,0 +1,190 @@
<?php
require '../epub.php';
class EPubTest extends PHPUnit_Framework_TestCase {
protected $epub;
protected function setUp(){
// sometime I might have accidentally broken the test file
if(filesize('test.epub') != 768780){
die('test.epub has wrong size, make sure it\'s unmodified');
}
// we work on a copy to test saving
if(!copy('test.epub','test.copy.epub')){
die('failed to create copy of the test book');
}
$this->epub = new EPub('test.copy.epub');
}
protected function tearDown(){
unlink('test.copy.epub');
}
public function testAuthors(){
// read curent value
$this->assertEquals(
$this->epub->Authors(),
array('Shakespeare, William' => 'William Shakespeare')
);
// remove value with string
$this->assertEquals(
$this->epub->Authors(''),
array()
);
// set single value by String
$this->assertEquals(
$this->epub->Authors('John Doe'),
array('John Doe' => 'John Doe')
);
// set single value by indexed array
$this->assertEquals(
$this->epub->Authors(array('John Doe')),
array('John Doe' => 'John Doe')
);
// remove value with array
$this->assertEquals(
$this->epub->Authors(array()),
array()
);
// set single value by associative array
$this->assertEquals(
$this->epub->Authors(array('Doe, John' => 'John Doe')),
array('Doe, John' => 'John Doe')
);
// set multi value by string
$this->assertEquals(
$this->epub->Authors('John Doe, Jane Smith'),
array('John Doe' => 'John Doe', 'Jane Smith' => 'Jane Smith')
);
// set multi value by indexed array
$this->assertEquals(
$this->epub->Authors(array('John Doe', 'Jane Smith')),
array('John Doe' => 'John Doe', 'Jane Smith' => 'Jane Smith')
);
// set multi value by associative array
$this->assertEquals(
$this->epub->Authors(array('Doe, John' => 'John Doe', 'Smith, Jane' => 'Jane Smith')),
array('Doe, John' => 'John Doe', 'Smith, Jane' => 'Jane Smith')
);
// check escaping
$this->assertEquals(
$this->epub->Authors(array('Doe, John&nbsp;' => 'John Doe&nbsp;')),
array('Doe, John&nbsp;' => 'John Doe&nbsp;')
);
}
public function testTitle(){
// get current value
$this->assertEquals(
$this->epub->Title(),
'Romeo and Juliet'
);
// delete current value
$this->assertEquals(
$this->epub->Title(''),
''
);
// get current value
$this->assertEquals(
$this->epub->Title(),
''
);
// set new value
$this->assertEquals(
$this->epub->Title('Foo Bar'),
'Foo Bar'
);
// check escaping
$this->assertEquals(
$this->epub->Title('Foo&nbsp;Bar'),
'Foo&nbsp;Bar'
);
}
public function testSubject(){
// get current values
$this->assertEquals(
$this->epub->Subjects(),
array('Fiction','Drama','Romance')
);
// delete current values with String
$this->assertEquals(
$this->epub->Subjects(''),
array()
);
// set new values with String
$this->assertEquals(
$this->epub->Subjects('Fiction, Drama, Romance'),
array('Fiction','Drama','Romance')
);
// delete current values with Array
$this->assertEquals(
$this->epub->Subjects(array()),
array()
);
// set new values with array
$this->assertEquals(
$this->epub->Subjects(array('Fiction','Drama','Romance')),
array('Fiction','Drama','Romance')
);
// check escaping
$this->assertEquals(
$this->epub->Subjects(array('Fiction','Drama&nbsp;','Romance')),
array('Fiction','Drama&nbsp;','Romance')
);
}
public function testCover(){
// read current cover
$cover = $this->epub->Cover();
$this->assertEquals($cover['mime'],'image/png');
$this->assertEquals($cover['found'],'OPS/images/cover.png');
$this->assertEquals(strlen($cover['data']), 657911);
// delete cover
$cover = $this->epub->Cover('');
$this->assertEquals($cover['mime'],'image/gif');
$this->assertEquals($cover['found'],false);
$this->assertEquals(strlen($cover['data']), 42);
// set new cover (will return a not-found as it's not yet saved)
$cover = $this->epub->Cover('test.jpg','image/jpeg');
$this->assertEquals($cover['mime'],'image/jpeg');
$this->assertEquals($cover['found'],'OPS/php-epub-meta-cover.img');
$this->assertEquals(strlen($cover['data']), 0);
// save
$this->epub->save();
// read now changed cover
$cover = $this->epub->Cover();
$this->assertEquals($cover['mime'],'image/jpeg');
$this->assertEquals($cover['found'],'OPS/php-epub-meta-cover.img');
$this->assertEquals(strlen($cover['data']), filesize('test.jpg'));
}
}

View file

@ -0,0 +1,30 @@
<?php
function to_file($input){
$input = str_replace(' ','_',$input);
$input = str_replace('__','_',$input);
$input = str_replace(',_',',',$input);
$input = str_replace('_,',',',$input);
$input = str_replace('-_','-',$input);
$input = str_replace('_-','-',$input);
$input = str_replace(',','__',$input);
return $input;
}
function book_output($input){
$input = str_replace('__',',',$input);
$input = str_replace('_',' ',$input);
$input = str_replace(',',', ',$input);
$input = str_replace('-',' - ',$input);
list($author,$title) = explode('-',$input,2);
$author = trim($author);
$title = trim($title);
if(!$title){
$title = $author;
$author = '';
}
return '<span class="title">'.htmlspecialchars($title).'</span>'.
'<span class="author">'.htmlspecialchars($author).'</author>';
}

View file

@ -1741,7 +1741,7 @@ dl.bb-dl > dd > li {
.cloud-icon i {
font-size: 32px;
color: #aaa;
color: #888;
margin-top: 8px;
}

View file

@ -83,6 +83,7 @@
<h3>{{$advanced}}</h3>
{{include file="field_input.tpl" field=$imagick_path}}
{{include file="field_checkbox.tpl" field=$thumbnail_security}}
{{include file="field_input.tpl" field=$proxy}}
{{include file="field_input.tpl" field=$proxyuser}}
{{include file="field_input.tpl" field=$timeout}}