merged trunk

This commit is contained in:
Fabian Graßl 2010-10-14 18:19:15 +02:00
commit eadf98bfd0
128 changed files with 27845 additions and 16898 deletions

View File

@ -4,6 +4,85 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.23
date: 2010-10-08
new features:
- title: "Drag and drop to Tag Browser. You can use this to conveniently add tags, set series/publisher etc for a group of books"
- title: "Allow switching of library even when a device is connected"
- title: "Support for the PD Novel running Kobo"
- title: "Move check library integrity from preferences to drop down menu accessed by clicking arrow next to calibre icon"
- title: "Nicer, non-blocking update available notification"
- title: "E-book viewer: If you choose to remeber last used window size, the state of the Table of Contents view is also remembered"
tickets: [7082]
- title: "Allow moving as well as copying of books to another library"
- title: "Apple devices: Add support for plugboards"
- title: "Allow DJVU to be sent to the DR1000"
bug fixes:
- title: "Searching: Fix search expression parser to allow use of escaped double quotes in the search expression"
- title: "When saving cover images don't re-encode the image data unless absolutely neccessary. This prevents information loss due to JPEG re-compression"
- title: "Fix regression that broke setting of metadata for some MOBI/AZW/PRC files"
- title: "Fix regression in last release that could cause download of metadata for multiple files to only download the metadata for a few of them"
tickets: [7071]
- title: "MOBI Output: More tweaking of the margin handling to yield results closer to the input document."
- title: "Device drivers: Fix regression that could cause geenration of invalid metadata.calibre cache files"
- title: "Fix saving to disk with ISBN in filename"
tickets: [7090]
- title: "Fix another regression in the ISBNdb.com metadata download plugin"
- title: "Fix dragging to not interfere with multi-selection. Also dont allow drag and drop from the library to itself"
- title: "CHM input: handle another class of broken CHM files"
tickets: [7058]
new recipes:
- title: "Communications of the Association for Computing Machinery"
author: jonmisurda
- title: "Anand Tech"
author: "Oliver Niesner"
- title: "gsp.ro"
author: "bucsie"
- title: "Il Fatto Quotidiano"
author: "egilh"
- title: "Serbian Literature blog and Rusia Hoy"
author: "Darko Miletic"
- title: "Medscape"
author: "Tony Stegall"
improved recipes:
- The Age
- Australian
- Wiki news
- Times Online
- New Yorker
- Guardian
- Sueddeutsche
- HNA
- Revista Muy Interesante
- version: 0.7.22
date: 2010-10-03

View File

@ -0,0 +1,262 @@
body {
background-color: #fffcf2;
font-family: sans-serif;
margin: 0 0 0 0;
padding: 0 0 0 0;
}
#container {
max-width: 1000px;
min-width: 400px;
min-height: 230px;
background-color: #F6F3E9;
margin-left: auto;
margin-right: auto;
-moz-box-shadow: 5px 5px 5px #ccc;
-webkit-box-shadow: 5px 5px 5px #ccc;
box-shadow: 5px 5px 5px #ccc;
}
#header {
height: 130px;
background: url('/static/logo.png') no-repeat scroll left top #39322b;
}
#content {
max-width: 1000px;
min-width: 400px;
min-height: 100px;
}
#main {
padding-left: 0.5em;
padding-right: 0.5em;
}
#footer {
font-size: small;
color: #a6a399;
text-align: right;
margin-right: 30px;
}
/* Header {{{ */
#header .area {
width: 300px;
height: 130px;
position: relative;
margin-left: 230px;
color: white;
font-size: xx-large;
font-family: monospace;
overflow: hidden;
z-index: 2;
}
#header .bubble {
position: absolute;
left: 93px;
top: 21px;
width: 135px;
height: 84px;
display: table;
}
#header .bubble p {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#header a {
text-decoration: none;
color: white;
cursor: pointer;
white-space: nowrap;
text-shadow: #27211b 2px 2px 2px;
}
#header a:hover {
background-color: #39a9cf;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
text-shadow: #27211b 1px 1px 1px;
-moz-box-shadow: 5px 5px 5px #222;
-webkit-box-shadow: 5px 5px 5px #222;
box-shadow: 5px 5px 5px #222;
}
#nav-container {
position: relative;
height: 130px;
top: -130px;
left: 0%;
}
ul#primary-nav {
display: block;
margin-right: 60px;
text-align: right;
margin-top: 90px;
line-height: 20px;
cursor: default;
position: relative;
top: -2ex;
}
ul#primary-nav li {
display: inline;
padding: 0 4px;
}
ul#primary-nav li a {
padding: 6px;
font-variant: small-caps;
font-weight: bold;
white-space: nowrap;
}
#donate {
display: block;
width: 200px;
height: 38px;
overflow: hidden;
position: relative;
top: -260px;
left: 65%;
}
#calibre-home-link {
position: relative;
top: -298px;
left: 0%;
z-index: 2;
height: 130px;
width: 230px;
cursor: pointer;
}
h2.library_name {
font-family: monospace;
font-size: 50px;
font-weight: normal;
color: white;
margin-left: 250px;
padding-top: 40px;
}
/* }}} */
/* Sort select {{{ */
.sort_select {
float: left;
margin-left: 1em;
margin-top: 2ex;
max-height: 2.75em;
overflow: hidden;
}
.sort_select label {
font-size: medium;
}
/* }}} */
/* Search bar {{{ */
#search_box {
float: right;
margin-right: 1em;
margin-top: 2ex;
max-height: 2.75em;
overflow: hidden;
}
/* }}} */
/* Top level {{{ */
.toplevel ul {
list-style-type: none;
margin: 0;
padding: 0;
}
.toplevel li {
margin: 0.75em;
padding: 0.75em;
text-align: center;
cursor: pointer;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
}
.toplevel li:hover {
background-color: #d6d3c9;
font-weight: bold;
-moz-box-shadow: 5px 5px 5px #ccc;
-webkit-box-shadow: 5px 5px 5px #ccc;
box-shadow: 5px 5px 5px #ccc;
}
.toplevel li span { display: none }
/* }}} */
/* Category {{{ */
.category > div.category-container {
width: 100%;
margin-top: 1ex;
margin-bottom: 1ex;
display: table;
}
.category div.category-item {
display: table-row;
}
.category div.category-item > div {
padding: 0.75em;
text-align: center;
cursor: pointer;
display: table-cell;
}
.category div.category-name { font-weight: bold }
.category div.category-item:hover > div {
background-color: #d6d3c9;
-moz-box-shadow: 5px 5px 5px #ccc;
-webkit-box-shadow: 5px 5px 5px #ccc;
box-shadow: 5px 5px 5px #ccc;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
}
.category div.category-item span.href { display: none }
#groups span.load_href { display: none }
#groups h3 {
font-weight: bold;
margin-top: 1ex;
padding-left: 30px;
padding-top: 0.5ex;
padding-bottom: 0.5ex;
}
#groups h3 span {
font-weight: normal
}
/* }}} */

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>..:: calibre library ::.. {title}</title>
<meta http-equiv="X-UA-Compatible" content="IE=100" />
<link rel="icon" type="image/x-icon" href="http://calibre-ebook.com/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/static/browse/browse.css" />
<link type="text/css" href="/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="/static/jquery.multiselect.css" />
<script type="text/javascript" src="/static/jquery.js"></script>
<script type="text/javascript" src="/static/jquery.corner.js"></script>
<script type="text/javascript"
src="/static/jquery_ui/js/jquery-ui-1.8.5.custom.min.js"></script>
<script type="text/javascript"
src="/static/jquery.multiselect.min.js"></script>
<script type="text/javascript" src="/static/browse/browse.js"></script>
<script type="text/javascript">
var sort_cookie_name = "{sort_cookie_name}";
var sort_select_label = "{sort_select_label}";
$(document).ready(function() {{
init();
{script}
}});
</script>
</head>
<body>
<div id="container">
<!-- Header -->
<div id="header">
<div class="area">
<div class="bubble">
<p><a href="/browse" title="Return to top level"
>&rarr;&nbsp;home&nbsp;&larr;</a></p>
</div>
</div>
<div id="nav-container">&nbsp;
<ul id="primary-nav">
<li><a id="nav-mobile" href="/mobile" title="A version of this website suited for mobile browsers">Mobile</a></li>
<li><a id="nav-demo" href="/old" title="The old version of this webiste">Old</a></li>
<li><a id="nav-download" href="/opds" title="An OPDS feed based version of this website, used in special purpose applications">Feed</a></li>
</ul>
</div>
<form id="donate" action="https://www.paypal.com/cgi-bin/webscr"
method="post" title="Donate to support the development of calibre">
<div>
<input type="hidden" name="cmd" value="_s-xclick"></input>
<input type="hidden" name="hosted_button_id" value="3028915"></input>
<input type="image"
src="http://calibre-ebook.com/site_media//img/button-donate.png"
name="submit"></input>
<img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif"
width="1" height="1"></img>
</div>
</form>
<div id="calibre-home-link" title="Go to the calibre website"></div>
</div>
<!-- End header -->
<div id="content">
<div class="sort_select">
<label>{sort_select_label}</label>
<select id="sort_combobox">
{sort_select_options}
</select>
</div>
<div id="search_box">
<form name="search_form" action="/browse/search" method="get">
<input value="" type="text" title="Search"
class="search_input" />&nbsp;
<input type="submit" value="Search" title="Search" alt="Search" />
</form>
</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div id="main">
{main}
</div>
<div id="footer">
[{library_path}] Created by Kovid Goyal
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,135 @@
// Cookies {{{
function cookie(name, value, options) {
if (typeof value != 'undefined') { // name and value given, set cookie
options = options || {};
if (value === null) {
value = '';
options.expires = -1;
}
var expires = '';
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
var date;
if (typeof options.expires == 'number') {
date = new Date();
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
} else {
date = options.expires;
}
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
}
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
// in the packed version for some reason...
var path = options.path ? '; path=' + (options.path) : '';
var domain = options.domain ? '; domain=' + (options.domain) : '';
var secure = options.secure ? '; secure' : '';
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
} else { // only name given, get cookie
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
};
// }}}
// Sort {{{
function init_sort_combobox() {
$("#sort_combobox").multiselect({
multiple: false,
header: sort_select_label,
noneSelectedText: sort_select_label,
selectedList: 1,
click: function(event, ui){
$(this).multiselect("close");
cookie(sort_cookie_name, ui.value, {expires: 365});
window.location.reload();
}
});
}
// }}}
function init() {
$("#container").corner("30px");
$("#header").corner("30px");
$("#calibre-home-link").click(function() { window.location = "http://calibre-ebook.com"; });
init_sort_combobox();
$("#search_box input:submit").button();
}
// Top-level feed {{{
function toplevel() {
$(".sort_select").hide();
$(".toplevel li").click(function() {
var href = $(this).children("span").html();
window.location = href;
});
}
// }}}
function render_error(msg) {
return '<div class="ui-widget"><div class="ui-state-error ui-corner-all" style="padding: 0pt 0.7em"><p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: 0.3em">&nbsp;</span><strong>Error: </strong>'+msg+"</p></div></div>"
}
// Category feed {{{
function category() {
$(".category .category-item").click(function() {
var href = $(this).find("span.href").html();
window.location = href;
});
$(".category a.navlink").button();
$("#groups").accordion({
collapsible: true,
active: false,
autoHeight: false,
clearStyle: true,
header: "h3",
change: function(event, ui) {
if (ui.newContent) {
var href = ui.newContent.children("span.load_href").html();
ui.newContent.children(".loading").show();
if (href) {
$.ajax({
url:href,
success: function(data) {
this.children(".loaded").html(data);
this.children(".loaded").show();
this.children(".loading").hide();
},
context: ui.newContent,
dataType: "json",
timeout: 600000, //milliseconds (10 minutes)
error: function(xhr, stat, err) {
this.children(".loaded").html(render_error(stat));
this.children(".loaded").show();
this.children(".loading").hide();
}
});
}
}
}
});
}
// }}}

View File

@ -0,0 +1,247 @@
/*!
* jQuery corner plugin: simple corner rounding
* Examples and documentation at: http://jquery.malsup.com/corner/
* version 2.11 (15-JUN-2010)
* Requires jQuery v1.3.2 or later
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
* Authors: Dave Methvin and Mike Alsup
*/
/**
* corner() takes a single string argument: $('#myDiv').corner("effect corners width")
*
* effect: name of the effect to apply, such as round, bevel, notch, bite, etc (default is round).
* corners: one or more of: top, bottom, tr, tl, br, or bl. (default is all corners)
* width: width of the effect; in the case of rounded corners this is the radius.
* specify this value using the px suffix such as 10px (yes, it must be pixels).
*/
;(function($) {
var style = document.createElement('div').style,
moz = style['MozBorderRadius'] !== undefined,
webkit = style['WebkitBorderRadius'] !== undefined,
radius = style['borderRadius'] !== undefined || style['BorderRadius'] !== undefined,
mode = document.documentMode || 0,
noBottomFold = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8),
expr = $.browser.msie && (function() {
var div = document.createElement('div');
try { div.style.setExpression('width','0+0'); div.style.removeExpression('width'); }
catch(e) { return false; }
return true;
})();
$.support = $.support || {};
$.support.borderRadius = moz || webkit || radius; // so you can do: if (!$.support.borderRadius) $('#myDiv').corner();
function sz(el, p) {
return parseInt($.css(el,p))||0;
};
function hex2(s) {
var s = parseInt(s).toString(16);
return ( s.length < 2 ) ? '0'+s : s;
};
function gpc(node) {
while(node) {
var v = $.css(node,'backgroundColor'), rgb;
if (v && v != 'transparent' && v != 'rgba(0, 0, 0, 0)') {
if (v.indexOf('rgb') >= 0) {
rgb = v.match(/\d+/g);
return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
}
return v;
}
if (node.nodeName.toLowerCase() == 'html')
break;
node = node.parentNode; // keep walking if transparent
}
return '#ffffff';
};
function getWidth(fx, i, width) {
switch(fx) {
case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width))));
case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
case 'curl': return Math.round(width*(Math.atan(i)));
case 'tear': return Math.round(width*(Math.cos(i)));
case 'wicked': return Math.round(width*(Math.tan(i)));
case 'long': return Math.round(width*(Math.sqrt(i)));
case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
case 'dogfold':
case 'dog': return (i&1) ? (i+1) : width;
case 'dog2': return (i&2) ? (i+1) : width;
case 'dog3': return (i&3) ? (i+1) : width;
case 'fray': return (i%2)*width;
case 'notch': return width;
case 'bevelfold':
case 'bevel': return i+1;
}
};
$.fn.corner = function(options) {
// in 1.3+ we can fix mistakes with the ready state
if (this.length == 0) {
if (!$.isReady && this.selector) {
var s = this.selector, c = this.context;
$(function() {
$(s,c).corner(options);
});
}
return this;
}
return this.each(function(index){
var $this = $(this),
// meta values override options
o = [$this.attr($.fn.corner.defaults.metaAttr) || '', options || ''].join(' ').toLowerCase(),
keep = /keep/.test(o), // keep borders?
cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]), // corner color
sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]), // strip color
width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10, // corner width
re = /round|bevelfold|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dogfold|dog/,
fx = ((o.match(re)||['round'])[0]),
fold = /dogfold|bevelfold/.test(o),
edges = { T:0, B:1 },
opts = {
TL: /top|tl|left/.test(o), TR: /top|tr|right/.test(o),
BL: /bottom|bl|left/.test(o), BR: /bottom|br|right/.test(o)
},
// vars used in func later
strip, pad, cssHeight, j, bot, d, ds, bw, i, w, e, c, common, $horz;
if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
opts = { TL:1, TR:1, BL:1, BR:1 };
// support native rounding
if ($.fn.corner.defaults.useNative && fx == 'round' && (radius || moz || webkit) && !cc && !sc) {
if (opts.TL)
$this.css(radius ? 'border-top-left-radius' : moz ? '-moz-border-radius-topleft' : '-webkit-border-top-left-radius', width + 'px');
if (opts.TR)
$this.css(radius ? 'border-top-right-radius' : moz ? '-moz-border-radius-topright' : '-webkit-border-top-right-radius', width + 'px');
if (opts.BL)
$this.css(radius ? 'border-bottom-left-radius' : moz ? '-moz-border-radius-bottomleft' : '-webkit-border-bottom-left-radius', width + 'px');
if (opts.BR)
$this.css(radius ? 'border-bottom-right-radius' : moz ? '-moz-border-radius-bottomright' : '-webkit-border-bottom-right-radius', width + 'px');
return;
}
strip = document.createElement('div');
$(strip).css({
overflow: 'hidden',
height: '1px',
minHeight: '1px',
fontSize: '1px',
backgroundColor: sc || 'transparent',
borderStyle: 'solid'
});
pad = {
T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
};
if (typeof this.style.zoom != undefined) this.style.zoom = 1; // force 'hasLayout' in IE
if (!keep) this.style.border = 'none';
strip.style.borderColor = cc || gpc(this.parentNode);
cssHeight = $(this).outerHeight();
for (j in edges) {
bot = edges[j];
// only add stips if needed
if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
d = document.createElement('div');
$(d).addClass('jquery-corner');
ds = d.style;
bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
if (bot && cssHeight != 'auto') {
if ($.css(this,'position') == 'static')
this.style.position = 'relative';
ds.position = 'absolute';
ds.bottom = ds.left = ds.padding = ds.margin = '0';
if (expr)
ds.setExpression('width', 'this.parentNode.offsetWidth');
else
ds.width = '100%';
}
else if (!bot && $.browser.msie) {
if ($.css(this,'position') == 'static')
this.style.position = 'relative';
ds.position = 'absolute';
ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
// fix ie6 problem when blocked element has a border width
if (expr) {
bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"');
}
else
ds.width = '100%';
}
else {
ds.position = 'relative';
ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
(pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
}
for (i=0; i < width; i++) {
w = Math.max(0,getWidth(fx,i, width));
e = strip.cloneNode(false);
e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
}
if (fold && $.support.boxModel) {
if (bot && noBottomFold) continue;
for (c in opts) {
if (!opts[c]) continue;
if (bot && (c == 'TL' || c == 'TR')) continue;
if (!bot && (c == 'BL' || c == 'BR')) continue;
common = { position: 'absolute', border: 'none', margin: 0, padding: 0, overflow: 'hidden', backgroundColor: strip.style.borderColor };
$horz = $('<div/>').css(common).css({ width: width + 'px', height: '1px' });
switch(c) {
case 'TL': $horz.css({ bottom: 0, left: 0 }); break;
case 'TR': $horz.css({ bottom: 0, right: 0 }); break;
case 'BL': $horz.css({ top: 0, left: 0 }); break;
case 'BR': $horz.css({ top: 0, right: 0 }); break;
}
d.appendChild($horz[0]);
var $vert = $('<div/>').css(common).css({ top: 0, bottom: 0, width: '1px', height: width + 'px' });
switch(c) {
case 'TL': $vert.css({ left: width }); break;
case 'TR': $vert.css({ right: width }); break;
case 'BL': $vert.css({ left: width }); break;
case 'BR': $vert.css({ right: width }); break;
}
d.appendChild($vert[0]);
}
}
}
}
});
};
$.fn.uncorner = function() {
if (radius || moz || webkit)
this.css(radius ? 'border-radius' : moz ? '-moz-border-radius' : '-webkit-border-radius', 0);
$('div.jquery-corner', this).remove();
return this;
};
// expose options
$.fn.corner.defaults = {
useNative: true, // true if plugin should attempt to use native browser support for border radius rounding
metaAttr: 'data-corner' // name of meta attribute to use for options
};
})(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
.ui-multiselect { padding:2px 0 2px 4px; text-align:left }
.ui-multiselect span.ui-icon { float:right }
.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px }
.ui-multiselect-header ul { font-size:0.9em }
.ui-multiselect-header ul li { float:left; padding:0 10px 0 0 }
.ui-multiselect-header a { text-decoration:none }
.ui-multiselect-header a:hover { text-decoration:underline }
.ui-multiselect-header span.ui-icon { float:left }
.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0 }
.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000 }
.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll }
.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px }
.ui-multiselect-checkboxes label input { position:relative; top:1px }
.ui-multiselect-checkboxes li { clear:both; font-size:0.9em; padding-right:3px }
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid }
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none }
/* remove label borders in IE6 because IE6 does not support transparency */
* html .ui-multiselect-checkboxes label { border:none }

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 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,350 @@
/*
* jQuery UI CSS Framework @VERSION
*
* 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/Theming/API
*/
/* Layout helpers
----------------------------------*/
.ui-helper-hidden { display: none; }
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
.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:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
.ui-helper-clearfix { display: inline-block; }
/* required comment for clearfix to work in Opera \*/
* html .ui-helper-clearfix { height:1%; }
.ui-helper-clearfix { display:block; }
/* end clearfix */
.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 @VERSION
*
* 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/Theming/API
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1em&cornerRadius=6px&bgColorHeader=cb842e&bgTextureHeader=02_glass.png&bgImgOpacityHeader=25&borderColorHeader=d49768&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=f4f0ec&bgTextureContent=05_inset_soft.png&bgImgOpacityContent=100&borderColorContent=e0cfc2&fcContent=1e1b1d&iconColorContent=c47a23&bgColorDefault=ede4d4&bgTextureDefault=02_glass.png&bgImgOpacityDefault=70&borderColorDefault=cdc3b7&fcDefault=3f3731&iconColorDefault=f08000&bgColorHover=f5f0e5&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=f5ad66&fcHover=a46313&iconColorHover=f08000&bgColorActive=f4f0ec&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=e0cfc2&fcActive=b85700&iconColorActive=f35f07&bgColorHighlight=f5f5b5&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=75&borderColorHighlight=d9bb73&fcHighlight=060200&iconColorHighlight=cb672b&bgColorError=fee4bd&bgTextureError=04_highlight_hard.png&bgImgOpacityError=65&borderColorError=f8893f&fcError=592003&iconColorError=ff7519&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=75&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=75&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 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 #e0cfc2; background: #f4f0ec url(images/ui-bg_inset-soft_100_f4f0ec_1x100.png) 50% bottom repeat-x; color: #1e1b1d; }
.ui-widget-content a { color: #1e1b1d; }
.ui-widget-header { border: 1px solid #d49768; background: #cb842e url(images/ui-bg_glass_25_cb842e_1x400.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
.ui-widget-header a { color: #ffffff; }
/* Interaction states
----------------------------------*/
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cdc3b7; background: #ede4d4 url(images/ui-bg_glass_70_ede4d4_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #3f3731; }
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #3f3731; 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 #f5ad66; background: #f5f0e5 url(images/ui-bg_glass_100_f5f0e5_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #a46313; }
.ui-state-hover a, .ui-state-hover a:hover { color: #a46313; text-decoration: none; }
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #e0cfc2; background: #f4f0ec url(images/ui-bg_highlight-hard_100_f4f0ec_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #b85700; }
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #b85700; 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 #d9bb73; background: #f5f5b5 url(images/ui-bg_highlight-hard_75_f5f5b5_1x100.png) 50% top repeat-x; color: #060200; }
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #060200; }
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #f8893f; background: #fee4bd url(images/ui-bg_highlight-hard_65_fee4bd_1x100.png) 50% top repeat-x; color: #592003; }
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #592003; }
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #592003; }
.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_c47a23_256x240.png); }
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_c47a23_256x240.png); }
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
.ui-state-default .ui-icon { background-image: url(images/ui-icons_f08000_256x240.png); }
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_f08000_256x240.png); }
.ui-state-active .ui-icon {background-image: url(images/ui-icons_f35f07_256x240.png); }
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_cb672b_256x240.png); }
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ff7519_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-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; }
.ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; }
.ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
.ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
.ui-corner-top { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; }
.ui-corner-bottom { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
.ui-corner-right { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; }
.ui-corner-left { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; }
.ui-corner-all { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; }
/* Overlays */
.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_75_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_75_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
* jQuery UI Accordion @VERSION
*
* 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/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 Button @VERSION
*
* 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/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: 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 */

View File

@ -0,0 +1,340 @@
/*!
* jQuery UI 1.8.5
*
* 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
*/
(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.5",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,
NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,
"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");
if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"));if(!isNaN(b)&&b!=0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind("mousedown.ui-disableSelection selectstart.ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f,
"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c.style(this,h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c.style(this,
h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}});
c(function(){var a=document.createElement("div"),b=document.body;c.extend(a.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.appendChild(a).offsetHeight===100;b.removeChild(a).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,
d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
;/*!
* jQuery UI Widget 1.8.5
*
* 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/Widget
*/
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.substring(0,1)===
"_")return h;e?this.each(function(){var g=b.data(this,a);if(!g)throw"cannot call methods on "+a+" prior to initialization; attempted to call method '"+d+"'";if(!b.isFunction(g[d]))throw"no such method '"+d+"' for "+a+" widget instance";var i=g[d].apply(g,f);if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",
widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,b.metadata&&b.metadata.get(c)[this.widgetName],a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._init()},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a,e=this;if(arguments.length===0)return b.extend({},e.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}b.each(d,function(f,h){e._setOption(f,h)});return e},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},enable:function(){return this._setOption("disabled",
false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
;/*!
* jQuery UI Mouse 1.8.5
*
* 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/Mouse
*
* Depends:
* jquery.ui.widget.js
*/
(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&
this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();
return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&
this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-
a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
;/*
* jQuery UI Position 1.8.5
*
* 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/Position
*/
(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.scrollTo&&d.document){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j=
{top:b.of.pageY,left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/
2;if(b.at[1]==="bottom")j.top+=k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+parseInt(c.curCSS(this,"marginRight",true))||0,w=m+q+parseInt(c.curCSS(this,"marginBottom",true))||0,i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]===
"center")i.top-=m/2;i.left=parseInt(i.left);i.top=parseInt(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();
b.left=d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];
b.left+=a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=
c(b),g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
;/*
* jQuery UI Accordion 1.8.5
*
* 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/Accordion
*
* Depends:
* jquery.ui.core.js
* jquery.ui.widget.js
*/
(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var f=d.closest(".ui-accordion-header");a.active=f.length?f:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",function(g){return a._keydown(g)}).next().attr("role",
"tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(g){a._clickHandler.call(a,g,this);g.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);
this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");
this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,f=this.headers.index(a.target),g=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:g=this.headers[(f+1)%d];break;case b.LEFT:case b.UP:g=this.headers[(f-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
a.preventDefault()}if(g){c(a.target).attr("tabIndex",-1);c(g).attr("tabIndex",0);g.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);
a.next().addClass("ui-accordion-content-active")}h=a.next();f=this.active.next();g={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):h,oldContent:f};d=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(h,f,g,b,d)}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
this.active.next().addClass("ui-accordion-content-active");var f=this.active.next(),g={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:f},h=this.active=c([]);this._toggle(h,f,g)}},_toggle:function(a,b,d,f,g){var h=this,e=h.options;h.toShow=a;h.toHide=b;h.data=d;var j=function(){if(h)return h._completed.apply(h,arguments)};h._trigger("changestart",null,h.data);h.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&f?{toShow:c([]),toHide:b,complete:j,
down:g,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:g,autoHeight:e.autoHeight||e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;f=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!f[k]&&!c.easing[k])k="slide";f[k]||(f[k]=function(l){this.slide(l,{easing:k,duration:i||700})});
f[k](d)}else{if(e.collapsible&&f)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.5",animations:{slide:function(a,
b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),f=0,g={},h={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){h[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);g[i]={value:j[1],
unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(h,{step:function(j,i){if(i.prop=="height")f=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=f*g[i.prop].value+g[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",paddingTop:"hide",
paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
;/*
* jQuery UI Button 1.8.5
*
* 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/Button
*
* Depends:
* jquery.ui.core.js
* jquery.ui.widget.js
*/
(function(a){var g,i=function(b){a(":ui-button",b.target.form).each(function(){var c=a(this).data("button");setTimeout(function(){c.refresh()},1)})},h=function(b){var c=b.name,d=b.form,e=a([]);if(c)e=d?a(d).find("[name='"+c+"']"):a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form});return e};a.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",
i);if(typeof this.options.disabled!=="boolean")this.options.disabled=this.element.attr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var b=this,c=this.options,d=this.type==="checkbox"||this.type==="radio",e="ui-state-hover"+(!d?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",
function(){if(!c.disabled){a(this).addClass("ui-state-hover");this===g&&a(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||a(this).removeClass(e)}).bind("focus.button",function(){a(this).addClass("ui-state-focus")}).bind("blur.button",function(){a(this).removeClass("ui-state-focus")});d&&this.element.bind("change.button",function(){b.refresh()});if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).toggleClass("ui-state-active");
b.buttonElement.attr("aria-pressed",b.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active");b.buttonElement.attr("aria-pressed",true);var f=b.element[0];h(f).not(f).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed",false)});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;a(this).addClass("ui-state-active");
g=this;a(document).one("mouseup",function(){g=null})}).bind("mouseup.button",function(){if(c.disabled)return false;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(f){if(c.disabled)return false;if(f.keyCode==a.ui.keyCode.SPACE||f.keyCode==a.ui.keyCode.ENTER)a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(f){f.keyCode===a.ui.keyCode.SPACE&&a(this).click()})}this._setOption("disabled",
c.disabled)},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type==="radio"){this.buttonElement=this.element.parents().last().find("label[for="+this.element.attr("id")+"]");this.element.addClass("ui-helper-hidden-accessible");var b=this.element.is(":checked");b&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",b)}else this.buttonElement=
this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||
this.buttonElement.removeAttr("title");a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);if(b==="disabled")c?this.element.attr("disabled",true):this.element.removeAttr("disabled");this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b);if(this.type==="radio")h(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
true):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed",false)});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed",true):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed",false)},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
c=a("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary;if(d.primary||d.secondary){b.addClass("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>");d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>");if(!this.options.text){b.addClass(e?"ui-button-icons-only":"ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary");
this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}});a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":visible").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end().end()},
destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery);
;/*
* jQuery UI Effects 1.8.5
*
* 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/Effects/
*/
jQuery.effects||function(f,j){function l(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return m.transparent;return m[f.trim(c).toLowerCase()]}function r(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return l(b)}function n(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function o(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in s||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function t(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:f.fx.speeds[b]||f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=r(b.elem,a);b.end=l(b.end);b.colorInit=
true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var m={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,
183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,
165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},p=["add","remove","toggle"],s={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,d){if(f.isFunction(b)){d=b;b=null}return this.each(function(){var e=f(this),g=e.attr("style")||" ",h=o(n.call(this)),q,u=e.attr("className");f.each(p,function(v,
i){c[i]&&e[i+"Class"](c[i])});q=o(n.call(this));e.attr("className",u);e.animate(t(h,q),a,b,function(){f.each(p,function(v,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)})})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?
f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.5",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==
null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();
var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});
c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c||
typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c])return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||!f.effects[c]||typeof c==
"boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,
a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=
e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+
b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/
2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*
a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;
return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,
a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
;/*
* jQuery UI Effects Blind 1.8.5
*
* 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/Effects/Blind
*
* Depends:
* jquery.effects.core.js
*/
(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","left"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,g);b.effects.removeWrapper(a);
c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
;/*
* jQuery UI Effects Bounce 1.8.5
*
* 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/Effects/Bounce
*
* Depends:
* jquery.effects.core.js
*/
(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","left"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
;/*
* jQuery UI Effects Clip 1.8.5
*
* 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/Effects/Clip
*
* Depends:
* jquery.effects.core.js
*/
(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","left","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,c/2)}var h={};h[g.size]=
f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Drop 1.8.5
*
* 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/Effects/Drop
*
* Depends:
* jquery.effects.core.js
*/
(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","left","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e=="show"?1:
0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Explode 1.8.5
*
* 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/Effects/Explode
*
* Depends:
* jquery.effects.core.js
*/
(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
;/*
* jQuery UI Effects Fade 1.8.5
*
* 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/Effects/Fade
*
* Depends:
* jquery.effects.core.js
*/
(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Fold 1.8.5
*
* 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/Effects/Fold
*
* Depends:
* jquery.effects.core.js
*/
(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100*
f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
;/*
* jQuery UI Effects Highlight 1.8.5
*
* 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/Effects/Highlight
*
* Depends:
* jquery.effects.core.js
*/
(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Pulsate 1.8.5
*
* 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/Effects/Pulsate
*
* Depends:
* jquery.effects.core.js
*/
(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
;/*
* jQuery UI Effects Scale 1.8.5
*
* 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/Effects/Scale
*
* Depends:
* jquery.effects.core.js
*/
(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","left","width","height","overflow","opacity"],g=["position","top","left","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=c.effects.setMode(a,
b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};if(m=="box"||m=="both"){if(d.from.y!=
d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);a.css("overflow","hidden").css(a.from);
if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);child.to=c.effects.setTransition(child,
f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,n?e:g);c.effects.removeWrapper(a);b.callback&&
b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Shake 1.8.5
*
* 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/Effects/Shake
*
* Depends:
* jquery.effects.core.js
*/
(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","left"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=(h=="pos"?"-=":"+=")+
e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
;/*
* jQuery UI Effects Slide 1.8.5
*
* 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/Effects/Slide
*
* Depends:
* jquery.effects.core.js
*/
(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","left"],e=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(e=="show")a.css(f,b=="pos"?-g:g);var i={};i[f]=(e=="show"?b=="pos"?
"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;/*
* jQuery UI Effects Transfer 1.8.5
*
* 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/Effects/Transfer
*
* Depends:
* jquery.effects.core.js
*/
(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
b.dequeue()})})}})(jQuery);
;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -13,6 +13,8 @@
font-size: 1.25em;
border: 1px solid black;
text-color: black;
text-decoration: none;
margin-right: 0.5em;
background-color: #ddd;
border-top: 1px solid ThreeDLightShadow;
border-right: 1px solid ButtonShadow;
@ -70,6 +72,7 @@ div.navigation {
padding-right: 0em;
overflow: hidden;
text-align: center;
text-decoration: none;
}
#logo {

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,7 +1,5 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.business-standard.com
'''
@ -28,30 +26,22 @@ class BusinessStandard(BasicNewsRecipe):
,'publisher' : publisher
,'linearize_tables': True
}
remove_attributes=['style']
remove_tags = [dict(name=['object','link','script','iframe'])]
keep_only_tags=[dict(attrs={'class':'TableClas'})]
remove_tags = [
dict(name=['object','link','script','iframe','base','meta'])
,dict(attrs={'class':'rightDiv2'})
,dict(name='table',attrs={'width':'450px'})
]
remove_attributes=['width','height']
feeds = [
(u'News Now' , u'http://feeds.business-standard.com/News-Now.xml' )
,(u'Banking & finance' , u'http://feeds.business-standard.com/Banking-Finance-All.xml' )
,(u'Companies & Industry', u'http://feeds.business-standard.com/Companies-Industry-All.xml')
,(u'Economy & Policy' , u'http://feeds.business-standard.com/Economy-Policy-All.xml' )
,(u'Tech World' , u'http://feeds.business-standard.com/Tech-World-All.xml' )
,(u'Life & Leisure' , u'http://feeds.business-standard.com/Life-Leisure-All.xml' )
,(u'Markets & Investing' , u'http://feeds.business-standard.com/Markets-Investing-All.xml' )
,(u'Management & Mktg' , u'http://feeds.business-standard.com/Management-Mktg-All.xml' )
,(u'Automobiles' , u'http://feeds.business-standard.com/Automobiles.xml' )
,(u'Aviation' , u'http://feeds.business-standard.com/Aviation.xml' )
(u'News Now' , u'http://feeds.business-standard.com/rss/online.xml')
,(u'Banking & finance' , u'http://feeds.business-standard.com/rss/3_0.xml' )
,(u'Companies & Industry', u'http://feeds.business-standard.com/rss/2_0.xml' )
,(u'Economy & Policy' , u'http://feeds.business-standard.com/rss/4_0.xml' )
,(u'Tech World' , u'http://feeds.business-standard.com/rss/8_0.xml' )
,(u'Life & Leisure' , u'http://feeds.business-standard.com/rss/6_0.xml' )
,(u'Markets & Investing' , u'http://feeds.business-standard.com/rss/1_0.xml' )
,(u'Management & Mktg' , u'http://feeds.business-standard.com/rss/7_0.xml' )
,(u'Opinion' , u'http://feeds.business-standard.com/rss/5_0.xml' )
]
def print_version(self, url):
autono = url.rpartition('autono=')[2]
tp = 'on'
hk = url.rpartition('bKeyFlag=')[1]
if hk == '':
tp = ''
return 'http://www.business-standard.com/india/printpage.php?autono=' + autono + '&tp=' + tp
def get_article_url(self, article):
return article.get('guid', None)

View File

@ -0,0 +1,86 @@
from calibre.web.feeds.recipes import BasicNewsRecipe
class RevistaElCultural(BasicNewsRecipe):
title = 'Revista El Cultural'
__author__ = 'Jefferson Frantz'
description = 'Revista de cultura'
timefmt = ' [%d %b, %Y]'
language = 'es'
no_stylesheets = True
remove_javascript = True
extra_css = 'h1{ font-family: sans-serif; font-size: large; font-weight: bolder; text-align: justify } h2{ font-family: sans-serif; font-size: small; font-weight: 500; text-align: justify } h3{ font-family: sans-serif; font-size: small; font-weight: 500; text-align: justify } h4{ font-family: sans-serif; font-weight: lighter; font-size: medium; font-style: italic; text-align: justify } .rtsArticuloFirma{ font-family: sans-serif; font-size: small; text-align: justify } .column span-13 last{ font-family: sans-serif; font-size: medium; text-align: justify } .rtsImgArticulo{font-family: serif; font-size: small; color: #000000; text-align: justify}'
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup
keep_only_tags = [dict(name='div', attrs={'class':['column span-13 last']}),dict(name='div', attrs={'class':['rtsImgArticulo']})]
remove_tags = [
dict(name=['object','link','script','ul'])
,dict(name='div', attrs={'class':['rtsRating']})
]
#TO GET ARTICLES IN SECTION
def ec_parse_section(self, url, titleSection):
print 'Section: '+ titleSection
soup = self.index_to_soup(url)
div = soup.find(attrs={'id':'gallery'})
current_articles = []
for a in div.findAllNext('a', href=True):
if a is None:
continue
title = self.tag_to_string(a)
url = a.get('href', False)
if not url or not title:
continue
if not url.startswith('/version_papel/'+titleSection+'/'):
if len(current_articles) > 0 and not url.startswith('/secciones/'):
break
continue
if url.startswith('/version_papel/'+titleSection+'/'):
url = 'http://www.elcultural.es'+url
self.log('\t\tFound article:', title[0:title.find("|")-1])
self.log('\t\t\t', url)
current_articles.append({'title': title[0:title.find("|")-1], 'url':url,
'description':'', 'date':''})
return current_articles
# To GET SECTIONS
def parse_index(self):
feeds = []
for title, url in [
('LETRAS',
'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
('ARTE',
'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
('CINE',
'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
('CIENCIA',
'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
## ('OPINION',
## 'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
('ESCENARIOS',
'http://www.elcultural.es/pdf_sumario/cultural/Sumario_El_Cultural_en_PDF'),
]:
articles = self.ec_parse_section(url,title)
if articles:
feeds.append((title, articles))
return feeds

View File

@ -1,7 +1,5 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
'''
emol.com
'''
@ -19,43 +17,34 @@ class ElMercurio(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
cover_url = 'http://www.emol.com/especiales/logo_emol/logo_emol.gif'
masthead_url = 'http://www.emol.com/especiales/logo_emol/logo_emol.gif'
remove_javascript = True
use_embedded_content = False
language = 'es'
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [
dict(name='div', attrs={'class':'despliegue-txt_750px'})
,dict(name='div', attrs={'id':'div_cuerpo_participa'})
]
remove_tags = [
dict(name='div', attrs={'class':'contenedor_despliegue-col-left300'})
,dict(name='div', attrs={'id':['div_centro_dn_opc','div_cabezera','div_secciones','div_contenidos','div_pie','nav']})
]
keep_only_tags = [dict(name='div', attrs={'id':['cont_iz_titulobajada','cont_iz_creditos_1_a','cont_iz_cuerpo']})]
remove_tags = [dict(name='div', attrs={'id':'cont_iz_cuerpo_relacionados'})]
remove_attributes = ['height','width']
feeds = [
(u'Noticias de ultima hora', u'http://www.emol.com/rss20/rss.asp?canal=0')
,(u'Nacional', u'http://www.emol.com/rss20/rss.asp?canal=1')
,(u'Mundo', u'http://www.emol.com/rss20/rss.asp?canal=2')
,(u'Deportes', u'http://www.emol.com/rss20/rss.asp?canal=4')
,(u'Magazine', u'http://www.emol.com/rss20/rss.asp?canal=6')
,(u'Tecnologia', u'http://www.emol.com/rss20/rss.asp?canal=5')
,(u'La Musica', u'http://www.emol.com/rss20/rss.asp?canal=7')
(u'Noticias de ultima hora', u'http://rss.emol.com/rss.asp?canal=0')
,(u'Nacional', u'http://rss.emol.com/rss.asp?canal=1')
,(u'Mundo', u'http://rss.emol.com/rss.asp?canal=2')
,(u'Deportes', u'http://rss.emol.com/rss.asp?canal=4')
,(u'Magazine', u'http://rss.emol.com/rss.asp?canal=6')
,(u'Tecnologia', u'http://rss.emol.com/rss.asp?canal=5')
]
def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Language" content="es-CL"/>'
soup.head.insert(0,mtag)
for item in soup.findAll(style=True):
del item['style']
return soup
language = 'es'

View File

@ -0,0 +1,74 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
ft.com
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class FinancialTimes(BasicNewsRecipe):
title = u'Financial Times - UK printed edition'
__author__ = 'Darko Miletic'
description = 'Financial world news'
oldest_article = 2
language = 'en_GB'
max_articles_per_feed = 250
no_stylesheets = True
use_embedded_content = False
needs_subscription = True
encoding = 'utf8'
simultaneous_downloads= 1
delay = 1
LOGIN = 'https://registration.ft.com/registration/barrier/login'
INDEX = 'http://www.ft.com/uk-edition'
PREFIX = 'http://www.ft.com'
def get_browser(self):
br = BasicNewsRecipe.get_browser()
if self.username is not None and self.password is not None:
br.open(self.LOGIN)
br.select_form(name='loginForm')
br['username'] = self.username
br['password'] = self.password
br.submit()
return br
keep_only_tags = [ dict(name='div', attrs={'id':'cont'}) ]
remove_tags_after = dict(name='p', attrs={'class':'copyright'})
remove_tags = [
dict(name='div', attrs={'id':'floating-con'})
,dict(name=['meta','iframe','base','object','embed','link'])
]
remove_attributes = ['width','height','lang']
extra_css = """
body{font-family:Arial,Helvetica,sans-serif;}
h2{font-size:large;}
.ft-story-header{font-size:xx-small;}
.ft-story-body{font-size:small;}
a{color:#003399;}
.container{font-size:x-small;}
h3{font-size:x-small;color:#003399;}
.copyright{font-size: x-small}
"""
def parse_index(self):
articles = []
soup = self.index_to_soup(self.INDEX)
wide = soup.find('div',attrs={'class':'wide'})
if wide:
for item in wide.findAll('a',href=True):
url = self.PREFIX + item['href']
title = self.tag_to_string(item)
date = strftime(self.timefmt)
articles.append({
'title' :title
,'date' :date
,'url' :url
,'description':''
})
return [('FT UK edition',articles)]
def preprocess_html(self, soup):
return self.adeify_images(soup)

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
'''
frazpc.pl
'''
from calibre.web.feeds.news import BasicNewsRecipe
import re
class FrazPC(BasicNewsRecipe):
title = u'frazpc.pl'
publisher = u'frazpc.pl'
description = u'Tw\xf3j Vortal Technologiczny'
language = 'pl'
__author__ = u'Tomasz D\u0142ugosz'
oldest_article = 7
max_articles_per_feed = 100
use_embedded_content = False
no_stylesheets = True
feeds = [(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed'), (u'Recenzje', u'http://www.frazpc.pl/kat/recenzje-2/feed') ]
keep_only_tags = [dict(name='div', attrs={'id':'FRAZ_CONTENT'})]
remove_tags = [dict(name='p', attrs={'class':'gray tagsP fs11'})]
preprocess_regexps = [
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
[(r'<div id="post-[0-9]*"', lambda match: '<div id="FRAZ_CONTENT"'),
(r'href="/f/news/', lambda match: 'href="http://www.frazpc.pl/f/news/'),
(r' &nbsp; <a href="http://www.frazpc.pl/[^>]*?">(Skomentuj|Komentarz(e)?\([0-9]*\))</a>&nbsp; \|', lambda match: '')]
]
remove_attributes = [ 'width', 'height' ]

View File

@ -11,8 +11,8 @@ import mechanize
class GoComics(BasicNewsRecipe):
title = 'GoComics'
__author__ = 'Starson17'
__version__ = '1.02'
__date__ = '14 August 2010'
__version__ = '1.03'
__date__ = '09 October 2010'
description = u'200+ Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.'
category = 'news, comics'
language = 'en'
@ -273,6 +273,7 @@ class GoComics(BasicNewsRecipe):
# ("Wit of the World","http://www.gocomics.com/witoftheworld"),
# ("Don Wright","http://www.gocomics.com/donwright"),
]:
print 'Working on: ', title
articles = self.make_links(url)
if articles:
feeds.append((title, articles))
@ -286,28 +287,30 @@ class GoComics(BasicNewsRecipe):
page_soup = self.index_to_soup(url)
if page_soup:
try:
strip_title = page_soup.h1.a.string
strip_title = page_soup.find(name='div', attrs={'class':'top'}).h1.a.string
except:
strip_title = 'Error - no page_soup.h1.a.string'
strip_title = 'Error - no Title found'
try:
date_title = page_soup.find('ul', attrs={'class': 'feature-nav'}).li.string
if not date_title:
date_title = page_soup.find('ul', attrs={'class': 'feature-nav'}).li.string
except:
date_title = 'Error - no page_soup.h1.li.string'
date_title = 'Error - no Date found'
title = strip_title + ' - ' + date_title
for i in range(2):
try:
strip_url_date = page_soup.h1.a['href']
strip_url_date = page_soup.find(name='div', attrs={'class':'top'}).h1.a['href']
break #success - this is normal exit
except:
strip_url_date = None
continue #try to get strip_url_date again
continue # give up on this strip date
for i in range(2):
try:
prev_strip_url_date = page_soup.find('a', attrs={'class': 'prev'})['href']
break #success - this is normal exit
except:
prev_strip_url_date = None
continue #try to get prev_strip_url_date again
continue # give up on this prev strip date
if strip_url_date:
page_url = 'http://www.gocomics.com' + strip_url_date
else:

View File

@ -38,7 +38,7 @@ class Guardian(BasicNewsRecipe):
dict(name='div', attrs={'id':["article-toolbox","subscribe-feeds",]}),
dict(name='ul', attrs={'class':["pagination"]}),
dict(name='ul', attrs={'id':["content-actions"]}),
dict(name='img'),
#dict(name='img'),
]
use_embedded_content = False

View File

@ -0,0 +1,64 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'Tony Stegall'
__copyright__ = '2010, Tony Stegall or Tonythebookworm on mobileread.com'
__version__ = '1'
__date__ = '01, October 2010'
__docformat__ = 'English'
from calibre.web.feeds.recipes import BasicNewsRecipe
class MedScrape(BasicNewsRecipe):
title = 'MedScape'
__author__ = 'Tony Stegall'
description = 'Nursing News'
language = 'en'
timefmt = ' [%a, %d %b, %Y]'
needs_subscription = True
masthead_url = 'http://images.medscape.com/pi/global/header/sp/bg-sp-medscape.gif'
no_stylesheets = True
remove_javascript = True
conversion_options = {'linearize_tables' : True}
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
p.authors{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
p.postingdate{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
h2{text-align:right; font-size:small;margin-top:0px;margin-bottom: 0px;}
p{font-family:Helvetica,Arial,sans-serif;font-size:small;}
'''
remove_tags = [dict(name='div', attrs={'class':['closewindow2']}),
dict(name='div', attrs={'id': ['basicheaderlinks']})
]
def get_browser(self):
br = BasicNewsRecipe.get_browser()
if self.username is not None and self.password is not None:
br.open('https://profreg.medscape.com/px/getlogin.do')
br.select_form(name='LoginForm')
br['userId'] = self.username
br['password'] = self.password
br.submit()
return br
feeds = [
('MedInfo', 'http://www.medscape.com/cx/rssfeeds/2685.xml'),
]
def print_version(self,url):
#the original url is: http://www.medscape.com/viewarticle/728955?src=rss
#the print url is: http://www.medscape.com/viewarticle/728955_print
print_url = url.partition('?')[0] +'_print'
#print 'the printable version is: ',print_url
return print_url
def preprocess_html(self, soup):
for item in soup.findAll(attrs={'style':True}):
del item['style']
return soup

View File

@ -1,6 +1,6 @@
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
'''
miamiherald.com
'''
@ -15,13 +15,11 @@ class TheMiamiHerald(BasicNewsRecipe):
max_articles_per_feed = 100
publisher = u'The Miami Herald'
category = u'miami herald, weather, dolphins, news, miami news, local news, miamiherald, miami newspaper, miamiherald.com, miami, the miami herald, broward, miami-dade'
language = 'en'
language = 'en'
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
remove_javascript = True
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-size:large; color:#1A272F; }
.subheadline{font-family:Arial,Helvetica,sans-serif; font-size:30%; color: #666666;}
@ -33,50 +31,35 @@ class TheMiamiHerald(BasicNewsRecipe):
.imageCaption{font-family:Arial,Helvetica,sans-serif; font-size:30%; color:#666666; }
'''
keep_only_tags = [dict(name='div', attrs={'id':['storyBody','storyPhotoContentArea']}),
]
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
remove_tags = [dict(name=['object','link','embed']),
dict(name='div', attrs={'class':["imageBuyButton","shareLinksArea","storyTools","spill_navigation pagination","circPromoArea","storyTools_footer","storyYahooContentMatch"]}) ,
dict(name='div', attrs={'id':["pluck","mlt","storyAssets"]}) ]
keep_only_tags = [dict(name='div', attrs={'id':'wide'}),]
remove_tags = [dict(name=['object','link','embed','iframe','meta'])]
feeds = [
(u'Breaking News' , u'http://www.miamiherald.com/416/index.xml' )
,(u'Miami-Dade' , u'http://www.miamiherald.com/460/index.xml' )
,(u'Broward' , u'http://www.miamiherald.com/467/index.xml' )
,(u'Florida Keys' , u'http://www.miamiherald.com/505/index.xml' )
,(u'Florida' , u'http://www.miamiherald.com/569/index.xml' )
,(u'Nation' , u'http://www.miamiherald.com/509/index.xml' )
,(u'World' , u'http://www.miamiherald.com/578/index.xml' )
,(u'Americas' , u'http://www.miamiherald.com/579/index.xml' )
,(u'Cuba' , u'http://www.miamiherald.com/581/index.xml' )
,(u'Haiti' , u'http://www.miamiherald.com/582/index.xml' )
,(u'Politics' , u'http://www.miamiherald.com/515/index.xml' )
,(u'Education' , u'http://www.miamiherald.com/295/index.xml' )
,(u'Environment' , u'http://www.miamiherald.com/573/index.xml' )
(u'Breaking News' , u'http://www.miamiherald.com/news/breaking-news/index.xml' )
,(u'Miami-Dade' , u'http://www.miamiherald.com/news/miami-dade/index.xml' )
,(u'Broward' , u'http://www.miamiherald.com/news/broward/index.xml' )
,(u'Florida Keys' , u'http://www.miamiherald.com/news/florida-keys/index.xml' )
,(u'Florida' , u'http://www.miamiherald.com/news/florida/index.xml' )
,(u'Nation' , u'http://www.miamiherald.com/news/nation/index.xml' )
,(u'World' , u'http://www.miamiherald.com/news/world/index.xml' )
,(u'Americas' , u'http://www.miamiherald.com/news/americas/index.xml' )
,(u'Cuba' , u'http://www.miamiherald.com/news/americas/cuba/index.xml' )
,(u'Haiti' , u'http://www.miamiherald.com/news/americas/haiti/index.xml' )
,(u'Politics' , u'http://www.miamiherald.com/news/politics/index.xml' )
,(u'Education' , u'http://www.miamiherald.com/news/education/index.xml' )
,(u'Environment' , u'http://www.miamiherald.com/news/environment/index.xml' )
]
def get_article_url(self, article):
ans = article.get('guid', None)
print ans
try:
self.log('Looking for full story link in', ans)
soup = self.index_to_soup(ans)
x = soup.find(text="Full Story")
if x is not None:
a = x.parent
if a and a.has_key('href'):
ans = 'http://www.miamiherald.com'+a['href']
self.log('Found full story link', ans)
except:
pass
return ans
def print_version(self, url):
art, sep, rest = url.rpartition('/')
art2, sep2, rest2 = art.rpartition('/')
return art2 + '/v-print/' + rest2 + '/' + rest

View File

@ -33,10 +33,14 @@ class NewYorker(BasicNewsRecipe):
, 'language' : language
}
keep_only_tags = [dict(name='div', attrs={'id':['articleheads','articleRail','articletext','photocredits']})]
keep_only_tags = [
dict(name='div', attrs={'class':'headers'})
,dict(name='div', attrs={'id':['articleheads','items-container','articleRail','articletext','photocredits']})
]
remove_tags = [
dict(name=['meta','iframe','base','link','embed','object'])
,dict(name='div', attrs={'class':['utils','articleRailLinks','icons'] })
,dict(attrs={'class':['utils','articleRailLinks','icons'] })
,dict(attrs={'id':['show-header','show-footer'] })
]
remove_attributes = ['lang']
feeds = [(u'The New Yorker', u'http://feeds.newyorker.com/services/rss/feeds/everything.xml')]

View File

@ -0,0 +1,46 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
nightfliersbookspace.blogspot.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class NightfliersBookspace(BasicNewsRecipe):
title = "Nightflier's Bookspace"
__author__ = 'Darko Miletic'
description = 'SF, Fantasy, Books, Knjige'
oldest_article = 35
max_articles_per_feed = 100
language = 'sr'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = True
publication_type = 'blog'
cover_url = ''
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: "Trebuchet MS",Trebuchet,Verdana,sans1,sans-serif}
.article_description{font-family: sans1, sans-serif}
img{margin-bottom: 0.8em; border: 1px solid #333333; padding: 4px }
"""
conversion_options = {
'comment' : description
, 'tags' : 'SF, fantasy, prevod, blog, Srbija'
, 'publisher': 'Ivan Jovanovic'
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [(u'Posts', u'http://nightfliersbookspace.blogspot.com/feeds/posts/default')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)

View File

@ -0,0 +1,18 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1286819935(BasicNewsRecipe):
title = u'Novaya Gazeta'
__author__ = 'muwa'
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
conversion_options = {'linearize_tables' : True}
remove_attributes = ['style']
language = 'ru'
feeds = [(u'Articles', u'http://www.novayagazeta.ru/rss_number.xml')]
def print_version(self, url):
return url + '?print=true'

View File

@ -0,0 +1,82 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'Tony Stegall'
__copyright__ = '2010, Tony Stegall or Tonythebookworm on mobileread.com'
__version__ = 'v1.01'
__date__ = '07, October 2010'
__description__ = 'Rolling Stones Mag'
'''
http://www.rollingstone.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class RollingStones(BasicNewsRecipe):
__author__ = 'Tony Stegall'
description = 'Rolling Stones Mag'
cover_url = 'http://gallery.celebritypro.com/data/media/648/kid-rock-rolling-stone-cover.jpg'
masthead_url = 'http://origin.myfonts.com/s/ec/cc-200804/Rolling_Stone-logo.gif'
title = 'Rolling Stones Mag'
category = 'Music Reviews, Movie Reviews, entertainment news'
language = 'en'
timefmt = '[%a, %d %b, %Y]'
oldest_article = 15
max_articles_per_feed = 25
use_embedded_content = False
no_stylesheets = True
remove_javascript = True
#####################################################################################
# cleanup section #
#####################################################################################
keep_only_tags = [
dict(name='div', attrs={'class':['c65l']}),
dict(name='div', attrs={'id':['col1']}),
]
remove_tags = [
dict(name='div', attrs={'class': ['storyActions upper','storyActions lowerArticleNav']}),
dict(name='div', attrs={'id': ['comments','related']}),
]
feeds = [
(u'News', u'http://www.rollingstone.com/siteServices/rss/allNews'),
(u'Blogs', u'http://www.rollingstone.com/siteServices/rss/allBlogs'),
(u'Movie Reviews', u'http://www.rollingstone.com/siteServices/rss/movieReviews'),
(u'Album Reviews', u'http://www.rollingstone.com/siteServices/rss/albumReviews'),
(u'Song Reviews', u'http://www.rollingstone.com/siteServices/rss/songReviews'),
]
def get_article_url(self, article):
return article.get('guid', None)
def append_page(self, soup, appendtag, position):
'''
Some are the articles are multipage so the below function
will get the articles that have <next>
'''
pager = soup.find('li',attrs={'class':'next'})
if pager:
nexturl = pager.a['href']
soup2 = self.index_to_soup(nexturl)
texttag = soup2.find('div', attrs={'id':'storyTextContainer'})
for it in texttag.findAll(style=True):
del it['style']
newpos = len(texttag.contents)
self.append_page(soup2,texttag,newpos)
texttag.extract()
appendtag.insert(position,texttag)

View File

@ -9,15 +9,19 @@ theage.com.au
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
import re
class TheAge(BasicNewsRecipe):
title = 'The Age'
description = 'Business News, World News and Breaking News in Melbourne, Australia'
__author__ = 'Matthew Briggs'
language = 'en_AU'
title = 'The Age'
description = 'Business News, World News and Breaking News in Melbourne, Australia'
publication_type = 'newspaper'
__author__ = 'Matthew Briggs'
language = 'en_AU'
max_articles_per_feed = 1000
recursions = 0
remove_tags = [dict(name=['table', 'script', 'noscript', 'style']), dict(name='a', attrs={'href':'/'}), dict(name='a', attrs={'href':'/text/'})]
def get_browser(self):
br = BasicNewsRecipe.get_browser()
@ -28,22 +32,22 @@ class TheAge(BasicNewsRecipe):
soup = BeautifulSoup(self.browser.open('http://www.theage.com.au/text/').read())
feeds, articles = [], []
feed = None
section = None
sections = {}
for tag in soup.findAll(['h3', 'a']):
if tag.name == 'h3':
if articles:
feeds.append((feed, articles))
articles = []
feed = self.tag_to_string(tag)
elif feed is not None and tag.has_key('href') and tag['href'].strip():
section = self.tag_to_string(tag)
sections[section] = []
# Make sure to skip: <a href="/">TheAge</a>
elif section and tag.has_key('href') and len(tag['href'].strip())>1:
url = tag['href'].strip()
if url.startswith('/'):
url = 'http://www.theage.com.au' + url
url = 'http://www.theage.com.au' + url
title = self.tag_to_string(tag)
articles.append({
sections[section].append({
'title': title,
'url' : url,
'date' : strftime('%a, %d %b'),
@ -51,7 +55,58 @@ class TheAge(BasicNewsRecipe):
'content' : '',
})
feeds = []
# Insert feeds in specified order, if available
feedSort = [ 'National', 'World', 'Opinion', 'Columns', 'Business', 'Sport', 'Entertainment' ]
for i in feedSort:
if i in sections:
feeds.append((i,sections[i]))
# Done with the sorted feeds
for i in feedSort:
del sections[i]
# Append what is left over...
for i in sections:
feeds.append((i,sections[i]))
return feeds
def get_cover_url(self):
soup = BeautifulSoup(self.browser.open('http://www.theage.com.au/todays-paper').read())
for i in soup.findAll('a'):
href = i['href']
if href and re.match('http://www.theage.com.au/frontpage/[0-9]+/[0-9]+/[0-9]+/frontpage.pdf',href):
return href
return None
def preprocess_html(self,soup):
for p in soup.findAll('p'):
# Collapse the paragraph by joining the non-tag contents
contents = [i for i in p.contents if isinstance(i,unicode)]
if len(contents):
contents = ''.join(contents)
# Filter out what's left of the text-mode navigation stuff
if re.match('((\s)|(\&nbsp\;))*\[[\|\s*]*\]((\s)|(\&nbsp\;))*$',contents):
p.extract()
continue
# Shrink the fine print font
if contents=='This material is subject to copyright and any unauthorised use, copying or mirroring is prohibited.':
p['style'] = 'font-size:small'
continue
return soup

View File

@ -16,7 +16,7 @@ class DailyTelegraph(BasicNewsRecipe):
language = 'en_AU'
oldest_article = 2
max_articles_per_feed = 20
max_articles_per_feed = 30
remove_javascript = True
no_stylesheets = True
encoding = 'utf8'
@ -48,22 +48,24 @@ class DailyTelegraph(BasicNewsRecipe):
.caption{font-family:Trebuchet MS,Trebuchet,Helvetica,sans-serif; font-size: xx-small;}
'''
feeds = [(u'News', u'http://feeds.news.com.au/public/rss/2.0/aus_news_807.xml'),
feeds = [ (u'News', u'http://feeds.news.com.au/public/rss/2.0/aus_news_807.xml'),
(u'Opinion', u'http://feeds.news.com.au/public/rss/2.0/aus_opinion_58.xml'),
(u'Business', u'http://feeds.news.com.au/public/rss/2.0/aus_business_811.xml'),
(u'Media', u'http://feeds.news.com.au/public/rss/2.0/aus_media_57.xml'),
(u'Higher Education', u'http://feeds.news.com.au/public/rss/2.0/aus_higher_education_56.xml'),
(u'The Arts', u'http://feeds.news.com.au/public/rss/2.0/aus_arts_51.xml'),
(u'Commercial Property', u'http://feeds.news.com.au/public/rss/2.0/aus_business_commercial_property_708.xml'),
(u'The Nation', u'http://feeds.news.com.au/public/rss/2.0/aus_the_nation_62.xml'),
(u'Sport', u'http://feeds.news.com.au/public/rss/2.0/aus_sport_61.xml'),
(u'Travel', u'http://feeds.news.com.au/public/rss/2.0/aus_travel_and_indulgence_63.xml'),
(u'Defence', u'http://feeds.news.com.au/public/rss/2.0/aus_defence_54.xml'),
(u'Aviation', u'http://feeds.news.com.au/public/rss/2.0/aus_business_aviation_706.xml'),
(u'Mining', u'http://feeds.news.com.au/public/rss/2.0/aus_business_mining_704.xml'),
(u'World News', u'http://feeds.news.com.au/public/rss/2.0/aus_world_808.xml'),
(u'US Election', u'http://feeds.news.com.au/public/rss/2.0/aus_uselection_687.xml'),
(u'Climate', u'http://feeds.news.com.au/public/rss/2.0/aus_climate_809.xml'),
(u'Media', u'http://feeds.news.com.au/public/rss/2.0/aus_media_57.xml'),
(u'IT', u'http://feeds.news.com.au/public/rss/2.0/ausit_itnews_topstories_367.xml'),
(u'Exec Tech', u'http://feeds.news.com.au/public/rss/2.0/ausit_exec_topstories_385.xml'),
(u'Higher Education', u'http://feeds.news.com.au/public/rss/2.0/aus_higher_education_56.xml'),
(u'Arts', u'http://feeds.news.com.au/public/rss/2.0/aus_arts_51.xml'),
(u'Travel', u'http://feeds.news.com.au/public/rss/2.0/aus_travel_and_indulgence_63.xml'),
(u'Property', u'http://feeds.news.com.au/public/rss/2.0/aus_property_59.xml'),
(u'US Election', u'http://feeds.news.com.au/public/rss/2.0/aus_uselection_687.xml')]
(u'Sport', u'http://feeds.news.com.au/public/rss/2.0/aus_sport_61.xml'),
(u'Business', u'http://feeds.news.com.au/public/rss/2.0/aus_business_811.xml'),
(u'Aviation', u'http://feeds.news.com.au/public/rss/2.0/aus_business_aviation_706.xml'),
(u'Commercial Property', u'http://feeds.news.com.au/public/rss/2.0/aus_business_commercial_property_708.xml'),
(u'Mining', u'http://feeds.news.com.au/public/rss/2.0/aus_business_mining_704.xml')]
def get_article_url(self, article):
return article.id

View File

@ -6,7 +6,18 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
'''
Modified by Tony Stegall
on 10/10/10 to include function to grab print version of articles
'''
from calibre.web.feeds.news import BasicNewsRecipe
'''
added by Tony Stegall
'''
#######################################################
from calibre.ptempfile import PersistentTemporaryFile
#######################################################
class AdvancedUserRecipe1249039563(BasicNewsRecipe):
title = u'De Volkskrant'
@ -16,20 +27,54 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
no_stylesheets = True
language = 'nl'
keep_only_tags = [dict(name='div', attrs={'id':'leftColumnArticle'}) ]
remove_tags = [
dict(name='div',attrs={'class':'article_tools'}),
dict(name='div',attrs={'id':'article_tools'}),
dict(name='div',attrs={'class':'articletools'}),
dict(name='div',attrs={'id':'articletools'}),
dict(name='div',attrs={'id':'myOverlay'}),
dict(name='div',attrs={'id':'trackback'}),
dict(name='div',attrs={'id':'googleBanner'}),
dict(name='div',attrs={'id':'article_headlines'}),
]
extra_css = '''
body{font-family:Arial,Helvetica,sans-serif; font-size:small;}
h1{font-size:large;}
'''
'''
Change Log:
Date: 10/10/10 - Modified code to include obfuscated to get the print version
Author: Tony Stegall
'''
#######################################################################################################
temp_files = []
articles_are_obfuscated = True
feeds = [(u'Laatste Nieuws', u'http://volkskrant.nl/rss/laatstenieuws.rss'), (u'Binnenlands nieuws', u'http://volkskrant.nl/rss/nederland.rss'), (u'Buitenlands nieuws', u'http://volkskrant.nl/rss/internationaal.rss'), (u'Economisch nieuws', u'http://volkskrant.nl/rss/economie.rss'), (u'Sportnieuws', u'http://volkskrant.nl/rss/sport.rss'), (u'Kunstnieuws', u'http://volkskrant.nl/rss/kunst.rss'), (u'Wetenschapsnieuws', u'http://feeds.feedburner.com/DeVolkskrantWetenschap'), (u'Technologienieuws', u'http://feeds.feedburner.com/vkmedia')]
def get_obfuscated_article(self, url):
br = self.get_browser()
br.open(url)
try:
response = br.follow_link(url_regex='.*?(2010)(\\/)(article)(\\/)(print)(\\/)', nr = 0)
html = response.read()
except:
response = br.open(url)
html = response.read()
self.temp_files.append(PersistentTemporaryFile('_fa.html'))
self.temp_files[-1].write(html)
self.temp_files[-1].close()
return self.temp_files[-1].name
###############################################################################################################
feeds = [
(u'Laatste Nieuws', u'http://volkskrant.nl/rss/laatstenieuws.rss'),
(u'Binnenlands nieuws', u'http://volkskrant.nl/rss/nederland.rss'),
(u'Buitenlands nieuws', u'http://volkskrant.nl/rss/internationaal.rss'),
(u'Economisch nieuws', u'http://volkskrant.nl/rss/economie.rss'),
(u'Sportnieuws', u'http://volkskrant.nl/rss/sport.rss'),
(u'Kunstnieuws', u'http://volkskrant.nl/rss/kunst.rss'),
#both of these rss feeds link back to the main volksrant.nl url a.k.a Broken
#If someone happens to know the correct paths then they can put them in here
#(u'Wetenschapsnieuws', u'http://feeds.feedburner.com/DeVolkskrantWetenschap'),
#(u'Technologienieuws', u'http://feeds.feedburner.com/vkmedia')
]
'''
example for formating
'''
# original url: http://www.volkskrant.nl/vk/nl/2668/Buitenland/article/detail/1031493/2010/10/10/Noord-Korea-ziet-nieuwe-leider.dhtml
# print url : http://www.volkskrant.nl/vk/nl/2668/2010/article/print/detail/1031493/Noord-Korea-ziet-nieuwe-leider.dhtml

View File

@ -55,6 +55,9 @@ class WikiNews(BasicNewsRecipe):
rest, sep, article_id = url.rpartition('/')
return 'http://en.wikinews.org/w/index.php?title=' + article_id + '&printable=yes'
def get_cover_url(self):
return 'http://upload.wikimedia.org/wikipedia/commons/b/bd/Wikinews-logo-en.png'
def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Language" content="en"/><meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
soup.head.insert(0,mtag)

View File

@ -17,6 +17,7 @@ function selector(elem) {
sel = selector_in_parent(obj) + sel;
obj = obj.parent();
}
if (sel.length > 2 && sel.charAt(1) == ">") sel = sel.substring(2);
return sel;
}
@ -26,7 +27,8 @@ function calculate_bookmark(y, node) {
var ratio = (y - elem.offset().top)/elem.height();
if (ratio > 1) { ratio = 1; }
if (ratio < 0) { ratio = 0; }
return sel + "|" + ratio;
sel = sel + "|" + ratio;
return sel;
}
function animated_scrolling_done() {
@ -37,6 +39,10 @@ function scroll_to_bookmark(bookmark) {
bm = bookmark.split("|");
var ratio = 0.7 * parseFloat(bm[1]);
$.scrollTo($(bm[0]), 1000,
{over:ratio, onAfter:function(){window.py_bridge.animated_scroll_done()}});
{
over:ratio,
onAfter:function(){window.py_bridge.animated_scroll_done()}
}
);
}

View File

@ -1,15 +1,15 @@
/**
* jQuery.ScrollTo
* Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 9/11/2008
* Date: 5/25/2009
*
* @projectDescription Easy element scrolling using jQuery.
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
* Tested with jQuery 1.2.6. On FF 2/3, IE 6/7, Opera 9.2/5 and Safari 3. on Windows.
* Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
*
* @author Ariel Flesler
* @version 1.4
* @version 1.4.2
*
* @id jQuery.scrollTo
* @id jQuery.fn.scrollTo
@ -20,6 +20,8 @@
* - A jQuery/DOM element ( logically, child of the element to scroll )
* - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
* - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
* - A percentage of the container's dimension/s, for example: 50% to go to the middle.
* - The string 'max' for go-to-end.
* @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
* @param {Object,Function} settings Optional set of settings or the onAfter callback.
* @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
@ -58,31 +60,31 @@
};
$scrollTo.defaults = {
axis:'y',
duration:1
axis:'xy',
duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
};
// Returns the element that needs to be animated to scroll the window.
// Kept for backwards compatibility (specially for localScroll & serialScroll)
$scrollTo.window = function( scope ){
return $(window).scrollable();
return $(window)._scrollable();
};
// Hack, hack, hack... stay away!
// Hack, hack, hack :)
// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
$.fn.scrollable = function(){
$.fn._scrollable = function(){
return this.map(function(){
// Just store it, we might need it
var win = this.parentWindow || this.defaultView,
// If it's a document, get its iframe or the window if it's THE document
elem = this.nodeName == '#document' ? win.frameElement || win : this,
// Get the corresponding document
doc = elem.contentDocument || (elem.contentWindow || elem).document,
isWin = elem.setInterval;
var elem = this,
isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body
: isWin ? doc.documentElement
: this;
if( !isWin )
return elem;
var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
return $.browser.safari || doc.compatMode == 'BackCompat' ?
doc.body :
doc.documentElement;
});
};
@ -94,6 +96,9 @@
if( typeof settings == 'function' )
settings = { onAfter:settings };
if( target == 'max' )
target = 9e9;
settings = $.extend( {}, $scrollTo.defaults, settings );
// Speed is still recognized for backwards compatibility
duration = duration || settings.speed || settings.duration;
@ -106,7 +111,7 @@
settings.offset = both( settings.offset );
settings.over = both( settings.over );
return this.scrollable().each(function(){
return this._scrollable().each(function(){
var elem = this,
$elem = $(elem),
targ = target, toff, attr = {},
@ -116,7 +121,7 @@
// A number will pass the regex
case 'number':
case 'string':
if( /^([+-]=)?\d+(px)?$/.test(targ) ){
if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
targ = both( targ );
// We are done
break;
@ -134,8 +139,7 @@
pos = Pos.toLowerCase(),
key = 'scroll' + Pos,
old = elem[key],
Dim = axis == 'x' ? 'Width' : 'Height',
dim = Dim.toLowerCase();
max = $scrollTo.max(elem, axis);
if( toff ){// jQuery / DOMElement
attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );
@ -150,14 +154,19 @@
if( settings.over[pos] )
// Scroll to a fraction of its width/height
attr[key] += targ[dim]() * settings.over[pos];
}else
attr[key] = targ[pos];
attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
}else{
var val = targ[pos];
// Handle percentage values
attr[key] = val.slice && val.slice(-1) == '%' ?
parseFloat(val) / 100 * max
: val;
}
// Number or 'number'
if( /^\d+$/.test(attr[key]) )
// Check the limits
attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );
attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );
// Queueing axes
if( !i && settings.queue ){
@ -169,6 +178,7 @@
delete attr[key];
}
});
animate( settings.onAfter );
function animate( callback ){
@ -176,17 +186,28 @@
callback.call(this, target, settings);
});
};
function max( Dim ){
var attr ='scroll'+Dim,
doc = elem.ownerDocument;
return win
? Math.max( doc.documentElement[attr], doc.body[attr] )
: elem[attr];
};
}).end();
};
// Max scrolling position, works on quirks mode
// It only fails (not too badly) on IE, quirks mode.
$scrollTo.max = function( elem, axis ){
var Dim = axis == 'x' ? 'Width' : 'Height',
scroll = 'scroll'+Dim;
if( !$(elem).is('html,body') )
return elem[scroll] - $(elem)[Dim.toLowerCase()]();
var size = 'client' + Dim,
html = elem.ownerDocument.documentElement,
body = elem.ownerDocument.body;
return Math.max( html[scroll], body[scroll] )
- Math.min( html[size] , body[size] );
};
function both( val ){
return typeof val == 'object' ? val : { top:val, left:val };
};

View File

@ -19,7 +19,7 @@ __all__ = [
'upload_user_manual', 'upload_to_mobileread', 'upload_demo',
'upload_to_sourceforge', 'upload_to_google_code',
'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2',
'osx32_freeze', 'osx32', 'osx', 'rsync',
'osx32_freeze', 'osx32', 'osx', 'rsync', 'push',
'win32_freeze', 'win32', 'win',
'stage1', 'stage2', 'stage3', 'stage4', 'publish'
]
@ -68,8 +68,9 @@ upload_to_server = UploadToServer()
upload_to_sourceforge = UploadToSourceForge()
upload_to_google_code = UploadToGoogleCode()
from setup.installer import Rsync
from setup.installer import Rsync, Push
rsync = Rsync()
push = Push()
from setup.installer.linux import Linux, Linux32, Linux64
linux = Linux()

View File

@ -11,16 +11,21 @@ import subprocess, tempfile, os, time
from setup import Command, installer_name
from setup.build_environment import HOST, PROJECT
BASE_RSYNC = 'rsync -avz --delete'.split()
EXCLUDES = []
for x in [
'src/calibre/plugins', 'src/calibre/manual', 'src/calibre/trac',
'.bzr', '.build', '.svn', 'build', 'dist', 'imgsrc', '*.pyc', '*.pyo', '*.swp',
'*.swo']:
EXCLUDES.extend(['--exclude', x])
SAFE_EXCLUDES = ['"%s"'%x if '*' in x else x for x in EXCLUDES]
class Rsync(Command):
description = 'Sync source tree from development machine'
SYNC_CMD = ('rsync -avz --delete --exclude src/calibre/plugins '
'--exclude src/calibre/manual --exclude src/calibre/trac '
'--exclude .bzr --exclude .build --exclude .svn --exclude build --exclude dist '
'--exclude imgsrc '
'--exclude "*.pyc" --exclude "*.pyo" --exclude "*.swp" --exclude "*.swo" '
'rsync://{host}/work/{project} ..')
SYNC_CMD = ' '.join(BASE_RSYNC+SAFE_EXCLUDES+
['rsync://{host}/work/{project}', '..'])
def run(self, opts):
cmd = self.SYNC_CMD.format(host=HOST, project=PROJECT)
@ -28,6 +33,21 @@ class Rsync(Command):
subprocess.check_call(cmd, shell=True)
class Push(Command):
description = 'Push code to another host'
def run(self, opts):
for host in (
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre',
'kovid@ox:calibre'
):
rcmd = BASE_RSYNC + EXCLUDES + ['.', host]
print '\n\nPushing to:', host, '\n'
subprocess.check_call(rcmd)
class VMInstaller(Command):
EXTRA_SLEEP = 5

View File

@ -6,9 +6,9 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, re, cStringIO, base64, httplib, subprocess
import os, re, cStringIO, base64, httplib, subprocess, hashlib, shutil
from subprocess import check_call
from tempfile import NamedTemporaryFile
from tempfile import NamedTemporaryFile, mkdtemp
from setup import Command, __version__, installer_name, __appname__
@ -331,5 +331,19 @@ class UploadToServer(Command):
%(__version__, DOWNLOADS), shell=True)
check_call('ssh divok /etc/init.d/apache2 graceful',
shell=True)
tdir = mkdtemp()
for installer in installers():
if not os.path.exists(installer):
continue
with open(installer, 'rb') as f:
raw = f.read()
fingerprint = hashlib.sha512(raw).hexdigest()
fname = os.path.basename(installer+'.sha512')
with open(os.path.join(tdir, fname), 'wb') as f:
f.write(fingerprint)
check_call('scp %s/*.sha512 divok:%s/signatures/' % (tdir, DOWNLOADS),
shell=True)
shutil.rmtree(tdir)

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.22'
__version__ = '0.7.23'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -462,7 +462,7 @@ from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, SOVOS
from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO
@ -576,6 +576,7 @@ plugins += [
SPECTRA,
GEMEI,
VELOCITYMICRO,
PDNOVEL_KOBO,
ITUNES,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -13,7 +13,8 @@ from calibre.devices.errors import UserFeedback
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre.devices.interface import DevicePlugin
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import authors_to_string, MetaInformation
from calibre.ebooks.metadata import authors_to_string, MetaInformation, \
title_sort
from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.epub import set_metadata
from calibre.library.server.utils import strftime
@ -96,6 +97,9 @@ class ITUNES(DriverBase):
OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...')
BACKLOADING_ERROR_MESSAGE = _(
"Cannot copy books directly from iDevice. "
"Drag from iTunes Library to desktop, then add to calibre's Library window.")
# Product IDs:
# 0x1291 iPod Touch
@ -259,6 +263,8 @@ class ITUNES(DriverBase):
self.report_progress(1.0, _('Updating device metadata listing...'))
# Add new books to booklists[0]
# Charles thinks this should be
# for new_book in metadata[0]:
for new_book in locations[0]:
if DEBUG:
self.log.info(" adding '%s' by '%s' to booklists[0]" %
@ -1208,6 +1214,10 @@ class ITUNES(DriverBase):
except:
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
import traceback
traceback.print_exc()
return thumb
if isosx:
@ -2400,7 +2410,7 @@ class ITUNES(DriverBase):
try:
storage_path = os.path.split(cached_book['lib_book'].location().path)
if cached_book['lib_book'].location().path.startswith(self.iTunes_media) and \
not storage_path[0].startswith(self.calibre_library_path):
not storage_path[0].startswith(prefs['library_path']):
title_storage_path = storage_path[0]
if DEBUG:
self.log.info(" removing title_storage_path: %s" % title_storage_path)
@ -2452,7 +2462,7 @@ class ITUNES(DriverBase):
if book:
if self.iTunes_media and path.startswith(self.iTunes_media) and \
not path.startswith(self.calibre_library_path):
not path.startswith(prefs['library_path']):
storage_path = os.path.split(path)
if DEBUG:
self.log.info(" removing '%s' at %s" %
@ -3122,6 +3132,9 @@ class Book(Metadata):
See ebooks.metadata.book.base
'''
def __init__(self,title,author):
Metadata.__init__(self, title, authors=[author])
@property
def title_sorter(self):
return title_sort(self.title)

View File

@ -39,6 +39,10 @@ class DevicePlugin(Plugin):
#: Whether the metadata on books can be set via the GUI.
CAN_SET_METADATA = ['title', 'authors', 'collections']
# Set this to None if the books on the device are files that the GUI can
# access in order to add the books from the device to the library
BACKLOADING_ERROR_MESSAGE = _('Cannot get files from this device')
#: Path separator for paths to books on device
path_sep = os.sep
@ -417,14 +421,16 @@ class DevicePlugin(Plugin):
select a specific plugboard. This method is called immediately before
add_books and sync_booklists.
pb_func is a callable with the following signature:
pb_func is a callable with the following signature::
def pb_func(device_name, format, plugboards)
You give it the current device name (either the class name or
DEVICE_PLUGBOARD_NAME), the format you are interested in (a 'real'
format or 'device_db'), and the plugboards (you were given those by
set_plugboards, the same place you got this method).
Return value: None or a single plugboard instance.
:return: None or a single plugboard instance.
'''
pass

View File

@ -108,6 +108,16 @@ class PDNOVEL(USBMS):
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
coverfile.write(coverdata[2])
class PDNOVEL_KOBO(PDNOVEL):
name = 'Pandigital Kobo device interface'
gui_name = 'PD Novel (Kobo)'
description = _('Communicate with the Pandigital Novel')
BCD = [0x222]
EBOOK_DIR_MAIN = 'eBooks/Kobo'
class VELOCITYMICRO(USBMS):
name = 'VelocityMicro device interface'
gui_name = 'VelocityMicro'

View File

@ -6,6 +6,7 @@ __docformat__ = 'restructuredtext en'
import os, re, time, sys
from calibre.ebooks.metadata import title_sort
from calibre.ebooks.metadata.book.base import Metadata
from calibre.devices.mime import mime_type_ext
from calibre.devices.interface import BookList as _BookList
@ -54,7 +55,7 @@ class Book(Metadata):
def title_sorter(self):
doc = '''String to sort the title. If absent, title is returned'''
def fget(self):
return re.sub('^\s*A\s+|^\s*The\s+|^\s*An\s+', '', self.title).rstrip()
return title_sort(self.title)
return property(doc=doc, fget=fget)
@dynamic_property
@ -124,7 +125,6 @@ class CollectionsBookList(BookList):
collections = {}
# This map of sets is used to avoid linear searches when testing for
# book equality
collections_lpaths = {}
for book in self:
# Make sure we can identify this book via the lpath
lpath = getattr(book, 'lpath', None)
@ -198,20 +198,22 @@ class CollectionsBookList(BookList):
cat_name = category
if cat_name not in collections:
collections[cat_name] = []
collections_lpaths[cat_name] = set()
if lpath in collections_lpaths[cat_name]:
continue
collections_lpaths[cat_name].add(lpath)
collections[cat_name] = {}
if is_series:
collections[cat_name].append(
(book, book.get(attr+'_index', sys.maxint)))
if doing_dc:
collections[cat_name][lpath] = \
(book, book.get('series_index', sys.maxint))
else:
collections[cat_name][lpath] = \
(book, book.get(attr+'_index', sys.maxint))
else:
collections[cat_name].append(
(book, book.get('title_sort', 'zzzz')))
if lpath not in collections[cat_name]:
collections[cat_name][lpath] = \
(book, book.get('title_sort', 'zzzz'))
# Sort collections
result = {}
for category, books in collections.items():
for category, lpaths in collections.items():
books = lpaths.values()
books.sort(cmp=lambda x,y:cmp(x[1], y[1]))
result[category] = [x[0] for x in books]
return result

View File

@ -94,6 +94,9 @@ class Device(DeviceConfig, DevicePlugin):
EBOOK_DIR_CARD_B = ''
DELETE_EXTS = []
# USB disk-based devices can see the book files on the device, so can
# copy these back to the library
BACKLOADING_ERROR_MESSAGE = None
def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None):
@ -626,6 +629,50 @@ class Device(DeviceConfig, DevicePlugin):
setattr(self, prefix, mp)
self._linux_mount_map[card] = mp
self.filter_read_only_mount_points()
def filter_read_only_mount_points(self):
def is_readonly(mp):
if mp is None:
return True
path = os.path.join(mp, 'calibre_readonly_test')
ro = True
try:
with open(path, 'wb'):
ro = False
except:
pass
else:
try:
os.remove(path)
except:
pass
return ro
for mp in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
if is_readonly(getattr(self, mp, None)):
setattr(self, mp, None)
if self._main_prefix is None:
for p in ('_card_a_prefix', '_card_b_prefix'):
nmp = getattr(self, p, None)
if nmp is not None:
self._main_prefix = nmp
setattr(self, p, None)
break
if self._main_prefix is None:
raise DeviceError(_('The main memory of %s is read only. '
'This usually happens because of file system errors.')
%self.__class__.__name__)
if self._card_a_prefix is None and self._card_b_prefix is not None:
self._card_a_prefix = self._card_b_prefix
self._card_b_prefix = None
def open(self):
time.sleep(5)
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None

View File

@ -15,7 +15,6 @@ from calibre.utils.chm.chmlib import (
chm_enumerate,
)
from calibre.utils.config import OptionParser
from calibre.ebooks.metadata.toc import TOC
from calibre.ebooks.chardet import xml_to_unicode
@ -37,41 +36,6 @@ def check_empty(s, rex = re.compile(r'\S')):
return rex.search(s) is None
def option_parser():
parser = OptionParser(usage=_('%prog [options] mybook.chm'))
parser.add_option('--output-dir', '-d', default='.', help=_('Output directory. Defaults to current directory'), dest='output')
parser.add_option('--verbose', default=False, action='store_true', dest='verbose')
parser.add_option("-t", "--title", action="store", type="string", \
dest="title", help=_("Set the book title"))
parser.add_option('--title-sort', action='store', type='string', default=None,
dest='title_sort', help=_('Set sort key for the title'))
parser.add_option("-a", "--author", action="store", type="string", \
dest="author", help=_("Set the author"))
parser.add_option('--author-sort', action='store', type='string', default=None,
dest='author_sort', help=_('Set sort key for the author'))
parser.add_option("-c", "--category", action="store", type="string", \
dest="category", help=_("The category this book belongs"
" to. E.g.: History"))
parser.add_option("--thumbnail", action="store", type="string", \
dest="thumbnail", help=_("Path to a graphic that will be"
" set as this files' thumbnail"))
parser.add_option("--comment", action="store", type="string", \
dest="freetext", help=_("Path to a txt file containing a comment."))
parser.add_option("--get-thumbnail", action="store_true", \
dest="get_thumbnail", default=False, \
help=_("Extract thumbnail from LRF file"))
parser.add_option('--publisher', default=None, help=_('Set the publisher'))
parser.add_option('--classification', default=None, help=_('Set the book classification'))
parser.add_option('--creator', default=None, help=_('Set the book creator'))
parser.add_option('--producer', default=None, help=_('Set the book producer'))
parser.add_option('--get-cover', action='store_true', default=False,
help=_('Extract cover from LRF file. Note that the LRF format has no defined cover, so we use some heuristics to guess the cover.'))
parser.add_option('--bookid', action='store', type='string', default=None,
dest='book_id', help=_('Set book ID'))
parser.add_option('--font-delta', action='store', type='int', default=0,
dest='font_delta', help=_('Set font delta'))
return parser
class CHMError(Exception):
pass

View File

@ -94,7 +94,7 @@ class PageProcessor(list):
from calibre.utils.magick import PixelWand
for i, wand in enumerate(self.pages):
pw = PixelWand()
pw.color = 'white'
pw.color = '#ffffff'
wand.set_border_color(pw)
if self.rotate:

View File

@ -38,7 +38,8 @@ class SafeFormat(TemplateFormatter):
def get_value(self, key, args, kwargs):
try:
key = field_metadata.search_term_to_field_key(key.lower())
if key != 'title_sort':
key = field_metadata.search_term_to_field_key(key.lower())
b = self.book.get_user_metadata(key, False)
if b and b['datatype'] == 'int' and self.book.get(key, 0) == 0:
v = ''

View File

@ -45,7 +45,7 @@ def fetch_metadata(url, max=100, timeout=5.):
class ISBNDBMetadata(Metadata):
def __init__(self, book):
Metadata.__init__(self, None, [])
Metadata.__init__(self, None)
def tostring(e):
if not hasattr(e, 'string'):
@ -58,21 +58,21 @@ class ISBNDBMetadata(Metadata):
return ans
self.isbn = unicode(book.get('isbn13', book.get('isbn')))
self.title = tostring(book.find('titlelong'))
if not self.title:
self.title = tostring(book.find('title'))
if not self.title:
self.title = _('Unknown')
title = tostring(book.find('titlelong'))
if not title:
title = tostring(book.find('title'))
self.title = title
self.title = unicode(self.title).strip()
self.authors = []
authors = []
au = tostring(book.find('authorstext'))
if au:
au = au.strip()
temp = au.split(',')
for au in temp:
if not au: continue
self.authors.extend([a.strip() for a in au.split('&amp;')])
authors.extend([a.strip() for a in au.split('&amp;')])
if authors:
self.authors = authors
try:
self.author_sort = tostring(book.find('authors').find('person'))
if self.authors and self.author_sort == self.authors[0]:

View File

@ -189,7 +189,7 @@ class MobiMLizer(object):
para = wrapper
emleft = int(round(left / self.profile.fbase)) - ems
emleft = min((emleft, 10))
while emleft > 0:
while emleft > ems/2.0:
para = etree.SubElement(para, XHTML('blockquote'))
emleft -= ems
else:

View File

@ -122,15 +122,14 @@ def rescale_image(data, maxsizeb, dimen=None):
img = Image()
quality = 95
if hasattr(img, 'set_compression_quality'):
img.load(data)
while len(data) >= maxsizeb and quality >= 10:
quality -= 5
img.set_compression_quality(quality)
data = img.export('jpg')
if len(data) <= maxsizeb:
return data
orig_data = data
img.load(data)
while len(data) >= maxsizeb and quality >= 10:
quality -= 5
img.set_compression_quality(quality)
data = img.export('jpg')
if len(data) <= maxsizeb:
return data
orig_data = data
scale = 0.9
while len(data) >= maxsizeb and scale >= 0.05:
@ -138,8 +137,7 @@ def rescale_image(data, maxsizeb, dimen=None):
img.load(orig_data)
w, h = img.size
img.size = (int(scale*w), int(scale*h))
if hasattr(img, 'set_compression_quality'):
img.set_compression_quality(quality)
img.set_compression_quality(quality)
data = img.export('jpg')
scale -= 0.05
return data

View File

@ -131,6 +131,7 @@ class InterfaceAction(QObject):
Called whenever the current library is changed.
:param db: The LibraryDatabase corresponding to the current library.
'''
pass
@ -148,6 +149,7 @@ class InterfaceAction(QObject):
long periods of time.
:return: False to halt the shutdown. You are responsible for telling
the user why the shutdown was halted.
the user why the shutdown was halted.
'''
return True

View File

@ -235,6 +235,10 @@ class AddAction(InterfaceAction):
self.gui.refresh_ondevice()
def add_books_from_device(self, view, paths=None):
backloading_err = self.gui.device_manager.device.BACKLOADING_ERROR_MESSAGE
if backloading_err is not None:
return error_dialog(self.gui, _('Add to library'), backloading_err,
show=True)
if paths is None:
rows = view.selectionModel().selectedRows()
if not rows or len(rows) == 0:

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
import os, shutil
from functools import partial
from PyQt4.Qt import QMenu, Qt, QInputDialog
from PyQt4.Qt import QMenu, Qt, QInputDialog, QThread, pyqtSignal, QProgressDialog
from calibre import isbytestring
from calibre.constants import filesystem_encoding
@ -16,6 +16,7 @@ from calibre.utils.config import prefs
from calibre.gui2 import gprefs, warning_dialog, Dispatcher, error_dialog, \
question_dialog, info_dialog
from calibre.gui2.actions import InterfaceAction
from calibre.gui2.dialogs.check_library import CheckLibraryDialog
class LibraryUsageStats(object): # {{{
@ -75,6 +76,72 @@ class LibraryUsageStats(object): # {{{
self.write_stats()
# }}}
# Check Integrity {{{
class VacThread(QThread):
check_done = pyqtSignal(object, object)
callback = pyqtSignal(object, object)
def __init__(self, parent, db):
QThread.__init__(self, parent)
self.db = db
self._parent = parent
def run(self):
err = bad = None
try:
bad = self.db.check_integrity(self.callbackf)
except:
import traceback
err = traceback.format_exc()
self.check_done.emit(bad, err)
def callbackf(self, progress, msg):
self.callback.emit(progress, msg)
class CheckIntegrity(QProgressDialog):
def __init__(self, db, parent=None):
QProgressDialog.__init__(self, parent)
self.db = db
self.setCancelButton(None)
self.setMinimum(0)
self.setMaximum(100)
self.setWindowTitle(_('Checking database integrity'))
self.setAutoReset(False)
self.setValue(0)
self.vthread = VacThread(self, db)
self.vthread.check_done.connect(self.check_done,
type=Qt.QueuedConnection)
self.vthread.callback.connect(self.callback, type=Qt.QueuedConnection)
self.vthread.start()
def callback(self, progress, msg):
self.setLabelText(msg)
self.setValue(int(100*progress))
def check_done(self, bad, err):
if err:
error_dialog(self, _('Error'),
_('Failed to check database integrity'),
det_msg=err, show=True)
elif bad:
titles = [self.db.title(x, index_is_id=True) for x in bad]
det_msg = '\n'.join(titles)
warning_dialog(self, _('Some inconsistencies found'),
_('The following books had formats listed in the '
'database that are not actually available. '
'The entries for the formats have been removed. '
'You should check them manually. This can '
'happen if you manipulate the files in the '
'library folder directly.'), det_msg=det_msg, show=True)
self.reset()
# }}}
class ChooseLibraryAction(InterfaceAction):
name = 'Choose Library'
@ -117,11 +184,28 @@ class ChooseLibraryAction(InterfaceAction):
self.rename_separator = self.choose_menu.addSeparator()
self.create_action(spec=(_('Library backup status...'), 'lt.png', None,
None), attr='action_backup_status')
self.action_backup_status.triggered.connect(self.backup_status,
type=Qt.QueuedConnection)
self.choose_menu.addAction(self.action_backup_status)
self.maintenance_menu = QMenu(_('Library Maintenance'))
ac = self.create_action(spec=(_('Library metadata backup status'),
'lt.png', None, None), attr='action_backup_status')
ac.triggered.connect(self.backup_status, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Start backing up metadata of all books'),
'lt.png', None, None), attr='action_backup_metadata')
ac.triggered.connect(self.mark_dirty, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Check library'), 'lt.png',
None, None), attr='action_check_library')
ac.triggered.connect(self.check_library, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Check database integrity'), 'lt.png',
None, None), attr='action_check_database')
ac.triggered.connect(self.check_database, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
ac = self.create_action(spec=(_('Recover database'), 'lt.png',
None, None), attr='action_restore_database')
ac.triggered.connect(self.restore_database, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
self.choose_menu.addMenu(self.maintenance_menu)
def library_name(self):
db = self.gui.library_view.model().db
@ -234,6 +318,37 @@ class ChooseLibraryAction(InterfaceAction):
_('Book metadata files remaining to be written: %s') % dirty_text,
show=True)
def mark_dirty(self):
db = self.gui.library_view.model().db
db.dirtied(list(db.data.iterallids()))
info_dialog(self.gui, _('Backup metadata'),
_('Metadata will be backed up while calibre is running, at the '
'rate of approximately 1 book per second.'), show=True)
def check_library(self):
db = self.gui.library_view.model().db
d = CheckLibraryDialog(self.gui.parent(), db)
d.exec_()
def check_database(self, *args):
m = self.gui.library_view.model()
m.stop_metadata_backup()
try:
d = CheckIntegrity(m.db, self.gui)
d.exec_()
finally:
m.start_metadata_backup()
def restore_database(self):
info_dialog(self.gui, _('Recover database'), '<p>'+
_(
'This command rebuilds your calibre database from the information '
'stored by calibre in the OPF files.<p>'
'This function is not currently available in the GUI. You can '
'recover your database using the \'calibredb restore_database\' '
'command line function.'
), show=True)
def switch_requested(self, location):
if not self.change_library_allowed():
return

View File

@ -18,7 +18,7 @@ from calibre.utils.config import prefs, tweaks
class Worker(Thread):
def __init__(self, ids, db, loc, progress, done):
def __init__(self, ids, db, loc, progress, done, delete_after):
Thread.__init__(self)
self.ids = ids
self.processed = set([])
@ -27,6 +27,7 @@ class Worker(Thread):
self.error = None
self.progress = progress
self.done = done
self.delete_after = delete_after
def run(self):
try:
@ -68,7 +69,8 @@ class Worker(Thread):
self.add_formats(identical_book, paths, newdb, replace=False)
if not added:
newdb.import_book(mi, paths, notify=False, import_hooks=False,
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'])
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
preserve_uuid=self.delete_after)
co = self.db.conversion_options(x, 'PIPE')
if co is not None:
newdb.set_conversion_options(x, 'PIPE', co)
@ -134,7 +136,8 @@ class CopyToLibraryAction(InterfaceAction):
self.pd.set_msg(_('Copying') + ' ' + title)
self.pd.set_value(idx)
self.worker = Worker(ids, db, loc, Dispatcher(progress), Dispatcher(self.pd.accept))
self.worker = Worker(ids, db, loc, Dispatcher(progress),
Dispatcher(self.pd.accept), delete_after)
self.worker.start()
self.pd.exec_()

View File

@ -169,7 +169,7 @@ class EditMetadataAction(InterfaceAction):
self.gui.tags_view.blockSignals(False)
if changed:
m = self.gui.library_view.model()
m.resort(reset=False)
m.refresh(reset=False)
m.research()
self.gui.tags_view.recount()
if self.gui.cover_flow:

View File

@ -172,7 +172,7 @@ class MetadataWidget(Widget, Ui_Form):
if _file:
_file = os.path.abspath(_file)
if not os.access(_file, os.R_OK):
d = error_dialog(self.window, _('Cannot read'),
d = error_dialog(self.parent(), _('Cannot read'),
_('You do not have permission to read the file: ') + _file)
d.exec_()
return
@ -181,14 +181,14 @@ class MetadataWidget(Widget, Ui_Form):
cf = open(_file, "rb")
cover = cf.read()
except IOError, e:
d = error_dialog(self.window, _('Error reading file'),
d = error_dialog(self.parent(), _('Error reading file'),
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
d.exec_()
if cover:
pix = QPixmap()
pix.loadFromData(cover)
if pix.isNull():
d = error_dialog(self.window, _('Error reading file'),
d = error_dialog(self.parent(), _('Error reading file'),
_file + _(" is not a valid picture"))
d.exec_()
else:

View File

@ -793,11 +793,17 @@ class DeviceMixin(object): # {{{
self.set_books_in_library(job.result, reset=True)
mainlist, cardalist, cardblist = job.result
self.memory_view.set_database(mainlist)
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
self.device_manager.device.BACKLOADING_ERROR_MESSAGE
is None)
self.card_a_view.set_database(cardalist)
self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
self.device_manager.device.BACKLOADING_ERROR_MESSAGE
is None)
self.card_b_view.set_database(cardblist)
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
self.device_manager.device.BACKLOADING_ERROR_MESSAGE
is None)
self.sync_news()
self.sync_catalogs()
self.refresh_ondevice()
@ -1413,15 +1419,16 @@ class DeviceMixin(object): # {{{
# Force a reset if the caches are not initialized
if reset or not hasattr(self, 'db_book_title_cache'):
# Build a cache (map) of the library, so the search isn't On**2
self.db_book_title_cache = {}
self.db_book_uuid_cache = {}
# It might be possible to get here without having initialized the
# library view. In this case, simply give up
try:
db = self.library_view.model().db
except:
return False
# Build a cache (map) of the library, so the search isn't On**2
self.db_book_title_cache = {}
self.db_book_uuid_cache = {}
for id in db.data.iterallids():
mi = db.get_metadata(id, index_is_id=True)
title = clean_string(mi.title)
@ -1455,7 +1462,7 @@ class DeviceMixin(object): # {{{
if update_metadata:
book.smart_update(self.db_book_uuid_cache[book.uuid],
replace_metadata=True)
book.in_library = True
book.in_library = 'UUID'
# ensure that the correct application_id is set
book.application_id = \
self.db_book_uuid_cache[book.uuid].application_id
@ -1468,21 +1475,21 @@ class DeviceMixin(object): # {{{
# will match if any of the db_id, author, or author_sort
# also match.
if getattr(book, 'application_id', None) in d['db_ids']:
book.in_library = True
# app_id already matches a db_id. No need to set it.
if update_metadata:
book.smart_update(d['db_ids'][book.application_id],
replace_metadata=True)
book.in_library = 'APP_ID'
continue
# Sonys know their db_id independent of the application_id
# in the metadata cache. Check that as well.
if getattr(book, 'db_id', None) in d['db_ids']:
book.in_library = True
book.application_id = \
d['db_ids'][book.db_id].application_id
if update_metadata:
book.smart_update(d['db_ids'][book.db_id],
replace_metadata=True)
book.in_library = 'DB_ID'
book.application_id = \
d['db_ids'][book.db_id].application_id
continue
# We now know that the application_id is not right. Set it
# to None to prevent book_on_device from accidentally
@ -1494,19 +1501,19 @@ class DeviceMixin(object): # {{{
# either can appear as the author
book_authors = clean_string(authors_to_string(book.authors))
if book_authors in d['authors']:
book.in_library = True
book.application_id = \
d['authors'][book_authors].application_id
if update_metadata:
book.smart_update(d['authors'][book_authors],
replace_metadata=True)
elif book_authors in d['author_sort']:
book.in_library = True
book.in_library = 'AUTHOR'
book.application_id = \
d['author_sort'][book_authors].application_id
d['authors'][book_authors].application_id
elif book_authors in d['author_sort']:
if update_metadata:
book.smart_update(d['author_sort'][book_authors],
replace_metadata=True)
book.in_library = 'AUTH_SORT'
book.application_id = \
d['author_sort'][book_authors].application_id
else:
# Book definitely not matched. Clear its application ID
book.application_id = None

View File

@ -32,7 +32,7 @@ class CheckLibraryDialog(QDialog):
self.copy = QPushButton(_('Copy to clipboard'))
self.copy.setDefault(False)
self.copy.clicked.connect(self.copy_to_clipboard)
self.ok = QPushButton('&OK')
self.ok = QPushButton('&Done')
self.ok.setDefault(True)
self.ok.clicked.connect(self.accept)
self.cancel = QPushButton('&Cancel')

View File

@ -263,7 +263,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>00</height>
<height>0</height>
</size>
</property>
</spacer>
@ -357,13 +357,13 @@ from the value in the box</string>
</item>
<item row="12" column="0" colspan="2">
<widget class="QCheckBox" name="change_title_to_title_case">
<property name="text">
<string>Change title to title case</string>
</property>
<property name="toolTip">
<string>Force the title to be in title case. If both this and swap authors are checked,
title and author are swapped before the title case is set</string>
</property>
<property name="text">
<string>Change title to title case</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
@ -486,15 +486,15 @@ Future conversion of these books will use the default settings.</string>
</item>
<item row="4" column="1">
<widget class="HistoryLineEdit" name="search_for">
<property name="toolTip">
<string>Enter the what you are looking for, either plain text or a regular expression, depending on the mode</string>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>100</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enter the what you are looking for, either plain text or a regular expression, depending on the mode</string>
</property>
</widget>
</item>
<item row="4" column="2">
@ -656,6 +656,14 @@ nothing should be put between the original text and the inserted text</string>
<bool>true</bool>
</property>
<widget class="QWidget" name="gridLayoutWidget_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>122</width>
<height>34</height>
</rect>
</property>
<layout class="QGridLayout" name="testgrid">
<item row="8" column="0">
<widget class="QLabel" name="label_31">
@ -733,14 +741,33 @@ nothing should be put between the original text and the inserted text</string>
<tabstop>author_sort</tabstop>
<tabstop>rating</tabstop>
<tabstop>publisher</tabstop>
<tabstop>tag_editor_button</tabstop>
<tabstop>tags</tabstop>
<tabstop>tag_editor_button</tabstop>
<tabstop>remove_tags</tabstop>
<tabstop>remove_all_tags</tabstop>
<tabstop>series</tabstop>
<tabstop>clear_series</tabstop>
<tabstop>autonumber_series</tabstop>
<tabstop>series_numbering_restarts</tabstop>
<tabstop>series_start_number</tabstop>
<tabstop>remove_format</tabstop>
<tabstop>remove_conversion_settings</tabstop>
<tabstop>swap_title_and_author</tabstop>
<tabstop>change_title_to_title_case</tabstop>
<tabstop>button_box</tabstop>
<tabstop>central_widget</tabstop>
<tabstop>search_field</tabstop>
<tabstop>search_mode</tabstop>
<tabstop>search_for</tabstop>
<tabstop>case_sensitive</tabstop>
<tabstop>replace_with</tabstop>
<tabstop>replace_func</tabstop>
<tabstop>destination_field</tabstop>
<tabstop>replace_mode</tabstop>
<tabstop>comma_separated</tabstop>
<tabstop>scrollArea11</tabstop>
<tabstop>test_text</tabstop>
<tabstop>test_result</tabstop>
</tabstops>
<resources>
<include location="../../../../resources/images.qrc"/>

View File

@ -25,7 +25,7 @@ from calibre.ebooks.metadata.covers import download_cover
from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.metadata import MetaInformation
from calibre.utils.config import prefs, tweaks
from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp
from calibre.utils.date import qt_to_dt, local_tz, utcfromtimestamp, utc_tz
from calibre.customize.ui import run_plugins_on_import, get_isbndb_key
from calibre.gui2.preferences.social import SocialMetadata
from calibre.gui2.custom_column_widgets import populate_metadata_page
@ -434,9 +434,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.pubdate.setDate(QDate(pubdate.year, pubdate.month,
pubdate.day))
timestamp = db.timestamp(self.id, index_is_id=True)
self.orig_timestamp = timestamp
self.date.setDate(QDate(timestamp.year, timestamp.month,
timestamp.day))
self.orig_date = qt_to_dt(self.date.date())
exts = self.db.formats(row)
if exts:
@ -802,7 +802,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.db.set_pubdate(self.id, d, notify=False, commit=False)
d = self.date.date()
d = qt_to_dt(d)
if d.date() != self.orig_timestamp.date():
if d != self.orig_date:
self.db.set_timestamp(self.id, d, notify=False, commit=False)
self.db.commit()

View File

@ -38,7 +38,10 @@ class CustomRecipeModel(QAbstractListModel):
return False
def rowCount(self, *args):
return len(self.recipe_model.custom_recipe_collection)
try:
return len(self.recipe_model.custom_recipe_collection)
except:
return 0
def data(self, index, role):
if role == Qt.DisplayRole:
@ -100,6 +103,8 @@ class UserProfiles(ResizableDialog, Ui_Dialog):
def break_cycles(self):
self.recipe_model = self._model.recipe_model = None
self.available_profiles = None
self.model = self._model = None
def remove_selected_items(self):
indices = self.available_profiles.selectionModel().selectedRows()

View File

@ -24,7 +24,7 @@ from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
REGEXP_MATCH, CoverCache, MetadataBackup
from calibre.library.cli import parse_series_string
from calibre import strftime, isbytestring, prepare_string_for_xml
from calibre.constants import filesystem_encoding
from calibre.constants import filesystem_encoding, DEBUG
from calibre.gui2.library import DEFAULT_SORT
def human_readable(size, precision=1):
@ -699,6 +699,10 @@ class BooksModel(QAbstractTableModel): # {{{
if role == Qt.DisplayRole:
return QVariant(self.headers[self.column_map[section]])
return NONE
if DEBUG and role == Qt.ToolTipRole and orientation == Qt.Vertical:
col = self.db.field_metadata['uuid']['rec_index']
return QVariant(_('This book\'s UUID is "{0}"').format(self.db.data[section][col]))
if role == Qt.DisplayRole: # orientation is vertical
return QVariant(section+1)
return NONE
@ -1206,6 +1210,8 @@ class DeviceBooksModel(BooksModel): # {{{
if tags:
tags.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
return QVariant(', '.join(tags))
elif DEBUG and cname == 'inlibrary':
return QVariant(self.db[self.map[row]].in_library)
elif role == Qt.ToolTipRole and index.isValid():
if self.map[row] in self.indices_to_be_deleted():
return QVariant(_('Marked for deletion'))
@ -1227,8 +1233,10 @@ class DeviceBooksModel(BooksModel): # {{{
return NONE
def headerData(self, section, orientation, role):
if role == Qt.ToolTipRole:
if role == Qt.ToolTipRole and orientation == Qt.Horizontal:
return QVariant(_('The lookup/search name is "{0}"').format(self.column_map[section]))
if DEBUG and role == Qt.ToolTipRole and orientation == Qt.Vertical:
return QVariant(_('This book\'s UUID is "{0}"').format(self.db[self.map[section]].uuid))
if role != Qt.DisplayRole:
return NONE
if orientation == Qt.Horizontal:

View File

@ -30,6 +30,7 @@ class BooksView(QTableView): # {{{
def __init__(self, parent, modelcls=BooksModel):
QTableView.__init__(self, parent)
self.drag_allowed = True
self.setDragEnabled(True)
self.setDragDropOverwriteMode(False)
self.setDragDropMode(self.DragDrop)
@ -505,6 +506,8 @@ class BooksView(QTableView): # {{{
return QTableView.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
if not self.drag_allowed:
return
if self.drag_start_pos is None:
return QTableView.mouseMoveEvent(self, event)
@ -613,7 +616,7 @@ class BooksView(QTableView): # {{{
def close(self):
self._model.close()
def set_editable(self, editable):
def set_editable(self, editable, supports_backloading):
self._model.set_editable(editable)
def connect_to_search_box(self, sb, search_done):
@ -700,5 +703,9 @@ class DeviceBooksView(BooksView): # {{{
error_dialog(self, _('Not allowed'),
_('Dropping onto a device is not supported. First add the book to the calibre library.')).exec_()
def set_editable(self, editable, supports_backloading):
self._model.set_editable(editable)
self.drag_allowed = supports_backloading
# }}}

View File

@ -5,81 +5,14 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QProgressDialog, QThread, Qt, pyqtSignal
from calibre.gui2.dialogs.check_library import CheckLibraryDialog
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.misc_ui import Ui_Form
from calibre.gui2 import error_dialog, config, warning_dialog, \
open_local_file, info_dialog
from calibre.gui2 import error_dialog, config, open_local_file, info_dialog
from calibre.constants import isosx
# Check Integrity {{{
class VacThread(QThread):
check_done = pyqtSignal(object, object)
callback = pyqtSignal(object, object)
def __init__(self, parent, db):
QThread.__init__(self, parent)
self.db = db
self._parent = parent
def run(self):
err = bad = None
try:
bad = self.db.check_integrity(self.callbackf)
except:
import traceback
err = traceback.format_exc()
self.check_done.emit(bad, err)
def callbackf(self, progress, msg):
self.callback.emit(progress, msg)
class CheckIntegrity(QProgressDialog):
def __init__(self, db, parent=None):
QProgressDialog.__init__(self, parent)
self.db = db
self.setCancelButton(None)
self.setMinimum(0)
self.setMaximum(100)
self.setWindowTitle(_('Checking database integrity'))
self.setAutoReset(False)
self.setValue(0)
self.vthread = VacThread(self, db)
self.vthread.check_done.connect(self.check_done,
type=Qt.QueuedConnection)
self.vthread.callback.connect(self.callback, type=Qt.QueuedConnection)
self.vthread.start()
def callback(self, progress, msg):
self.setLabelText(msg)
self.setValue(int(100*progress))
def check_done(self, bad, err):
if err:
error_dialog(self, _('Error'),
_('Failed to check database integrity'),
det_msg=err, show=True)
elif bad:
titles = [self.db.title(x, index_is_id=True) for x in bad]
det_msg = '\n'.join(titles)
warning_dialog(self, _('Some inconsistencies found'),
_('The following books had formats listed in the '
'database that are not actually available. '
'The entries for the formats have been removed. '
'You should check them manually. This can '
'happen if you manipulate the files in the '
'library folder directly.'), det_msg=det_msg, show=True)
self.reset()
# }}}
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
@ -88,39 +21,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('worker_limit', config, restart_required=True)
r('enforce_cpu_limit', config, restart_required=True)
self.device_detection_button.clicked.connect(self.debug_device_detection)
self.compact_button.clicked.connect(self.compact)
self.button_all_books_dirty.clicked.connect(self.mark_dirty)
self.button_check_library.clicked.connect(self.check_library)
self.button_open_config_dir.clicked.connect(self.open_config_dir)
self.button_osx_symlinks.clicked.connect(self.create_symlinks)
self.button_osx_symlinks.setVisible(isosx)
def mark_dirty(self):
db = self.gui.library_view.model().db
db.dirtied(list(db.data.iterallids()))
info_dialog(self, _('Backup metadata'),
_('Metadata will be backed up while calibre is running, at the '
'rate of 30 books per minute.'), show=True)
def check_library(self):
db = self.gui.library_view.model().db
d = CheckLibraryDialog(self.gui.parent(), db)
d.exec_()
def debug_device_detection(self, *args):
from calibre.gui2.preferences.device_debug import DebugDevice
d = DebugDevice(self)
d.exec_()
def compact(self, *args):
m = self.gui.library_view.model()
m.stop_metadata_backup()
try:
d = CheckIntegrity(m.db, self)
d.exec_()
finally:
m.start_metadata_backup()
def open_config_dir(self, *args):
from calibre.utils.config import config_dir
open_local_file(config_dir)

View File

@ -77,13 +77,6 @@
</property>
</spacer>
</item>
<item row="5" column="0" colspan="2">
<widget class="QPushButton" name="compact_button">
<property name="text">
<string>&amp;Check database integrity</string>
</property>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer_7">
<property name="orientation">
@ -124,20 +117,6 @@
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="QPushButton" name="button_all_books_dirty">
<property name="text">
<string>Back up metadata of all books</string>
</property>
</widget>
</item>
<item row="11" column="0" colspan="2">
<widget class="QPushButton" name="button_check_library">
<property name="text">
<string>Check the library folders for potential problems</string>
</property>
</widget>
</item>
<item row="20" column="0">
<spacer name="verticalSpacer_9">
<property name="orientation">

View File

@ -73,6 +73,8 @@ class SearchBox2(QComboBox):
self.normal_background = 'rgb(255, 255, 255, 0%)'
self.line_edit = SearchLineEdit(self)
self.setLineEdit(self.line_edit)
c = self.line_edit.completer()
c.setCompletionMode(c.PopupCompletion)
self.line_edit.key_pressed.connect(self.key_pressed,
type=Qt.DirectConnection)
self.line_edit.mouse_released.connect(self.mouse_released,

View File

@ -84,12 +84,14 @@ class TagsView(QTreeView): # {{{
self.setAcceptDrops(True)
self.setDragDropMode(self.DropOnly)
self.setDropIndicatorShown(True)
self.setAutoExpandDelay(500)
def set_database(self, db, tag_match, sort_by):
self.hidden_categories = config['tag_browser_hidden_categories']
self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories,
search_restriction=None)
search_restriction=None,
drag_drop_finished=self.drag_drop_finished)
self.sort_by = sort_by
self.tag_match = tag_match
self.db = db
@ -109,103 +111,6 @@ class TagsView(QTreeView): # {{{
def database_changed(self, event, ids):
self.refresh_required.emit()
def dragEnterEvent(self, event):
md = event.mimeData()
if md.hasFormat("application/calibre+from_library"):
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
allowed = False
idx = self.indexAt(event.pos())
m = self.model()
p = m.parent(idx)
if idx.isValid() and p.isValid():
item = m.data(p, Qt.UserRole)
fm = self.db.metadata_for_field(item.category_key)
if item.category_key in \
('tags', 'series', 'authors', 'rating', 'publisher') or\
(fm['is_custom'] and \
fm['datatype'] in ['text', 'rating', 'series']):
allowed = True
if allowed:
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
idx = self.indexAt(event.pos())
m = self.model()
p = m.parent(idx)
if idx.isValid() and p.isValid():
item = m.data(p, Qt.UserRole)
if item.type == TagTreeItem.CATEGORY:
fm = self.db.metadata_for_field(item.category_key)
if item.category_key in \
('tags', 'series', 'authors', 'rating', 'publisher') or\
(fm['is_custom'] and \
fm['datatype'] in ['text', 'rating', 'series']):
child = m.data(idx, Qt.UserRole)
md = event.mimeData()
mime = 'application/calibre+from_library'
ids = list(map(int, str(md.data(mime)).split()))
self.handle_drop(item, child, ids)
event.accept()
return
event.ignore()
def handle_drop(self, parent, child, ids):
# print 'Dropped ids:', ids, parent.category_key, child.tag.name
key = parent.category_key
if (key == 'authors' and len(ids) >= 5):
if not confirm('<p>'+_('Changing the authors for several books can '
'take a while. Are you sure?')
+'</p>', 'tag_browser_drop_authors', self):
return
elif len(ids) > 15:
if not confirm('<p>'+_('Changing the metadata for that many books '
'can take a while. Are you sure?')
+'</p>', 'tag_browser_many_changes', self):
return
fm = self.db.metadata_for_field(key)
is_multiple = fm['is_multiple']
val = child.tag.name
for id in ids:
mi = self.db.get_metadata(id, index_is_id=True)
# Prepare to ignore the author, unless it is changed. Title is
# always ignored -- see the call to set_metadata
set_authors = False
# Author_sort cannot change explicitly. Changing the author might
# change it.
mi.author_sort = None # Never will change by itself.
if key == 'authors':
mi.authors = [val]
set_authors=True
elif fm['datatype'] == 'rating':
mi.set(key, len(val) * 2)
elif fm['is_custom'] and fm['datatype'] == 'series':
mi.set(key, val, extra=1.0)
elif is_multiple:
new_val = mi.get(key, [])
if val in new_val:
# Fortunately, only one field can change, so the continue
# won't break anything
continue
new_val.append(val)
mi.set(key, new_val)
else:
mi.set(key, val)
self.db.set_metadata(id, mi, set_title=False,
set_authors=set_authors, commit=False)
self.db.commit()
self.drag_drop_finished.emit(ids)
@property
def match_all(self):
return self.tag_match and self.tag_match.currentIndex() > 0
@ -374,7 +279,8 @@ class TagsView(QTreeView): # {{{
try:
self._model = TagsModel(self.db, parent=self,
hidden_categories=self.hidden_categories,
search_restriction=self.search_restriction)
search_restriction=self.search_restriction,
drag_drop_finished=self.drag_drop_finished)
self.setModel(self._model)
except:
# The DB must be gone. Set the model to None and hope that someone
@ -469,7 +375,8 @@ class TagTreeItem(object): # {{{
class TagsModel(QAbstractItemModel): # {{{
def __init__(self, db, parent, hidden_categories=None, search_restriction=None):
def __init__(self, db, parent, hidden_categories=None,
search_restriction=None, drag_drop_finished=None):
QAbstractItemModel.__init__(self, parent)
# must do this here because 'QPixmap: Must construct a QApplication
@ -487,6 +394,7 @@ class TagsModel(QAbstractItemModel): # {{{
':user' : QIcon(I('drawer.png')),
'search' : QIcon(I('search.png'))})
self.categories_with_ratings = ['authors', 'series', 'publisher', 'tags']
self.drag_drop_finished = drag_drop_finished
self.icon_state_map = [None, QIcon(I('plus.png')), QIcon(I('minus.png'))]
self.db = db
@ -519,6 +427,79 @@ class TagsModel(QAbstractItemModel): # {{{
tag.avg_rating = None
TagTreeItem(parent=c, data=tag, icon_map=self.icon_state_map)
def mimeTypes(self):
return ["application/calibre+from_library"]
def dropMimeData(self, md, action, row, column, parent):
if not md.hasFormat("application/calibre+from_library") or \
action != Qt.CopyAction:
return False
idx = parent
if idx.isValid():
node = self.data(idx, Qt.UserRole)
if node.type == TagTreeItem.TAG:
fm = self.db.metadata_for_field(node.tag.category)
if node.tag.category in \
('tags', 'series', 'authors', 'rating', 'publisher') or \
(fm['is_custom'] and \
fm['datatype'] in ['text', 'rating', 'series']):
mime = 'application/calibre+from_library'
ids = list(map(int, str(md.data(mime)).split()))
self.handle_drop(node, ids)
return True
return False
def handle_drop(self, on_node, ids):
#print 'Dropped ids:', ids, on_node.tag
key = on_node.tag.category
if (key == 'authors' and len(ids) >= 5):
if not confirm('<p>'+_('Changing the authors for several books can '
'take a while. Are you sure?')
+'</p>', 'tag_browser_drop_authors', self.parent()):
return
elif len(ids) > 15:
if not confirm('<p>'+_('Changing the metadata for that many books '
'can take a while. Are you sure?')
+'</p>', 'tag_browser_many_changes', self.parent()):
return
fm = self.db.metadata_for_field(key)
is_multiple = fm['is_multiple']
val = on_node.tag.name
for id in ids:
mi = self.db.get_metadata(id, index_is_id=True)
# Prepare to ignore the author, unless it is changed. Title is
# always ignored -- see the call to set_metadata
set_authors = False
# Author_sort cannot change explicitly. Changing the author might
# change it.
mi.author_sort = None # Never will change by itself.
if key == 'authors':
mi.authors = [val]
set_authors=True
elif fm['datatype'] == 'rating':
mi.set(key, len(val) * 2)
elif fm['is_custom'] and fm['datatype'] == 'series':
mi.set(key, val, extra=1.0)
elif is_multiple:
new_val = mi.get(key, [])
if val in new_val:
# Fortunately, only one field can change, so the continue
# won't break anything
continue
new_val.append(val)
mi.set(key, new_val)
else:
mi.set(key, val)
self.db.set_metadata(id, mi, set_title=False,
set_authors=set_authors, commit=False)
self.db.commit()
self.drag_drop_finished.emit(ids)
def set_search_restriction(self, s):
self.search_restriction = s
@ -650,12 +631,19 @@ class TagsModel(QAbstractItemModel): # {{{
def flags(self, index, *args):
ans = Qt.ItemIsEnabled|Qt.ItemIsSelectable|Qt.ItemIsEditable
if index.isValid() and self.parent(index).isValid():
ans |= Qt.ItemIsDropEnabled
if index.isValid():
node = self.data(index, Qt.UserRole)
if node.type == TagTreeItem.TAG:
fm = self.db.metadata_for_field(node.tag.category)
if node.tag.category in \
('tags', 'series', 'authors', 'rating', 'publisher') or \
(fm['is_custom'] and \
fm['datatype'] in ['text', 'rating', 'series']):
ans |= Qt.ItemIsDropEnabled
return ans
def supportedDropActions(self):
return Qt.CopyAction|Qt.MoveAction
return Qt.CopyAction
def path_for_index(self, index):
ans = []
@ -836,11 +824,11 @@ class TagBrowserMixin(object): # {{{
rename_func = partial(db.rename_custom_item, label=cc_label)
delete_func = partial(db.delete_custom_item_using_id, label=cc_label)
if rename_func:
for item in to_delete:
delete_func(item)
for text in to_rename:
for old_id in to_rename[text]:
rename_func(old_id, new_name=unicode(text))
for item in to_delete:
delete_func(item)
# Clean up everything, as information could have changed for many books.
self.library_view.model().refresh()

View File

@ -3,13 +3,14 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import traceback
from PyQt4.Qt import QThread, pyqtSignal, Qt, QUrl
from PyQt4.Qt import QThread, pyqtSignal, Qt, QUrl, QDialog, QGridLayout, \
QLabel, QCheckBox, QDialogButtonBox, QIcon, QPixmap
import mechanize
from calibre.constants import __appname__, __version__, iswindows, isosx
from calibre import browser
from calibre.utils.config import prefs
from calibre.gui2 import config, dynamic, question_dialog, open_url
from calibre.gui2 import config, dynamic, open_url
URL = 'http://status.calibre-ebook.com/latest'
@ -37,6 +38,53 @@ class CheckForUpdates(QThread):
traceback.print_exc()
self.sleep(self.INTERVAL)
class UpdateNotification(QDialog):
def __init__(self, version, parent=None):
QDialog.__init__(self, parent)
self.resize(400, 250)
self.l = QGridLayout()
self.setLayout(self.l)
self.logo = QLabel()
self.logo.setMaximumWidth(110)
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
self.label = QLabel('<p>'+
_('%s has been updated to version <b>%s</b>. '
'See the <a href="http://calibre-ebook.com/whats-new'
'">new features</a>. Visit the download pa'
'ge?')%(__appname__, version))
self.label.setOpenExternalLinks(True)
self.label.setWordWrap(True)
self.setWindowTitle(_('Update available!'))
self.setWindowIcon(QIcon(I('lt.png')))
self.l.addWidget(self.logo, 0, 0)
self.l.addWidget(self.label, 0, 1)
self.cb = QCheckBox(
_('Show this notification for future updates'), self)
self.l.addWidget(self.cb, 1, 0, 1, -1)
self.cb.setChecked(config.get('new_version_notification'))
self.cb.stateChanged.connect(self.show_future)
self.bb = QDialogButtonBox(self)
b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole)
b.setDefault(True)
b.setIcon(QIcon(I('arrow-down.png')))
self.bb.addButton(self.bb.Cancel)
self.l.addWidget(self.bb, 2, 0, 1, -1)
self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject)
dynamic.set('update to version %s'%version, False)
def show_future(self, *args):
config.set('new_version_notification', bool(self.cb.isChecked()))
def accept(self):
url = 'http://calibre-ebook.com/download_'+\
('windows' if iswindows else 'osx' if isosx else 'linux')
open_url(QUrl(url))
QDialog.accept(self)
class UpdateMixin(object):
def __init__(self, opts):
@ -53,15 +101,8 @@ class UpdateMixin(object):
if config.get('new_version_notification') and \
dynamic.get('update to version %s'%version, True):
if question_dialog(self, _('Update available'),
_('%s has been updated to version %s. '
'See the <a href="http://calibre-ebook.com/whats-new'
'">new features</a>. Visit the download pa'
'ge?')%(__appname__, version)):
url = 'http://calibre-ebook.com/download_'+\
('windows' if iswindows else 'osx' if isosx else 'linux')
open_url(QUrl(url))
dynamic.set('update to version %s'%version, False)
self._update_notification__ = UpdateNotification(version,
parent=self)
self._update_notification__.show()

View File

@ -150,6 +150,7 @@ class Document(QWebPage):
self.setObjectName("py_bridge")
self.debug_javascript = False
self.current_language = None
self.loaded_javascript = False
self.setLinkDelegationPolicy(self.DelegateAllLinks)
self.scroll_marks = []
@ -175,9 +176,9 @@ class Document(QWebPage):
self.set_user_stylesheet()
self.misc_config()
# Load jQuery
self.connect(self.mainFrame(), SIGNAL('javaScriptWindowObjectCleared()'),
self.load_javascript_libraries)
# Load javascript
self.mainFrame().javaScriptWindowObjectCleared.connect(
self.add_window_objects)
def set_user_stylesheet(self):
raw = config().parse().user_css
@ -196,16 +197,20 @@ class Document(QWebPage):
if self.do_fit_images:
self.javascript('setup_image_scaling_handlers()')
def add_window_objects(self):
self.mainFrame().addToJavaScriptWindowObject("py_bridge", self)
self.loaded_javascript = False
def load_javascript_libraries(self):
global bookmarks, referencing, hyphenation, jquery, jquery_scrollTo, hyphenator, images
self.mainFrame().addToJavaScriptWindowObject("py_bridge", self)
if self.loaded_javascript:
return
self.loaded_javascript = True
if jquery is None:
jquery = P('content_server/jquery.js', data=True)
self.javascript(jquery)
if jquery_scrollTo is None:
jquery_scrollTo = P('viewer/jquery_scrollTo.js', data=True)
if hyphenator is None:
hyphenator = P('viewer/hyphenate/Hyphenator.js', data=True).decode('utf-8')
self.javascript(jquery)
self.javascript(jquery_scrollTo)
if bookmarks is None:
bookmarks = P('viewer/bookmarks.js', data=True)
@ -224,6 +229,8 @@ class Document(QWebPage):
if not lang:
lang = default_lang
lang = lang.lower()[:2]
if hyphenator is None:
hyphenator = P('viewer/hyphenate/Hyphenator.js', data=True).decode('utf-8')
self.javascript(hyphenator)
p = P('viewer/hyphenate/patterns/%s.js'%lang)
if not os.path.exists(p):
@ -256,6 +263,9 @@ class Document(QWebPage):
self.javascript('goto_reference("%s")'%ref)
def goto_bookmark(self, bm):
bm = bm.strip()
if bm.startswith('>'):
bm = bm[1:].strip()
self.javascript('scroll_to_bookmark("%s")'%bm)
def javascript(self, string, typ=None):
@ -641,6 +651,7 @@ class DocumentView(QWebView):
# An <iframe> finished loading
return
self.loading_url = None
self.document.load_javascript_libraries()
self.document.set_bottom_padding(0)
self.document.fit_images()
self._size_hint = self.document.mainFrame().contentsSize()
@ -804,6 +815,7 @@ class DocumentView(QWebView):
def wheelEvent(self, event):
if event.delta() < -14:
if self.document.at_bottom:
self.scroll_by(y=15) # at_bottom can lie on windows
if self.manager is not None:
self.manager.next_document()
event.accept()

View File

@ -695,6 +695,9 @@ def config(defaults=None):
c.add_opt('raise_window', ['--raise-window'], default=False,
help=_('If specified, viewer window will try to come to the '
'front when started.'))
c.add_opt('full_screen', ['--full-screen', '--fullscreen', '-f'], default=False,
help=_('If specified, viewer window will try to open '
'full screen when started.'))
c.add_opt('remember_window_size', default=False,
help=_('Remember last used window size'))
c.add_opt('debug_javascript', ['--debug-javascript'], default=False,
@ -726,8 +729,10 @@ def main(args=sys.argv):
main.show()
if opts.raise_window:
main.raise_()
with main:
return app.exec_()
if opts.full_screen:
main.action_full_screen.trigger()
with main:
return app.exec_()
return 0
if __name__ == '__main__':

View File

@ -958,16 +958,22 @@ def command_check_library(args, dbpath):
def restore_database_option_parser():
parser = get_parser(_(
'''
%prog restore_database [options]
'''\
%prog restore_database [options]
Restore this database from the metadata stored in OPF
files in each directory of the calibre library. This is
useful if your metadata.db file has been corrupted.
Restore this database from the metadata stored in OPF files in each
directory of the calibre library. This is useful if your metadata.db file
has been corrupted.
WARNING: This completely regenerates your database. You will
lose stored per-book conversion settings and custom recipes.
WARNING: This command completely regenerates your database. You will lose
all saved searches, user categories, plugboards, stored per-book conversion
settings, and custom recipes. Restored metadata will only be as accurate as
what is found in the OPF files.
'''))
parser.add_option('-r', '--really-do-it', default=False, action='store_true',
help=_('Really do the recovery. The command will not run '
'unless this option is specified.'))
return parser
def command_restore_database(args, dbpath):
@ -978,6 +984,12 @@ def command_restore_database(args, dbpath):
parser.print_help()
return 1
if not opts.really_do_it:
prints(_('You must provide the --really-do-it option to do a'
' recovery'), end='\n\n')
parser.print_help()
return 1
if opts.library_path is not None:
dbpath = opts.library_path
@ -1025,10 +1037,10 @@ information is the equivalent of what is shown in the tags pane.
parser.add_option('-q', '--quote', default='"',
help=_('The character to put around the category value in CSV mode. '
'Default is quotes (").'))
parser.add_option('-r', '--categories', default=None, dest='report',
parser.add_option('-r', '--categories', default='', dest='report',
help=_("Comma-separated list of category lookup names.\n"
"Default: all"))
parser.add_option('-w', '--line-width', default=-1, type=int,
parser.add_option('-w', '--idth', default=-1, type=int,
help=_('The maximum width of a single line in the output. '
'Defaults to detecting screen size.'))
parser.add_option('-s', '--separator', default=',',
@ -1052,8 +1064,10 @@ def command_list_categories(args, dbpath):
db = LibraryDatabase2(dbpath)
category_data = db.get_categories()
data = []
report_on = [c.strip() for c in opts.report.split(',') if c.strip()]
categories = [k for k in category_data.keys()
if db.metadata_for_field(k)['kind'] not in ['user', 'search']]
if db.metadata_for_field(k)['kind'] not in ['user', 'search'] and
(not report_on or k in report_on)]
categories.sort(cmp=lambda x,y: cmp(x if x[0] != '#' else x[1:],
y if y[0] != '#' else y[1:]))

View File

@ -10,6 +10,7 @@ import os, sys, shutil, cStringIO, glob, time, functools, traceback, re
from itertools import repeat
from math import floor
from Queue import Queue
from operator import itemgetter
from PyQt4.QtGui import QImage
@ -68,7 +69,7 @@ copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
class Tag(object):
def __init__(self, name, id=None, count=0, state=0, avg=0, sort=None,
tooltip=None, icon=None):
tooltip=None, icon=None, category=None):
self.name = name
self.id = id
self.count = count
@ -81,9 +82,11 @@ class Tag(object):
tooltip = _('%sAverage rating is %3.1f')%(tooltip, self.avg_rating)
self.tooltip = tooltip
self.icon = icon
self.category = category
def __unicode__(self):
return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.tooltip)
return u'%s:%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state,
self.category, self.tooltip)
def __str__(self):
return unicode(self).encode('utf-8')
@ -681,7 +684,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
mi = self.data.get(idx, self.FIELD_MAP['all_metadata'],
row_is_id = index_is_id)
if mi is not None:
if get_cover and mi.cover is None:
if get_cover:
# Always get the cover, because the value can be wrong if the
# original mi was from the OPF
mi.cover = self.cover(idx, index_is_id=index_is_id, as_path=True)
return mi
@ -1100,21 +1105,22 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
tooltip = self.custom_column_label_map[label]['name']
datatype = cat['datatype']
avgr = itemgetter(3)
item_not_zero_func = lambda x: x[2] > 0
if datatype == 'rating':
# eliminate the zero ratings line as well as count == 0
item_not_zero_func = (lambda x: x[1] > 0 and x[2] > 0)
formatter = (lambda x:u'\u2605'*int(x/2))
avgr = itemgetter(1)
elif category == 'authors':
item_not_zero_func = (lambda x: x[2] > 0)
# Clean up the authors strings to human-readable form
formatter = (lambda x: x.replace('|', ','))
else:
item_not_zero_func = (lambda x: x[2] > 0)
formatter = (lambda x:unicode(x))
categories[category] = [Tag(formatter(r[1]), count=r[2], id=r[0],
avg=r[3], sort=r[4],
icon=icon, tooltip=tooltip)
avg=avgr(r), sort=r[4], icon=icon,
tooltip=tooltip, category=category)
for r in data if item_not_zero_func(r)]
# Needed for legacy databases that have multiple ratings that
@ -1146,7 +1152,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
WHERE format="%s"'''%fmt,
all=False)
if count > 0:
categories['formats'].append(Tag(fmt, count=count, icon=icon))
categories['formats'].append(Tag(fmt, count=count, icon=icon,
category='formats'))
if sort == 'popularity':
categories['formats'].sort(key=lambda x: x.count, reverse=True)
@ -1192,7 +1199,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if icon_map and 'search' in icon_map:
icon = icon_map['search']
for srch in saved_searches().names():
items.append(Tag(srch, tooltip=saved_searches().lookup(srch), icon=icon))
items.append(Tag(srch, tooltip=saved_searches().lookup(srch),
icon=icon, category='search'))
if len(items):
if icon_map is not None:
icon_map['search'] = icon_map['search']
@ -1252,6 +1260,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
'''
Set metadata for the book `id` from the `Metadata` object `mi`
'''
if callable(getattr(mi, 'to_book_metadata', None)):
# Handle code passing in a OPF object instead of a Metadata object
mi = mi.to_book_metadata()
def doit(func, *args, **kwargs):
try:
func(*args, **kwargs)
@ -1281,8 +1293,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
doit(self.set_series, id, mi.series, notify=False, commit=False)
if mi.cover_data[1] is not None:
doit(self.set_cover, id, mi.cover_data[1]) # doesn't use commit
elif mi.cover is not None and os.access(mi.cover, os.R_OK):
doit(self.set_cover, id, lopen(mi.cover, 'rb'))
elif mi.cover is not None:
if os.access(mi.cover, os.R_OK):
with lopen(mi.cover, 'rb') as f:
doit(self.set_cover, id, f)
if mi.tags:
doit(self.set_tags, id, mi.tags, notify=False, commit=False)
if mi.comments:
@ -1462,6 +1476,16 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if notify:
self.notify('metadata', [id])
def set_uuid(self, id, uuid, notify=True, commit=True):
if uuid:
self.conn.execute('UPDATE books SET uuid=? WHERE id=?', (uuid, id))
self.data.set(id, self.FIELD_MAP['uuid'], uuid, row_is_id=True)
self.dirtied([id], commit=False)
if commit:
self.conn.commit()
if notify:
self.notify('metadata', [id])
# Convenience methods for tags_list_editor
# Note: we generally do not need to refresh_ids because library_view will
# refresh everything.
@ -1485,7 +1509,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return result
def rename_tag(self, old_id, new_name):
new_name = new_name.strip()
# It is possible that new_name is in fact a set of names. Split it on
# comma to find out. If it is, then rename the first one and append the
# rest
new_names = [t.strip() for t in new_name.strip().split(',') if t.strip()]
new_name = new_names[0]
new_names = new_names[1:]
# get the list of books that reference the tag being changed
books = self.conn.get('''SELECT book from books_tags_link
WHERE tag=?''', (old_id,))
books = [b[0] for b in books]
new_id = self.conn.get(
'''SELECT id from tags
WHERE name=?''', (new_name,), all=False)
@ -1501,9 +1536,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
# all the changes. To get around this, we first delete any links
# to the new_id from books referencing the old_id, so that
# renaming old_id to new_id will be unique on the book
books = self.conn.get('''SELECT book from books_tags_link
WHERE tag=?''', (old_id,))
for (book_id,) in books:
for book_id in books:
self.conn.execute('''DELETE FROM books_tags_link
WHERE book=? and tag=?''', (book_id, new_id))
@ -1512,7 +1545,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
WHERE tag=?''',(new_id, old_id,))
# Get rid of the no-longer used publisher
self.conn.execute('DELETE FROM tags WHERE id=?', (old_id,))
self.dirty_books_referencing('tags', new_id, commit=False)
if new_names:
# have some left-over names to process. Add them to the book.
for book_id in books:
self.set_tags(book_id, new_names, append=True, notify=False,
commit=False)
self.dirtied(books, commit=False)
self.conn.commit()
def delete_tag_using_id(self, id):
@ -2110,7 +2149,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
return None, len(ids)
def import_book(self, mi, formats, notify=True, import_hooks=True,
apply_import_tags=True):
apply_import_tags=True, preserve_uuid=False):
series_index = 1.0 if mi.series_index is None else mi.series_index
if apply_import_tags:
self._add_newbook_tag(mi)
@ -2133,6 +2172,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
if mi.pubdate is None:
mi.pubdate = utcnow()
self.set_metadata(id, mi, ignore_errors=True)
if preserve_uuid and mi.uuid:
self.set_uuid(id, mi.uuid, commit=False)
for path in formats:
ext = os.path.splitext(path)[1][1:].lower()
if ext == 'opf':
@ -2142,6 +2183,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
else:
with lopen(path, 'rb') as f:
self.add_format(id, ext, f, index_is_id=True)
# Mark the book dirty, It probably already has been done by
# set_metadata, but probably isn't good enough
self.dirtied([id], commit=False)
self.conn.commit()
self.data.refresh_ids(self, [id]) # Needed to update format list and size
if notify:

View File

@ -200,6 +200,8 @@ class Restore(Thread):
def restore_book(self, book, db):
db.create_book_entry(book['mi'], add_duplicates=True,
force_id=book['id'])
if book['mi'].uuid:
db.set_uuid(book['id'], book['mi'].uuid, commit=False, notify=False)
db.conn.execute('UPDATE books SET path=? WHERE id=?', (book['path'],
book['id']))

View File

@ -22,6 +22,7 @@ from calibre.library.server.mobile import MobileServer
from calibre.library.server.xml import XMLServer
from calibre.library.server.opds import OPDSServer
from calibre.library.server.cache import Cache
from calibre.library.server.browse import BrowseServer
class DispatchController(object): # {{{
@ -53,7 +54,8 @@ class DispatchController(object): # {{{
# }}}
class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache):
class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
BrowseServer):
server_name = __appname__ + '/' + __version__

View File

@ -0,0 +1,385 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import operator, os, json
from urllib import quote
from binascii import hexlify
import cherrypy
from calibre.constants import filesystem_encoding
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
from calibre.utils.ordered_dict import OrderedDict
def paginate(offsets, content, base_url, up_url=None): # {{{
'Create markup for pagination'
if '?' not in base_url:
base_url += '?'
if base_url[-1] != '?':
base_url += '&'
def navlink(decoration, name, cls, offset):
label = xml(name)
if cls in ('next', 'last'):
label += '&nbsp;' + decoration
else:
label = decoration + '&nbsp;' + label
return (u'<a class="{cls}" href="{base_url}&amp;offset={offset}" title={name}>'
u'{label}</a>').format(cls=cls, decoration=decoration,
name=xml(name, True), offset=offset,
base_url=xml(base_url, True), label=label)
left = ''
if offsets.offset > 0 and offsets.previous_offset > 0:
left += navlink(u'\u219e', _('First'), 'first', 0)
if offsets.offset > 0:
left += ' ' + navlink('&larr;', _('Previous'), 'previous',
offsets.previous_offset)
middle = ''
if up_url:
middle = '<a href="{0}" title="{1}">[{1} &uarr;]</a>'.format(xml(up_url, True),
xml(_('Up')))
right = ''
if offsets.next_offset > -1:
right += navlink('&rarr', _('Next'), 'next', offsets.next_offset)
if offsets.last_offset > offsets.next_offset and offsets.last_offset > 0:
right += ' ' + navlink(u'\u21A0', _('Last'), 'last', offsets.last_offset)
navbar = u'''
<table class="navbar">
<tr>
<td class="left">{left}</td>
<td class="middle">{middle}</td>
<td class="right">{right}</td>
</tr>
<table>
'''.format(left=left, right=right, middle=middle)
templ = u'''
<div class="page">
{navbar}
<div class="page-contents">
{content}
</div>
{navbar}
</div>
'''
return templ.format(navbar=navbar, content=content)
# }}}
def utf8(x): # {{{
if isinstance(x, unicode):
x = x.encode('utf-8')
return x
# }}}
def render_rating(rating, container='span'): # {{{
if rating < 0.1:
return '', ''
added = 0
rstring = xml(_('Average rating: %.1f stars')% (rating if rating else 0.0),
True)
ans = ['<%s class="rating">' % (container)]
for i in range(5):
n = rating - added
x = 'half'
if n <= 0.1:
x = 'off'
elif n >= 0.9:
x = 'on'
ans.append(
u'<img alt="{0}" title="{0}" src="/static/star-{1}.png" />'.format(
rstring, x))
added += 1
ans.append('</%s>'%container)
return u''.join(ans), rstring
# }}}
def get_category_items(category, items, db, datatype): # {{{
def item(i):
templ = (u'<div title="{4}" class="category-item">'
'<div class="category-name">{0}</div><div>{1}</div>'
'<div>{2}'
'<span class="href">{3}</span></div></div>')
rating, rstring = render_rating(i.avg_rating)
name = xml(i.name)
if datatype == 'rating':
name = xml(_('%d stars')%int(i.avg_rating))
id_ = i.id
if id_ is None:
id_ = hexlify(force_unicode(name).encode('utf-8'))
id_ = xml(str(id_))
desc = ''
if i.count > 0:
desc += '[' + _('%d items')%i.count + ']'
href = '/browse/matches/%s/%s'%(category, id_)
return templ.format(xml(name), rating,
xml(desc), xml(quote(href)), rstring)
items = list(map(item, items))
return '\n'.join(['<div class="category-container">'] + items + ['</div>'])
# }}}
class Endpoint(object): # {{{
'Manage encoding, mime-type, last modified, cookies, etc.'
def __init__(self, mimetype='text/html; charset=utf-8', sort_type='category'):
self.mimetype = mimetype
self.sort_type = sort_type
self.sort_kwarg = sort_type + '_sort'
self.sort_cookie_name = 'calibre_browse_server_sort_'+self.sort_type
def __call__(eself, func):
def do(self, *args, **kwargs):
sort_val = None
cookie = cherrypy.request.cookie
if cookie.has_key(eself.sort_cookie_name):
sort_val = cookie[eself.sort_cookie_name].value
kwargs[eself.sort_kwarg] = sort_val
ans = func(self, *args, **kwargs)
cherrypy.response.headers['Content-Type'] = eself.mimetype
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = \
self.last_modified(max(updated, self.build_time))
ans = utf8(ans)
return ans
do.__name__ = func.__name__
return do
# }}}
class BrowseServer(object):
def add_routes(self, connect):
base_href = '/browse'
connect('browse', base_href, self.browse_catalog)
connect('browse_catalog', base_href+'/category/{category}',
self.browse_catalog)
connect('browse_category_group',
base_href+'/category_group/{category}/{group}',
self.browse_category_group)
connect('browse_list', base_href+'/list/{query}', self.browse_list)
connect('browse_search', base_href+'/search/{query}',
self.browse_search)
connect('browse_book', base_href+'/book/{uuid}', self.browse_book)
def browse_template(self, sort, category=True):
def generate():
scn = 'calibre_browse_server_sort_'
if category:
sort_opts = [('rating', _('Average rating')), ('name',
_('Name')), ('popularity', _('Popularity'))]
scn += 'category'
else:
scn += 'list'
fm = self.db.field_metadata
sort_opts, added = [], set([])
for x in fm.sortable_field_keys():
n = fm[x]['name']
if n not in added:
added.add(n)
sort_opts.append((x, n))
ans = P('content_server/browse/browse.html',
data=True).decode('utf-8')
ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
ans = ans.replace('{sort_cookie_name}', scn)
opts = ['<option %svalue="%s">%s</option>' % (
'selected="selected" ' if k==sort else '',
xml(k), xml(n), ) for k, n in
sorted(sort_opts, key=operator.itemgetter(1))]
ans = ans.replace('{sort_select_options}', ('\n'+' '*20).join(opts))
lp = self.db.library_path
if isbytestring(lp):
lp = force_unicode(lp, filesystem_encoding)
if isinstance(ans, unicode):
ans = ans.encode('utf-8')
ans = ans.replace('{library_name}', xml(os.path.basename(lp)))
ans = ans.replace('{library_path}', xml(lp, True))
return ans
if self.opts.develop:
return generate()
if not hasattr(self, '__browse_template__'):
self.__browse_template__ = generate()
return self.__browse_template__
# Catalogs {{{
def browse_toplevel(self):
categories = self.categories_cache()
category_meta = self.db.field_metadata
cats = [
(_('Newest'), 'newest'),
]
def getter(x):
return category_meta[x]['name'].lower()
for category in sorted(categories,
cmp=lambda x,y: cmp(getter(x), getter(y))):
if len(categories[category]) == 0:
continue
if category == 'formats':
continue
meta = category_meta.get(category, None)
if meta is None:
continue
cats.append((meta['name'], category))
cats = ['<li title="{2} {0}">{0}<span>/browse/category/{1}</span></li>'\
.format(xml(x, True), xml(quote(y)), xml(_('Browse books by')))
for x, y in cats]
main = '<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
.format(_('Choose a category to browse by:'), '\n\n'.join(cats))
return self.browse_template('name').format(title='',
script='toplevel();', main=main)
def browse_sort_categories(self, items, sort):
if sort not in ('rating', 'name', 'popularity'):
sort = 'name'
def sorter(x):
ans = getattr(x, 'sort', x.name)
if hasattr(ans, 'upper'):
ans = ans.upper()
return ans
items.sort(key=sorter)
if sort == 'popularity':
items.sort(key=operator.attrgetter('count'), reverse=True)
elif sort == 'rating':
items.sort(key=operator.attrgetter('avg_rating'), reverse=True)
return sort
def browse_category(self, category, sort):
categories = self.categories_cache()
category_meta = self.db.field_metadata
category_name = category_meta[category]['name']
datatype = category_meta[category]['datatype']
if category not in categories:
raise cherrypy.HTTPError(404, 'category not found')
items = categories[category]
sort = self.browse_sort_categories(items, sort)
script = 'true'
if len(items) <= self.opts.max_opds_ungrouped_items:
script = 'false'
items = get_category_items(category, items, self.db, datatype)
else:
getter = lambda x: unicode(getattr(x, 'sort', x.name))
starts = set([])
for x in items:
val = getter(x)
if not val:
val = u'A'
starts.add(val[0].upper())
category_groups = OrderedDict()
for x in sorted(starts):
category_groups[x] = len([y for y in items if
getter(y).upper().startswith(x)])
items = [(u'<h3 title="{0}">{0} <span>[{2}]</span></h3><div>'
u'<div class="loaded" style="display:none"></div>'
u'<div class="loading"><img alt="{1}" src="/static/loading.gif" /><em>{1}</em></div>'
u'<span class="load_href">{3}</span></div>').format(
xml(s, True),
xml(_('Loading, please wait'))+'&hellip;',
unicode(c),
xml(u'/browse/category_group/%s/%s'%(category, s)))
for s, c in category_groups.items()]
items = '\n\n'.join(items)
items = u'<div id="groups">\n{0}</div>'.format(items)
script = 'category(%s);'%script
main = u'''
<div class="category">
<h3>{0}</h3>
<a class="navlink" href="/browse"
title="{2}">{2}&nbsp;&uarr;</a>
{1}
</div>
'''.format(
xml(_('Browsing by')+': ' + category_name), items,
xml(_('Up'), True))
return self.browse_template(sort).format(title=category_name,
script=script, main=main)
@Endpoint(mimetype='application/json; charset=utf-8')
def browse_category_group(self, category=None, group=None,
category_sort=None):
sort = category_sort
if sort not in ('rating', 'name', 'popularity'):
sort = 'name'
categories = self.categories_cache()
category_meta = self.db.field_metadata
datatype = category_meta[category]['datatype']
if category not in categories:
raise cherrypy.HTTPError(404, 'category not found')
if not group:
raise cherrypy.HTTPError(404, 'invalid group')
items = categories[category]
entries = []
getter = lambda x: unicode(getattr(x, 'sort', x.name))
for x in items:
val = getter(x)
if not val:
val = u'A'
if val.upper().startswith(group):
entries.append(x)
sort = self.browse_sort_categories(entries, sort)
entries = get_category_items(category, entries, self.db, datatype)
return json.dumps(entries, ensure_ascii=False)
@Endpoint()
def browse_catalog(self, category=None, category_sort=None):
'Entry point for top-level, categories and sub-categories'
if category == None:
ans = self.browse_toplevel()
else:
ans = self.browse_category(category, category_sort)
return ans
# }}}
# Book Lists {{{
def browse_list(self, query=None, offset=0, sort=None):
raise NotImplementedError()
# }}}
# Search {{{
def browse_search(self, query=None, offset=0, sort=None):
raise NotImplementedError()
# }}}
# Book {{{
def browse_book(self, uuid=None):
raise NotImplementedError()
# }}}

View File

@ -29,6 +29,11 @@ class Cache(object):
def categories_cache(self, restrict_to=frozenset([])):
base_restriction = self.search_cache('')
if restrict_to:
restrict_to = frozenset(restrict_to).intersection(base_restriction)
else:
restrict_to = base_restriction
old = self._category_cache.pop(frozenset(restrict_to), None)
if old is None or old[0] <= self.db.last_modified():
categories = self.db.get_categories(ids=restrict_to)

View File

@ -35,9 +35,10 @@ class ContentServer(object):
def add_routes(self, connect):
connect('root', '/', self.index)
connect('old', '/old', self.old)
connect('get', '/get/{what}/{id}', self.get,
conditions=dict(method=["GET", "HEAD"]))
connect('static', '/static/{name}', self.static,
connect('static', '/static/{name:.*?}', self.static,
conditions=dict(method=["GET", "HEAD"]))
# Utility methods {{{
@ -123,6 +124,9 @@ class ContentServer(object):
return self.static('index.html')
def old(self, **kwargs):
return self.static('index.html')
# Actually get content from the database {{{
def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=False)

View File

@ -121,7 +121,7 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
book['id'], fmt)
),
CLASS('button'))
s.tail = u'\u202f' # &nbsp;
s.tail = u''
last = s
data.append(s)

View File

@ -18,7 +18,7 @@ from calibre.constants import __appname__
from calibre.ebooks.metadata import fmt_sidx
from calibre.library.comments import comments_to_html
from calibre.library.server import custom_fields_to_display
from calibre.library.server.utils import format_tag_string
from calibre.library.server.utils import format_tag_string, Offsets
from calibre import guess_type
from calibre.utils.ordered_dict import OrderedDict
@ -321,26 +321,6 @@ class CategoryGroupFeed(NavFeed):
self.root.append(CATALOG_GROUP_ENTRY(item, which, base_href, version, updated))
class OPDSOffsets(object):
def __init__(self, offset, delta, total):
if offset < 0:
offset = 0
if offset >= total:
raise cherrypy.HTTPError(404, 'Invalid offset: %r'%offset)
last_allowed_index = total - 1
last_current_index = offset + delta - 1
self.offset = offset
self.next_offset = last_current_index + 1
if self.next_offset > last_allowed_index:
self.next_offset = -1
self.previous_offset = self.offset - delta
if self.previous_offset < 0:
self.previous_offset = 0
self.last_offset = last_allowed_index - delta
if self.last_offset < 0:
self.last_offset = 0
class OPDSServer(object):
@ -374,7 +354,7 @@ class OPDSServer(object):
items = [x for x in self.db.data.iterall() if x[idx] in ids]
self.sort(items, sort_by, ascending)
max_items = self.opts.max_opds_items
offsets = OPDSOffsets(offset, max_items, len(items))
offsets = Offsets(offset, max_items, len(items))
items = items[offsets.offset:offsets.offset+max_items]
updated = self.db.last_modified()
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
@ -448,7 +428,7 @@ class OPDSServer(object):
id_ = 'calibre-category-group-feed:'+category+':'+which
max_items = self.opts.max_opds_items
offsets = OPDSOffsets(offset, max_items, len(items))
offsets = Offsets(offset, max_items, len(items))
items = list(items)[offsets.offset:offsets.offset+max_items]
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
@ -495,7 +475,7 @@ class OPDSServer(object):
if len(items) <= MAX_ITEMS:
max_items = self.opts.max_opds_items
offsets = OPDSOffsets(offset, max_items, len(items))
offsets = Offsets(offset, max_items, len(items))
items = list(items)[offsets.offset:offsets.offset+max_items]
ans = CategoryFeed(items, which, id_, updated, version, offsets,
page_url, up_url, self.db)
@ -516,7 +496,7 @@ class OPDSServer(object):
getattr(y, 'sort', y.name).startswith(x)])
items = [Group(x, y) for x, y in category_groups.items()]
max_items = self.opts.max_opds_items
offsets = OPDSOffsets(offset, max_items, len(items))
offsets = Offsets(offset, max_items, len(items))
items = items[offsets.offset:offsets.offset+max_items]
ans = CategoryGroupFeed(items, which, id_, updated, version, offsets,
page_url, up_url)

View File

@ -13,6 +13,28 @@ from calibre import strftime as _strftime, prints
from calibre.utils.date import now as nowf
from calibre.utils.config import tweaks
class Offsets(object):
'Calculate offsets for a paginated view'
def __init__(self, offset, delta, total):
if offset < 0:
offset = 0
if offset >= total:
raise cherrypy.HTTPError(404, 'Invalid offset: %r'%offset)
last_allowed_index = total - 1
last_current_index = offset + delta - 1
self.slice_upper_bound = offset+delta
self.offset = offset
self.next_offset = last_current_index + 1
if self.next_offset > last_allowed_index:
self.next_offset = -1
self.previous_offset = self.offset - delta
if self.previous_offset < 0:
self.previous_offset = 0
self.last_offset = last_allowed_index - delta
if self.last_offset < 0:
self.last_offset = 0
def expose(func):

View File

@ -418,3 +418,14 @@ How do I run calibre from my USB stick?
A portable version of calibre is available at: `portableapps.com <http://portableapps.com/node/20518>`_. However, this is usually out of date. You can also setup your own portable calibre install by following :ref:`these instructions <portablecalibre>`.
Why are there so many calibre-parallel processes on my system?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| maintains two separate worker process pools. One is used for adding books/saving to disk and the other for conversions. You can control the number of worker processes via :guilabel:`Preferences->Advanced->Miscellaneous`. So if you set it to 6 that means a maximum of 3 conversions will run simultaneously. And that is why you will see the number of worker processes changes by two when you use the up and down arrows. On windows, you can set the priority that these processes run with. This can be useful on older, single CPU machines, if you find them slowing down to a crawl when conversions are running.
In addition to this some conversion plugins run tasks in their own pool of processes, so for example if you bulk convert comics, each comic conversion will use three separate processes to render the images. The job manager knows this so it will run only a single comic conversion simultaneously.
And since I'm sure someone will ask: The reason adding/saving books are in separate processes is because of PDF. PDF processing libraries can crash on reading PDFs and I dont want the crash to take down all of calibre. Also when adding EPUB books, in order to extract the cover you have to sometimes render the HTML of the first page, which means that it either has to run the GUI thread of the main process or in a separate process.
Finally, the reason calibre keep workers alive and idle instead of launching on demand is to workaround the slow startup time of python processes.

View File

@ -17,9 +17,9 @@ To get started with more advanced usage, you should read about the :ref:`Graphic
You will find the list of :ref:`Frequently Asked Questions <faq>` useful as well.
.. only:: html and online
.. only:: online
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_. Because the User Manual uses advanced formatting, it is only suitable for use with the |app| e-book viewer.
An e-book version of this User Manual is available in `EPUB format <calibre.epub>`_.
Sections
------------

View File

@ -62,9 +62,9 @@ If you want the search to ignore upper/lowercase differences, uncheck the `Case
You can have |app| change the case of the result (information after the replace has happened) by choosing one of the functions from the `Apply function after replace` box. The operations available are:
*`Lower case` -- change all the characters in the field to lower case
*`Upper case` -- change all the characters in the field to upper case
*`Title case` -- capitalize each word in the result.
* `Lower case` -- change all the characters in the field to lower case
* `Upper case` -- change all the characters in the field to upper case
* `Title case` -- capitalize each word in the result.
The `Your test` box is provided for you to enter text to check that search/replace is doing what you want. In the majority of cases the book test boxes will be sufficient, but it is possible that there is a case you want to check that isn't shown in these boxes. Enter that case into `Your test`.

View File

@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Provides platform independent temporary files that persist even after
being closed.
"""
import tempfile, os, atexit
import tempfile, os, atexit, binascii, cPickle
from calibre import __version__, __appname__
@ -30,9 +30,18 @@ def remove_dir(x):
def base_dir():
global _base_dir
if _base_dir is None:
_base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__,
__version__))
atexit.register(remove_dir, _base_dir)
td = os.environ.get('CALIBRE_WORKER_TEMP_DIR', None)
if td is not None:
try:
td = cPickle.loads(binascii.unhexlify(td))
except:
td = None
if td and os.path.exists(td):
_base_dir = td
else:
_base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__,
__version__))
atexit.register(remove_dir, _base_dir)
return _base_dir
class PersistentTemporaryFile(object):

Some files were not shown because too many files have changed in this diff Show More