merged trunk
@ -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
|
||||
|
||||
|
262
resources/content_server/browse/browse.css
Normal 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
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
|
98
resources/content_server/browse/browse.html
Normal 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"
|
||||
>→ home ←</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nav-container">
|
||||
<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" />
|
||||
<input type="submit" value="Search" title="Search" alt="Search" />
|
||||
</form>
|
||||
</div>
|
||||
<div> </div>
|
||||
<div> </div>
|
||||
<div> </div>
|
||||
|
||||
<div id="main">
|
||||
{main}
|
||||
</div>
|
||||
<div id="footer">
|
||||
[{library_path}] Created by Kovid Goyal
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
135
resources/content_server/browse/browse.js
Normal 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"> </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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// }}}
|
||||
|
||||
|
247
resources/content_server/jquery.corner.js
Normal 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);
|
8081
resources/content_server/jquery.js
vendored
21
resources/content_server/jquery.multiselect.css
Normal 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 }
|
16
resources/content_server/jquery.multiselect.min.js
vendored
Normal file
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 157 B |
After Width: | Height: | Size: 130 B |
After Width: | Height: | Size: 125 B |
After Width: | Height: | Size: 103 B |
After Width: | Height: | Size: 114 B |
After Width: | Height: | Size: 147 B |
After Width: | Height: | Size: 141 B |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
350
resources/content_server/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css
vendored
Normal 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 */
|
340
resources/content_server/jquery_ui/js/jquery-ui-1.8.5.custom.min.js
vendored
Normal 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);
|
||||
;
|
BIN
resources/content_server/logo.png
Normal file
After Width: | Height: | Size: 13 KiB |
@ -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 {
|
||||
|
BIN
resources/content_server/star-half.png
Normal file
After Width: | Height: | Size: 667 B |
BIN
resources/content_server/star-off.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
resources/content_server/star-on.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
resources/images/news/frazpc.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
resources/images/news/rstones.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
@ -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)
|
||||
|
86
resources/recipes/el_cultural.recipe
Normal 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
|
@ -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 + '"'
|
||||
|
||||
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']})
|
||||
]
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
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'
|
||||
|
74
resources/recipes/financial_times_uk.recipe
Normal 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)
|
||||
|
35
resources/recipes/frazpc.recipe
Normal 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' <a href="http://www.frazpc.pl/[^>]*?">(Skomentuj|Komentarz(e)?\([0-9]*\))</a> \|', lambda match: '')]
|
||||
]
|
||||
|
||||
remove_attributes = [ 'width', 'height' ]
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
64
resources/recipes/medscape.recipe
Normal 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
|
@ -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
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'wide'}),]
|
||||
|
||||
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"]}) ]
|
||||
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
|
||||
|
@ -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')]
|
||||
|
46
resources/recipes/nightflier.recipe
Normal 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)
|
||||
|
||||
|
18
resources/recipes/novaya_gazeta.recipe
Normal 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'
|
||||
|
82
resources/recipes/rstones.recipe
Normal 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)
|
||||
|
||||
|
@ -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,30 +32,81 @@ 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'),
|
||||
'description' : '',
|
||||
'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)|(\ \;))*\[[\|\s*]*\]((\s)|(\ \;))*$',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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
93
resources/viewer/jquery_scrollTo.js
vendored
@ -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 ){
|
||||
@ -168,7 +177,8 @@
|
||||
// Don't animate this axis again in the next iteration.
|
||||
delete attr[key];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
animate( settings.onAfter );
|
||||
|
||||
function animate( callback ){
|
||||
@ -176,16 +186,27 @@
|
||||
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 };
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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 = ''
|
||||
|
@ -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('&')])
|
||||
|
||||
authors.extend([a.strip() for a in au.split('&')])
|
||||
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]:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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_()
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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"/>
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -77,13 +77,6 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="compact_button">
|
||||
<property name="text">
|
||||
<string>&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">
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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__':
|
||||
|
@ -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:]))
|
||||
|
@ -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:
|
||||
|
@ -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']))
|
||||
|
||||
|
@ -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__
|
||||
|
||||
|
385
src/calibre/library/server/browse.py
Normal 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 += ' ' + decoration
|
||||
else:
|
||||
label = decoration + ' ' + label
|
||||
return (u'<a class="{cls}" href="{base_url}&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('←', _('Previous'), 'previous',
|
||||
offsets.previous_offset)
|
||||
|
||||
middle = ''
|
||||
if up_url:
|
||||
middle = '<a href="{0}" title="{1}">[{1} ↑]</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'))+'…',
|
||||
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} ↑</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()
|
||||
# }}}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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' #
|
||||
s.tail = u''
|
||||
last = s
|
||||
data.append(s)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -17,10 +17,10 @@ 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>`_.
|
||||
|
||||
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.
|
||||
|
||||
Sections
|
||||
------------
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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):
|
||||
|