mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
3cf7e789c1
BIN
resources/images/news/guardian.png
Normal file
BIN
resources/images/news/guardian.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 548 B |
BIN
resources/images/news/lwn.png
Normal file
BIN
resources/images/news/lwn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 387 B |
12
resources/recipes/an_druma_mor.recipe
Normal file
12
resources/recipes/an_druma_mor.recipe
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1257775999(BasicNewsRecipe):
|
||||||
|
title = u'An Druma M\xf3r'
|
||||||
|
__author__ = "David O'Calaghan"
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
language = 'ga'
|
||||||
|
use_embedded_content = True
|
||||||
|
|
||||||
|
feeds = [(u'Nuacht Laeth\xfail', u'http://feeds.feedburner.com/NuachtLneLaethilArAnDrumaMr')]
|
||||||
|
|
@ -16,44 +16,60 @@ class CNN(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
oldest_article = 15
|
oldest_article = 15
|
||||||
|
recursions = 1
|
||||||
|
match_regexps = [r'http://sportsillustrated.cnn.com/.*/[1-9].html']
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
h1{font-family :Arial,Helvetica,sans-serif; font-size:large}
|
.cnn_strycntntlft{font-family :Arial,Helvetica,sans-serif;}
|
||||||
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
h2{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
.cnnTxtCmpnt{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
.cnnTxtCmpnt{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
.cnnTMcontent{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;color:#575757}
|
.cnnTMcontent{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#575757}
|
||||||
.storytext{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
.storytext{font-family :Arial,Helvetica,sans-serif; font-size:small}
|
||||||
.storybyline{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
.storybyline{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#575757}
|
||||||
.credit{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
.credit{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
||||||
.storyBrandingBanner{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
.storyBrandingBanner{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#575757}
|
||||||
.storytimestamp{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
.storytimestamp{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#575757}
|
||||||
.timestamp{font-family :Arial,Helvetica,sans-serif; font-size:xx-small; color:#575757}
|
.timestamp{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#575757}
|
||||||
.subhead p{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;}
|
.cnn_strytmstmp{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
.cnnStoryContent{font-family :Arial,Helvetica,sans-serif; font-size:xx-small}
|
.cnn_stryimg640caption{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
.cnnContentContainer{font-family :Arial,Helvetica,sans-serif; font-size:xx-small}
|
.cnn_strylccimg300cntr{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
|
.cnn_stryichgfcpt{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
|
.cnnByline{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
|
.cnn_bulletbin cnnStryHghLght{ font-size:xx-small;}
|
||||||
|
.subhead p{font-family :Arial,Helvetica,sans-serif; font-size:x-small;}
|
||||||
|
.cnnStoryContent{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
|
.cnnContentContainer{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
.col1{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
.col1{font-family :Arial,Helvetica,sans-serif; font-size:x-small; color:#666666;}
|
||||||
.col3{color:#333333; font-family :Arial,Helvetica,sans-serif; font-size:x-small;font-weight:bold;}
|
.col3{color:#333333; font-family :Arial,Helvetica,sans-serif; font-size:x-small;font-weight:bold;}
|
||||||
.cnnInlineT1Caption{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;font-weight:bold;}
|
.cnnInlineT1Caption{font-family :Arial,Helvetica,sans-serif; font-size:x-small;font-weight:bold;}
|
||||||
.cnnInlineT1Credit{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;color:#333333;}
|
.cnnInlineT1Credit{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#333333;}
|
||||||
.col10{color:#5A637E}
|
.col10{color:#5A637E;}
|
||||||
.cnnTimeStamp{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;color:#333333;}
|
.cnnInlineRailBulletList{color:black;}
|
||||||
.galleryhedDek{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;color:#575757;}
|
.cnnLine0{font-family :Arial,Helvetica,sans-serif; color:#666666;font-weight:bold;}
|
||||||
.galleryWidgetHeader{font-family :Arial,Helvetica,sans-serif; font-size:xx-small;color:#004276;}
|
.cnnTimeStamp{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#333333;}
|
||||||
.article-content{font-family :Arial,Helvetica,sans-serif; font-size:xx-small}
|
.galleryhedDek{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#575757;}
|
||||||
.cnnRecapStory{font-family :Arial,Helvetica,sans-serif; font-size:xx-small}
|
.galleryWidgetHeader{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#004276;}
|
||||||
|
.article-content{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
|
.cnnRecapStory{font-family :Arial,Helvetica,sans-serif; font-size:x-small}
|
||||||
|
h1{font-family :Arial,Helvetica,sans-serif; font-size:x-large}
|
||||||
|
.captionname{font-family :Arial,Helvetica,sans-serif; font-size:x-small;color:#575757;}
|
||||||
|
inStoryIE{{font-family :Arial,Helvetica,sans-serif; font-size:x-small;}
|
||||||
'''
|
'''
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name='div', attrs={'class':["cnnWCBoxContent","cnnContent","cnnMainBodySecs"]}),
|
dict(name='div', attrs={'class':["cnnWCBoxContent","cnnContent","cnnMainBodySecs","cnn_storyarea","cnn_strycntntlft","cnn_strytmstmp"]}),
|
||||||
dict(name='div', attrs={'id':["contentBody","content"]}),
|
dict(name='div', attrs={'id':["contentBody","content"]}),
|
||||||
dict(name='td', attrs={'id':["cnnRecapStory"]}),]
|
dict(name='td', attrs={'id':["cnnRecapStory"]}),
|
||||||
|
dict(name='td', attrs={'class':["cnnBigSideLeft"]}),
|
||||||
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'class':["storyLink","article-tools clearfix","widget video related-video vList","cnnFooterBox","scrollArrows","boxHeading","cnnInlineMailbag","mainCol_lastBlock","cnn_bookmarks","cnnFooterBox","cnnEndOfStory","cnnInlineSL","cnnStoryHighlights","cnnFooterClick","cnnSnapShotHeader","cnnStoryToolsFooter","cnnWsnr","cnnUGCBox","cnnTopNewsModule","cnnStoryElementBox","cnnStoryPhotoBoxNavigation"]}),
|
dict(name='div', attrs={'class':["cnnInlineRailSelectList","cnn_strycntntrgt","cnn_strybtntools","cnn_strylctcntr cnn_strylctcqrelt","cnn_strylceclbtn","cnn_stryftsbttm","cnn_strybtmmorebx","storyLink","article-tools clearfix","widget video related-video vList","cnnFooterBox","scrollArrows","boxHeading","cnnInlineMailbag","mainCol_lastBlock","cnn_bookmarks","cnnFooterBox","cnnEndOfStory","cnnInlineSL","cnnStoryHighlights","cnnFooterClick","cnnSnapShotHeader","cnnStoryToolsFooter","cnnWsnr","cnnUGCBox","cnnTopNewsModule","cnnStoryElementBox","cnnStoryPhotoBoxNavigation"]}),
|
||||||
dict(name='span', attrs={'class':["cnnEmbeddedMosLnk"]}),
|
dict(name='span', attrs={'class':["cnnEmbeddedMosLnk"]}),
|
||||||
dict(name='div', attrs={'id':["cnnIncldHlder","articleCommentsContainer","featuredContent","superstarsWidget","shareMenuContainer","rssMenuContainer","storyBrandingBanner","cnnRightCol","siteFeatures","quigo628","rightColumn","clickIncludeBox","cnnHeaderRightCol","cnnSCFontLabel","cnnSnapShotBottomRight","cnnSCFontButtons","rightColumn"]}),
|
dict(name='div', attrs={'id':["cnnIncldHlder","articleCommentsContainer","featuredContent","superstarsWidget","shareMenuContainer","rssMenuContainer","storyBrandingBanner","cnnRightCol","siteFeatures","quigo628","rightColumn","clickIncludeBox","cnnHeaderRightCol","cnnSCFontLabel","cnnSnapShotBottomRight","cnnSCFontButtons","rightColumn"]}),
|
||||||
dict(name='p', attrs={'class':["cnnTopics"]}),
|
dict(name='p', attrs={'class':["cnnTopics"]}),
|
||||||
dict(name='td', attrs={'class':["cnnRightRail"]}),
|
dict(name='td', attrs={'class':["cnnRightRail"]}),
|
||||||
dict(name='table', attrs={'class':["cnnTMbox"]}),
|
dict(name='table', attrs={'class':["cnnTMbox"]}),
|
||||||
dict(name='ul', attrs={'id':["cnnTopNav","cnnBotNav","cnnSBNav"]}),
|
dict(name='ul', attrs={'id':["cnnTopNav","cnnBotNav","cnnSBNav"]}),
|
||||||
|
dict(name='div', attrs={'id':["cnn_ftrcntnt"]})
|
||||||
]
|
]
|
||||||
|
|
||||||
# def print_version(self, url):
|
# def print_version(self, url):
|
||||||
@ -75,3 +91,10 @@ class CNN(BasicNewsRecipe):
|
|||||||
('Offbeat', 'http://rss.cnn.com/rss/cnn_offbeat.rss'),
|
('Offbeat', 'http://rss.cnn.com/rss/cnn_offbeat.rss'),
|
||||||
('Most Popular', 'http://rss.cnn.com/rss/cnn_mostpopular.rss')
|
('Most Popular', 'http://rss.cnn.com/rss/cnn_mostpopular.rss')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
|
||||||
|
for tag in soup.findAll(name=['ul','li']):
|
||||||
|
tag.name = 'div'
|
||||||
|
|
||||||
|
return soup
|
||||||
|
@ -1,24 +1,56 @@
|
|||||||
import re
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Cyberpresse(BasicNewsRecipe):
|
class Cyberpresse(BasicNewsRecipe):
|
||||||
|
|
||||||
title = u'Cyberpresse'
|
title = u'Cyberpresse'
|
||||||
__author__ = 'balok'
|
__author__ = 'balok and Sujata Raman'
|
||||||
description = 'Canadian news in French'
|
description = 'Canadian news in French'
|
||||||
language = 'fr'
|
language = 'fr'
|
||||||
|
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
html2lrf_options = ['--left-margin=0','--right-margin=0','--top-margin=0','--bottom-margin=0']
|
html2lrf_options = ['--left-margin=0','--right-margin=0','--top-margin=0','--bottom-margin=0']
|
||||||
|
encoding = 'utf-8'
|
||||||
preprocess_regexps = [
|
|
||||||
(re.compile(r'<body.*?<!-- END .centerbar -->', re.IGNORECASE | re.DOTALL), lambda match : '<BODY>'),
|
|
||||||
(re.compile(r'<!-- END .entry -->.*?</body>', re.IGNORECASE | re.DOTALL), lambda match : '</BODY>'),
|
|
||||||
(re.compile(r'<strong>Agrandir.*?</strong>', re.IGNORECASE | re.DOTALL), lambda match : '<br>'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [(u'Manchettes', u'http://www.cyberpresse.ca/rss/225.xml'),(u'Capitale nationale', u'http://www.cyberpresse.ca/rss/501.xml'),(u'Opinions', u'http://www.cyberpresse.ca/rss/977.xml'),(u'Insolite', u'http://www.cyberpresse.ca/rss/279.xml')]
|
keep_only_tags = [dict(name='div', attrs={'class':'article-page'}),
|
||||||
|
dict(name='div', attrs={'id':'articlePage'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
.photodata{font-family:Arial,Helvetica,Verdana,sans-serif;color: #999999; font-size: 90%; }
|
||||||
|
h1{font-family:Georgia,Times,serif ; font-size: large; }
|
||||||
|
.amorce{font-family:Arial,Helvetica,Verdana,sans-serif; font-weight:bold;}
|
||||||
|
.article-page{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||||
|
#articlePage{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||||
|
.auteur{font-family:Georgia,Times,sans-serif; font-size: 90%; color:#006699 ;}
|
||||||
|
.bodyText{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||||
|
.byLine{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: 90%;}
|
||||||
|
.entry{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: x-small;}
|
||||||
|
.minithumb-auteurs{font-family:Arial,Helvetica,Verdana,sans-serif; font-size: 90%; }
|
||||||
|
a{color:#003399; font-weight:bold; }
|
||||||
|
'''
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':['centerbar','colspan','share-module']}),
|
||||||
|
dict(name='p', attrs={'class':['zoom']}),
|
||||||
|
dict(name='ul', attrs={'class':['stories']}),
|
||||||
|
dict(name='h4', attrs={'class':['general-cat']}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [(u'Manchettes', u'http://www.cyberpresse.ca/rss/225.xml'),
|
||||||
|
(u'Capitale nationale', u'http://www.cyberpresse.ca/rss/501.xml'),
|
||||||
|
(u'Opinions', u'http://www.cyberpresse.ca/rss/977.xml'),
|
||||||
|
(u'Insolite', u'http://www.cyberpresse.ca/rss/279.xml')
|
||||||
|
]
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first):
|
||||||
|
|
||||||
|
for tag in soup.findAll(name=['i','strong']):
|
||||||
|
tag.name = 'div'
|
||||||
|
|
||||||
|
return soup
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,9 +16,11 @@ class Economist(BasicNewsRecipe):
|
|||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
__author__ = "Kovid Goyal"
|
__author__ = "Kovid Goyal"
|
||||||
description = 'Global news and current affairs from a European perspective'
|
|
||||||
oldest_article = 7.0
|
|
||||||
INDEX = 'http://www.economist.com/printedition'
|
INDEX = 'http://www.economist.com/printedition'
|
||||||
|
description = ('Global news and current affairs from a European perspective.'
|
||||||
|
' Needs a subscription from ')+INDEX
|
||||||
|
|
||||||
|
oldest_article = 7.0
|
||||||
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
||||||
remove_tags = [dict(name=['script', 'noscript', 'title'])]
|
remove_tags = [dict(name=['script', 'noscript', 'title'])]
|
||||||
remove_tags_before = dict(name=lambda tag: tag.name=='title' and tag.parent.name=='body')
|
remove_tags_before = dict(name=lambda tag: tag.name=='title' and tag.parent.name=='body')
|
||||||
|
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
'''
|
'''
|
||||||
www.guardian.co.uk
|
www.guardian.co.uk
|
||||||
'''
|
'''
|
||||||
|
from calibre import strftime
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Guardian(BasicNewsRecipe):
|
class Guardian(BasicNewsRecipe):
|
||||||
@ -15,8 +15,8 @@ class Guardian(BasicNewsRecipe):
|
|||||||
__author__ = 'Seabound and Sujata Raman'
|
__author__ = 'Seabound and Sujata Raman'
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
|
|
||||||
oldest_article = 7
|
#oldest_article = 7
|
||||||
max_articles_per_feed = 20
|
#max_articles_per_feed = 100
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
|
||||||
timefmt = ' [%a, %d %b %Y]'
|
timefmt = ' [%a, %d %b %Y]'
|
||||||
@ -43,29 +43,97 @@ class Guardian(BasicNewsRecipe):
|
|||||||
#match-stats-summary{font-size:small; font-family:Arial,Helvetica,sans-serif;font-weight:normal;}
|
#match-stats-summary{font-size:small; font-family:Arial,Helvetica,sans-serif;font-weight:normal;}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
|
||||||
|
soup = self.index_to_soup('http://www.guardian.co.uk/theguardian')
|
||||||
|
# find cover pic
|
||||||
|
img = soup.find( 'img',attrs ={'alt':'Guardian digital edition'})
|
||||||
|
if img is None: return None
|
||||||
|
else:
|
||||||
|
self.cover_url = img['src']
|
||||||
|
# end find cover pic
|
||||||
|
sections = []
|
||||||
|
ans = []
|
||||||
|
for li in soup.findAll( 'li'):
|
||||||
|
section = ''
|
||||||
|
articles = []
|
||||||
|
|
||||||
|
if li.a and li.a.has_key('href'):
|
||||||
|
url = li.a['href']
|
||||||
|
if 'mainsection' in url:
|
||||||
|
section = self.tag_to_string(url)
|
||||||
|
i = len(section)
|
||||||
|
|
||||||
|
index1 = section.rfind('/',0,i)
|
||||||
|
section = section[index1+1:i]
|
||||||
|
sections.append(section)
|
||||||
|
|
||||||
|
#find the articles in the Main Section start
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
date = strftime('%a, %d %b')
|
||||||
|
descl = []
|
||||||
|
|
||||||
|
for desclist in soup.findAll(name='div',attrs={'class':"trailtext"}):
|
||||||
|
descl.append(self.tag_to_string(desclist).strip())
|
||||||
|
|
||||||
|
t = -1
|
||||||
|
for tag in soup.findAll('h3'):
|
||||||
|
t = t+1
|
||||||
|
|
||||||
|
for a in tag.findAll('a'):
|
||||||
|
|
||||||
|
if t < len(descl):
|
||||||
|
desc = descl[t]
|
||||||
|
else:
|
||||||
|
desc = ''
|
||||||
|
if a and a.has_key('href'):
|
||||||
|
url2 = a['href']
|
||||||
|
else:
|
||||||
|
url2 =''
|
||||||
|
title = self.tag_to_string(a)
|
||||||
|
|
||||||
|
if len(articles) == 0: #First article
|
||||||
|
|
||||||
|
articles.append({
|
||||||
|
'title':title,
|
||||||
|
'date':date,
|
||||||
|
'url':url2,
|
||||||
|
'description':desc,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
#eliminate duplicates start
|
||||||
|
if {'title':title,'date':date,'url':url2,'description':desc} in articles :
|
||||||
|
url2 = ''
|
||||||
|
#eliminate duplicates end
|
||||||
|
else:
|
||||||
|
if 'http://jobs.guardian.co.uk/' in url2:
|
||||||
|
url2 = ''
|
||||||
|
else:
|
||||||
|
|
||||||
|
articles.append({
|
||||||
|
'title':title,
|
||||||
|
'date':date,
|
||||||
|
'url':url2,
|
||||||
|
'description':desc,
|
||||||
|
})
|
||||||
|
#find the articles in the Main Section end
|
||||||
|
ans.append( articles)
|
||||||
|
|
||||||
|
else:
|
||||||
|
url =''
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
titles = map(self.find_title, sections)
|
||||||
('Front Page', 'http://www.guardian.co.uk/rss'),
|
ans1 = list(zip(titles,ans))
|
||||||
('Business', 'http://www.guardian.co.uk/business/rss'),
|
|
||||||
('Sport', 'http://www.guardian.co.uk/sport/rss'),
|
|
||||||
('Culture', 'http://www.guardian.co.uk/culture/rss'),
|
|
||||||
('Money', 'http://www.guardian.co.uk/money/rss'),
|
|
||||||
('Life & Style', 'http://www.guardian.co.uk/lifeandstyle/rss'),
|
|
||||||
('Travel', 'http://www.guardian.co.uk/travel/rss'),
|
|
||||||
('Environment', 'http://www.guardian.co.uk/environment/rss'),
|
|
||||||
('Comment','http://www.guardian.co.uk/commentisfree/rss'),
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_article_url(self, article):
|
return ans1[2:]
|
||||||
url = article.get('guid', None)
|
|
||||||
if '/video/' in url or '/flyer/' in url or '/quiz/' in url or \
|
|
||||||
'/gallery/' in url or 'ivebeenthere' in url or \
|
|
||||||
'pickthescore' in url or 'audioslideshow' in url :
|
|
||||||
url = None
|
|
||||||
return url
|
|
||||||
|
|
||||||
|
def find_title(self, section):
|
||||||
|
d = {'topstories':'Top Stories', 'international':'International', 'editorialsandreply':'Editorials and Reply',
|
||||||
|
'commentanddebate':'Comment and Debate','uknews':'UK News','saturday':'Saturday','sunday':'Sunday',
|
||||||
|
'reviews':'Reviews', 'obituaries':'Obituaries'}
|
||||||
|
|
||||||
|
return d.get(section, section)
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Derry FitzGerald. 2009 Modified by Ray Kinsella'
|
__copyright__ = "2008, Derry FitzGerald. 2009 Modified by Ray Kinsella and David O'Callaghan"
|
||||||
'''
|
'''
|
||||||
irishtimes.com
|
irishtimes.com
|
||||||
'''
|
'''
|
||||||
@ -9,18 +9,21 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
|
|
||||||
class IrishTimes(BasicNewsRecipe):
|
class IrishTimes(BasicNewsRecipe):
|
||||||
title = u'The Irish Times'
|
title = u'The Irish Times'
|
||||||
__author__ = 'Derry FitzGerald and Ray Kinsella'
|
__author__ = "Derry FitzGerald, Ray Kinsella and David O'Callaghan"
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
timefmt = ' (%A, %B %e, %Y)'
|
||||||
|
|
||||||
|
|
||||||
|
oldest_article = 3
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
simultaneous_downloads= 1
|
simultaneous_downloads= 1
|
||||||
|
|
||||||
r = re.compile('.*(?P<url>http:\/\/www.irishtimes.com\/.*\.html).*')
|
r = re.compile('.*(?P<url>http:\/\/(www.irishtimes.com)|(rss.feedsportal.com\/c)\/.*\.html?).*')
|
||||||
remove_tags = [dict(name='div', attrs={'class':'footer'})]
|
remove_tags = [dict(name='div', attrs={'class':'footer'})]
|
||||||
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('Frontpage', 'http://www.irishtimes.com/feeds/rss/newspaper/index.rss'),
|
('Frontpage', 'http://www.irishtimes.com/feeds/rss/newspaper/index.rss'),
|
||||||
('Ireland', 'http://www.irishtimes.com/feeds/rss/newspaper/ireland.rss'),
|
('Ireland', 'http://www.irishtimes.com/feeds/rss/newspaper/ireland.rss'),
|
||||||
('World', 'http://www.irishtimes.com/feeds/rss/newspaper/world.rss'),
|
('World', 'http://www.irishtimes.com/feeds/rss/newspaper/world.rss'),
|
||||||
('Finance', 'http://www.irishtimes.com/feeds/rss/newspaper/finance.rss'),
|
('Finance', 'http://www.irishtimes.com/feeds/rss/newspaper/finance.rss'),
|
||||||
@ -29,12 +32,14 @@ class IrishTimes(BasicNewsRecipe):
|
|||||||
('Opinion', 'http://www.irishtimes.com/feeds/rss/newspaper/opinion.rss'),
|
('Opinion', 'http://www.irishtimes.com/feeds/rss/newspaper/opinion.rss'),
|
||||||
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
|
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
return url.replace('.html', '_pf.html')
|
if url.count('rss.feedsportal.com'):
|
||||||
|
u = url.replace('0Bhtml/story01.htm','_pf0Bhtml/story01.htm')
|
||||||
|
else:
|
||||||
|
u = url.replace('.html','_pf.html')
|
||||||
|
return u
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
m = self.r.match(article.get('description', None))
|
return article.link
|
||||||
print m.group('url')
|
|
||||||
return m.group('url')
|
|
||||||
|
55
resources/recipes/lwn.recipe
Normal file
55
resources/recipes/lwn.recipe
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
'''
|
||||||
|
lwn.net
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class LWN(BasicNewsRecipe):
|
||||||
|
title = u'lwn'
|
||||||
|
__author__ = 'Oliver Niesner'
|
||||||
|
description = 'Linux Weekly News'
|
||||||
|
oldest_article = 54
|
||||||
|
language = _('English')
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
needs_subscription = True
|
||||||
|
language = 'en'
|
||||||
|
remove_javascript = True
|
||||||
|
simultaneous_downloads= 1
|
||||||
|
delay = 1
|
||||||
|
LOGIN = 'https://lwn.net/login'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='td', attrs={'class':'LeftColumn'}),
|
||||||
|
dict(name='td', attrs={'class':'NavLink'}),
|
||||||
|
dict(name='div', attrs={'class':'FormattedComment'}),
|
||||||
|
dict(name='td', attrs={'class':'MCTopBanner'}),
|
||||||
|
dict(name='div', attrs={'class':'CommentBox'})
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'lwn' , u'http://lwn.net/headlines/newrss' ),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first):
|
||||||
|
for tag in soup.findAll(name=['table', 'tr', 'td']):
|
||||||
|
tag.name = 'div'
|
||||||
|
return soup
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace ('rss', '?format=printable')
|
@ -12,8 +12,7 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
title = 'New Scientist - Online News'
|
title = 'New Scientist - Online News'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic'
|
||||||
description = 'Science news and science articles from New Scientist.'
|
description = 'Science news and science articles from New Scientist.'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
publisher = 'New Scientist'
|
publisher = 'New Scientist'
|
||||||
category = 'science news, science articles, science jobs, drugs, cancer, depression, computer software, sex'
|
category = 'science news, science articles, science jobs, drugs, cancer, depression, computer software, sex'
|
||||||
delay = 3
|
delay = 3
|
||||||
@ -21,16 +20,14 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
remove_javascript = True
|
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
|
|
||||||
html2lrf_options = [
|
conversion_options = {
|
||||||
'--comment', description
|
'comment' : description
|
||||||
, '--category', category
|
, 'tags' : category
|
||||||
, '--publisher', publisher
|
, 'publisher' : publisher
|
||||||
]
|
, 'language' : language
|
||||||
|
}
|
||||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':['pgtop','maincol']})]
|
keep_only_tags = [dict(name='div', attrs={'id':['pgtop','maincol']})]
|
||||||
|
|
||||||
@ -53,18 +50,9 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
url = article.get('link', None)
|
url = article.get('guid', None)
|
||||||
raw = article.get('description', None)
|
|
||||||
rsoup = self.index_to_soup(raw)
|
|
||||||
atags = rsoup.findAll('a',href=True)
|
|
||||||
for atag in atags:
|
|
||||||
if atag['href'].startswith('http://res.feedsportal.com/viral/sendemail2.html?'):
|
|
||||||
st, sep, rest = atag['href'].partition('&link=')
|
|
||||||
real_url, sep2, drest = rest.partition('" target=')
|
|
||||||
return real_url
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
rawurl, sep, params = url.partition('?')
|
return url + '?full=true&print=true'
|
||||||
return rawurl + '?full=true&print=true'
|
|
||||||
|
|
||||||
|
@ -12,14 +12,42 @@ from lxml import html
|
|||||||
from calibre.constants import preferred_encoding
|
from calibre.constants import preferred_encoding
|
||||||
|
|
||||||
class NewYorkReviewOfBooks(BasicNewsRecipe):
|
class NewYorkReviewOfBooks(BasicNewsRecipe):
|
||||||
|
|
||||||
title = u'New York Review of Books (no subscription)'
|
title = u'New York Review of Books (no subscription)'
|
||||||
description = u'Book reviews'
|
description = u'Book reviews'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
__author__ = 'Kovid Goyal'
|
__author__ = 'Kovid Goyal and Sujata Raman'
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
no_javascript = True
|
||||||
remove_tags_before = {'id':'container'}
|
remove_tags_before = {'id':'container'}
|
||||||
remove_tags = [{'class':['noprint', 'ad', 'footer']}, {'id':'right-content'}]
|
remove_tags = [{'class':['noprint', 'ad', 'footer']}, {'id':'right-content'},
|
||||||
|
dict(name='img', attrs={'src':"/images/1x1-clear.gif"}),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
p{font-family:"Times New Roman",Georgia,serif; font-size: 60%;}
|
||||||
|
.caption{ font-family:"Times New Roman",Georgia,serif; font-size:40%;}
|
||||||
|
h2{font-family:"Times New Roman",Georgia,serif; font-size:90%;}
|
||||||
|
a{ color:#003399; }
|
||||||
|
.reviewed-title{font-family:"Times New Roman",Georgia,serif;font-size : 50%; font-style:italic;}
|
||||||
|
.reviewed-author{font-family:"Times New Roman",Georgia,serif;font-size : 50%;}
|
||||||
|
.reviewed-info{font-family:"Times New Roman",Georgia,serif;font-size : 50%;}
|
||||||
|
h5{font-family:"Times New Roman",Georgia,serif;font-size : 50%;}
|
||||||
|
.date{font-family:"Times New Roman",Georgia,serif;font-variant:small-caps;font-size : 50%;}
|
||||||
|
h4{font-family:"Times New Roman",Georgia,serif;font-size : 50%;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
|
||||||
|
for tag in soup.findAll(name=['span',]):
|
||||||
|
tag.name = 'div'
|
||||||
|
for tag in soup.findAll(name=['blockquote',]):
|
||||||
|
tag.name = 'p'
|
||||||
|
|
||||||
|
return soup
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
root = html.fromstring(self.browser.open('http://www.nybooks.com/current-issue').read())
|
root = html.fromstring(self.browser.open('http://www.nybooks.com/current-issue').read())
|
||||||
@ -40,6 +68,6 @@ class NewYorkReviewOfBooks(BasicNewsRecipe):
|
|||||||
'description': '',
|
'description': '',
|
||||||
}
|
}
|
||||||
articles.append(article)
|
articles.append(article)
|
||||||
|
|
||||||
return [('Current Issue', articles)]
|
return [('Current Issue', articles)]
|
||||||
|
|
||||||
|
71
resources/viewer/bookmarks.js
Normal file
71
resources/viewer/bookmarks.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* bookmarks management
|
||||||
|
* Copyright 2008 Kovid Goyal
|
||||||
|
* License: GNU GPL v3
|
||||||
|
*/
|
||||||
|
|
||||||
|
function selector_in_parent(elem) {
|
||||||
|
var num = elem.prevAll().length;
|
||||||
|
var sel = " > *:eq("+num+") ";
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selector(elem) {
|
||||||
|
var obj = elem;
|
||||||
|
var sel = "";
|
||||||
|
while (obj[0] != document) {
|
||||||
|
sel = selector_in_parent(obj) + sel;
|
||||||
|
obj = obj.parent();
|
||||||
|
}
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function find_closest_enclosing_block(top) {
|
||||||
|
var START = top-1000;
|
||||||
|
var STOP = top;
|
||||||
|
var matches = [];
|
||||||
|
var elem, temp;
|
||||||
|
var width = 1000;
|
||||||
|
|
||||||
|
for (y = START; y < STOP; y += 20) {
|
||||||
|
for ( x = 0; x < width; x += 20) {
|
||||||
|
elem = document.elementFromPoint(x, y);
|
||||||
|
try {
|
||||||
|
elem = $(elem);
|
||||||
|
temp = elem.offset().top
|
||||||
|
matches.push(elem);
|
||||||
|
if (Math.abs(temp - START) < 25) { y = STOP; break}
|
||||||
|
} catch(error) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var miny = Math.abs(matches[0].offset().top - START), min_elem = matches[0];
|
||||||
|
|
||||||
|
for (i = 1; i < matches.length; i++) {
|
||||||
|
elem = matches[i];
|
||||||
|
temp = Math.abs(elem.offset().top - START);
|
||||||
|
if ( temp < miny ) { miny = temp; min_elem = elem; }
|
||||||
|
}
|
||||||
|
return min_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculate_bookmark(y) {
|
||||||
|
var elem = find_closest_enclosing_block(y);
|
||||||
|
var sel = selector(elem);
|
||||||
|
var ratio = (y - elem.offset().top)/elem.height();
|
||||||
|
if (ratio > 1) { ratio = 1; }
|
||||||
|
if (ratio < 0) { ratio = 0; }
|
||||||
|
return sel + "|" + ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function animated_scrolling_done() {
|
||||||
|
window.py_bridge.animated_scroll_done();
|
||||||
|
}
|
||||||
|
|
||||||
|
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()}});
|
||||||
|
}
|
||||||
|
|
26
resources/viewer/hyphenation.js
Normal file
26
resources/viewer/hyphenation.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* bookmarks management
|
||||||
|
* Copyright 2008 Kovid Goyal
|
||||||
|
* License: GNU GPL v3
|
||||||
|
*/
|
||||||
|
|
||||||
|
function init_hyphenate() {
|
||||||
|
window.py_bridge.init_hyphenate();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", init_hyphenate, false);
|
||||||
|
|
||||||
|
function do_hyphenation(lang) {
|
||||||
|
Hyphenator.config(
|
||||||
|
{
|
||||||
|
'minwordlength' : 6,
|
||||||
|
//'hyphenchar' : '|',
|
||||||
|
'displaytogglebox' : false,
|
||||||
|
'remoteloading' : false,
|
||||||
|
'onerrorhandler' : function (e) {
|
||||||
|
window.py_bridge.debug(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Hyphenator.hyphenate(document.body, lang);
|
||||||
|
}
|
||||||
|
|
62
resources/viewer/referencing.js
Normal file
62
resources/viewer/referencing.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* reference management
|
||||||
|
* Copyright 2008 Kovid Goyal
|
||||||
|
* License: GNU GPL v3
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var reference_old_bgcol = "transparent";
|
||||||
|
var reference_prefix = "1.";
|
||||||
|
|
||||||
|
function show_reference_panel(ref) {
|
||||||
|
panel = $("#calibre_reference_panel");
|
||||||
|
if (panel.length < 1) {
|
||||||
|
$(document.body).append('<div id="calibre_reference_panel" style="top:20px; left:20px; padding-left:30px; padding-right:30px; font:monospace normal;text-align:center; z-index:10000; background: beige; border:red ridge 2px; position:absolute;"><h5>Paragraph</h5><p style="text-indent:0pt">None</p></div>')
|
||||||
|
panel = $("#calibre_reference_panel");
|
||||||
|
}
|
||||||
|
$("> p", panel).text(ref);
|
||||||
|
panel.css({top:(window.pageYOffset+20)+"px"});
|
||||||
|
panel.fadeIn(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle_reference(e) {
|
||||||
|
p = $(this);
|
||||||
|
if (e.type == "mouseenter") {
|
||||||
|
reference_old_bgcol = p.css("background-color");
|
||||||
|
p.css({backgroundColor:"beige"});
|
||||||
|
var i = 0;
|
||||||
|
var paras = $("p");
|
||||||
|
for (j = 0; j < paras.length; j++,i++) {
|
||||||
|
if (paras[j] == p[0]) break;
|
||||||
|
}
|
||||||
|
show_reference_panel(reference_prefix+(i+1) );
|
||||||
|
} else {
|
||||||
|
p.css({backgroundColor:reference_old_bgcol});
|
||||||
|
panel = $("#calibre_reference_panel").hide();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enter_reference_mode() {
|
||||||
|
$("p").bind("mouseenter mouseleave", toggle_reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave_reference_mode() {
|
||||||
|
$("p").unbind("mouseenter mouseleave", toggle_reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
function goto_reference(ref) {
|
||||||
|
var tokens = ref.split(".");
|
||||||
|
if (tokens.length != 2) {alert("Invalid reference: "+ref); return;}
|
||||||
|
var num = parseInt(tokens[1]);
|
||||||
|
if (isNaN(num)) {alert("Invalid reference: "+ref); return;}
|
||||||
|
num -= 1;
|
||||||
|
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
||||||
|
var p = $("p");
|
||||||
|
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
||||||
|
$.scrollTo($(p[num]), 1000,
|
||||||
|
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@ from setup import __version__ as VERSION, __appname__ as APPNAME, SRC, Command,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
setup
|
||||||
except:
|
except:
|
||||||
class setup:
|
class setup:
|
||||||
pass
|
pass
|
||||||
@ -385,7 +386,7 @@ def main():
|
|||||||
{
|
{
|
||||||
'optimize' : 2,
|
'optimize' : 2,
|
||||||
'dist_dir' : 'build/py2app',
|
'dist_dir' : 'build/py2app',
|
||||||
'argv_emulation' : False,
|
'argv_emulation' : True,
|
||||||
'iconfile' : icon,
|
'iconfile' : icon,
|
||||||
'frameworks': ['libusb.dylib', 'libunrar.dylib'],
|
'frameworks': ['libusb.dylib', 'libunrar.dylib'],
|
||||||
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtXml',
|
'includes' : ['sip', 'pkg_resources', 'PyQt4.QtXml',
|
||||||
|
@ -303,9 +303,6 @@ class UploadInstallers(Command):
|
|||||||
installers.append(installer_name('tar.bz2', is64bit=True))
|
installers.append(installer_name('tar.bz2', is64bit=True))
|
||||||
map(self.upload_installer, installers)
|
map(self.upload_installer, installers)
|
||||||
|
|
||||||
check_call('''ssh divok echo %s \\> %s/latest_version'''\
|
|
||||||
%(__version__, DOWNLOADS), shell=True)
|
|
||||||
|
|
||||||
class UploadUserManual(Command):
|
class UploadUserManual(Command):
|
||||||
description = 'Build and upload the User Manual'
|
description = 'Build and upload the User Manual'
|
||||||
sub_commands = ['manual']
|
sub_commands = ['manual']
|
||||||
@ -349,6 +346,8 @@ class UploadToServer(Command):
|
|||||||
shell=True)
|
shell=True)
|
||||||
check_call('ssh divok bzr update /usr/local/calibre',
|
check_call('ssh divok bzr update /usr/local/calibre',
|
||||||
shell=True)
|
shell=True)
|
||||||
|
check_call('''ssh divok echo %s \\> %s/latest_version'''\
|
||||||
|
%(__version__, DOWNLOADS), shell=True)
|
||||||
check_call('ssh divok /etc/init.d/apache2 graceful',
|
check_call('ssh divok /etc/init.d/apache2 graceful',
|
||||||
shell=True)
|
shell=True)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.6.20'
|
__version__ = '0.6.21'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -372,6 +372,7 @@ from calibre.devices.prs700.driver import PRS700
|
|||||||
from calibre.devices.android.driver import ANDROID
|
from calibre.devices.android.driver import ANDROID
|
||||||
from calibre.devices.eslick.driver import ESLICK
|
from calibre.devices.eslick.driver import ESLICK
|
||||||
from calibre.devices.nuut2.driver import NUUT2
|
from calibre.devices.nuut2.driver import NUUT2
|
||||||
|
from calibre.devices.iriver.driver import IRIVER_STORY
|
||||||
|
|
||||||
plugins = [HTML2ZIP]
|
plugins = [HTML2ZIP]
|
||||||
plugins += [
|
plugins += [
|
||||||
@ -427,7 +428,8 @@ plugins += [
|
|||||||
COOL_ER,
|
COOL_ER,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
ESLICK,
|
ESLICK,
|
||||||
NUUT2
|
NUUT2,
|
||||||
|
IRIVER_STORY
|
||||||
]
|
]
|
||||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||||
x.__name__.endswith('MetadataReader')]
|
x.__name__.endswith('MetadataReader')]
|
||||||
|
@ -250,7 +250,7 @@ class HanlinV3Output(OutputProfile):
|
|||||||
|
|
||||||
name = 'Hanlin V3'
|
name = 'Hanlin V3'
|
||||||
short_name = 'hanlinv3'
|
short_name = 'hanlinv3'
|
||||||
description = _('This profile is intended for the Hanlin V3 and its clones.')
|
description = _('This profile is intended for the Hanlin V3/V5 and its clones.')
|
||||||
|
|
||||||
# Screen size is a best guess
|
# Screen size is a best guess
|
||||||
screen_size = (584, 754)
|
screen_size = (584, 754)
|
||||||
|
@ -276,8 +276,10 @@ def input_format_plugins():
|
|||||||
yield plugin
|
yield plugin
|
||||||
|
|
||||||
def plugin_for_input_format(fmt):
|
def plugin_for_input_format(fmt):
|
||||||
|
customization = config['plugin_customization']
|
||||||
for plugin in input_format_plugins():
|
for plugin in input_format_plugins():
|
||||||
if fmt.lower() in plugin.file_types:
|
if fmt.lower() in plugin.file_types:
|
||||||
|
plugin.site_customization = customization.get(plugin.name, None)
|
||||||
return plugin
|
return plugin
|
||||||
|
|
||||||
def all_input_formats():
|
def all_input_formats():
|
||||||
@ -302,8 +304,10 @@ def output_format_plugins():
|
|||||||
yield plugin
|
yield plugin
|
||||||
|
|
||||||
def plugin_for_output_format(fmt):
|
def plugin_for_output_format(fmt):
|
||||||
|
customization = config['plugin_customization']
|
||||||
for plugin in output_format_plugins():
|
for plugin in output_format_plugins():
|
||||||
if fmt.lower() == plugin.file_type:
|
if fmt.lower() == plugin.file_type:
|
||||||
|
plugin.site_customization = customization.get(plugin.name, None)
|
||||||
return plugin
|
return plugin
|
||||||
|
|
||||||
def available_output_formats():
|
def available_output_formats():
|
||||||
|
10
src/calibre/devices/iriver/__init__.py
Normal file
10
src/calibre/devices/iriver/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
|
46
src/calibre/devices/iriver/driver.py
Normal file
46
src/calibre/devices/iriver/driver.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
|
class IRIVER_STORY(USBMS):
|
||||||
|
|
||||||
|
name = 'Iriver Story Device Interface'
|
||||||
|
gui_name = 'Iriver Story'
|
||||||
|
description = _('Communicate with the Iriver Story reader.')
|
||||||
|
author = _('Kovid Goyal')
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
|
# Ordered list of supported formats
|
||||||
|
FORMATS = ['epub', 'pdf', 'txt']
|
||||||
|
|
||||||
|
VENDOR_ID = [0x1006]
|
||||||
|
PRODUCT_ID = [0x4023]
|
||||||
|
BCD = [0x0323]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'IRIVER'
|
||||||
|
WINDOWS_MAIN_MEM = 'STORY'
|
||||||
|
WINDOWS_CARD_A_MEM = 'STORY'
|
||||||
|
|
||||||
|
#OSX_MAIN_MEM = 'Kindle Internal Storage Media'
|
||||||
|
#OSX_CARD_A_MEM = 'Kindle Card Storage Media'
|
||||||
|
|
||||||
|
MAIN_MEMORY_VOLUME_LABEL = 'Story Main Memory'
|
||||||
|
STORAGE_CARD_VOLUME_LABEL = 'Story Storage Card'
|
||||||
|
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
def windows_sort_drives(self, drives):
|
||||||
|
main = drives.get('main', None)
|
||||||
|
card = drives.get('carda', None)
|
||||||
|
if card and main and card < main:
|
||||||
|
drives['main'] = card
|
||||||
|
drives['carda'] = main
|
||||||
|
|
||||||
|
return drives
|
||||||
|
|
@ -19,7 +19,7 @@ class NUUT2(USBMS):
|
|||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
|
||||||
# Ordered list of supported formats
|
# Ordered list of supported formats
|
||||||
FORMATS = ['epub', 'pdft', 'txt']
|
FORMATS = ['epub', 'pdf', 'txt']
|
||||||
DRM_FORMATS = ['epub']
|
DRM_FORMATS = ['epub']
|
||||||
|
|
||||||
VENDOR_ID = [0x140e]
|
VENDOR_ID = [0x140e]
|
||||||
|
@ -34,6 +34,7 @@ def metadata_from_formats(formats):
|
|||||||
mi = metadata_from_filename(list(iter(formats))[0])
|
mi = metadata_from_filename(list(iter(formats))[0])
|
||||||
if not mi.authors:
|
if not mi.authors:
|
||||||
mi.authors = [_('Unknown')]
|
mi.authors = [_('Unknown')]
|
||||||
|
return mi
|
||||||
|
|
||||||
def _metadata_from_formats(formats):
|
def _metadata_from_formats(formats):
|
||||||
mi = MetaInformation(None, None)
|
mi = MetaInformation(None, None)
|
||||||
|
@ -60,9 +60,11 @@ class FormatState(object):
|
|||||||
self.valign = 'baseline'
|
self.valign = 'baseline'
|
||||||
self.italic = False
|
self.italic = False
|
||||||
self.bold = False
|
self.bold = False
|
||||||
|
self.strikethrough = False
|
||||||
self.preserve = False
|
self.preserve = False
|
||||||
self.family = 'serif'
|
self.family = 'serif'
|
||||||
self.bgcolor = 'transparent'
|
self.bgcolor = 'transparent'
|
||||||
|
self.fgcolor = 'black'
|
||||||
self.href = None
|
self.href = None
|
||||||
self.list_num = 0
|
self.list_num = 0
|
||||||
self.attrib = {}
|
self.attrib = {}
|
||||||
@ -75,7 +77,9 @@ class FormatState(object):
|
|||||||
and self.valign == other.valign \
|
and self.valign == other.valign \
|
||||||
and self.preserve == other.preserve \
|
and self.preserve == other.preserve \
|
||||||
and self.family == other.family \
|
and self.family == other.family \
|
||||||
and self.bgcolor == other.bgcolor
|
and self.bgcolor == other.bgcolor \
|
||||||
|
and self.fgcolor == other.fgcolor \
|
||||||
|
and self.strikethrough == other.strikethrough
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
@ -239,6 +243,11 @@ class MobiMLizer(object):
|
|||||||
if istate.bgcolor is not None and istate.bgcolor != 'transparent' :
|
if istate.bgcolor is not None and istate.bgcolor != 'transparent' :
|
||||||
inline = etree.SubElement(inline, XHTML('span'),
|
inline = etree.SubElement(inline, XHTML('span'),
|
||||||
bgcolor=istate.bgcolor)
|
bgcolor=istate.bgcolor)
|
||||||
|
if istate.fgcolor != 'black':
|
||||||
|
inline = etree.SubElement(inline, XHTML('font'),
|
||||||
|
color=istate.fgcolor)
|
||||||
|
if istate.strikethrough:
|
||||||
|
inline = etree.SubElement(inline, XHTML('s'))
|
||||||
bstate.inline = inline
|
bstate.inline = inline
|
||||||
bstate.istate = istate
|
bstate.istate = istate
|
||||||
inline = bstate.inline
|
inline = bstate.inline
|
||||||
@ -316,6 +325,8 @@ class MobiMLizer(object):
|
|||||||
istate.bold = weight in ('bold', 'bolder') or asfloat(weight) > 400
|
istate.bold = weight in ('bold', 'bolder') or asfloat(weight) > 400
|
||||||
istate.preserve = (style['white-space'] in ('pre', 'pre-wrap'))
|
istate.preserve = (style['white-space'] in ('pre', 'pre-wrap'))
|
||||||
istate.bgcolor = style['background-color']
|
istate.bgcolor = style['background-color']
|
||||||
|
istate.fgcolor = style['color']
|
||||||
|
istate.strikethrough = style['text-decoration'] == 'line-through'
|
||||||
if 'monospace' in style['font-family']:
|
if 'monospace' in style['font-family']:
|
||||||
istate.family = 'monospace'
|
istate.family = 'monospace'
|
||||||
elif 'sans-serif' in style['font-family']:
|
elif 'sans-serif' in style['font-family']:
|
||||||
|
@ -709,6 +709,10 @@ class MobiWriter(object):
|
|||||||
|
|
||||||
if self.opts.verbose > 3 :self._oeb.logger.info(" node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X" % \
|
if self.opts.verbose > 3 :self._oeb.logger.info(" node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X" % \
|
||||||
(myIndex, self._ctoc_map[i]['klass'], child.title if child.title.strip() > "" else "(missing)", thisRecord, interimSpanRecord, offset, length) )
|
(myIndex, self._ctoc_map[i]['klass'], child.title if child.title.strip() > "" else "(missing)", thisRecord, interimSpanRecord, offset, length) )
|
||||||
|
elif thisRecord == numberOfHTMLRecords-1:
|
||||||
|
# Check for short terminating record (GR provisional)
|
||||||
|
if self._HTMLRecords[thisRecord].continuingNode == -1:
|
||||||
|
self._HTMLRecords[thisRecord].continuingNode = self._HTMLRecords[thisRecord].openingNode - 1
|
||||||
else :
|
else :
|
||||||
if self.opts.verbose > 3 : self._oeb.logger.info(" node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X" % \
|
if self.opts.verbose > 3 : self._oeb.logger.info(" node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X" % \
|
||||||
(myIndex, self._ctoc_map[i]['klass'], child.title if child.title.strip() > "" else "(missing)", thisRecord, thisRecord, offset, length) )
|
(myIndex, self._ctoc_map[i]['klass'], child.title if child.title.strip() > "" else "(missing)", thisRecord, thisRecord, offset, length) )
|
||||||
|
@ -19,7 +19,7 @@ from calibre.utils.zipfile import safe_replace, ZipFile
|
|||||||
from calibre.utils.config import DynamicConfig
|
from calibre.utils.config import DynamicConfig
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre.ebooks.epub.output import EPUBOutput
|
from calibre.ebooks.epub.output import EPUBOutput
|
||||||
from calibre import guess_type
|
from calibre import guess_type, prints
|
||||||
|
|
||||||
TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8')
|
TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8')
|
||||||
|
|
||||||
@ -99,29 +99,63 @@ class EbookIterator(object):
|
|||||||
if text in open(path, 'rb').read().decode(path.encoding).lower():
|
if text in open(path, 'rb').read().decode(path.encoding).lower():
|
||||||
return i
|
return i
|
||||||
|
|
||||||
|
def find_missing_css_files(self):
|
||||||
|
for x in os.walk(os.path.dirname(self.pathtoopf)):
|
||||||
|
for f in x[-1]:
|
||||||
|
if f.endswith('.css'):
|
||||||
|
yield os.path.join(x[0], f)
|
||||||
|
|
||||||
|
def find_declared_css_files(self):
|
||||||
|
for item in self.opf.manifest:
|
||||||
|
if item.mime_type and 'css' in item.mime_type.lower():
|
||||||
|
yield item.path
|
||||||
|
|
||||||
def find_embedded_fonts(self):
|
def find_embedded_fonts(self):
|
||||||
'''
|
'''
|
||||||
This will become unnecessary once Qt WebKit supports the @font-face rule.
|
This will become unnecessary once Qt WebKit supports the @font-face rule.
|
||||||
'''
|
'''
|
||||||
for item in self.opf.manifest:
|
css_files = set(self.find_declared_css_files())
|
||||||
if item.mime_type and 'css' in item.mime_type.lower():
|
if not css_files:
|
||||||
css = open(item.path, 'rb').read().decode('utf-8', 'replace')
|
css_files = set(self.find_missing_css_files())
|
||||||
for match in re.compile(r'@font-face\s*{([^}]+)}').finditer(css):
|
bad_map = {}
|
||||||
block = match.group(1)
|
font_family_pat = re.compile(r'font-family\s*:\s*([^;]+)')
|
||||||
family = re.compile(r'font-family\s*:\s*([^;]+)').search(block)
|
for csspath in css_files:
|
||||||
url = re.compile(r'url\s*\([\'"]*(.+?)[\'"]*\)', re.DOTALL).search(block)
|
css = open(csspath, 'rb').read().decode('utf-8', 'replace')
|
||||||
if url:
|
for match in re.compile(r'@font-face\s*{([^}]+)}').finditer(css):
|
||||||
path = url.group(1).split('/')
|
block = match.group(1)
|
||||||
path = os.path.join(os.path.dirname(item.path), *path)
|
family = font_family_pat.search(block)
|
||||||
id = QFontDatabase.addApplicationFont(path)
|
url = re.compile(r'url\s*\([\'"]*(.+?)[\'"]*\)', re.DOTALL).search(block)
|
||||||
if id != -1:
|
if url:
|
||||||
families = [unicode(f) for f in QFontDatabase.applicationFontFamilies(id)]
|
path = url.group(1).split('/')
|
||||||
if family:
|
path = os.path.join(os.path.dirname(csspath), *path)
|
||||||
family = family.group(1).strip().replace('"', '')
|
if not os.access(path, os.R_OK):
|
||||||
if family not in families:
|
continue
|
||||||
print 'WARNING: Family aliasing not supported:', block
|
id = QFontDatabase.addApplicationFont(path)
|
||||||
else:
|
if id != -1:
|
||||||
print 'Loaded embedded font:', repr(family)
|
families = [unicode(f) for f in QFontDatabase.applicationFontFamilies(id)]
|
||||||
|
if family:
|
||||||
|
family = family.group(1).strip().replace('"', '')
|
||||||
|
bad_map[family] = families[0]
|
||||||
|
if family not in families:
|
||||||
|
prints('WARNING: Family aliasing not fully supported.')
|
||||||
|
prints('\tDeclared family: %s not in actual families: %s'
|
||||||
|
% (family, families))
|
||||||
|
else:
|
||||||
|
prints('Loaded embedded font:', repr(family))
|
||||||
|
if bad_map:
|
||||||
|
def prepend_embedded_font(match):
|
||||||
|
for bad, good in bad_map.items():
|
||||||
|
if bad in match.group(1):
|
||||||
|
prints('Substituting font family: %s -> %s'%(bad, good))
|
||||||
|
return match.group().replace(bad, '"%s"'%good)
|
||||||
|
|
||||||
|
for csspath in css_files:
|
||||||
|
with open(csspath, 'r+b') as f:
|
||||||
|
css = f.read()
|
||||||
|
css = font_family_pat.sub(prepend_embedded_font, css)
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
f.write(css)
|
||||||
|
|
||||||
def __enter__(self, processed=False):
|
def __enter__(self, processed=False):
|
||||||
self.delete_on_exit = []
|
self.delete_on_exit = []
|
||||||
|
@ -274,6 +274,7 @@ class GetMetadata(QObject):
|
|||||||
self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
|
self.emit(SIGNAL('metadata(PyQt_PyObject, PyQt_PyObject)'), id, mi)
|
||||||
|
|
||||||
class TableView(QTableView):
|
class TableView(QTableView):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QTableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
self.read_settings()
|
self.read_settings()
|
||||||
@ -585,8 +586,11 @@ def build_forms(srcdir, info=None):
|
|||||||
if form.endswith('viewer%smain.ui'%os.sep):
|
if form.endswith('viewer%smain.ui'%os.sep):
|
||||||
info('\t\tPromoting WebView')
|
info('\t\tPromoting WebView')
|
||||||
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
|
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
|
||||||
|
dat = dat.replace('from PyQt4 import QtWebKit', '')
|
||||||
|
if iswindows:
|
||||||
|
dat = dat.replace('self.view = QWebView(', 'self.view = DocumentView(')
|
||||||
|
dat = dat.replace('from QtWebKit.QWebView import QWebView', '')
|
||||||
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
|
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
|
||||||
dat += '\nQtWebKit'
|
|
||||||
|
|
||||||
open(compiled_form, 'wb').write(dat)
|
open(compiled_form, 'wb').write(dat)
|
||||||
|
|
||||||
|
@ -157,7 +157,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
self.formats_changed = True
|
self.formats_changed = True
|
||||||
added = True
|
added = True
|
||||||
if bad_perms:
|
if bad_perms:
|
||||||
error_dialog(self.window, _('You do not have '
|
error_dialog(self.window, _('No permission'),
|
||||||
|
_('You do not have '
|
||||||
'permission to read the following files:'),
|
'permission to read the following files:'),
|
||||||
det_msg='\n'.join(bad_perms), show=True)
|
det_msg='\n'.join(bad_perms), show=True)
|
||||||
|
|
||||||
|
@ -742,6 +742,16 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
|
|
||||||
########################## Connect to device ##############################
|
########################## Connect to device ##############################
|
||||||
|
|
||||||
|
def save_device_view_settings(self):
|
||||||
|
model = self.location_view.model()
|
||||||
|
self.memory_view.write_settings()
|
||||||
|
for x in range(model.rowCount()):
|
||||||
|
if x > 1:
|
||||||
|
if model.location_for_row(x) == 'carda':
|
||||||
|
self.card_a_view.write_settings()
|
||||||
|
elif model.location_for_row(x) == 'cardb':
|
||||||
|
self.card_b_view.write_settings()
|
||||||
|
|
||||||
def device_detected(self, connected):
|
def device_detected(self, connected):
|
||||||
'''
|
'''
|
||||||
Called when a device is connected to the computer.
|
Called when a device is connected to the computer.
|
||||||
@ -757,6 +767,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.device_connected = True
|
self.device_connected = True
|
||||||
self._sync_menu.enable_device_actions(True, self.device_manager.device.card_prefix())
|
self._sync_menu.enable_device_actions(True, self.device_manager.device.card_prefix())
|
||||||
else:
|
else:
|
||||||
|
self.save_device_view_settings()
|
||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
self._sync_menu.enable_device_actions(False)
|
self._sync_menu.enable_device_actions(False)
|
||||||
self.location_view.model().update_devices()
|
self.location_view.model().update_devices()
|
||||||
@ -765,7 +776,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.device_info = ' '
|
self.device_info = ' '
|
||||||
if self.current_view() != self.library_view:
|
if self.current_view() != self.library_view:
|
||||||
self.status_bar.reset_info()
|
self.status_bar.reset_info()
|
||||||
self.location_selected('library')
|
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
||||||
|
|
||||||
def info_read(self, job):
|
def info_read(self, job):
|
||||||
'''
|
'''
|
||||||
@ -807,6 +818,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.card_b_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
self.card_b_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||||
view.sortByColumn(3, Qt.DescendingOrder)
|
view.sortByColumn(3, Qt.DescendingOrder)
|
||||||
|
view.read_settings()
|
||||||
if not view.restore_column_widths():
|
if not view.restore_column_widths():
|
||||||
view.resizeColumnsToContents()
|
view.resizeColumnsToContents()
|
||||||
view.resizeRowsToContents()
|
view.resizeRowsToContents()
|
||||||
@ -1662,7 +1674,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
dynamic.set('sort_column', self.library_view.model().sorted_on)
|
dynamic.set('sort_column', self.library_view.model().sorted_on)
|
||||||
self.library_view.write_settings()
|
self.library_view.write_settings()
|
||||||
if self.device_connected:
|
if self.device_connected:
|
||||||
self.memory_view.write_settings()
|
self.save_device_view_settings()
|
||||||
|
|
||||||
def restart(self):
|
def restart(self):
|
||||||
self.quit(restart=True)
|
self.quit(restart=True)
|
||||||
|
@ -15,11 +15,12 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
|||||||
from calibre.utils.config import Config, StringConfig
|
from calibre.utils.config import Config, StringConfig
|
||||||
from calibre.utils.localization import get_language
|
from calibre.utils.localization import get_language
|
||||||
from calibre.gui2.viewer.config_ui import Ui_Dialog
|
from calibre.gui2.viewer.config_ui import Ui_Dialog
|
||||||
from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation
|
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
from calibre import prints, guess_type
|
from calibre import prints, guess_type
|
||||||
|
|
||||||
|
bookmarks = referencing = hyphenation = jquery = jquery_scrollTo = hyphenator = None
|
||||||
|
|
||||||
def load_builtin_fonts():
|
def load_builtin_fonts():
|
||||||
base = P('fonts/liberation/*.ttf')
|
base = P('fonts/liberation/*.ttf')
|
||||||
for f in glob.glob(base):
|
for f in glob.glob(base):
|
||||||
@ -192,15 +193,24 @@ class Document(QWebPage):
|
|||||||
self.hyphenate_default_lang = opts.hyphenate_default_lang
|
self.hyphenate_default_lang = opts.hyphenate_default_lang
|
||||||
|
|
||||||
def load_javascript_libraries(self):
|
def load_javascript_libraries(self):
|
||||||
|
global bookmarks, referencing, hyphenation, jquery, jquery_scrollTo, hyphenator
|
||||||
self.mainFrame().addToJavaScriptWindowObject("py_bridge", self)
|
self.mainFrame().addToJavaScriptWindowObject("py_bridge", self)
|
||||||
jquery = open(P('content_server/jquery.js'), 'rb').read()
|
if jquery is None:
|
||||||
jquery_scrollTo = open(P('viewer/jquery_scrollTo.js'), 'rb').read()
|
jquery = P('content_server/jquery.js', data=True)
|
||||||
hyphenator = open(P('viewer/hyphenate/Hyphenator.js'),
|
if jquery_scrollTo is None:
|
||||||
'rb').read().decode('utf-8')
|
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)
|
||||||
self.javascript(jquery_scrollTo)
|
self.javascript(jquery_scrollTo)
|
||||||
|
if bookmarks is None:
|
||||||
|
bookmarks = P('viewer/bookmarks.js', data=True)
|
||||||
self.javascript(bookmarks)
|
self.javascript(bookmarks)
|
||||||
|
if referencing is None:
|
||||||
|
referencing = P('viewer/referencing.js', data=True)
|
||||||
self.javascript(referencing)
|
self.javascript(referencing)
|
||||||
|
if hyphenation is None:
|
||||||
|
hyphenation = P('viewer/hyphenation.js', data=True)
|
||||||
self.javascript(hyphenation)
|
self.javascript(hyphenation)
|
||||||
default_lang = self.hyphenate_default_lang
|
default_lang = self.hyphenate_default_lang
|
||||||
lang = self.current_language
|
lang = self.current_language
|
||||||
@ -333,6 +343,7 @@ class Document(QWebPage):
|
|||||||
def width(self):
|
def width(self):
|
||||||
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
|
return self.mainFrame().contentsSize().width() # offsetWidth gives inaccurate results
|
||||||
|
|
||||||
|
|
||||||
class EntityDeclarationProcessor(object):
|
class EntityDeclarationProcessor(object):
|
||||||
|
|
||||||
def __init__(self, html):
|
def __init__(self, html):
|
||||||
@ -447,6 +458,12 @@ class DocumentView(QWebView):
|
|||||||
def path(self):
|
def path(self):
|
||||||
return os.path.abspath(unicode(self.url().toLocalFile()))
|
return os.path.abspath(unicode(self.url().toLocalFile()))
|
||||||
|
|
||||||
|
def self_closing_sub(self, match):
|
||||||
|
tag = match.group(1)
|
||||||
|
if tag.lower().strip() == 'br':
|
||||||
|
return match.group()
|
||||||
|
return '<%s %s></%s>'%(match.group(1), match.group(2), match.group(1))
|
||||||
|
|
||||||
def load_path(self, path, pos=0.0):
|
def load_path(self, path, pos=0.0):
|
||||||
self.initial_pos = pos
|
self.initial_pos = pos
|
||||||
mt = getattr(path, 'mime_type', None)
|
mt = getattr(path, 'mime_type', None)
|
||||||
@ -455,7 +472,7 @@ class DocumentView(QWebView):
|
|||||||
html = open(path, 'rb').read().decode(path.encoding, 'replace')
|
html = open(path, 'rb').read().decode(path.encoding, 'replace')
|
||||||
html = EntityDeclarationProcessor(html).processed_html
|
html = EntityDeclarationProcessor(html).processed_html
|
||||||
if 'xhtml' in mt:
|
if 'xhtml' in mt:
|
||||||
html = self.self_closing_pat.sub(r'<\1 \2></\1>', html)
|
html = self.self_closing_pat.sub(self.self_closing_sub, html)
|
||||||
#self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path))
|
#self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path))
|
||||||
self.setHtml(html, QUrl.fromLocalFile(path))
|
self.setHtml(html, QUrl.fromLocalFile(path))
|
||||||
self.turn_off_internal_scrollbars()
|
self.turn_off_internal_scrollbars()
|
||||||
@ -508,6 +525,7 @@ class DocumentView(QWebView):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def test_line(cls, img, y):
|
def test_line(cls, img, y):
|
||||||
|
'Test if line contains pixels of exactly the same color'
|
||||||
start = img.pixel(0, y)
|
start = img.pixel(0, y)
|
||||||
for i in range(1, img.width()):
|
for i in range(1, img.width()):
|
||||||
if img.pixel(i, y) != start:
|
if img.pixel(i, y) != start:
|
||||||
@ -517,6 +535,7 @@ class DocumentView(QWebView):
|
|||||||
def find_next_blank_line(self, overlap):
|
def find_next_blank_line(self, overlap):
|
||||||
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
||||||
painter = QPainter(img)
|
painter = QPainter(img)
|
||||||
|
# Render a region of width x overlap pixels atthe bottom of the current viewport
|
||||||
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
||||||
painter.end()
|
painter.end()
|
||||||
for i in range(overlap-1, -1, -1):
|
for i in range(overlap-1, -1, -1):
|
||||||
@ -542,18 +561,20 @@ class DocumentView(QWebView):
|
|||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
|
||||||
def next_page(self):
|
def next_page(self):
|
||||||
delta_y = self.document.window_height - 25
|
window_height = self.document.window_height
|
||||||
|
delta_y = window_height - 25
|
||||||
if self.document.at_bottom:
|
if self.document.at_bottom:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
else:
|
else:
|
||||||
opos = self.document.ypos
|
opos = self.document.ypos
|
||||||
lower_limit = opos + delta_y
|
lower_limit = opos + delta_y
|
||||||
max_y = self.document.height - self.document.window_height
|
max_y = self.document.height - window_height
|
||||||
lower_limit = min(max_y, lower_limit)
|
lower_limit = min(max_y, lower_limit)
|
||||||
if lower_limit > opos:
|
if lower_limit > opos:
|
||||||
self.document.scroll_to(self.document.xpos, lower_limit)
|
self.document.scroll_to(self.document.xpos, lower_limit)
|
||||||
self.find_next_blank_line( self.height() - (self.document.ypos-opos) )
|
actually_scrolled = self.document.ypos - opos
|
||||||
|
self.find_next_blank_line(window_height - actually_scrolled)
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
bookmarks = '''
|
|
||||||
|
|
||||||
function selector_in_parent(elem) {
|
|
||||||
var num = elem.prevAll().length;
|
|
||||||
var sel = " > *:eq("+num+") ";
|
|
||||||
return sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function selector(elem) {
|
|
||||||
var obj = elem;
|
|
||||||
var sel = "";
|
|
||||||
while (obj[0] != document) {
|
|
||||||
sel = selector_in_parent(obj) + sel;
|
|
||||||
obj = obj.parent();
|
|
||||||
}
|
|
||||||
return sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
function find_closest_enclosing_block(top) {
|
|
||||||
var START = top-1000;
|
|
||||||
var STOP = top;
|
|
||||||
var matches = [];
|
|
||||||
var elem, temp;
|
|
||||||
var width = 1000;
|
|
||||||
|
|
||||||
for (y = START; y < STOP; y += 20) {
|
|
||||||
for ( x = 0; x < width; x += 20) {
|
|
||||||
elem = document.elementFromPoint(x, y);
|
|
||||||
try {
|
|
||||||
elem = $(elem);
|
|
||||||
temp = elem.offset().top
|
|
||||||
matches.push(elem);
|
|
||||||
if (Math.abs(temp - START) < 25) { y = STOP; break}
|
|
||||||
} catch(error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var miny = Math.abs(matches[0].offset().top - START), min_elem = matches[0];
|
|
||||||
|
|
||||||
for (i = 1; i < matches.length; i++) {
|
|
||||||
elem = matches[i];
|
|
||||||
temp = Math.abs(elem.offset().top - START);
|
|
||||||
if ( temp < miny ) { miny = temp; min_elem = elem; }
|
|
||||||
}
|
|
||||||
return min_elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculate_bookmark(y) {
|
|
||||||
var elem = find_closest_enclosing_block(y);
|
|
||||||
var sel = selector(elem);
|
|
||||||
var ratio = (y - elem.offset().top)/elem.height();
|
|
||||||
if (ratio > 1) { ratio = 1; }
|
|
||||||
if (ratio < 0) { ratio = 0; }
|
|
||||||
return sel + "|" + ratio;
|
|
||||||
}
|
|
||||||
|
|
||||||
function animated_scrolling_done() {
|
|
||||||
window.py_bridge.animated_scroll_done();
|
|
||||||
}
|
|
||||||
|
|
||||||
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()}});
|
|
||||||
}
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
referencing = '''
|
|
||||||
var reference_old_bgcol = "transparent";
|
|
||||||
var reference_prefix = "1.";
|
|
||||||
|
|
||||||
function show_reference_panel(ref) {
|
|
||||||
panel = $("#calibre_reference_panel");
|
|
||||||
if (panel.length < 1) {
|
|
||||||
$(document.body).append('<div id="calibre_reference_panel" style="top:20px; left:20px; padding-left:30px; padding-right:30px; font:monospace normal;text-align:center; z-index:10000; background: beige; border:red ridge 2px; position:absolute;"><h5>Paragraph</h5><p style="text-indent:0pt">None</p></div>')
|
|
||||||
panel = $("#calibre_reference_panel");
|
|
||||||
}
|
|
||||||
$("> p", panel).text(ref);
|
|
||||||
panel.css({top:(window.pageYOffset+20)+"px"});
|
|
||||||
panel.fadeIn(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle_reference(e) {
|
|
||||||
p = $(this);
|
|
||||||
if (e.type == "mouseenter") {
|
|
||||||
reference_old_bgcol = p.css("background-color");
|
|
||||||
p.css({backgroundColor:"beige"});
|
|
||||||
var i = 0;
|
|
||||||
var paras = $("p");
|
|
||||||
for (j = 0; j < paras.length; j++,i++) {
|
|
||||||
if (paras[j] == p[0]) break;
|
|
||||||
}
|
|
||||||
show_reference_panel(reference_prefix+(i+1) );
|
|
||||||
} else {
|
|
||||||
p.css({backgroundColor:reference_old_bgcol});
|
|
||||||
panel = $("#calibre_reference_panel").hide();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enter_reference_mode() {
|
|
||||||
$("p").bind("mouseenter mouseleave", toggle_reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
function leave_reference_mode() {
|
|
||||||
$("p").unbind("mouseenter mouseleave", toggle_reference);
|
|
||||||
}
|
|
||||||
|
|
||||||
function goto_reference(ref) {
|
|
||||||
var tokens = ref.split(".");
|
|
||||||
if (tokens.length != 2) {alert("Invalid reference: "+ref); return;}
|
|
||||||
var num = parseInt(tokens[1]);
|
|
||||||
if (isNaN(num)) {alert("Invalid reference: "+ref); return;}
|
|
||||||
num -= 1;
|
|
||||||
if (num < 0) {alert("Invalid reference: "+ref); return;}
|
|
||||||
var p = $("p");
|
|
||||||
if (num >= p.length) {alert("Reference not found: "+ref); return;}
|
|
||||||
$.scrollTo($(p[num]), 1000,
|
|
||||||
{onAfter:function(){window.py_bridge.animated_scroll_done()}});
|
|
||||||
}
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
test = '''
|
|
||||||
$(document.body).click(function(e) {
|
|
||||||
bm = calculate_bookmark(e.pageY);
|
|
||||||
scroll_to_bookmark(bm);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(enter_reference_mode);
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
hyphenation = '''
|
|
||||||
function init_hyphenate() {
|
|
||||||
window.py_bridge.init_hyphenate();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", init_hyphenate, false);
|
|
||||||
|
|
||||||
function do_hyphenation(lang) {
|
|
||||||
Hyphenator.config(
|
|
||||||
{
|
|
||||||
'minwordlength' : 6,
|
|
||||||
//'hyphenchar' : '|',
|
|
||||||
'displaytogglebox' : false,
|
|
||||||
'remoteloading' : false,
|
|
||||||
'onerrorhandler' : function (e) {
|
|
||||||
window.py_bridge.debug(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Hyphenator.hyphenate(document.body, lang);
|
|
||||||
}
|
|
||||||
'''
|
|
@ -468,6 +468,11 @@ class LibraryPage(QWizardPage, LibraryUI):
|
|||||||
self.init_languages()
|
self.init_languages()
|
||||||
self.connect(self.language, SIGNAL('currentIndexChanged(int)'),
|
self.connect(self.language, SIGNAL('currentIndexChanged(int)'),
|
||||||
self.change_language)
|
self.change_language)
|
||||||
|
self.connect(self.location, SIGNAL('textChanged(QString)'),
|
||||||
|
self.location_text_changed)
|
||||||
|
|
||||||
|
def location_text_changed(self, newtext):
|
||||||
|
self.emit(SIGNAL('completeChanged()'))
|
||||||
|
|
||||||
def init_languages(self):
|
def init_languages(self):
|
||||||
self.language.blockSignals(True)
|
self.language.blockSignals(True)
|
||||||
@ -525,9 +530,13 @@ class LibraryPage(QWizardPage, LibraryUI):
|
|||||||
self.location.setText(lp)
|
self.location.setText(lp)
|
||||||
|
|
||||||
def isComplete(self):
|
def isComplete(self):
|
||||||
lp = unicode(self.location.text())
|
try:
|
||||||
return lp and os.path.exists(lp) and os.path.isdir(lp) and os.access(lp,
|
lp = unicode(self.location.text())
|
||||||
os.W_OK)
|
ans = bool(lp) and os.path.exists(lp) and os.path.isdir(lp) and os.access(lp,
|
||||||
|
os.W_OK)
|
||||||
|
except:
|
||||||
|
ans = False
|
||||||
|
return ans
|
||||||
|
|
||||||
def commit(self, completed):
|
def commit(self, completed):
|
||||||
oldloc = prefs['library_path']
|
oldloc = prefs['library_path']
|
||||||
|
@ -20,7 +20,7 @@ from calibre.utils.genshi.template import MarkupTemplate
|
|||||||
|
|
||||||
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
||||||
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
||||||
'formats', 'isbn', 'uuid', 'cover'])
|
'formats', 'isbn', 'uuid', 'pubdate', 'cover'])
|
||||||
|
|
||||||
XML_TEMPLATE = '''\
|
XML_TEMPLATE = '''\
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@ -38,6 +38,7 @@ XML_TEMPLATE = '''\
|
|||||||
<publisher>${record['publisher']}</publisher>
|
<publisher>${record['publisher']}</publisher>
|
||||||
<rating>${record['rating']}</rating>
|
<rating>${record['rating']}</rating>
|
||||||
<date>${record['timestamp']}</date>
|
<date>${record['timestamp']}</date>
|
||||||
|
<pubdate>${record['pubdate']}</pubdate>
|
||||||
<size>${record['size']}</size>
|
<size>${record['size']}</size>
|
||||||
<tags py:if="record['tags']">
|
<tags py:if="record['tags']">
|
||||||
<py:for each="tag in record['tags']">
|
<py:for each="tag in record['tags']">
|
||||||
|
@ -1584,7 +1584,7 @@ class LibraryDatabase2(LibraryDatabase):
|
|||||||
prefix = self.library_path
|
prefix = self.library_path
|
||||||
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating',
|
||||||
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
|
||||||
'isbn', 'uuid'])
|
'isbn', 'uuid', 'pubdate'])
|
||||||
data = []
|
data = []
|
||||||
for record in self.data:
|
for record in self.data:
|
||||||
if record is None: continue
|
if record is None: continue
|
||||||
|
@ -108,7 +108,7 @@ will appear in the next release of |app|.
|
|||||||
Can I use both |app| and the SONY software to manage my reader?
|
Can I use both |app| and the SONY software to manage my reader?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Yes, you can use both, provided you don not run them at the same time. That is, you should use the following sequence:
|
Yes, you can use both, provided you do not run them at the same time. That is, you should use the following sequence:
|
||||||
Connect reader->Use one of the programs->Disconnect reader. Reconnect reader->Use the other program->disconnect reader.
|
Connect reader->Use one of the programs->Disconnect reader. Reconnect reader->Use the other program->disconnect reader.
|
||||||
|
|
||||||
The underlying reason is that the Reader uses a single file to keep track
|
The underlying reason is that the Reader uses a single file to keep track
|
||||||
@ -122,7 +122,7 @@ other via the computers hard disk.
|
|||||||
|
|
||||||
If you do need to reset your metadata due to problems caused by using both
|
If you do need to reset your metadata due to problems caused by using both
|
||||||
at the same time, then just delete the media.xml file on the Reader using
|
at the same time, then just delete the media.xml file on the Reader using
|
||||||
your PC's file explorer and it'll be recreated after disconnection.
|
your PC's file explorer and it will be recreated after disconnection.
|
||||||
|
|
||||||
|
|
||||||
Can I use the collections feature of the SONY reader?
|
Can I use the collections feature of the SONY reader?
|
||||||
@ -149,6 +149,21 @@ How do I use |app| with my Android phone?
|
|||||||
|
|
||||||
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
|
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
|
||||||
|
|
||||||
|
Can I access my |app| books using the web browser in my Kindle or other reading device?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|app| has a *Content Server* that exports the books in |app| as a web page. You can turn it on under
|
||||||
|
Preferences->Content Server. Then just point the web browser on your device to the computer running
|
||||||
|
the Content Server and you will be able to browse your book collection. For example, if the computer running
|
||||||
|
the server has IP address 63.45.128.5, in the browser, you would type::
|
||||||
|
|
||||||
|
http://63.45.128.5:8080
|
||||||
|
|
||||||
|
Some devices, like the Kindle, do not allow you to access port 8080 (the default port on which the content
|
||||||
|
server runs. In that case, change the port in the |app| Preferences to 80. (On some operating systems,
|
||||||
|
you may not be able to run the server on a port number less than 1024 because of security settings. In
|
||||||
|
this case the simplest solution is to adjust your router to forward requests on port 80 to port 8080).
|
||||||
|
|
||||||
I get the error message "Failed to start content server: Port 8080 not free on '0.0.0.0'"?
|
I get the error message "Failed to start content server: Port 8080 not free on '0.0.0.0'"?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
7157
src/calibre/translations/af.po
Normal file
7157
src/calibre/translations/af.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,13 +8,13 @@ msgstr ""
|
|||||||
"Project-Id-Version: calibre\n"
|
"Project-Id-Version: calibre\n"
|
||||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"POT-Creation-Date: 2009-10-26 17:36+0000\n"
|
"POT-Creation-Date: 2009-10-26 17:36+0000\n"
|
||||||
"PO-Revision-Date: 2009-10-29 20:45+0000\n"
|
"PO-Revision-Date: 2009-10-31 00:18+0000\n"
|
||||||
"Last-Translator: Kovid Goyal <Unknown>\n"
|
"Last-Translator: Kovid Goyal <Unknown>\n"
|
||||||
"Language-Team: Arabic <ar@li.org>\n"
|
"Language-Team: Arabic <ar@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2009-10-30 08:26+0000\n"
|
"X-Launchpad-Export-Date: 2009-10-31 08:35+0000\n"
|
||||||
"X-Generator: Launchpad (build Unknown)\n"
|
"X-Generator: Launchpad (build Unknown)\n"
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.6.20\n"
|
"Project-Id-Version: calibre 0.6.21\n"
|
||||||
"POT-Creation-Date: 2009-10-30 16:50+MDT\n"
|
"POT-Creation-Date: 2009-11-09 08:59+MST\n"
|
||||||
"PO-Revision-Date: 2009-10-30 16:50+MDT\n"
|
"PO-Revision-Date: 2009-11-09 08:59+MST\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -43,10 +43,10 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:45
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:45
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:46
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:46
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:36
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:36
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:61
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:62
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:63
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:64
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:103
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:104
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:105
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:106
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:171
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:171
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:329
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:329
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:444
|
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:444
|
||||||
@ -109,14 +109,14 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:48
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:48
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:431
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:432
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:170
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:170
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:876
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:876
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1002
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1002
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:211
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:211
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:280
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:281
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database.py:913
|
#: /home/kovid/work/calibre/src/calibre/library/database.py:913
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:702
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:702
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:714
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:714
|
||||||
@ -244,7 +244,6 @@ msgid "This profile is intended for the Mobipocket books."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:93
|
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:93
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:253
|
|
||||||
msgid "This profile is intended for the Hanlin V3 and its clones."
|
msgid "This profile is intended for the Hanlin V3 and its clones."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -289,6 +288,10 @@ msgstr ""
|
|||||||
msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics."
|
msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:253
|
||||||
|
msgid "This profile is intended for the Hanlin V3/V5 and its clones."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:306
|
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:306
|
||||||
msgid "This profile is intended for the Amazon Kindle DX."
|
msgid "This profile is intended for the Amazon Kindle DX."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -317,7 +320,7 @@ msgstr ""
|
|||||||
msgid "Initialization of plugin %s failed with traceback:"
|
msgid "Initialization of plugin %s failed with traceback:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:363
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:367
|
||||||
msgid ""
|
msgid ""
|
||||||
" %prog options\n"
|
" %prog options\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -325,27 +328,27 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:369
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:373
|
||||||
msgid "Add a plugin by specifying the path to the zip file containing it."
|
msgid "Add a plugin by specifying the path to the zip file containing it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:371
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:375
|
||||||
msgid "Remove a custom plugin by name. Has no effect on builtin plugins"
|
msgid "Remove a custom plugin by name. Has no effect on builtin plugins"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:373
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:377
|
||||||
msgid "Customize plugin. Specify name of plugin and customization string separated by a comma."
|
msgid "Customize plugin. Specify name of plugin and customization string separated by a comma."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:375
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:379
|
||||||
msgid "List all installed plugins"
|
msgid "List all installed plugins"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:377
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:381
|
||||||
msgid "Enable the named plugin"
|
msgid "Enable the named plugin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:379
|
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:383
|
||||||
msgid "Disable the named plugin"
|
msgid "Disable the named plugin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -368,6 +371,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:13
|
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:13
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:24
|
#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:24
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/eslick/driver.py:17
|
#: /home/kovid/work/calibre/src/calibre/devices/eslick/driver.py:17
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/devices/iriver/driver.py:16
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/nuut2/driver.py:18
|
#: /home/kovid/work/calibre/src/calibre/devices/nuut2/driver.py:18
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:88
|
#: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:88
|
||||||
msgid "Kovid Goyal"
|
msgid "Kovid Goyal"
|
||||||
@ -423,6 +427,10 @@ msgstr ""
|
|||||||
msgid "Communicate with the IRex Digital Reader 1000 eBook reader."
|
msgid "Communicate with the IRex Digital Reader 1000 eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/devices/iriver/driver.py:15
|
||||||
|
msgid "Communicate with the Iriver Story reader."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:22
|
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:22
|
||||||
msgid "Communicate with the JetBook eBook reader."
|
msgid "Communicate with the JetBook eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -956,9 +964,9 @@ msgstr ""
|
|||||||
msgid "Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover."
|
msgid "Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:123
|
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/fb2ml.py:126
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pml/pmlml.py:113
|
#: /home/kovid/work/calibre/src/calibre/ebooks/pml/pmlml.py:113
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/rb/rbml.py:98
|
#: /home/kovid/work/calibre/src/calibre/ebooks/rb/rbml.py:101
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/txtml.py:77
|
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/txtml.py:77
|
||||||
msgid "Table of Contents:"
|
msgid "Table of Contents:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1970,7 +1978,7 @@ msgstr ""
|
|||||||
msgid "Copy to Clipboard"
|
msgid "Copy to Clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:386
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:387
|
||||||
msgid "Choose Files"
|
msgid "Choose Files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2668,7 +2676,7 @@ msgid "RB Output"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:77
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:77
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1386
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1398
|
||||||
msgid "Choose the format to view"
|
msgid "Choose the format to view"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3272,7 +3280,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:797
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:797
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:152
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:152
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1060
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1072
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
|
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3347,7 +3355,7 @@ msgid "Failed to start content server"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:678
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:678
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:503
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:508
|
||||||
msgid "Select location for books"
|
msgid "Select location for books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3892,88 +3900,92 @@ msgid "Choose formats for "
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:134
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:134
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:900
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:912
|
||||||
msgid "Books"
|
msgid "Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:160
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:160
|
||||||
|
msgid "No permission"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:161
|
||||||
msgid "You do not have permission to read the following files:"
|
msgid "You do not have permission to read the following files:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:183
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:184
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:184
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:185
|
||||||
msgid "No format selected"
|
msgid "No format selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:195
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:196
|
||||||
msgid "Could not read metadata"
|
msgid "Could not read metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:196
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:197
|
||||||
msgid "Could not read metadata from %s format"
|
msgid "Could not read metadata from %s format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:241
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:242
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:247
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:248
|
||||||
msgid "Could not read cover"
|
msgid "Could not read cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:242
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:243
|
||||||
msgid "Could not read cover from %s format"
|
msgid "Could not read cover from %s format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:248
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:249
|
||||||
msgid "The cover in the %s format is invalid"
|
msgid "The cover in the %s format is invalid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:285
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:286
|
||||||
msgid "Abort the editing of all remaining books"
|
msgid "Abort the editing of all remaining books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:488
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:489
|
||||||
msgid "Downloading cover..."
|
msgid "Downloading cover..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:500
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:501
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:505
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:506
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:511
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:512
|
||||||
msgid "Cannot fetch cover"
|
msgid "Cannot fetch cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:501
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:502
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:512
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:513
|
||||||
msgid "<b>Could not fetch cover.</b><br/>"
|
msgid "<b>Could not fetch cover.</b><br/>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:502
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:503
|
||||||
msgid "The download timed out."
|
msgid "The download timed out."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:506
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:507
|
||||||
msgid "Could not find cover for this book. Try specifying the ISBN first."
|
msgid "Could not find cover for this book. Try specifying the ISBN first."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:518
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:519
|
||||||
msgid "Bad cover"
|
msgid "Bad cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:519
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:520
|
||||||
msgid "The cover is not a valid picture"
|
msgid "The cover is not a valid picture"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:559
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:560
|
||||||
msgid "Cannot fetch metadata"
|
msgid "Cannot fetch metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:560
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:561
|
||||||
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
|
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:612
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:613
|
||||||
msgid "Permission denied"
|
msgid "Permission denied"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:613
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:614
|
||||||
msgid "Could not open %s. Is it being used by another program?"
|
msgid "Could not open %s. Is it being used by another program?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -4793,7 +4805,7 @@ msgid "Save to disk in a single directory"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:297
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:297
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1488
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1500
|
||||||
msgid "Save only %s format to disk"
|
msgid "Save only %s format to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -4828,12 +4840,12 @@ msgid "Bad database location"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:445
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:445
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:511
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:516
|
||||||
msgid "Calibre Library"
|
msgid "Calibre Library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:455
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:455
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1631
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1643
|
||||||
msgid "Choose a location for your ebook library."
|
msgid "Choose a location for your ebook library."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -4841,23 +4853,23 @@ msgstr ""
|
|||||||
msgid "Browse by covers"
|
msgid "Browse by covers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:754
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:764
|
||||||
msgid "Device: "
|
msgid "Device: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:756
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:766
|
||||||
msgid " detected."
|
msgid " detected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:778
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:789
|
||||||
msgid "Connected "
|
msgid "Connected "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:790
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:801
|
||||||
msgid "Device database corrupted"
|
msgid "Device database corrupted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:791
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:802
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" <p>The database of books on the reader is corrupted. Try the following:\n"
|
" <p>The database of books on the reader is corrupted. Try the following:\n"
|
||||||
@ -4868,297 +4880,297 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:892
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:904
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:935
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:947
|
||||||
msgid "Uploading books to device."
|
msgid "Uploading books to device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:901
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:913
|
||||||
msgid "EPUB Books"
|
msgid "EPUB Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:902
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:914
|
||||||
msgid "LRF Books"
|
msgid "LRF Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:903
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:915
|
||||||
msgid "HTML Books"
|
msgid "HTML Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:904
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:916
|
||||||
msgid "LIT Books"
|
msgid "LIT Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:905
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:917
|
||||||
msgid "MOBI Books"
|
msgid "MOBI Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:906
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:918
|
||||||
msgid "Text books"
|
msgid "Text books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:907
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:919
|
||||||
msgid "PDF Books"
|
msgid "PDF Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:908
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:920
|
||||||
msgid "Comics"
|
msgid "Comics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:909
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:921
|
||||||
msgid "Archives"
|
msgid "Archives"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:944
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:956
|
||||||
msgid "Failed to read metadata"
|
msgid "Failed to read metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:945
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:957
|
||||||
msgid "Failed to read metadata from the following"
|
msgid "Failed to read metadata from the following"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:964
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:976
|
||||||
msgid "The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?"
|
msgid "The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:991
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1003
|
||||||
msgid "Deleting books from device."
|
msgid "Deleting books from device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1022
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1034
|
||||||
msgid "Cannot download metadata"
|
msgid "Cannot download metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1023
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1035
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1071
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1083
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1104
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1116
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1129
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1141
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1241
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1253
|
||||||
msgid "No books selected"
|
msgid "No books selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1032
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1044
|
||||||
msgid "covers"
|
msgid "covers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1032
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1044
|
||||||
msgid "metadata"
|
msgid "metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1034
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1046
|
||||||
msgid "Downloading %s for %d book(s)"
|
msgid "Downloading %s for %d book(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1055
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1067
|
||||||
msgid "Failed to download some metadata"
|
msgid "Failed to download some metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1056
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1068
|
||||||
msgid "Failed to download metadata for the following:"
|
msgid "Failed to download metadata for the following:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1059
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1071
|
||||||
msgid "Failed to download metadata:"
|
msgid "Failed to download metadata:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1070
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1082
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1103
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1115
|
||||||
msgid "Cannot edit metadata"
|
msgid "Cannot edit metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1128
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1140
|
||||||
msgid "Cannot save to disk"
|
msgid "Cannot save to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1131
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1143
|
||||||
msgid "Choose destination directory"
|
msgid "Choose destination directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1158
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1170
|
||||||
msgid "Error while saving"
|
msgid "Error while saving"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1159
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1171
|
||||||
msgid "There was an error while saving."
|
msgid "There was an error while saving."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1166
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1178
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1167
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1179
|
||||||
msgid "Could not save some books"
|
msgid "Could not save some books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1168
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1180
|
||||||
msgid "Click the show details button to see which ones."
|
msgid "Click the show details button to see which ones."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1187
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1199
|
||||||
msgid "Fetching news from "
|
msgid "Fetching news from "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1200
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1212
|
||||||
msgid " fetched."
|
msgid " fetched."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1240
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1252
|
||||||
msgid "Cannot convert"
|
msgid "Cannot convert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1269
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1281
|
||||||
msgid "Starting conversion of %d book(s)"
|
msgid "Starting conversion of %d book(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1380
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1392
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1399
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1411
|
||||||
msgid "No book selected"
|
msgid "No book selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1380
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1392
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1430
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1442
|
||||||
msgid "Cannot view"
|
msgid "Cannot view"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1398
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1410
|
||||||
msgid "Cannot open folder"
|
msgid "Cannot open folder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1415
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1427
|
||||||
msgid "Multiple Books Selected"
|
msgid "Multiple Books Selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1416
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1428
|
||||||
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
|
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1431
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1443
|
||||||
msgid "%s has no available formats."
|
msgid "%s has no available formats."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1472
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1484
|
||||||
msgid "Cannot configure"
|
msgid "Cannot configure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1473
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1485
|
||||||
msgid "Cannot configure while there are running jobs."
|
msgid "Cannot configure while there are running jobs."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1516
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1528
|
||||||
msgid "No detailed info available"
|
msgid "No detailed info available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1517
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1529
|
||||||
msgid "No detailed information is available for books on the device."
|
msgid "No detailed information is available for books on the device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1569
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1581
|
||||||
msgid "Error talking to device"
|
msgid "Error talking to device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1570
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1582
|
||||||
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
|
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1593
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1605
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1611
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1623
|
||||||
msgid "Conversion Error"
|
msgid "Conversion Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1594
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1606
|
||||||
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
|
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1612
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1624
|
||||||
msgid "<b>Failed</b>"
|
msgid "<b>Failed</b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1640
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1652
|
||||||
msgid "Invalid library location"
|
msgid "Invalid library location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1641
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1653
|
||||||
msgid "Could not access %s. Using %s as the library."
|
msgid "Could not access %s. Using %s as the library."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1689
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1701
|
||||||
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development."
|
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1713
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1725
|
||||||
msgid "There are active jobs. Are you sure you want to quit?"
|
msgid "There are active jobs. Are you sure you want to quit?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1716
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1728
|
||||||
msgid ""
|
msgid ""
|
||||||
" is communicating with the device!<br>\n"
|
" is communicating with the device!<br>\n"
|
||||||
" Quitting may cause corruption on the device.<br>\n"
|
" Quitting may cause corruption on the device.<br>\n"
|
||||||
" Are you sure you want to quit?"
|
" Are you sure you want to quit?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1720
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1732
|
||||||
msgid "WARNING: Active jobs"
|
msgid "WARNING: Active jobs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1771
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1783
|
||||||
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
|
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1790
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1802
|
||||||
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
|
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1798
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1810
|
||||||
msgid "Update available"
|
msgid "Update available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1799
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1811
|
||||||
msgid "%s has been updated to version %s. See the <a href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. Visit the download page?"
|
msgid "%s has been updated to version %s. See the <a href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. Visit the download page?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1817
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1829
|
||||||
msgid "Use the library located at the specified path."
|
msgid "Use the library located at the specified path."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1819
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1831
|
||||||
msgid "Start minimized to system tray."
|
msgid "Start minimized to system tray."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1821
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1833
|
||||||
msgid "Log debugging information to console"
|
msgid "Log debugging information to console"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1823
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1835
|
||||||
msgid "Do not check for updates"
|
msgid "Do not check for updates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1871
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1883
|
||||||
msgid "If you are sure it is not running"
|
msgid "If you are sure it is not running"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1873
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1885
|
||||||
msgid "Cannot Start "
|
msgid "Cannot Start "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1874
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1886
|
||||||
msgid "%s is already running."
|
msgid "%s is already running."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1877
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1889
|
||||||
msgid "may be running in the system tray, in the"
|
msgid "may be running in the system tray, in the"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1879
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1891
|
||||||
msgid "upper right region of the screen."
|
msgid "upper right region of the screen."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1881
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1893
|
||||||
msgid "lower right region of the screen."
|
msgid "lower right region of the screen."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1884
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1896
|
||||||
msgid "try rebooting your computer."
|
msgid "try rebooting your computer."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1886
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1898
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1898
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1910
|
||||||
msgid "try deleting the file"
|
msgid "try deleting the file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5509,57 +5521,57 @@ msgstr ""
|
|||||||
msgid "&User stylesheet"
|
msgid "&User stylesheet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:30
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:31
|
||||||
msgid "Options to customize the ebook viewer"
|
msgid "Options to customize the ebook viewer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:37
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:38
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:650
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:650
|
||||||
msgid "Remember last used window size"
|
msgid "Remember last used window size"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:39
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:40
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:88
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:89
|
||||||
msgid "Set the user CSS stylesheet. This can be used to customize the look of all books."
|
msgid "Set the user CSS stylesheet. This can be used to customize the look of all books."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:41
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:42
|
||||||
msgid "Maximum width of the viewer window, in pixels."
|
msgid "Maximum width of the viewer window, in pixels."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:42
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:43
|
||||||
msgid "Hyphenate text"
|
msgid "Hyphenate text"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:44
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:45
|
||||||
msgid "Default language for hyphenation rules"
|
msgid "Default language for hyphenation rules"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:46
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:47
|
||||||
msgid "Font options"
|
msgid "Font options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:48
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:49
|
||||||
msgid "The serif font family"
|
msgid "The serif font family"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:50
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:51
|
||||||
msgid "The sans-serif font family"
|
msgid "The sans-serif font family"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:52
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:53
|
||||||
msgid "The monospaced font family"
|
msgid "The monospaced font family"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:53
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:54
|
||||||
msgid "The standard font size in px"
|
msgid "The standard font size in px"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:54
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:55
|
||||||
msgid "The monospaced font size in px"
|
msgid "The monospaced font size in px"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:55
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:56
|
||||||
msgid "The standard font type"
|
msgid "The standard font type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5803,7 +5815,7 @@ msgstr ""
|
|||||||
msgid "Could not move library"
|
msgid "Could not move library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:574
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:583
|
||||||
msgid "welcome wizard"
|
msgid "welcome wizard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6028,68 +6040,68 @@ msgstr ""
|
|||||||
msgid "The maximum number of matches to return per OPDS query. This affects Stanza, WordPlayer, etc. integration."
|
msgid "The maximum number of matches to return per OPDS query. This affects Stanza, WordPlayer, etc. integration."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:120
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:121
|
||||||
msgid "Path to the calibre library. Default is to use the path stored in the settings."
|
msgid "Path to the calibre library. Default is to use the path stored in the settings."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:199
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:200
|
||||||
msgid ""
|
msgid ""
|
||||||
"%prog list [options]\n"
|
"%prog list [options]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"List the books available in the calibre database.\n"
|
"List the books available in the calibre database.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:207
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:208
|
||||||
msgid ""
|
msgid ""
|
||||||
"The fields to display when listing books in the database. Should be a comma separated list of fields.\n"
|
"The fields to display when listing books in the database. Should be a comma separated list of fields.\n"
|
||||||
"Available fields: %s\n"
|
"Available fields: %s\n"
|
||||||
"Default: %%default. The special field \"all\" can be used to select all fields. Only has effect in the text output format."
|
"Default: %%default. The special field \"all\" can be used to select all fields. Only has effect in the text output format."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:209
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:210
|
||||||
msgid ""
|
msgid ""
|
||||||
"The field by which to sort the results.\n"
|
"The field by which to sort the results.\n"
|
||||||
"Available fields: %s\n"
|
"Available fields: %s\n"
|
||||||
"Default: %%default"
|
"Default: %%default"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:211
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:212
|
||||||
msgid "Sort results in ascending order"
|
msgid "Sort results in ascending order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:213
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:214
|
||||||
msgid "Filter the results by the search query. For the format of the search query, please see the search related documentation in the User Manual. Default is to do no filtering."
|
msgid "Filter the results by the search query. For the format of the search query, please see the search related documentation in the User Manual. Default is to do no filtering."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:215
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:216
|
||||||
msgid "The maximum width of a single line in the output. Defaults to detecting screen size."
|
msgid "The maximum width of a single line in the output. Defaults to detecting screen size."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:216
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:217
|
||||||
msgid "The string used to separate fields. Default is a space."
|
msgid "The string used to separate fields. Default is a space."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:217
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:218
|
||||||
msgid "The prefix for all file paths. Default is the absolute path to the library folder."
|
msgid "The prefix for all file paths. Default is the absolute path to the library folder."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:220
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:221
|
||||||
msgid "The format in which to output the data. Available choices: %s. Defaults is text."
|
msgid "The format in which to output the data. Available choices: %s. Defaults is text."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:233
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:234
|
||||||
msgid "Invalid fields. Available fields:"
|
msgid "Invalid fields. Available fields:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:240
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:241
|
||||||
msgid "Invalid sort field. Available fields:"
|
msgid "Invalid sort field. Available fields:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:311
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:312
|
||||||
msgid "The following books were not added as they already exist in the database (see --duplicates option):"
|
msgid "The following books were not added as they already exist in the database (see --duplicates option):"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:334
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:335
|
||||||
msgid ""
|
msgid ""
|
||||||
"%prog add [options] file1 file2 file3 ...\n"
|
"%prog add [options] file1 file2 file3 ...\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -6097,49 +6109,49 @@ msgid ""
|
|||||||
"the directory related options below.\n"
|
"the directory related options below.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:343
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:344
|
||||||
msgid "Assume that each directory has only a single logical book and that all files in it are different e-book formats of that book"
|
msgid "Assume that each directory has only a single logical book and that all files in it are different e-book formats of that book"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:345
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:346
|
||||||
msgid "Process directories recursively"
|
msgid "Process directories recursively"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:347
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:348
|
||||||
msgid "Add books to database even if they already exist. Comparison is done based on book titles."
|
msgid "Add books to database even if they already exist. Comparison is done based on book titles."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:357
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:358
|
||||||
msgid "You must specify at least one file to add"
|
msgid "You must specify at least one file to add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:373
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:374
|
||||||
msgid ""
|
msgid ""
|
||||||
"%prog remove ids\n"
|
"%prog remove ids\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Remove the books identified by ids from the database. ids should be a comma separated list of id numbers (you can get id numbers by using the list command). For example, 23,34,57-85\n"
|
"Remove the books identified by ids from the database. ids should be a comma separated list of id numbers (you can get id numbers by using the list command). For example, 23,34,57-85\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:388
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:389
|
||||||
msgid "You must specify at least one book to remove"
|
msgid "You must specify at least one book to remove"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:407
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:408
|
||||||
msgid ""
|
msgid ""
|
||||||
"%prog add_format [options] id ebook_file\n"
|
"%prog add_format [options] id ebook_file\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Add the ebook in ebook_file to the available formats for the logical book identified by id. You can get id by using the list command. If the format already exists, it is replaced.\n"
|
"Add the ebook in ebook_file to the available formats for the logical book identified by id. You can get id by using the list command. If the format already exists, it is replaced.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:422
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:423
|
||||||
msgid "You must specify an id and an ebook file"
|
msgid "You must specify an id and an ebook file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:427
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:428
|
||||||
msgid "ebook file must have an extension"
|
msgid "ebook file must have an extension"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:435
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:436
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"%prog remove_format [options] id fmt\n"
|
"%prog remove_format [options] id fmt\n"
|
||||||
@ -6147,11 +6159,11 @@ msgid ""
|
|||||||
"Remove the format fmt from the logical book identified by id. You can get id by using the list command. fmt should be a file extension like LRF or TXT or EPUB. If the logical book does not have fmt available, do nothing.\n"
|
"Remove the format fmt from the logical book identified by id. You can get id by using the list command. fmt should be a file extension like LRF or TXT or EPUB. If the logical book does not have fmt available, do nothing.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:452
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:453
|
||||||
msgid "You must specify an id and a format"
|
msgid "You must specify an id and a format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:470
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:471
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"%prog show_metadata [options] id\n"
|
"%prog show_metadata [options] id\n"
|
||||||
@ -6160,15 +6172,15 @@ msgid ""
|
|||||||
"id is an id number from the list command.\n"
|
"id is an id number from the list command.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:478
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:479
|
||||||
msgid "Print metadata in OPF form (XML)"
|
msgid "Print metadata in OPF form (XML)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:487
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:488
|
||||||
msgid "You must specify an id"
|
msgid "You must specify an id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:500
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:501
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
"%prog set_metadata [options] id /path/to/metadata.opf\n"
|
"%prog set_metadata [options] id /path/to/metadata.opf\n"
|
||||||
@ -6179,11 +6191,11 @@ msgid ""
|
|||||||
"show_metadata command.\n"
|
"show_metadata command.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:516
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:517
|
||||||
msgid "You must specify an id and a metadata file"
|
msgid "You must specify an id and a metadata file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:536
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:537
|
||||||
msgid ""
|
msgid ""
|
||||||
"%prog export [options] ids\n"
|
"%prog export [options] ids\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -6192,27 +6204,27 @@ msgid ""
|
|||||||
"an opf file). You can get id numbers from the list command.\n"
|
"an opf file). You can get id numbers from the list command.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:544
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:545
|
||||||
msgid "Export all books in database, ignoring the list of ids."
|
msgid "Export all books in database, ignoring the list of ids."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:546
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:547
|
||||||
msgid "Export books to the specified directory. Default is"
|
msgid "Export books to the specified directory. Default is"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:548
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:549
|
||||||
msgid "Export all books into a single directory"
|
msgid "Export all books into a single directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:555
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:556
|
||||||
msgid "Specifying this switch will turn this behavior off."
|
msgid "Specifying this switch will turn this behavior off."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:578
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:579
|
||||||
msgid "You must specify some ids or the %s option"
|
msgid "You must specify some ids or the %s option"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:590
|
#: /home/kovid/work/calibre/src/calibre/library/cli.py:591
|
||||||
msgid ""
|
msgid ""
|
||||||
"%%prog command [options] [arguments]\n"
|
"%%prog command [options] [arguments]\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7152
src/calibre/translations/ko.po
Normal file
7152
src/calibre/translations/ko.po
Normal file
File diff suppressed because it is too large
Load Diff
7152
src/calibre/translations/lv.po
Normal file
7152
src/calibre/translations/lv.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -70,11 +70,11 @@ Usage may be::
|
|||||||
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
|
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__author__ = 'Christof Hoeke with contributions by Walter Doerwald'
|
__author__ = 'Christof Hoeke with contributions by Walter Doerwald'
|
||||||
__date__ = '$LastChangedDate:: 2009-08-01 16:10:11 -0600 #$:'
|
__date__ = '$LastChangedDate:: 2009-10-17 15:12:28 -0600 #$:'
|
||||||
|
|
||||||
VERSION = '0.9.6b3'
|
VERSION = '0.9.7a1'
|
||||||
|
|
||||||
__version__ = '%s $Id: __init__.py 1832 2009-08-01 22:10:11Z cthedot $' % VERSION
|
__version__ = '%s $Id: __init__.py 1877 2009-10-17 21:12:28Z cthedot $' % VERSION
|
||||||
|
|
||||||
import codec
|
import codec
|
||||||
import xml.dom
|
import xml.dom
|
||||||
@ -270,6 +270,12 @@ def replaceUrls(sheet, replacer):
|
|||||||
|
|
||||||
def resolveImports(sheet, target=None):
|
def resolveImports(sheet, target=None):
|
||||||
"""Recurcively combine all rules in given `sheet` into a `target` sheet.
|
"""Recurcively combine all rules in given `sheet` into a `target` sheet.
|
||||||
|
@import rules which use media information are tried to be wrapped into
|
||||||
|
@media rules so keeping the media information. This may not work in
|
||||||
|
all instances (if e.g. an @import rule itself contains an @import rule
|
||||||
|
with different media infos or if it is contains rules which may not be
|
||||||
|
used inside an @media block like @namespace rules.). In these cases
|
||||||
|
the @import rule is kept as in the original sheet and a WARNING is issued.
|
||||||
|
|
||||||
:param sheet:
|
:param sheet:
|
||||||
in this given :class:`cssutils.css.CSSStyleSheet` all import rules are
|
in this given :class:`cssutils.css.CSSStyleSheet` all import rules are
|
||||||
@ -290,8 +296,22 @@ def resolveImports(sheet, target=None):
|
|||||||
log.info(u'Processing @import %r' % rule.href, neverraise=True)
|
log.info(u'Processing @import %r' % rule.href, neverraise=True)
|
||||||
if rule.styleSheet:
|
if rule.styleSheet:
|
||||||
target.add(css.CSSComment(cssText=u'/* START @import "%s" */' % rule.href))
|
target.add(css.CSSComment(cssText=u'/* START @import "%s" */' % rule.href))
|
||||||
resolveImports(rule.styleSheet, target)
|
if rule.media.mediaText == 'all':
|
||||||
target.add(css.CSSComment(cssText=u'/* END "%s" */' % rule.href))
|
t = target
|
||||||
|
else:
|
||||||
|
log.info(u'Replacing @import media with @media: %s' %
|
||||||
|
rule.media.mediaText, neverraise=True)
|
||||||
|
t = css.CSSMediaRule(rule.media.mediaText)
|
||||||
|
try:
|
||||||
|
resolveImports(rule.styleSheet, t)
|
||||||
|
except xml.dom.HierarchyRequestErr, e:
|
||||||
|
log.warn(u'Cannot resolve @import: %s' %
|
||||||
|
e, neverraise=True)
|
||||||
|
target.add(rule)
|
||||||
|
else:
|
||||||
|
if t != target:
|
||||||
|
target.add(t)
|
||||||
|
t.add(css.CSSComment(cssText=u'/* END "%s" */' % rule.href))
|
||||||
else:
|
else:
|
||||||
log.error(u'Cannot get referenced stylesheet %r' %
|
log.error(u'Cannot get referenced stylesheet %r' %
|
||||||
rule.href, neverraise=True)
|
rule.href, neverraise=True)
|
||||||
|
@ -37,12 +37,13 @@ __all__ = [
|
|||||||
'CSSPageRule',
|
'CSSPageRule',
|
||||||
'CSSStyleRule',
|
'CSSStyleRule',
|
||||||
'CSSUnknownRule',
|
'CSSUnknownRule',
|
||||||
|
'CSSVariablesRule'
|
||||||
'Selector', 'SelectorList',
|
'Selector', 'SelectorList',
|
||||||
'CSSStyleDeclaration', 'Property',
|
'CSSStyleDeclaration', 'Property',
|
||||||
'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
|
'CSSValue', 'CSSPrimitiveValue', 'CSSValueList'
|
||||||
]
|
]
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: __init__.py 1610 2009-01-03 21:07:57Z cthedot $'
|
__version__ = '$Id: __init__.py 1859 2009-10-10 21:50:27Z cthedot $'
|
||||||
|
|
||||||
from cssstylesheet import *
|
from cssstylesheet import *
|
||||||
from cssrulelist import *
|
from cssrulelist import *
|
||||||
@ -55,9 +56,11 @@ from cssmediarule import *
|
|||||||
from cssnamespacerule import *
|
from cssnamespacerule import *
|
||||||
from csspagerule import *
|
from csspagerule import *
|
||||||
from cssstylerule import *
|
from cssstylerule import *
|
||||||
|
from cssvariablesrule import *
|
||||||
from cssunknownrule import *
|
from cssunknownrule import *
|
||||||
from selector import *
|
from selector import *
|
||||||
from selectorlist import *
|
from selectorlist import *
|
||||||
from cssstyledeclaration import *
|
from cssstyledeclaration import *
|
||||||
|
from cssvariablesdeclaration import *
|
||||||
from property import *
|
from property import *
|
||||||
from cssvalue import *
|
from cssvalue import *
|
||||||
|
@ -5,7 +5,7 @@ added http://www.w3.org/TR/css3-fonts/.
|
|||||||
"""
|
"""
|
||||||
__all__ = ['CSSFontFaceRule']
|
__all__ = ['CSSFontFaceRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssfontfacerule.py 1818 2009-07-30 21:39:00Z cthedot $'
|
__version__ = '$Id: cssfontfacerule.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from cssstyledeclaration import CSSStyleDeclaration
|
from cssstyledeclaration import CSSStyleDeclaration
|
||||||
import cssrule
|
import cssrule
|
||||||
@ -85,12 +85,16 @@ class CSSFontFaceRule(cssrule.CSSRule):
|
|||||||
self._valuestr(cssText),
|
self._valuestr(cssText),
|
||||||
error=xml.dom.InvalidModificationErr)
|
error=xml.dom.InvalidModificationErr)
|
||||||
else:
|
else:
|
||||||
wellformed = True
|
# save if parse goes wrong
|
||||||
|
oldstyle = CSSStyleDeclaration()
|
||||||
|
oldstyle._absorb(self.style)
|
||||||
|
|
||||||
|
ok = True
|
||||||
beforetokens, brace = self._tokensupto2(tokenizer,
|
beforetokens, brace = self._tokensupto2(tokenizer,
|
||||||
blockstartonly=True,
|
blockstartonly=True,
|
||||||
separateEnd=True)
|
separateEnd=True)
|
||||||
if self._tokenvalue(brace) != u'{':
|
if self._tokenvalue(brace) != u'{':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSFontFaceRule: No start { of style declaration found: %r' %
|
u'CSSFontFaceRule: No start { of style declaration found: %r' %
|
||||||
self._valuestr(cssText), brace)
|
self._valuestr(cssText), brace)
|
||||||
@ -102,7 +106,7 @@ class CSSFontFaceRule(cssrule.CSSRule):
|
|||||||
beforewellformed, expected = self._parse(expected=':',
|
beforewellformed, expected = self._parse(expected=':',
|
||||||
seq=newseq, tokenizer=self._tokenize2(beforetokens),
|
seq=newseq, tokenizer=self._tokenize2(beforetokens),
|
||||||
productions={})
|
productions={})
|
||||||
wellformed = wellformed and beforewellformed and new['wellformed']
|
ok = ok and beforewellformed and new['wellformed']
|
||||||
|
|
||||||
styletokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
styletokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
||||||
blockendonly=True,
|
blockendonly=True,
|
||||||
@ -110,32 +114,30 @@ class CSSFontFaceRule(cssrule.CSSRule):
|
|||||||
|
|
||||||
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||||
if val != u'}' and typ != 'EOF':
|
if val != u'}' and typ != 'EOF':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSFontFaceRule: No "}" after style declaration found: %r' %
|
u'CSSFontFaceRule: No "}" after style declaration found: %r' %
|
||||||
self._valuestr(cssText))
|
self._valuestr(cssText))
|
||||||
|
|
||||||
nonetoken = self._nexttoken(tokenizer)
|
nonetoken = self._nexttoken(tokenizer)
|
||||||
if nonetoken:
|
if nonetoken:
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(u'CSSFontFaceRule: Trailing content found.',
|
self._log.error(u'CSSFontFaceRule: Trailing content found.',
|
||||||
token=nonetoken)
|
token=nonetoken)
|
||||||
|
|
||||||
teststyle = CSSStyleDeclaration(parentRule=self)
|
|
||||||
if 'EOF' == typ:
|
if 'EOF' == typ:
|
||||||
# add again as style needs it
|
# add again as style needs it
|
||||||
styletokens.append(braceorEOFtoken)
|
styletokens.append(braceorEOFtoken)
|
||||||
# may raise:
|
|
||||||
teststyle.cssText = styletokens
|
|
||||||
|
|
||||||
if wellformed:
|
# SET, may raise:
|
||||||
# contains probably comments only upto {
|
self.style.cssText = styletokens
|
||||||
self._setSeq(newseq)
|
|
||||||
|
if ok:
|
||||||
# known as correct from before
|
# contains probably comments only (upto ``{``)
|
||||||
cssutils.log.enabled = False
|
self._setSeq(newseq)
|
||||||
self.style.cssText = styletokens
|
else:
|
||||||
cssutils.log.enabled = True
|
# RESET
|
||||||
|
self.style._absorb(oldstyle)
|
||||||
|
|
||||||
|
|
||||||
cssText = property(_getCssText, _setCssText,
|
cssText = property(_getCssText, _setCssText,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
``name`` property from http://www.w3.org/TR/css3-cascade/#cascading."""
|
``name`` property from http://www.w3.org/TR/css3-cascade/#cascading."""
|
||||||
__all__ = ['CSSImportRule']
|
__all__ = ['CSSImportRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssimportrule.py 1824 2009-08-01 21:00:34Z cthedot $'
|
__version__ = '$Id: cssimportrule.py 1871 2009-10-17 19:57:37Z cthedot $'
|
||||||
|
|
||||||
import cssrule
|
import cssrule
|
||||||
import cssutils
|
import cssutils
|
||||||
@ -105,6 +105,10 @@ class CSSImportRule(cssrule.CSSRule):
|
|||||||
self._valuestr(cssText),
|
self._valuestr(cssText),
|
||||||
error=xml.dom.InvalidModificationErr)
|
error=xml.dom.InvalidModificationErr)
|
||||||
else:
|
else:
|
||||||
|
# save if parse goes wrong
|
||||||
|
oldmedia = cssutils.stylesheets.MediaList()
|
||||||
|
oldmedia._absorb(self.media)
|
||||||
|
|
||||||
# for closures: must be a mutable
|
# for closures: must be a mutable
|
||||||
new = {'keyword': self._tokenvalue(attoken),
|
new = {'keyword': self._tokenvalue(attoken),
|
||||||
'href': None,
|
'href': None,
|
||||||
@ -164,12 +168,14 @@ class CSSImportRule(cssrule.CSSRule):
|
|||||||
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
self._log.error(u'CSSImportRule: No ";" found: %s' %
|
||||||
self._valuestr(cssText), token=token)
|
self._valuestr(cssText), token=token)
|
||||||
|
|
||||||
media = cssutils.stylesheets.MediaList()
|
#media = cssutils.stylesheets.MediaList()
|
||||||
media.mediaText = mediatokens
|
self.media.mediaText = mediatokens
|
||||||
if media.wellformed:
|
if self.media.wellformed:
|
||||||
new['media'] = media
|
new['media'] = self.media
|
||||||
seq.append(media, 'media')
|
seq.append(self.media, 'media')
|
||||||
else:
|
else:
|
||||||
|
# RESET
|
||||||
|
self.media._absorb(oldmedia)
|
||||||
new['wellformed'] = False
|
new['wellformed'] = False
|
||||||
self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
|
self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
|
||||||
self._valuestr(cssText), token=token)
|
self._valuestr(cssText), token=token)
|
||||||
@ -227,17 +233,8 @@ class CSSImportRule(cssrule.CSSRule):
|
|||||||
if wellformed:
|
if wellformed:
|
||||||
self.atkeyword = new['keyword']
|
self.atkeyword = new['keyword']
|
||||||
self.hreftype = new['hreftype']
|
self.hreftype = new['hreftype']
|
||||||
if new['media']:
|
if not new['media']:
|
||||||
# use same object
|
# reset media to base media
|
||||||
self.media.mediaText = new['media'].mediaText
|
|
||||||
# put it in newseq too
|
|
||||||
for index, x in enumerate(newseq):
|
|
||||||
if x.type == 'media':
|
|
||||||
newseq.replace(index, self.media,
|
|
||||||
x.type, x.line, x.col)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# reset media
|
|
||||||
self.media.mediaText = u'all'
|
self.media.mediaText = u'all'
|
||||||
newseq.append(self.media, 'media')
|
newseq.append(self.media, 'media')
|
||||||
self.name = new['name']
|
self.name = new['name']
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule."""
|
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule."""
|
||||||
__all__ = ['CSSMediaRule']
|
__all__ = ['CSSMediaRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssmediarule.py 1820 2009-08-01 20:53:08Z cthedot $'
|
__version__ = '$Id: cssmediarule.py 1871 2009-10-17 19:57:37Z cthedot $'
|
||||||
|
|
||||||
import cssrule
|
import cssrule
|
||||||
import cssutils
|
import cssutils
|
||||||
@ -87,6 +87,7 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||||
Raised if the rule is readonly.
|
Raised if the rule is readonly.
|
||||||
"""
|
"""
|
||||||
|
# media "name"? { cssRules }
|
||||||
super(CSSMediaRule, self)._setCssText(cssText)
|
super(CSSMediaRule, self)._setCssText(cssText)
|
||||||
|
|
||||||
# might be (cssText, namespaces)
|
# might be (cssText, namespaces)
|
||||||
@ -104,7 +105,9 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
self._valuestr(cssText),
|
self._valuestr(cssText),
|
||||||
error=xml.dom.InvalidModificationErr)
|
error=xml.dom.InvalidModificationErr)
|
||||||
else:
|
else:
|
||||||
# media "name"? { cssRules }
|
# save if parse goes wrong
|
||||||
|
oldmedia = cssutils.stylesheets.MediaList()
|
||||||
|
oldmedia._absorb(self.media)
|
||||||
|
|
||||||
# media
|
# media
|
||||||
wellformed = True
|
wellformed = True
|
||||||
@ -112,8 +115,7 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
mediaqueryendonly=True,
|
mediaqueryendonly=True,
|
||||||
separateEnd=True)
|
separateEnd=True)
|
||||||
if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
|
if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
|
||||||
newmedia = cssutils.stylesheets.MediaList()
|
self.media.mediaText = mediatokens
|
||||||
newmedia.mediaText = mediatokens
|
|
||||||
|
|
||||||
# name (optional)
|
# name (optional)
|
||||||
name = None
|
name = None
|
||||||
@ -209,14 +211,16 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
new=new)
|
new=new)
|
||||||
|
|
||||||
# no post condition
|
# no post condition
|
||||||
if newmedia.wellformed and wellformed:
|
if self.media.wellformed and wellformed:
|
||||||
# keep reference
|
|
||||||
self._media.mediaText = newmedia.mediaText
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self._setSeq(nameseq)
|
self._setSeq(nameseq)
|
||||||
del self._cssRules[:]
|
del self._cssRules[:]
|
||||||
for r in newcssrules:
|
for r in newcssrules:
|
||||||
self._cssRules.append(r)
|
self._cssRules.append(r)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# RESET
|
||||||
|
self.media._absorb(oldmedia)
|
||||||
|
|
||||||
cssText = property(_getCssText, _setCssText,
|
cssText = property(_getCssText, _setCssText,
|
||||||
doc="(DOM) The parsable textual representation of this rule.")
|
doc="(DOM) The parsable textual representation of this rule.")
|
||||||
@ -243,7 +247,13 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
Delete the rule at `index` from the media block.
|
Delete the rule at `index` from the media block.
|
||||||
|
|
||||||
:param index:
|
:param index:
|
||||||
of the rule to remove within the media block's rule collection
|
The `index` of the rule to be removed from the media block's rule
|
||||||
|
list. For an `index` < 0 **no** :exc:`~xml.dom.IndexSizeErr` is
|
||||||
|
raised but rules for normal Python lists are used. E.g.
|
||||||
|
``deleteRule(-1)`` removes the last rule in cssRules.
|
||||||
|
|
||||||
|
`index` may also be a CSSRule object which will then be removed
|
||||||
|
from the media block.
|
||||||
|
|
||||||
:Exceptions:
|
:Exceptions:
|
||||||
- :exc:`~xml.dom.IndexSizeErr`:
|
- :exc:`~xml.dom.IndexSizeErr`:
|
||||||
@ -254,6 +264,16 @@ class CSSMediaRule(cssrule.CSSRule):
|
|||||||
"""
|
"""
|
||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
|
|
||||||
|
if isinstance(index, cssrule.CSSRule):
|
||||||
|
for i, r in enumerate(self.cssRules):
|
||||||
|
if index == r:
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise xml.dom.IndexSizeErr(u"CSSMediaRule: Not a rule in"
|
||||||
|
" this rule'a cssRules list: %s"
|
||||||
|
% index)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._cssRules[index]._parentRule = None # detach
|
self._cssRules[index]._parentRule = None # detach
|
||||||
del self._cssRules[index] # remove from @media
|
del self._cssRules[index] # remove from @media
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule."""
|
"""CSSPageRule implements DOM Level 2 CSS CSSPageRule."""
|
||||||
__all__ = ['CSSPageRule']
|
__all__ = ['CSSPageRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: csspagerule.py 1824 2009-08-01 21:00:34Z cthedot $'
|
__version__ = '$Id: csspagerule.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from cssstyledeclaration import CSSStyleDeclaration
|
from cssstyledeclaration import CSSStyleDeclaration
|
||||||
from selectorlist import SelectorList
|
from selectorlist import SelectorList
|
||||||
@ -141,7 +141,6 @@ class CSSPageRule(cssrule.CSSRule):
|
|||||||
# if not newselector in (None, u':first', u':left', u':right'):
|
# if not newselector in (None, u':first', u':left', u':right'):
|
||||||
# self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' %
|
# self._log.warn(u'CSSPageRule: Unknown CSS 2.1 @page selector: %r' %
|
||||||
# newselector, neverraise=True)
|
# newselector, neverraise=True)
|
||||||
|
|
||||||
return wellformed, newseq
|
return wellformed, newseq
|
||||||
|
|
||||||
def _getCssText(self):
|
def _getCssText(self):
|
||||||
@ -171,7 +170,11 @@ class CSSPageRule(cssrule.CSSRule):
|
|||||||
self._valuestr(cssText),
|
self._valuestr(cssText),
|
||||||
error=xml.dom.InvalidModificationErr)
|
error=xml.dom.InvalidModificationErr)
|
||||||
else:
|
else:
|
||||||
wellformed = True
|
# save if parse goes wrong
|
||||||
|
oldstyle = CSSStyleDeclaration()
|
||||||
|
oldstyle._absorb(self.style)
|
||||||
|
|
||||||
|
ok = True
|
||||||
selectortokens, startbrace = self._tokensupto2(tokenizer,
|
selectortokens, startbrace = self._tokensupto2(tokenizer,
|
||||||
blockstartonly=True,
|
blockstartonly=True,
|
||||||
separateEnd=True)
|
separateEnd=True)
|
||||||
@ -180,22 +183,21 @@ class CSSPageRule(cssrule.CSSRule):
|
|||||||
separateEnd=True)
|
separateEnd=True)
|
||||||
nonetoken = self._nexttoken(tokenizer)
|
nonetoken = self._nexttoken(tokenizer)
|
||||||
if self._tokenvalue(startbrace) != u'{':
|
if self._tokenvalue(startbrace) != u'{':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSPageRule: No start { of style declaration found: %r' %
|
u'CSSPageRule: No start { of style declaration found: %r' %
|
||||||
self._valuestr(cssText), startbrace)
|
self._valuestr(cssText), startbrace)
|
||||||
elif nonetoken:
|
elif nonetoken:
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSPageRule: Trailing content found.', token=nonetoken)
|
u'CSSPageRule: Trailing content found.', token=nonetoken)
|
||||||
|
|
||||||
|
selok, newselectorseq = self.__parseSelectorText(selectortokens)
|
||||||
wellformed, newselectorseq = self.__parseSelectorText(selectortokens)
|
ok = ok and selok
|
||||||
|
|
||||||
teststyle = CSSStyleDeclaration(parentRule=self)
|
|
||||||
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||||
if val != u'}' and typ != 'EOF':
|
if val != u'}' and typ != 'EOF':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSPageRule: No "}" after style declaration found: %r' %
|
u'CSSPageRule: No "}" after style declaration found: %r' %
|
||||||
self._valuestr(cssText))
|
self._valuestr(cssText))
|
||||||
@ -203,14 +205,14 @@ class CSSPageRule(cssrule.CSSRule):
|
|||||||
if 'EOF' == typ:
|
if 'EOF' == typ:
|
||||||
# add again as style needs it
|
# add again as style needs it
|
||||||
styletokens.append(braceorEOFtoken)
|
styletokens.append(braceorEOFtoken)
|
||||||
teststyle.cssText = styletokens
|
|
||||||
|
|
||||||
if wellformed:
|
|
||||||
# known as correct from before
|
|
||||||
cssutils.log.enabled = False
|
|
||||||
self._selectorText = newselectorseq # TODO: TEST and REFS
|
|
||||||
self.style.cssText = styletokens
|
self.style.cssText = styletokens
|
||||||
cssutils.log.enabled = True
|
|
||||||
|
if ok:
|
||||||
|
# TODO: TEST and REFS
|
||||||
|
self._selectorText = newselectorseq
|
||||||
|
else:
|
||||||
|
# RESET
|
||||||
|
self.style._absorb(oldstyle)
|
||||||
|
|
||||||
cssText = property(_getCssText, _setCssText,
|
cssText = property(_getCssText, _setCssText,
|
||||||
doc="(DOM) The parsable textual representation of this rule.")
|
doc="(DOM) The parsable textual representation of this rule.")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""CSSRule implements DOM Level 2 CSS CSSRule."""
|
"""CSSRule implements DOM Level 2 CSS CSSRule."""
|
||||||
__all__ = ['CSSRule']
|
__all__ = ['CSSRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssrule.py 1808 2009-07-29 13:09:36Z cthedot $'
|
__version__ = '$Id: cssrule.py 1855 2009-10-07 17:03:19Z cthedot $'
|
||||||
|
|
||||||
import cssutils
|
import cssutils
|
||||||
import xml.dom
|
import xml.dom
|
||||||
@ -27,9 +27,11 @@ class CSSRule(cssutils.util.Base2):
|
|||||||
FONT_FACE_RULE = 5 #f
|
FONT_FACE_RULE = 5 #f
|
||||||
PAGE_RULE = 6 #p
|
PAGE_RULE = 6 #p
|
||||||
NAMESPACE_RULE = 7 # CSSOM
|
NAMESPACE_RULE = 7 # CSSOM
|
||||||
|
VARIABLES_RULE = 8 # CSS Variables
|
||||||
|
|
||||||
_typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE',
|
_typestrings = ['UNKNOWN_RULE', 'STYLE_RULE', 'CHARSET_RULE', 'IMPORT_RULE',
|
||||||
'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE',
|
'MEDIA_RULE', 'FONT_FACE_RULE', 'PAGE_RULE', 'NAMESPACE_RULE',
|
||||||
|
'VARIABLES_RULE',
|
||||||
'COMMENT']
|
'COMMENT']
|
||||||
|
|
||||||
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
|
def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
|
||||||
|
@ -51,7 +51,7 @@ TODO:
|
|||||||
"""
|
"""
|
||||||
__all__ = ['CSSStyleDeclaration', 'Property']
|
__all__ = ['CSSStyleDeclaration', 'Property']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssstyledeclaration.py 1819 2009-08-01 20:52:43Z cthedot $'
|
__version__ = '$Id: cssstyledeclaration.py 1870 2009-10-17 19:56:59Z cthedot $'
|
||||||
|
|
||||||
from cssproperties import CSS2Properties
|
from cssproperties import CSS2Properties
|
||||||
from property import Property
|
from property import Property
|
||||||
@ -201,6 +201,12 @@ class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base2):
|
|||||||
names.append(val.name)
|
names.append(val.name)
|
||||||
return reversed(names)
|
return reversed(names)
|
||||||
|
|
||||||
|
def _absorb(self, other):
|
||||||
|
"""Replace all own data with data from other object."""
|
||||||
|
self._parentRule = other._parentRule
|
||||||
|
self.seq.absorb(other.seq)
|
||||||
|
self._readonly = other._readonly
|
||||||
|
|
||||||
# overwritten accessor functions for CSS2Properties' properties
|
# overwritten accessor functions for CSS2Properties' properties
|
||||||
def _getP(self, CSSName):
|
def _getP(self, CSSName):
|
||||||
"""(DOM CSS2Properties) Overwritten here and effectively the same as
|
"""(DOM CSS2Properties) Overwritten here and effectively the same as
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule."""
|
"""CSSStyleRule implements DOM Level 2 CSS CSSStyleRule."""
|
||||||
__all__ = ['CSSStyleRule']
|
__all__ = ['CSSStyleRule']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssstylerule.py 1815 2009-07-29 16:51:58Z cthedot $'
|
__version__ = '$Id: cssstylerule.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from cssstyledeclaration import CSSStyleDeclaration
|
from cssstyledeclaration import CSSStyleDeclaration
|
||||||
from selectorlist import SelectorList
|
from selectorlist import SelectorList
|
||||||
@ -104,28 +104,30 @@ class CSSStyleRule(cssrule.CSSRule):
|
|||||||
self._log.error(u'CSSStyleRule: No style rule: %r' %
|
self._log.error(u'CSSStyleRule: No style rule: %r' %
|
||||||
self._valuestr(cssText),
|
self._valuestr(cssText),
|
||||||
error=xml.dom.InvalidModificationErr)
|
error=xml.dom.InvalidModificationErr)
|
||||||
else:
|
else:
|
||||||
wellformed = True
|
# save if parse goes wrong
|
||||||
|
oldstyle = CSSStyleDeclaration()
|
||||||
testselectorlist, teststyle = None, None
|
oldstyle._absorb(self.style)
|
||||||
|
oldselector = SelectorList()
|
||||||
|
oldselector._absorb(self.selectorList)
|
||||||
|
|
||||||
|
ok = True
|
||||||
|
|
||||||
bracetoken = selectortokens.pop()
|
bracetoken = selectortokens.pop()
|
||||||
if self._tokenvalue(bracetoken) != u'{':
|
if self._tokenvalue(bracetoken) != u'{':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStyleRule: No start { of style declaration found: %r' %
|
u'CSSStyleRule: No start { of style declaration found: %r' %
|
||||||
self._valuestr(cssText), bracetoken)
|
self._valuestr(cssText), bracetoken)
|
||||||
elif not selectortokens:
|
elif not selectortokens:
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(u'CSSStyleRule: No selector found: %r.' %
|
self._log.error(u'CSSStyleRule: No selector found: %r.' %
|
||||||
self._valuestr(cssText), bracetoken)
|
self._valuestr(cssText), bracetoken)
|
||||||
|
# SET
|
||||||
testselectorlist = SelectorList(selectorText=(selectortokens,
|
self.selectorList.selectorText = (selectortokens,
|
||||||
namespaces),
|
namespaces)
|
||||||
parentRule=self)
|
|
||||||
|
|
||||||
if not styletokens:
|
if not styletokens:
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStyleRule: No style declaration or "}" found: %r' %
|
u'CSSStyleRule: No style declaration or "}" found: %r' %
|
||||||
self._valuestr(cssText))
|
self._valuestr(cssText))
|
||||||
@ -133,7 +135,7 @@ class CSSStyleRule(cssrule.CSSRule):
|
|||||||
braceorEOFtoken = styletokens.pop()
|
braceorEOFtoken = styletokens.pop()
|
||||||
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||||
if val != u'}' and typ != 'EOF':
|
if val != u'}' and typ != 'EOF':
|
||||||
wellformed = False
|
ok = False
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStyleRule: No "}" after style declaration found: %r' %
|
u'CSSStyleRule: No "}" after style declaration found: %r' %
|
||||||
self._valuestr(cssText))
|
self._valuestr(cssText))
|
||||||
@ -141,14 +143,19 @@ class CSSStyleRule(cssrule.CSSRule):
|
|||||||
if 'EOF' == typ:
|
if 'EOF' == typ:
|
||||||
# add again as style needs it
|
# add again as style needs it
|
||||||
styletokens.append(braceorEOFtoken)
|
styletokens.append(braceorEOFtoken)
|
||||||
teststyle = CSSStyleDeclaration(styletokens, parentRule=self)
|
|
||||||
|
# SET
|
||||||
|
try:
|
||||||
|
self.style.cssText = styletokens
|
||||||
|
except:
|
||||||
|
# reset in case of error
|
||||||
|
self.selectorList._absorb(oldselector)
|
||||||
|
raise
|
||||||
|
|
||||||
if wellformed and testselectorlist and teststyle:
|
if not ok or not self.wellformed:
|
||||||
# known as correct from before
|
# reset as not ok
|
||||||
cssutils.log.enabled = False
|
self.selectorList._absorb(oldselector)
|
||||||
self.style.cssText = styletokens
|
self.style._absorb(oldstyle)
|
||||||
self.selectorList.selectorText=(selectortokens, namespaces)
|
|
||||||
cssutils.log.enabled = True
|
|
||||||
|
|
||||||
cssText = property(_getCssText, _setCssText,
|
cssText = property(_getCssText, _setCssText,
|
||||||
doc="(DOM) The parsable textual representation of this rule.")
|
doc="(DOM) The parsable textual representation of this rule.")
|
||||||
|
@ -9,10 +9,11 @@ TODO:
|
|||||||
"""
|
"""
|
||||||
__all__ = ['CSSStyleSheet']
|
__all__ = ['CSSStyleSheet']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssstylesheet.py 1820 2009-08-01 20:53:08Z cthedot $'
|
__version__ = '$Id: cssstylesheet.py 1857 2009-10-10 21:49:33Z cthedot $'
|
||||||
|
|
||||||
from cssutils.helper import Deprecated
|
from cssutils.helper import Deprecated
|
||||||
from cssutils.util import _Namespaces, _SimpleNamespaces, _readUrl
|
from cssutils.util import _Namespaces, _SimpleNamespaces, _readUrl
|
||||||
|
from cssrule import CSSRule
|
||||||
import cssutils.stylesheets
|
import cssutils.stylesheets
|
||||||
import xml.dom
|
import xml.dom
|
||||||
|
|
||||||
@ -204,11 +205,26 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
token, xml.dom.HierarchyRequestErr)
|
token, xml.dom.HierarchyRequestErr)
|
||||||
else:
|
else:
|
||||||
if rule.wellformed:
|
if rule.wellformed:
|
||||||
|
for i, r in enumerate(seq):
|
||||||
|
if r.type == r.NAMESPACE_RULE and r.prefix == rule.prefix:
|
||||||
|
# replace as doubled:
|
||||||
|
seq[i] = rule
|
||||||
|
self._log.info(
|
||||||
|
u'CSSStylesheet: CSSNamespaceRule with same prefix found, replacing: %r'
|
||||||
|
% r.cssText,
|
||||||
|
token, neverraise=True)
|
||||||
seq.append(rule)
|
seq.append(rule)
|
||||||
# temporary namespaces given to CSSStyleRule and @media
|
# temporary namespaces given to CSSStyleRule and @media
|
||||||
new['namespaces'][rule.prefix] = rule.namespaceURI
|
new['namespaces'][rule.prefix] = rule.namespaceURI
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
def variablesrule(expected, seq, token, tokenizer):
|
||||||
|
rule = cssutils.css.CSSVariablesRule(parentStyleSheet=self)
|
||||||
|
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||||
|
if rule.wellformed:
|
||||||
|
seq.append(rule)
|
||||||
|
return 2
|
||||||
|
|
||||||
def fontfacerule(expected, seq, token, tokenizer):
|
def fontfacerule(expected, seq, token, tokenizer):
|
||||||
rule = cssutils.css.CSSFontFaceRule(parentStyleSheet=self)
|
rule = cssutils.css.CSSFontFaceRule(parentStyleSheet=self)
|
||||||
rule.cssText = self._tokensupto2(tokenizer, token)
|
rule.cssText = self._tokensupto2(tokenizer, token)
|
||||||
@ -266,6 +282,7 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
'NAMESPACE_SYM': namespacerule,
|
'NAMESPACE_SYM': namespacerule,
|
||||||
'PAGE_SYM': pagerule,
|
'PAGE_SYM': pagerule,
|
||||||
'MEDIA_SYM': mediarule,
|
'MEDIA_SYM': mediarule,
|
||||||
|
'VARIABLES_SYM': variablesrule,
|
||||||
'ATKEYWORD': unknownrule
|
'ATKEYWORD': unknownrule
|
||||||
},
|
},
|
||||||
default=ruleset)
|
default=ruleset)
|
||||||
@ -366,10 +383,14 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
"""Delete rule at `index` from the style sheet.
|
"""Delete rule at `index` from the style sheet.
|
||||||
|
|
||||||
:param index:
|
:param index:
|
||||||
of the rule to remove in the StyleSheet's rule list. For an
|
The `index` of the rule to be removed from the StyleSheet's rule
|
||||||
`index` < 0 **no** :exc:`~xml.dom.IndexSizeErr` is raised but
|
list. For an `index` < 0 **no** :exc:`~xml.dom.IndexSizeErr` is
|
||||||
rules for normal Python lists are used. E.g. ``deleteRule(-1)``
|
raised but rules for normal Python lists are used. E.g.
|
||||||
removes the last rule in cssRules.
|
``deleteRule(-1)`` removes the last rule in cssRules.
|
||||||
|
|
||||||
|
`index` may also be a CSSRule object which will then be removed
|
||||||
|
from the StyleSheet.
|
||||||
|
|
||||||
:exceptions:
|
:exceptions:
|
||||||
- :exc:`~xml.dom.IndexSizeErr`:
|
- :exc:`~xml.dom.IndexSizeErr`:
|
||||||
Raised if the specified index does not correspond to a rule in
|
Raised if the specified index does not correspond to a rule in
|
||||||
@ -381,6 +402,16 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
"""
|
"""
|
||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
|
|
||||||
|
if isinstance(index, CSSRule):
|
||||||
|
for i, r in enumerate(self.cssRules):
|
||||||
|
if index == r:
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise xml.dom.IndexSizeErr(u"CSSStyleSheet: Not a rule in"
|
||||||
|
" this sheets'a cssRules list: %s"
|
||||||
|
% index)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rule = self._cssRules[index]
|
rule = self._cssRules[index]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -495,14 +526,16 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
if inOrder:
|
if inOrder:
|
||||||
index = 0
|
index = 0
|
||||||
# always first and only
|
# always first and only
|
||||||
if (self._cssRules and self._cssRules[0].type == rule.CHARSET_RULE):
|
if (self._cssRules
|
||||||
|
and self._cssRules[0].type == rule.CHARSET_RULE):
|
||||||
self._cssRules[0].encoding = rule.encoding
|
self._cssRules[0].encoding = rule.encoding
|
||||||
else:
|
else:
|
||||||
self._cssRules.insert(0, rule)
|
self._cssRules.insert(0, rule)
|
||||||
elif index != 0 or (self._cssRules and
|
elif index != 0 or (self._cssRules and
|
||||||
self._cssRules[0].type == rule.CHARSET_RULE):
|
self._cssRules[0].type == rule.CHARSET_RULE):
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStylesheet: @charset only allowed once at the beginning of a stylesheet.',
|
u'CSSStylesheet: @charset only allowed once at the'
|
||||||
|
' beginning of a stylesheet.',
|
||||||
error=xml.dom.HierarchyRequestErr)
|
error=xml.dom.HierarchyRequestErr)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@ -532,7 +565,7 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
else:
|
else:
|
||||||
# find first point to insert
|
# find first point to insert
|
||||||
if self._cssRules and self._cssRules[0].type in (rule.CHARSET_RULE,
|
if self._cssRules and self._cssRules[0].type in (rule.CHARSET_RULE,
|
||||||
rule.COMMENT):
|
rule.COMMENT):
|
||||||
index = 1
|
index = 1
|
||||||
else:
|
else:
|
||||||
index = 0
|
index = 0
|
||||||
@ -544,12 +577,18 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
u'CSSStylesheet: Found @charset at index 0.',
|
u'CSSStylesheet: Found @charset at index 0.',
|
||||||
error=xml.dom.HierarchyRequestErr)
|
error=xml.dom.HierarchyRequestErr)
|
||||||
return
|
return
|
||||||
# before @namespace, @page, @font-face, @media and stylerule
|
# before @namespace @variables @page @font-face @media stylerule
|
||||||
for r in self._cssRules[:index]:
|
for r in self._cssRules[:index]:
|
||||||
if r.type in (r.NAMESPACE_RULE, r.MEDIA_RULE, r.PAGE_RULE,
|
if r.type in (r.NAMESPACE_RULE,
|
||||||
r.STYLE_RULE, r.FONT_FACE_RULE):
|
r.VARIABLES_RULE,
|
||||||
|
r.MEDIA_RULE,
|
||||||
|
r.PAGE_RULE,
|
||||||
|
r.STYLE_RULE,
|
||||||
|
r.FONT_FACE_RULE):
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStylesheet: Cannot insert @import here, found @namespace, @media, @page or CSSStyleRule before index %s.' %
|
u'CSSStylesheet: Cannot insert @import here,'
|
||||||
|
' found @namespace, @variables, @media, @page or'
|
||||||
|
' CSSStyleRule before index %s.' %
|
||||||
index,
|
index,
|
||||||
error=xml.dom.HierarchyRequestErr)
|
error=xml.dom.HierarchyRequestErr)
|
||||||
return
|
return
|
||||||
@ -567,8 +606,10 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
else:
|
else:
|
||||||
# find first point to insert
|
# find first point to insert
|
||||||
for i, r in enumerate(self._cssRules):
|
for i, r in enumerate(self._cssRules):
|
||||||
if r.type in (r.MEDIA_RULE, r.PAGE_RULE, r.STYLE_RULE,
|
if r.type in (r.VARIABLES_RULE, r.MEDIA_RULE,
|
||||||
r.FONT_FACE_RULE, r.UNKNOWN_RULE, r.COMMENT):
|
r.PAGE_RULE, r.STYLE_RULE,
|
||||||
|
r.FONT_FACE_RULE, r.UNKNOWN_RULE,
|
||||||
|
r.COMMENT):
|
||||||
index = i # before these
|
index = i # before these
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@ -576,16 +617,22 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
for r in self._cssRules[index:]:
|
for r in self._cssRules[index:]:
|
||||||
if r.type in (r.CHARSET_RULE, r.IMPORT_RULE):
|
if r.type in (r.CHARSET_RULE, r.IMPORT_RULE):
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStylesheet: Cannot insert @namespace here, found @charset or @import after index %s.' %
|
u'CSSStylesheet: Cannot insert @namespace here,'
|
||||||
|
' found @charset or @import after index %s.' %
|
||||||
index,
|
index,
|
||||||
error=xml.dom.HierarchyRequestErr)
|
error=xml.dom.HierarchyRequestErr)
|
||||||
return
|
return
|
||||||
# before @media and stylerule
|
# before @variables @media @page @font-face and stylerule
|
||||||
for r in self._cssRules[:index]:
|
for r in self._cssRules[:index]:
|
||||||
if r.type in (r.MEDIA_RULE, r.PAGE_RULE, r.STYLE_RULE,
|
if r.type in (r.VARIABLES_RULE,
|
||||||
|
r.MEDIA_RULE,
|
||||||
|
r.PAGE_RULE,
|
||||||
|
r.STYLE_RULE,
|
||||||
r.FONT_FACE_RULE):
|
r.FONT_FACE_RULE):
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSStylesheet: Cannot insert @namespace here, found @media, @page or CSSStyleRule before index %s.' %
|
u'CSSStylesheet: Cannot insert @namespace here,'
|
||||||
|
' found @variables, @media, @page or CSSStyleRule'
|
||||||
|
' before index %s.' %
|
||||||
index,
|
index,
|
||||||
error=xml.dom.HierarchyRequestErr)
|
error=xml.dom.HierarchyRequestErr)
|
||||||
return
|
return
|
||||||
@ -597,6 +644,56 @@ class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
|
|||||||
if _clean:
|
if _clean:
|
||||||
self._cleanNamespaces()
|
self._cleanNamespaces()
|
||||||
|
|
||||||
|
|
||||||
|
# @variables
|
||||||
|
elif rule.type == rule.VARIABLES_RULE:
|
||||||
|
if inOrder:
|
||||||
|
if rule.type in (r.type for r in self):
|
||||||
|
# find last of this type
|
||||||
|
for i, r in enumerate(reversed(self._cssRules)):
|
||||||
|
if r.type == rule.type:
|
||||||
|
index = len(self._cssRules) - i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# find first point to insert
|
||||||
|
for i, r in enumerate(self._cssRules):
|
||||||
|
if r.type in (r.MEDIA_RULE,
|
||||||
|
r.PAGE_RULE,
|
||||||
|
r.STYLE_RULE,
|
||||||
|
r.FONT_FACE_RULE,
|
||||||
|
r.UNKNOWN_RULE,
|
||||||
|
r.COMMENT):
|
||||||
|
index = i # before these
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# after @charset @import @namespace
|
||||||
|
for r in self._cssRules[index:]:
|
||||||
|
if r.type in (r.CHARSET_RULE,
|
||||||
|
r.IMPORT_RULE,
|
||||||
|
r.NAMESPACE_RULE):
|
||||||
|
self._log.error(
|
||||||
|
u'CSSStylesheet: Cannot insert @variables here,'
|
||||||
|
' found @charset, @import or @namespace after'
|
||||||
|
' index %s.' %
|
||||||
|
index,
|
||||||
|
error=xml.dom.HierarchyRequestErr)
|
||||||
|
return
|
||||||
|
# before @media @page @font-face and stylerule
|
||||||
|
for r in self._cssRules[:index]:
|
||||||
|
if r.type in (r.MEDIA_RULE,
|
||||||
|
r.PAGE_RULE,
|
||||||
|
r.STYLE_RULE,
|
||||||
|
r.FONT_FACE_RULE):
|
||||||
|
self._log.error(
|
||||||
|
u'CSSStylesheet: Cannot insert @variables here,'
|
||||||
|
' found @media, @page or CSSStyleRule'
|
||||||
|
' before index %s.' %
|
||||||
|
index,
|
||||||
|
error=xml.dom.HierarchyRequestErr)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._cssRules.insert(index, rule)
|
||||||
|
|
||||||
# all other where order is not important
|
# all other where order is not important
|
||||||
else:
|
else:
|
||||||
if inOrder:
|
if inOrder:
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
- CSSValueList implements DOM Level 2 CSS CSSValueList
|
- CSSValueList implements DOM Level 2 CSS CSSValueList
|
||||||
|
|
||||||
"""
|
"""
|
||||||
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList', 'RGBColor']
|
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList',
|
||||||
|
'CSSVariable', 'RGBColor']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssvalue.py 1834 2009-08-02 12:20:21Z cthedot $'
|
__version__ = '$Id: cssvalue.py 1864 2009-10-11 15:11:39Z cthedot $'
|
||||||
|
|
||||||
from cssutils.prodparser import *
|
from cssutils.prodparser import *
|
||||||
import cssutils
|
import cssutils
|
||||||
@ -29,13 +30,16 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
CSS_VALUE_LIST = 2
|
CSS_VALUE_LIST = 2
|
||||||
# The value is a custom value.
|
# The value is a custom value.
|
||||||
CSS_CUSTOM = 3
|
CSS_CUSTOM = 3
|
||||||
|
# The value is a CSSVariable.
|
||||||
|
CSS_VARIABLE = 4
|
||||||
|
|
||||||
_typestrings = {0: 'CSS_INHERIT' ,
|
_typestrings = {0: 'CSS_INHERIT' ,
|
||||||
1: 'CSS_PRIMITIVE_VALUE',
|
1: 'CSS_PRIMITIVE_VALUE',
|
||||||
2: 'CSS_VALUE_LIST',
|
2: 'CSS_VALUE_LIST',
|
||||||
3: 'CSS_CUSTOM'}
|
3: 'CSS_CUSTOM',
|
||||||
|
4: 'CSS_VARIABLE'}
|
||||||
|
|
||||||
def __init__(self, cssText=None, readonly=False):
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
||||||
"""
|
"""
|
||||||
:param cssText:
|
:param cssText:
|
||||||
the parsable cssText of the value
|
the parsable cssText of the value
|
||||||
@ -46,6 +50,7 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
|
|
||||||
self._cssValueType = None
|
self._cssValueType = None
|
||||||
self.wellformed = False
|
self.wellformed = False
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
if cssText is not None: # may be 0
|
if cssText is not None: # may be 0
|
||||||
if type(cssText) in (int, float):
|
if type(cssText) in (int, float):
|
||||||
@ -109,9 +114,9 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
|
|
||||||
# used as operator is , / or S
|
# used as operator is , / or S
|
||||||
nextSor = u',/'
|
nextSor = u',/'
|
||||||
|
|
||||||
term = Choice(Sequence(PreDef.unary(),
|
term = Choice(Sequence(PreDef.unary(),
|
||||||
Choice(PreDef.number(nextSor=nextSor),
|
Choice(PreDef.number(nextSor=nextSor),
|
||||||
PreDef.percentage(nextSor=nextSor),
|
PreDef.percentage(nextSor=nextSor),
|
||||||
PreDef.dimension(nextSor=nextSor))),
|
PreDef.dimension(nextSor=nextSor))),
|
||||||
PreDef.string(nextSor=nextSor),
|
PreDef.string(nextSor=nextSor),
|
||||||
@ -120,33 +125,55 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
PreDef.hexcolor(nextSor=nextSor),
|
PreDef.hexcolor(nextSor=nextSor),
|
||||||
PreDef.unicode_range(nextSor=nextSor),
|
PreDef.unicode_range(nextSor=nextSor),
|
||||||
# special case IE only expression
|
# special case IE only expression
|
||||||
Prod(name='expression',
|
Prod(name='expression',
|
||||||
match=lambda t, v: t == self._prods.FUNCTION and (
|
match=lambda t, v: t == self._prods.FUNCTION and (
|
||||||
cssutils.helper.normalize(v) in (u'expression(',
|
cssutils.helper.normalize(v) in (u'expression(',
|
||||||
u'alpha(') or
|
u'alpha(') or
|
||||||
v.startswith(u'progid:DXImageTransform.Microsoft.') ),
|
v.startswith(u'progid:DXImageTransform.Microsoft.')
|
||||||
|
),
|
||||||
nextSor=nextSor,
|
nextSor=nextSor,
|
||||||
toSeq=lambda t, tokens: (ExpressionValue.name,
|
toSeq=lambda t, tokens: (ExpressionValue._functionName,
|
||||||
ExpressionValue(cssutils.helper.pushtoken(t,
|
ExpressionValue(cssutils.helper.pushtoken(t,
|
||||||
tokens)))
|
tokens)))
|
||||||
),
|
),
|
||||||
|
# CSS Variable var(
|
||||||
|
PreDef.variable(nextSor=nextSor,
|
||||||
|
toSeq=lambda t, tokens: ('CSSVariable',
|
||||||
|
CSSVariable(
|
||||||
|
cssutils.helper.pushtoken(t, tokens))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
# other functions like rgb( etc
|
||||||
PreDef.function(nextSor=nextSor,
|
PreDef.function(nextSor=nextSor,
|
||||||
toSeq=lambda t, tokens: ('FUNCTION',
|
toSeq=lambda t, tokens: ('FUNCTION',
|
||||||
CSSFunction(cssutils.helper.pushtoken(t,
|
CSSFunction(
|
||||||
tokens)))))
|
cssutils.helper.pushtoken(t, tokens))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
operator = Choice(PreDef.S(),
|
operator = Choice(PreDef.S(),
|
||||||
PreDef.char('comma', ',', toSeq=lambda t, tokens: ('operator', t[1])),
|
PreDef.char('comma', ',',
|
||||||
PreDef.char('slash', '/', toSeq=lambda t, tokens: ('operator', t[1])),
|
toSeq=lambda t, tokens: ('operator', t[1])),
|
||||||
|
PreDef.char('slash', '/',
|
||||||
|
toSeq=lambda t, tokens: ('operator', t[1])),
|
||||||
optional=True)
|
optional=True)
|
||||||
# CSSValue PRODUCTIONS
|
# CSSValue PRODUCTIONS
|
||||||
valueprods = Sequence(term,
|
valueprods = Sequence(term,
|
||||||
|
# TODO: only when setting via other class
|
||||||
|
PreDef.char('END', ';',
|
||||||
|
stopAndKeep=True,
|
||||||
|
optional=True),
|
||||||
Sequence(operator, # mayEnd this Sequence if whitespace
|
Sequence(operator, # mayEnd this Sequence if whitespace
|
||||||
term,
|
term,
|
||||||
|
PreDef.char('END', ';',
|
||||||
|
stopAndKeep=True,
|
||||||
|
optional=True),
|
||||||
minmax=lambda: (0, None)))
|
minmax=lambda: (0, None)))
|
||||||
# parse
|
# parse
|
||||||
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
wellformed, seq, store, notused = ProdParser().parse(cssText,
|
||||||
u'CSSValue',
|
u'CSSValue',
|
||||||
valueprods)
|
valueprods,
|
||||||
|
keepS=True)
|
||||||
if wellformed:
|
if wellformed:
|
||||||
# - count actual values and set firstvalue which is used later on
|
# - count actual values and set firstvalue which is used later on
|
||||||
# - combine comma separated list, e.g. font-family to a single item
|
# - combine comma separated list, e.g. font-family to a single item
|
||||||
@ -183,7 +210,7 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
break
|
break
|
||||||
|
|
||||||
newval = item.value + next.value
|
newval = item.value + next.value
|
||||||
newseq.append(newval, next.type,
|
newseq.append(newval, next.type,
|
||||||
item.line, item.col)
|
item.line, item.col)
|
||||||
if not firstvalue:
|
if not firstvalue:
|
||||||
firstvalue = (newval, next.type)
|
firstvalue = (newval, next.type)
|
||||||
@ -202,7 +229,7 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
|
|
||||||
if not firstvalue:
|
if not firstvalue:
|
||||||
self._log.error(
|
self._log.error(
|
||||||
u'CSSValue: Unknown syntax or no value: %r.' %
|
u'CSSValue: Unknown syntax or no value: %r.' %
|
||||||
self._valuestr(cssText))
|
self._valuestr(cssText))
|
||||||
else:
|
else:
|
||||||
# ok and set
|
# ok and set
|
||||||
@ -214,16 +241,24 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
del self._value
|
del self._value
|
||||||
|
|
||||||
if count == 1:
|
if count == 1:
|
||||||
|
# inherit, primitive or variable
|
||||||
if isinstance(firstvalue[0], basestring) and\
|
if isinstance(firstvalue[0], basestring) and\
|
||||||
u'inherit' == cssutils.helper.normalize(firstvalue[0]):
|
u'inherit' == cssutils.helper.normalize(firstvalue[0]):
|
||||||
self.__class__ = CSSValue
|
self.__class__ = CSSValue
|
||||||
self._cssValueType = CSSValue.CSS_INHERIT
|
self._cssValueType = CSSValue.CSS_INHERIT
|
||||||
|
elif 'CSSVariable' == firstvalue[1]:
|
||||||
|
self.__class__ = CSSVariable
|
||||||
|
self._value = firstvalue
|
||||||
|
# TODO: remove major hack!
|
||||||
|
self._name = firstvalue[0]._name
|
||||||
else:
|
else:
|
||||||
self.__class__ = CSSPrimitiveValue
|
self.__class__ = CSSPrimitiveValue
|
||||||
self._value = firstvalue
|
self._value = firstvalue
|
||||||
|
|
||||||
elif count > 1:
|
elif count > 1:
|
||||||
|
# valuelist
|
||||||
self.__class__ = CSSValueList
|
self.__class__ = CSSValueList
|
||||||
|
|
||||||
# change items in list to specific type (primitive etc)
|
# change items in list to specific type (primitive etc)
|
||||||
newseq = self._tempSeq()
|
newseq = self._tempSeq()
|
||||||
commalist = []
|
commalist = []
|
||||||
@ -235,7 +270,8 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
return cssutils.helper.string(item.value)
|
return cssutils.helper.string(item.value)
|
||||||
elif self._prods.URI == item.type:
|
elif self._prods.URI == item.type:
|
||||||
return cssutils.helper.uri(item.value)
|
return cssutils.helper.uri(item.value)
|
||||||
elif self._prods.FUNCTION == item.type:
|
elif self._prods.FUNCTION == item.type or\
|
||||||
|
'CSSVariable' == item.type:
|
||||||
return item.value.cssText
|
return item.value.cssText
|
||||||
else:
|
else:
|
||||||
return item.value
|
return item.value
|
||||||
@ -246,9 +282,9 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
if anything in there
|
if anything in there
|
||||||
"""
|
"""
|
||||||
if commalist:
|
if commalist:
|
||||||
newseq.replace(-1,
|
newseq.replace(-1,
|
||||||
CSSPrimitiveValue(cssText=u''.join(
|
CSSPrimitiveValue(cssText=u''.join(
|
||||||
commalist)),
|
commalist)),
|
||||||
CSSPrimitiveValue,
|
CSSPrimitiveValue,
|
||||||
newseq[-1].line,
|
newseq[-1].line,
|
||||||
newseq[-1].col)
|
newseq[-1].col)
|
||||||
@ -263,7 +299,8 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
self._prods.PERCENTAGE,
|
self._prods.PERCENTAGE,
|
||||||
self._prods.STRING,
|
self._prods.STRING,
|
||||||
self._prods.URI,
|
self._prods.URI,
|
||||||
self._prods.UNICODE_RANGE):
|
self._prods.UNICODE_RANGE,
|
||||||
|
'CSSVariable'):
|
||||||
if nexttocommalist:
|
if nexttocommalist:
|
||||||
# wait until complete
|
# wait until complete
|
||||||
commalist.append(itemValue(item))
|
commalist.append(itemValue(item))
|
||||||
@ -271,13 +308,13 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
saveifcommalist(commalist, newseq)
|
saveifcommalist(commalist, newseq)
|
||||||
# append new item
|
# append new item
|
||||||
if hasattr(item.value, 'cssText'):
|
if hasattr(item.value, 'cssText'):
|
||||||
newseq.append(item.value,
|
newseq.append(item.value,
|
||||||
item.value.__class__,
|
item.value.__class__,
|
||||||
item.line, item.col)
|
item.line, item.col)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
newseq.append(CSSPrimitiveValue(itemValue(item)),
|
newseq.append(CSSPrimitiveValue(itemValue(item)),
|
||||||
CSSPrimitiveValue,
|
CSSPrimitiveValue,
|
||||||
item.line, item.col)
|
item.line, item.col)
|
||||||
|
|
||||||
nexttocommalist = False
|
nexttocommalist = False
|
||||||
@ -285,7 +322,7 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
elif u',' == item.value:
|
elif u',' == item.value:
|
||||||
if not commalist:
|
if not commalist:
|
||||||
# save last item to commalist
|
# save last item to commalist
|
||||||
commalist.append(itemValue(self._seq[i-1]))
|
commalist.append(itemValue(self._seq[i - 1]))
|
||||||
commalist.append(u',')
|
commalist.append(u',')
|
||||||
nexttocommalist = True
|
nexttocommalist = True
|
||||||
|
|
||||||
@ -297,12 +334,13 @@ class CSSValue(cssutils.util._NewBase):
|
|||||||
|
|
||||||
saveifcommalist(commalist, newseq)
|
saveifcommalist(commalist, newseq)
|
||||||
self._setSeq(newseq)
|
self._setSeq(newseq)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# should not happen...
|
# should not happen...
|
||||||
self.__class__ = CSSValue
|
self.__class__ = CSSValue
|
||||||
self._cssValueType = CSSValue.CSS_CUSTOM
|
self._cssValueType = CSSValue.CSS_CUSTOM
|
||||||
|
|
||||||
cssText = property(lambda self: cssutils.ser.do_css_CSSValue(self),
|
cssText = property(lambda self: cssutils.ser.do_css_CSSValue(self),
|
||||||
_setCssText,
|
_setCssText,
|
||||||
doc="A string representation of the current value.")
|
doc="A string representation of the current value.")
|
||||||
|
|
||||||
@ -373,7 +411,7 @@ class CSSPrimitiveValue(CSSValue):
|
|||||||
_countertypes = (CSS_COUNTER,)
|
_countertypes = (CSS_COUNTER,)
|
||||||
_recttypes = (CSS_RECT,)
|
_recttypes = (CSS_RECT,)
|
||||||
_rbgtypes = (CSS_RGBCOLOR, CSS_RGBACOLOR)
|
_rbgtypes = (CSS_RGBCOLOR, CSS_RGBACOLOR)
|
||||||
_lengthtypes = (CSS_NUMBER, CSS_EMS, CSS_EXS,
|
_lengthtypes = (CSS_NUMBER, CSS_EMS, CSS_EXS,
|
||||||
CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC)
|
CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC)
|
||||||
|
|
||||||
# oldtype: newType: converterfunc
|
# oldtype: newType: converterfunc
|
||||||
@ -436,14 +474,14 @@ class CSSPrimitiveValue(CSSValue):
|
|||||||
'CSS_DEG', 'CSS_RAD', 'CSS_GRAD',
|
'CSS_DEG', 'CSS_RAD', 'CSS_GRAD',
|
||||||
'CSS_MS', 'CSS_S',
|
'CSS_MS', 'CSS_S',
|
||||||
'CSS_HZ', 'CSS_KHZ',
|
'CSS_HZ', 'CSS_KHZ',
|
||||||
'CSS_DIMENSION',
|
'CSS_DIMENSION',
|
||||||
'CSS_STRING', 'CSS_URI', 'CSS_IDENT',
|
'CSS_STRING', 'CSS_URI', 'CSS_IDENT',
|
||||||
'CSS_ATTR', 'CSS_COUNTER', 'CSS_RECT',
|
'CSS_ATTR', 'CSS_COUNTER', 'CSS_RECT',
|
||||||
'CSS_RGBCOLOR', 'CSS_RGBACOLOR',
|
'CSS_RGBCOLOR', 'CSS_RGBACOLOR',
|
||||||
'CSS_UNICODE_RANGE'
|
'CSS_UNICODE_RANGE'
|
||||||
]
|
]
|
||||||
|
|
||||||
_reNumDim = re.compile(ur'([+-]?\d*\.\d+|[+-]?\d+)(.*)$', re.I| re.U|re.X)
|
_reNumDim = re.compile(ur'([+-]?\d*\.\d+|[+-]?\d+)(.*)$', re.I | re.U | re.X)
|
||||||
|
|
||||||
def _unitDIMENSION(value):
|
def _unitDIMENSION(value):
|
||||||
"""Check val for dimension name."""
|
"""Check val for dimension name."""
|
||||||
@ -467,8 +505,8 @@ class CSSPrimitiveValue(CSSValue):
|
|||||||
'rgb(': 'CSS_RGBCOLOR',
|
'rgb(': 'CSS_RGBCOLOR',
|
||||||
'rgba(': 'CSS_RGBACOLOR',
|
'rgba(': 'CSS_RGBACOLOR',
|
||||||
}
|
}
|
||||||
return units.get(re.findall(ur'^(.*?\()',
|
return units.get(re.findall(ur'^(.*?\()',
|
||||||
cssutils.helper.normalize(value.cssText),
|
cssutils.helper.normalize(value.cssText),
|
||||||
re.U)[0],
|
re.U)[0],
|
||||||
'CSS_UNKNOWN')
|
'CSS_UNKNOWN')
|
||||||
|
|
||||||
@ -601,13 +639,13 @@ class CSSPrimitiveValue(CSSValue):
|
|||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
if unitType not in self._floattypes:
|
if unitType not in self._floattypes:
|
||||||
raise xml.dom.InvalidAccessErr(
|
raise xml.dom.InvalidAccessErr(
|
||||||
u'CSSPrimitiveValue: unitType %r is not a float type' %
|
u'CSSPrimitiveValue: unitType %r is not a float type' %
|
||||||
self._getCSSPrimitiveTypeString(unitType))
|
self._getCSSPrimitiveTypeString(unitType))
|
||||||
try:
|
try:
|
||||||
val = float(floatValue)
|
val = float(floatValue)
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
raise xml.dom.InvalidAccessErr(
|
raise xml.dom.InvalidAccessErr(
|
||||||
u'CSSPrimitiveValue: floatValue %r is not a float' %
|
u'CSSPrimitiveValue: floatValue %r is not a float' %
|
||||||
floatValue)
|
floatValue)
|
||||||
|
|
||||||
oldval, dim = self._getNumDim()
|
oldval, dim = self._getNumDim()
|
||||||
@ -761,17 +799,17 @@ class CSSValueList(CSSValue):
|
|||||||
"""
|
"""
|
||||||
cssValueType = CSSValue.CSS_VALUE_LIST
|
cssValueType = CSSValue.CSS_VALUE_LIST
|
||||||
|
|
||||||
def __init__(self, cssText=None, readonly=False):
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
||||||
"""Init a new CSSValueList"""
|
"""Init a new CSSValueList"""
|
||||||
super(CSSValueList, self).__init__(cssText=cssText, readonly=readonly)
|
super(CSSValueList, self).__init__(cssText=cssText,
|
||||||
|
parent=parent,
|
||||||
|
readonly=readonly)
|
||||||
self._items = []
|
self._items = []
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"CSSValueList is iterable."
|
"CSSValueList is iterable."
|
||||||
def itemsiter():
|
for item in self.__items():
|
||||||
for i in range (0, self.length):
|
yield item.value
|
||||||
yield self.item(i)
|
|
||||||
return itemsiter()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<cssutils.css.%s object cssValueType=%r cssText=%r length=%r at 0x%x>" % (
|
return "<cssutils.css.%s object cssValueType=%r cssText=%r length=%r at 0x%x>" % (
|
||||||
@ -799,7 +837,7 @@ class CSSValueList(CSSValue):
|
|||||||
|
|
||||||
class CSSFunction(CSSPrimitiveValue):
|
class CSSFunction(CSSPrimitiveValue):
|
||||||
"""A CSS function value like rect() etc."""
|
"""A CSS function value like rect() etc."""
|
||||||
name = u'CSSFunction'
|
_functionName = u'CSSFunction'
|
||||||
primitiveType = CSSPrimitiveValue.CSS_UNKNOWN
|
primitiveType = CSSPrimitiveValue.CSS_UNKNOWN
|
||||||
|
|
||||||
def __init__(self, cssText=None, readonly=False):
|
def __init__(self, cssText=None, readonly=False):
|
||||||
@ -812,46 +850,37 @@ class CSSFunction(CSSPrimitiveValue):
|
|||||||
defaults to False
|
defaults to False
|
||||||
"""
|
"""
|
||||||
super(CSSFunction, self).__init__()
|
super(CSSFunction, self).__init__()
|
||||||
|
self._funcType = None
|
||||||
self.valid = False
|
self.valid = False
|
||||||
self.wellformed = False
|
self.wellformed = False
|
||||||
if cssText is not None:
|
if cssText is not None:
|
||||||
self.cssText = cssText
|
self.cssText = cssText
|
||||||
|
|
||||||
self._funcType = None
|
|
||||||
|
|
||||||
self._readonly = readonly
|
self._readonly = readonly
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "<cssutils.css.%s object primitiveType=%s cssText=%r at 0x%x>" % (
|
|
||||||
self.__class__.__name__, self.primitiveTypeString, self.cssText,
|
|
||||||
id(self))
|
|
||||||
|
|
||||||
def _productiondefinition(self):
|
def _productiondefinition(self):
|
||||||
"""Return defintion used for parsing."""
|
"""Return defintion used for parsing."""
|
||||||
types = self._prods # rename!
|
types = self._prods # rename!
|
||||||
valueProd = Prod(name='PrimitiveValue',
|
valueProd = Prod(name='PrimitiveValue',
|
||||||
match=lambda t, v: t in (types.DIMENSION,
|
match=lambda t, v: t in (types.DIMENSION,
|
||||||
types.IDENT,
|
types.IDENT,
|
||||||
types.NUMBER,
|
types.NUMBER,
|
||||||
types.PERCENTAGE,
|
types.PERCENTAGE,
|
||||||
types.STRING),
|
types.STRING),
|
||||||
toSeq=lambda t, tokens: (t[0], CSSPrimitiveValue(t[1])))
|
toSeq=lambda t, tokens: (t[0], CSSPrimitiveValue(t[1])))
|
||||||
|
|
||||||
funcProds = Sequence(Prod(name='FUNC',
|
funcProds = Sequence(Prod(name='FUNC',
|
||||||
match=lambda t, v: t == types.FUNCTION,
|
match=lambda t, v: t == types.FUNCTION,
|
||||||
toSeq=lambda t, tokens: (t[0], cssutils.helper.normalize(t[1]))),
|
toSeq=lambda t, tokens: (t[0], cssutils.helper.normalize(t[1]))),
|
||||||
Choice(Sequence(PreDef.unary(),
|
Choice(Sequence(PreDef.unary(),
|
||||||
valueProd,
|
valueProd,
|
||||||
# more values starting with Comma
|
# more values starting with Comma
|
||||||
# should use store where colorType is saved to
|
# should use store where colorType is saved to
|
||||||
# define min and may, closure?
|
# define min and may, closure?
|
||||||
Sequence(PreDef.comma(),
|
Sequence(PreDef.comma(),
|
||||||
PreDef.unary(),
|
PreDef.unary(),
|
||||||
valueProd,
|
valueProd,
|
||||||
minmax=lambda: (0, 3)),
|
minmax=lambda: (0, 3)),
|
||||||
PreDef.funcEnd(stop=True)),
|
PreDef.funcEnd(stop=True)),
|
||||||
PreDef.funcEnd(stop=True))
|
PreDef.funcEnd(stop=True))
|
||||||
)
|
)
|
||||||
@ -861,8 +890,9 @@ class CSSFunction(CSSPrimitiveValue):
|
|||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
# store: colorType, parts
|
# store: colorType, parts
|
||||||
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
||||||
self.name,
|
self._functionName,
|
||||||
self._productiondefinition())
|
self._productiondefinition(),
|
||||||
|
keepS=True)
|
||||||
if wellformed:
|
if wellformed:
|
||||||
# combine +/- and following CSSPrimitiveValue, remove S
|
# combine +/- and following CSSPrimitiveValue, remove S
|
||||||
newseq = self._tempSeq()
|
newseq = self._tempSeq()
|
||||||
@ -876,9 +906,9 @@ class CSSFunction(CSSPrimitiveValue):
|
|||||||
next = seq[i]
|
next = seq[i]
|
||||||
newval = next.value
|
newval = next.value
|
||||||
if isinstance(newval, CSSPrimitiveValue):
|
if isinstance(newval, CSSPrimitiveValue):
|
||||||
newval.setFloatValue(newval.primitiveType,
|
newval.setFloatValue(newval.primitiveType,
|
||||||
float(item.value + str(newval.getFloatValue())))
|
float(item.value + str(newval.getFloatValue())))
|
||||||
newseq.append(newval, next.type,
|
newseq.append(newval, next.type,
|
||||||
item.line, item.col)
|
item.line, item.col)
|
||||||
else:
|
else:
|
||||||
# expressions only?
|
# expressions only?
|
||||||
@ -893,7 +923,7 @@ class CSSFunction(CSSPrimitiveValue):
|
|||||||
self._setSeq(newseq)
|
self._setSeq(newseq)
|
||||||
self._funcType = newseq[0].value
|
self._funcType = newseq[0].value
|
||||||
|
|
||||||
cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
|
cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
|
||||||
_setCssText)
|
_setCssText)
|
||||||
|
|
||||||
funcType = property(lambda self: self._funcType)
|
funcType = property(lambda self: self._funcType)
|
||||||
@ -930,37 +960,38 @@ class RGBColor(CSSPrimitiveValue):
|
|||||||
def _setCssText(self, cssText):
|
def _setCssText(self, cssText):
|
||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
types = self._prods # rename!
|
types = self._prods # rename!
|
||||||
valueProd = Prod(name='value',
|
valueProd = Prod(name='value',
|
||||||
match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
|
match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
|
||||||
toSeq=lambda t, v: (CSSPrimitiveValue, CSSPrimitiveValue(v)),
|
toSeq=lambda t, v: (CSSPrimitiveValue, CSSPrimitiveValue(v)),
|
||||||
toStore='parts'
|
toStore='parts'
|
||||||
)
|
)
|
||||||
# COLOR PRODUCTION
|
# COLOR PRODUCTION
|
||||||
funccolor = Sequence(Prod(name='FUNC',
|
funccolor = Sequence(Prod(name='FUNC',
|
||||||
match=lambda t, v: self._normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla(') and t == types.FUNCTION,
|
match=lambda t, v: self._normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla(') and t == types.FUNCTION,
|
||||||
toSeq=lambda t, v: (t, self._normalize(v)),
|
toSeq=lambda t, v: (t, self._normalize(v)),
|
||||||
toStore='colorType' ),
|
toStore='colorType'),
|
||||||
PreDef.unary(),
|
PreDef.unary(),
|
||||||
valueProd,
|
valueProd,
|
||||||
# 2 or 3 more values starting with Comma
|
# 2 or 3 more values starting with Comma
|
||||||
Sequence(PreDef.comma(),
|
Sequence(PreDef.comma(),
|
||||||
PreDef.unary(),
|
PreDef.unary(),
|
||||||
valueProd,
|
valueProd,
|
||||||
minmax=lambda: (2, 3)),
|
minmax=lambda: (2, 3)),
|
||||||
PreDef.funcEnd()
|
PreDef.funcEnd()
|
||||||
)
|
)
|
||||||
colorprods = Choice(funccolor,
|
colorprods = Choice(funccolor,
|
||||||
PreDef.hexcolor('colorType'),
|
PreDef.hexcolor('colorType'),
|
||||||
Prod(name='named color',
|
Prod(name='named color',
|
||||||
match=lambda t, v: t == types.IDENT,
|
match=lambda t, v: t == types.IDENT,
|
||||||
toStore='colorType'
|
toStore='colorType'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# store: colorType, parts
|
# store: colorType, parts
|
||||||
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
||||||
u'RGBColor',
|
u'RGBColor',
|
||||||
colorprods,
|
colorprods,
|
||||||
{'parts': []})
|
keepS=True,
|
||||||
|
store={'parts': []})
|
||||||
|
|
||||||
if wellformed:
|
if wellformed:
|
||||||
self.wellformed = True
|
self.wellformed = True
|
||||||
@ -973,16 +1004,16 @@ class RGBColor(CSSPrimitiveValue):
|
|||||||
|
|
||||||
self._setSeq(seq)
|
self._setSeq(seq)
|
||||||
|
|
||||||
cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
|
cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
|
||||||
_setCssText)
|
_setCssText)
|
||||||
|
|
||||||
colorType = property(lambda self: self._colorType)
|
colorType = property(lambda self: self._colorType)
|
||||||
|
|
||||||
|
|
||||||
class ExpressionValue(CSSFunction):
|
class ExpressionValue(CSSFunction):
|
||||||
"""Special IE only CSSFunction which may contain *anything*.
|
"""Special IE only CSSFunction which may contain *anything*.
|
||||||
Used for expressions and ``alpha(opacity=100)`` currently."""
|
Used for expressions and ``alpha(opacity=100)`` currently."""
|
||||||
name = u'Expression (IE only)'
|
_functionName = u'Expression (IE only)'
|
||||||
|
|
||||||
def _productiondefinition(self):
|
def _productiondefinition(self):
|
||||||
"""Return defintion used for parsing."""
|
"""Return defintion used for parsing."""
|
||||||
@ -992,19 +1023,19 @@ class ExpressionValue(CSSFunction):
|
|||||||
"Do not normalize function name!"
|
"Do not normalize function name!"
|
||||||
return t[0], t[1]
|
return t[0], t[1]
|
||||||
|
|
||||||
funcProds = Sequence(Prod(name='expression',
|
funcProds = Sequence(Prod(name='expression',
|
||||||
match=lambda t, v: t == types.FUNCTION,
|
match=lambda t, v: t == types.FUNCTION,
|
||||||
toSeq=toSeq
|
toSeq=toSeq
|
||||||
),
|
),
|
||||||
Sequence(Choice(Prod(name='nested function',
|
Sequence(Choice(Prod(name='nested function',
|
||||||
match=lambda t, v: t == self._prods.FUNCTION,
|
match=lambda t, v: t == self._prods.FUNCTION,
|
||||||
toSeq=lambda t, tokens: (CSSFunction.name,
|
toSeq=lambda t, tokens: (CSSFunction._functionName,
|
||||||
CSSFunction(cssutils.helper.pushtoken(t,
|
CSSFunction(cssutils.helper.pushtoken(t,
|
||||||
tokens)))
|
tokens)))
|
||||||
),
|
),
|
||||||
Prod(name='part',
|
Prod(name='part',
|
||||||
match=lambda t, v: v != u')',
|
match=lambda t, v: v != u')',
|
||||||
toSeq=lambda t, tokens: (t[0], t[1])),
|
toSeq=lambda t, tokens: (t[0], t[1])),
|
||||||
),
|
),
|
||||||
minmax=lambda: (0, None)),
|
minmax=lambda: (0, None)),
|
||||||
PreDef.funcEnd(stop=True))
|
PreDef.funcEnd(stop=True))
|
||||||
@ -1018,3 +1049,80 @@ class ExpressionValue(CSSFunction):
|
|||||||
|
|
||||||
cssText = property(_getCssText, _setCssText,
|
cssText = property(_getCssText, _setCssText,
|
||||||
doc="A string representation of the current value.")
|
doc="A string representation of the current value.")
|
||||||
|
|
||||||
|
|
||||||
|
class CSSVariable(CSSValue):
|
||||||
|
"""The CSSVariable represents a call to CSS Variable."""
|
||||||
|
|
||||||
|
def __init__(self, cssText=None, readonly=False):
|
||||||
|
"""Init a new CSSVariable.
|
||||||
|
|
||||||
|
:param cssText:
|
||||||
|
the parsable cssText of the value, e.g. ``var(x)``
|
||||||
|
:param readonly:
|
||||||
|
defaults to False
|
||||||
|
"""
|
||||||
|
self._name = None
|
||||||
|
super(CSSVariable, self).__init__(cssText=cssText,
|
||||||
|
readonly=readonly)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<cssutils.css.%s object name=%r value=%r at 0x%x>" % (
|
||||||
|
self.__class__.__name__, self.name, self.value,
|
||||||
|
id(self))
|
||||||
|
|
||||||
|
def _setCssText(self, cssText):
|
||||||
|
self._checkReadonly()
|
||||||
|
|
||||||
|
types = self._prods # rename!
|
||||||
|
|
||||||
|
funcProds = Sequence(Prod(name='var',
|
||||||
|
match=lambda t, v: t == types.FUNCTION
|
||||||
|
),
|
||||||
|
PreDef.ident(toStore='ident'),
|
||||||
|
PreDef.funcEnd(stop=True))
|
||||||
|
|
||||||
|
# store: name of variable
|
||||||
|
store = {'ident': None}
|
||||||
|
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
||||||
|
u'CSSVariable',
|
||||||
|
funcProds,
|
||||||
|
keepS=True)
|
||||||
|
if wellformed:
|
||||||
|
self._name = store['ident'].value
|
||||||
|
self._setSeq(seq)
|
||||||
|
self.wellformed = True
|
||||||
|
|
||||||
|
cssText = property(lambda self: cssutils.ser.do_css_CSSVariable(self),
|
||||||
|
_setCssText,
|
||||||
|
doc="A string representation of the current variable.")
|
||||||
|
|
||||||
|
cssValueType = CSSValue.CSS_VARIABLE
|
||||||
|
|
||||||
|
# TODO: writable? check if var (value) available?
|
||||||
|
name = property(lambda self: self._name)
|
||||||
|
|
||||||
|
def _getValue(self):
|
||||||
|
"Find contained sheet and @variables there"
|
||||||
|
# TODO: imports!
|
||||||
|
|
||||||
|
# property:
|
||||||
|
if self.parent:
|
||||||
|
# styleDeclaration:
|
||||||
|
if self.parent.parent:
|
||||||
|
# styleRule:
|
||||||
|
if self.parent.parent.parentRule:
|
||||||
|
# stylesheet
|
||||||
|
if self.parent.parent.parentRule.parentStyleSheet:
|
||||||
|
sheet = self.parent.parent.parentRule.parentStyleSheet
|
||||||
|
for r in sheet.cssRules:
|
||||||
|
if r.VARIABLES_RULE == r.type and r.variables:
|
||||||
|
try:
|
||||||
|
return r.variables[self.name]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
value = property(_getValue)
|
||||||
|
292
src/cssutils/css/cssvariablesdeclaration.py
Normal file
292
src/cssutils/css/cssvariablesdeclaration.py
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
"""CSSVariablesDeclaration
|
||||||
|
http://disruptive-innovations.com/zoo/cssvariables/#mozTocId496530
|
||||||
|
"""
|
||||||
|
__all__ = ['CSSVariablesDeclaration']
|
||||||
|
__docformat__ = 'restructuredtext'
|
||||||
|
__version__ = '$Id: cssstyledeclaration.py 1819 2009-08-01 20:52:43Z cthedot $'
|
||||||
|
|
||||||
|
from cssutils.prodparser import *
|
||||||
|
from cssvalue import CSSValue
|
||||||
|
import cssutils
|
||||||
|
import itertools
|
||||||
|
import xml.dom
|
||||||
|
|
||||||
|
class CSSVariablesDeclaration(cssutils.util._NewBase):
|
||||||
|
"""The CSSVariablesDeclaration interface represents a single block of
|
||||||
|
variable declarations.
|
||||||
|
"""
|
||||||
|
def __init__(self, cssText=u'', parentRule=None, readonly=False):
|
||||||
|
"""
|
||||||
|
:param cssText:
|
||||||
|
Shortcut, sets CSSVariablesDeclaration.cssText
|
||||||
|
:param parentRule:
|
||||||
|
The CSS rule that contains this declaration block or
|
||||||
|
None if this CSSVariablesDeclaration is not attached to a CSSRule.
|
||||||
|
:param readonly:
|
||||||
|
defaults to False
|
||||||
|
"""
|
||||||
|
super(CSSVariablesDeclaration, self).__init__()
|
||||||
|
self._parentRule = parentRule
|
||||||
|
self._vars = {}
|
||||||
|
if cssText:
|
||||||
|
self.cssText = cssText
|
||||||
|
|
||||||
|
self._readonly = readonly
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "cssutils.css.%s(cssText=%r)" % (
|
||||||
|
self.__class__.__name__, self.cssText)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<cssutils.css.%s object length=%r at 0x%x>" % (
|
||||||
|
self.__class__.__name__, self.length, id(self))
|
||||||
|
|
||||||
|
def __contains__(self, variableName):
|
||||||
|
"""Check if a variable is in variable declaration block.
|
||||||
|
|
||||||
|
:param variableName:
|
||||||
|
a string
|
||||||
|
"""
|
||||||
|
return variableName.lower() in self.keys()
|
||||||
|
|
||||||
|
def __getitem__(self, variableName):
|
||||||
|
"""Retrieve the value of variable ``variableName`` from this
|
||||||
|
declaration.
|
||||||
|
"""
|
||||||
|
return self.getVariableValue(variableName.lower())
|
||||||
|
|
||||||
|
def __setitem__(self, variableName, value):
|
||||||
|
self.setVariable(variableName.lower(), value)
|
||||||
|
|
||||||
|
def __delitem__(self, variableName):
|
||||||
|
return self.removeVariable(variableName.lower())
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterator of names of set variables."""
|
||||||
|
for name in self.keys():
|
||||||
|
yield name
|
||||||
|
|
||||||
|
def _absorb(self, other):
|
||||||
|
"""Replace all own data with data from other object."""
|
||||||
|
self._parentRule = other._parentRule
|
||||||
|
self.seq.absorb(other.seq)
|
||||||
|
self._readonly = other._readonly
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
"""Analoguous to standard dict returns variable names which are set in
|
||||||
|
this declaration."""
|
||||||
|
return self._vars.keys()
|
||||||
|
|
||||||
|
def _getCssText(self):
|
||||||
|
"""Return serialized property cssText."""
|
||||||
|
return cssutils.ser.do_css_CSSVariablesDeclaration(self)
|
||||||
|
|
||||||
|
def _setCssText(self, cssText):
|
||||||
|
"""Setting this attribute will result in the parsing of the new value
|
||||||
|
and resetting of all the properties in the declaration block
|
||||||
|
including the removal or addition of properties.
|
||||||
|
|
||||||
|
:exceptions:
|
||||||
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||||
|
Raised if this declaration is readonly or a property is readonly.
|
||||||
|
- :exc:`~xml.dom.SyntaxErr`:
|
||||||
|
Raised if the specified CSS string value has a syntax error and
|
||||||
|
is unparsable.
|
||||||
|
|
||||||
|
Format::
|
||||||
|
|
||||||
|
variableset
|
||||||
|
: vardeclaration [ ';' S* vardeclaration ]*
|
||||||
|
;
|
||||||
|
|
||||||
|
vardeclaration
|
||||||
|
: varname ':' S* term
|
||||||
|
;
|
||||||
|
|
||||||
|
varname
|
||||||
|
: IDENT S*
|
||||||
|
;
|
||||||
|
|
||||||
|
expr
|
||||||
|
: [ VARCALL | term ] [ operator [ VARCALL | term ] ]*
|
||||||
|
;
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._checkReadonly()
|
||||||
|
|
||||||
|
vardeclaration = Sequence(
|
||||||
|
PreDef.ident(),
|
||||||
|
PreDef.char(u':', u':', toSeq=False),
|
||||||
|
#PreDef.S(toSeq=False, optional=True),
|
||||||
|
Prod(name=u'term', match=lambda t, v: True,
|
||||||
|
toSeq=lambda t, tokens: (u'value',
|
||||||
|
CSSValue(itertools.chain([t],
|
||||||
|
tokens))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
PreDef.char(u';', u';', toSeq=False, optional=True),
|
||||||
|
)
|
||||||
|
prods = Sequence(vardeclaration, minmax=lambda: (0, None))
|
||||||
|
# parse
|
||||||
|
wellformed, seq, store, notused = \
|
||||||
|
ProdParser().parse(cssText,
|
||||||
|
u'CSSVariableDeclaration',
|
||||||
|
prods)
|
||||||
|
if wellformed:
|
||||||
|
newseq = self._tempSeq()
|
||||||
|
|
||||||
|
# seq contains only name: value pairs plus comments etc
|
||||||
|
lastname = None
|
||||||
|
for item in seq:
|
||||||
|
if u'IDENT' == item.type:
|
||||||
|
lastname = item
|
||||||
|
self._vars[lastname.value.lower()] = None
|
||||||
|
elif u'value' == item.type:
|
||||||
|
self._vars[lastname.value.lower()] = item.value
|
||||||
|
newseq.append((lastname.value, item.value),
|
||||||
|
'var',
|
||||||
|
lastname.line, lastname.col)
|
||||||
|
else:
|
||||||
|
newseq.appendItem(item)
|
||||||
|
|
||||||
|
self._setSeq(newseq)
|
||||||
|
self.wellformed = True
|
||||||
|
|
||||||
|
|
||||||
|
cssText = property(_getCssText, _setCssText,
|
||||||
|
doc="(DOM) A parsable textual representation of the declaration\
|
||||||
|
block excluding the surrounding curly braces.")
|
||||||
|
|
||||||
|
def _setParentRule(self, parentRule):
|
||||||
|
self._parentRule = parentRule
|
||||||
|
|
||||||
|
parentRule = property(lambda self: self._parentRule, _setParentRule,
|
||||||
|
doc="(DOM) The CSS rule that contains this"
|
||||||
|
" declaration block or None if this block"
|
||||||
|
" is not attached to a CSSRule.")
|
||||||
|
|
||||||
|
def getVariableValue(self, variableName):
|
||||||
|
"""Used to retrieve the value of a variable if it has been explicitly
|
||||||
|
set within this variable declaration block.
|
||||||
|
|
||||||
|
:param variableName:
|
||||||
|
The name of the variable.
|
||||||
|
:returns:
|
||||||
|
the value of the variable if it has been explicitly set in this
|
||||||
|
variable declaration block. Returns the empty string if the
|
||||||
|
variable has not been set.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._vars[variableName.lower()].cssText
|
||||||
|
except KeyError, e:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
def removeVariable(self, variableName):
|
||||||
|
"""Used to remove a variable if it has been explicitly set within this
|
||||||
|
variable declaration block.
|
||||||
|
|
||||||
|
:param variableName:
|
||||||
|
The name of the variable.
|
||||||
|
:returns:
|
||||||
|
the value of the variable if it has been explicitly set for this
|
||||||
|
variable declaration block. Returns the empty string if the
|
||||||
|
variable has not been set.
|
||||||
|
|
||||||
|
:exceptions:
|
||||||
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||||
|
Raised if this declaration is readonly is readonly.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
r = self._vars[variableName.lower()]
|
||||||
|
except KeyError, e:
|
||||||
|
return u''
|
||||||
|
else:
|
||||||
|
self.seq._readonly = False
|
||||||
|
if variableName in self._vars:
|
||||||
|
for i, x in enumerate(self.seq):
|
||||||
|
if x.value[0] == variableName:
|
||||||
|
del self.seq[i]
|
||||||
|
self.seq._readonly = True
|
||||||
|
del self._vars[variableName.lower()]
|
||||||
|
|
||||||
|
return r.cssText
|
||||||
|
|
||||||
|
def setVariable(self, variableName, value):
|
||||||
|
"""Used to set a variable value within this variable declaration block.
|
||||||
|
|
||||||
|
:param variableName:
|
||||||
|
The name of the CSS variable.
|
||||||
|
:param value:
|
||||||
|
The new value of the variable, may also be a CSSValue object.
|
||||||
|
|
||||||
|
:exceptions:
|
||||||
|
- :exc:`~xml.dom.SyntaxErr`:
|
||||||
|
Raised if the specified value has a syntax error and is
|
||||||
|
unparsable.
|
||||||
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||||
|
Raised if this declaration is readonly or the property is
|
||||||
|
readonly.
|
||||||
|
"""
|
||||||
|
self._checkReadonly()
|
||||||
|
|
||||||
|
# check name
|
||||||
|
wellformed, seq, store, unused = ProdParser().parse(variableName.lower(),
|
||||||
|
u'variableName',
|
||||||
|
Sequence(PreDef.ident()
|
||||||
|
))
|
||||||
|
if not wellformed:
|
||||||
|
self._log.error(u'Invalid variableName: %r: %r'
|
||||||
|
% (variableName, value))
|
||||||
|
else:
|
||||||
|
# check value
|
||||||
|
if isinstance(value, CSSValue):
|
||||||
|
v = value
|
||||||
|
else:
|
||||||
|
v = CSSValue(cssText=value)
|
||||||
|
|
||||||
|
if not v.wellformed:
|
||||||
|
self._log.error(u'Invalid variable value: %r: %r'
|
||||||
|
% (variableName, value))
|
||||||
|
else:
|
||||||
|
# update seq
|
||||||
|
self.seq._readonly = False
|
||||||
|
if variableName in self._vars:
|
||||||
|
for i, x in enumerate(self.seq):
|
||||||
|
if x.value[0] == variableName:
|
||||||
|
x.replace(i,
|
||||||
|
[variableName, v],
|
||||||
|
x.type,
|
||||||
|
x.line,
|
||||||
|
x.col)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.seq.append([variableName, v], 'var')
|
||||||
|
self.seq._readonly = True
|
||||||
|
self._vars[variableName] = v
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def item(self, index):
|
||||||
|
"""Used to retrieve the variables that have been explicitly set in
|
||||||
|
this variable declaration block. The order of the variables
|
||||||
|
retrieved using this method does not have to be the order in which
|
||||||
|
they were set. This method can be used to iterate over all variables
|
||||||
|
in this variable declaration block.
|
||||||
|
|
||||||
|
:param index:
|
||||||
|
of the variable name to retrieve, negative values behave like
|
||||||
|
negative indexes on Python lists, so -1 is the last element
|
||||||
|
|
||||||
|
:returns:
|
||||||
|
The name of the variable at this ordinal position. The empty
|
||||||
|
string if no variable exists at this position.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.keys()[index]
|
||||||
|
except IndexError:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
length = property(lambda self: len(self._vars),
|
||||||
|
doc="The number of variables that have been explicitly set in this"
|
||||||
|
" variable declaration block. The range of valid indices is 0"
|
||||||
|
" to length-1 inclusive.")
|
164
src/cssutils/css/cssvariablesrule.py
Normal file
164
src/cssutils/css/cssvariablesrule.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
"""CSSVariables implements (and only partly) experimental
|
||||||
|
`CSS Variables <http://disruptive-innovations.com/zoo/cssvariables/>`_
|
||||||
|
"""
|
||||||
|
__all__ = ['CSSVariablesRule']
|
||||||
|
__docformat__ = 'restructuredtext'
|
||||||
|
__version__ = '$Id: cssfontfacerule.py 1818 2009-07-30 21:39:00Z cthedot $'
|
||||||
|
|
||||||
|
from cssvariablesdeclaration import CSSVariablesDeclaration
|
||||||
|
import cssrule
|
||||||
|
import cssutils
|
||||||
|
import xml.dom
|
||||||
|
|
||||||
|
class CSSVariablesRule(cssrule.CSSRule):
|
||||||
|
"""
|
||||||
|
The CSSVariablesRule interface represents a @variables rule within a CSS
|
||||||
|
style sheet. The @variables rule is used to specify variables.
|
||||||
|
|
||||||
|
cssutils uses a :class:`~cssutils.css.CSSVariablesDeclaration` to
|
||||||
|
represent the variables.
|
||||||
|
"""
|
||||||
|
def __init__(self, mediaText=None, variables=None, parentRule=None,
|
||||||
|
parentStyleSheet=None, readonly=False):
|
||||||
|
"""
|
||||||
|
If readonly allows setting of properties in constructor only.
|
||||||
|
"""
|
||||||
|
super(CSSVariablesRule, self).__init__(parentRule=parentRule,
|
||||||
|
parentStyleSheet=parentStyleSheet)
|
||||||
|
self._atkeyword = u'@variables'
|
||||||
|
self._media = cssutils.stylesheets.MediaList(mediaText,
|
||||||
|
readonly=readonly)
|
||||||
|
self._variables = CSSVariablesDeclaration(parentRule=self)
|
||||||
|
if variables:
|
||||||
|
self.variables = variables
|
||||||
|
|
||||||
|
self._readonly = readonly
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "cssutils.css.%s(mediaText=%r, variables=%r)" % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
self._media.mediaText, self.variables.cssText)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "<cssutils.css.%s object mediaText=%r variables=%r valid=%r at 0x%x>" % (
|
||||||
|
self.__class__.__name__, self._media.mediaText,
|
||||||
|
self.variables.cssText, self.valid, id(self))
|
||||||
|
|
||||||
|
def _getCssText(self):
|
||||||
|
"""Return serialized property cssText."""
|
||||||
|
return cssutils.ser.do_CSSVariablesRule(self)
|
||||||
|
|
||||||
|
def _setCssText(self, cssText):
|
||||||
|
"""
|
||||||
|
:exceptions:
|
||||||
|
- :exc:`~xml.dom.SyntaxErr`:
|
||||||
|
Raised if the specified CSS string value has a syntax error and
|
||||||
|
is unparsable.
|
||||||
|
- :exc:`~xml.dom.InvalidModificationErr`:
|
||||||
|
Raised if the specified CSS string value represents a different
|
||||||
|
type of rule than the current one.
|
||||||
|
- :exc:`~xml.dom.HierarchyRequestErr`:
|
||||||
|
Raised if the rule cannot be inserted at this point in the
|
||||||
|
style sheet.
|
||||||
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
||||||
|
Raised if the rule is readonly.
|
||||||
|
|
||||||
|
Format::
|
||||||
|
|
||||||
|
variables
|
||||||
|
: VARIABLES_SYM S* medium [ COMMA S* medium ]* LBRACE S* variableset* '}' S*
|
||||||
|
;
|
||||||
|
|
||||||
|
variableset
|
||||||
|
: LBRACE S* vardeclaration [ ';' S* vardeclaration ]* '}' S*
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
super(CSSVariablesRule, self)._setCssText(cssText)
|
||||||
|
|
||||||
|
tokenizer = self._tokenize2(cssText)
|
||||||
|
attoken = self._nexttoken(tokenizer, None)
|
||||||
|
if self._type(attoken) != self._prods.VARIABLES_SYM:
|
||||||
|
self._log.error(u'CSSVariablesRule: No CSSVariablesRule found: %s' %
|
||||||
|
self._valuestr(cssText),
|
||||||
|
error=xml.dom.InvalidModificationErr)
|
||||||
|
else:
|
||||||
|
# save if parse goes wrong
|
||||||
|
oldvariables = CSSVariablesDeclaration()
|
||||||
|
oldvariables._absorb(self.variables)
|
||||||
|
|
||||||
|
ok = True
|
||||||
|
beforetokens, brace = self._tokensupto2(tokenizer,
|
||||||
|
blockstartonly=True,
|
||||||
|
separateEnd=True)
|
||||||
|
if self._tokenvalue(brace) != u'{':
|
||||||
|
ok = False
|
||||||
|
self._log.error(
|
||||||
|
u'CSSVariablesRule: No start { of variable declaration found: %r' %
|
||||||
|
self._valuestr(cssText), brace)
|
||||||
|
|
||||||
|
# parse stuff before { which should be comments and S only
|
||||||
|
new = {'wellformed': True}
|
||||||
|
newseq = self._tempSeq()#[]
|
||||||
|
|
||||||
|
beforewellformed, expected = self._parse(expected=':',
|
||||||
|
seq=newseq, tokenizer=self._tokenize2(beforetokens),
|
||||||
|
productions={})
|
||||||
|
ok = ok and beforewellformed and new['wellformed']
|
||||||
|
|
||||||
|
variablestokens, braceorEOFtoken = self._tokensupto2(tokenizer,
|
||||||
|
blockendonly=True,
|
||||||
|
separateEnd=True)
|
||||||
|
|
||||||
|
val, typ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken)
|
||||||
|
if val != u'}' and typ != 'EOF':
|
||||||
|
ok = False
|
||||||
|
self._log.error(
|
||||||
|
u'CSSVariablesRule: No "}" after variables declaration found: %r' %
|
||||||
|
self._valuestr(cssText))
|
||||||
|
|
||||||
|
nonetoken = self._nexttoken(tokenizer)
|
||||||
|
if nonetoken:
|
||||||
|
ok = False
|
||||||
|
self._log.error(u'CSSVariablesRule: Trailing content found.',
|
||||||
|
token=nonetoken)
|
||||||
|
|
||||||
|
if 'EOF' == typ:
|
||||||
|
# add again as variables needs it
|
||||||
|
variablestokens.append(braceorEOFtoken)
|
||||||
|
# may raise:
|
||||||
|
self.variables.cssText = variablestokens
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
# contains probably comments only upto {
|
||||||
|
self._setSeq(newseq)
|
||||||
|
else:
|
||||||
|
# RESET
|
||||||
|
self.variables._absorb(oldvariables)
|
||||||
|
|
||||||
|
cssText = property(_getCssText, _setCssText,
|
||||||
|
doc="(DOM) The parsable textual representation of this rule.")
|
||||||
|
|
||||||
|
def _setVariables(self, variables):
|
||||||
|
"""
|
||||||
|
:param variables:
|
||||||
|
a CSSVariablesDeclaration or string
|
||||||
|
"""
|
||||||
|
self._checkReadonly()
|
||||||
|
if isinstance(variables, basestring):
|
||||||
|
self._variables.cssText = variables
|
||||||
|
else:
|
||||||
|
self._variables = variables
|
||||||
|
self._variables.parentRule = self
|
||||||
|
|
||||||
|
variables = property(lambda self: self._variables, _setVariables,
|
||||||
|
doc="(DOM) The variables of this rule set, "
|
||||||
|
"a :class:`~cssutils.css.CSSVariablesDeclaration`.")
|
||||||
|
|
||||||
|
type = property(lambda self: self.VARIABLES_RULE,
|
||||||
|
doc="The type of this rule, as defined by a CSSRule "
|
||||||
|
"type constant.")
|
||||||
|
|
||||||
|
valid = property(lambda self: True, doc='TODO')
|
||||||
|
|
||||||
|
# constant but needed:
|
||||||
|
wellformed = property(lambda self: True)
|
@ -1,7 +1,7 @@
|
|||||||
"""Property is a single CSS property in a CSSStyleDeclaration."""
|
"""Property is a single CSS property in a CSSStyleDeclaration."""
|
||||||
__all__ = ['Property']
|
__all__ = ['Property']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: property.py 1811 2009-07-29 13:11:15Z cthedot $'
|
__version__ = '$Id: property.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from cssutils.helper import Deprecated
|
from cssutils.helper import Deprecated
|
||||||
from cssvalue import CSSValue
|
from cssvalue import CSSValue
|
||||||
@ -44,7 +44,7 @@ class Property(cssutils.util.Base):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, name=None, value=None, priority=u'',
|
def __init__(self, name=None, value=None, priority=u'',
|
||||||
_mediaQuery=False, parent=None, parentStyle=None):
|
_mediaQuery=False, parent=None):
|
||||||
"""
|
"""
|
||||||
:param name:
|
:param name:
|
||||||
a property name string (will be normalized)
|
a property name string (will be normalized)
|
||||||
@ -58,8 +58,6 @@ class Property(cssutils.util.Base):
|
|||||||
:param parent:
|
:param parent:
|
||||||
the parent object, normally a
|
the parent object, normally a
|
||||||
:class:`cssutils.css.CSSStyleDeclaration`
|
:class:`cssutils.css.CSSStyleDeclaration`
|
||||||
:param parentStyle:
|
|
||||||
DEPRECATED: Use ``parent`` instead
|
|
||||||
"""
|
"""
|
||||||
super(Property, self).__init__()
|
super(Property, self).__init__()
|
||||||
self.seqs = [[], None, []]
|
self.seqs = [[], None, []]
|
||||||
@ -76,7 +74,7 @@ class Property(cssutils.util.Base):
|
|||||||
if value:
|
if value:
|
||||||
self.cssValue = value
|
self.cssValue = value
|
||||||
else:
|
else:
|
||||||
self.seqs[1] = CSSValue()
|
self.seqs[1] = CSSValue(parent=self)
|
||||||
|
|
||||||
self._priority = u''
|
self._priority = u''
|
||||||
self._literalpriority = u''
|
self._literalpriority = u''
|
||||||
@ -246,31 +244,28 @@ class Property(cssutils.util.Base):
|
|||||||
type of values than the values allowed by the CSS property.
|
type of values than the values allowed by the CSS property.
|
||||||
"""
|
"""
|
||||||
if self._mediaQuery and not cssText:
|
if self._mediaQuery and not cssText:
|
||||||
self.seqs[1] = CSSValue()
|
self.seqs[1] = CSSValue(parent=self)
|
||||||
else:
|
else:
|
||||||
if not self.seqs[1]:
|
#if not self.seqs[1]:
|
||||||
self.seqs[1] = CSSValue()
|
# self.seqs[1] = CSSValue(parent=self)
|
||||||
|
|
||||||
cssvalue = self.seqs[1]
|
self.seqs[1] = CSSValue(parent=self)
|
||||||
cssvalue.cssText = cssText
|
|
||||||
if cssvalue.wellformed: #cssvalue._value and
|
self.seqs[1].cssText = cssText
|
||||||
self.seqs[1] = cssvalue
|
self.wellformed = self.wellformed and self.seqs[1].wellformed
|
||||||
self.wellformed = self.wellformed and cssvalue.wellformed
|
# self.valid = self.valid and self.cssValue.valid
|
||||||
|
|
||||||
cssValue = property(_getCSSValue, _setCSSValue,
|
cssValue = property(_getCSSValue, _setCSSValue,
|
||||||
doc="(cssutils) CSSValue object of this property")
|
doc="(cssutils) CSSValue object of this property")
|
||||||
|
|
||||||
|
|
||||||
def _getValue(self):
|
def _getValue(self):
|
||||||
if self.cssValue:
|
if self.cssValue:
|
||||||
return self.cssValue.cssText # _value # [0]
|
return self.cssValue.cssText
|
||||||
else:
|
else:
|
||||||
return u''
|
return u''
|
||||||
|
|
||||||
def _setValue(self, value):
|
def _setValue(self, value):
|
||||||
self.cssValue.cssText = value
|
self._setCSSValue(value)
|
||||||
# self.valid = self.valid and self.cssValue.valid
|
|
||||||
self.wellformed = self.wellformed and self.cssValue.wellformed
|
|
||||||
|
|
||||||
value = property(_getValue, _setValue,
|
value = property(_getValue, _setValue,
|
||||||
doc="The textual value of this Properties cssValue.")
|
doc="The textual value of this Properties cssValue.")
|
||||||
@ -483,12 +478,3 @@ class Property(cssutils.util.Base):
|
|||||||
|
|
||||||
valid = property(validate, doc="Check if value of this property is valid "
|
valid = property(validate, doc="Check if value of this property is valid "
|
||||||
"in the properties context.")
|
"in the properties context.")
|
||||||
|
|
||||||
|
|
||||||
@Deprecated('Use ``parent`` attribute instead.')
|
|
||||||
def _getParentStyle(self):
|
|
||||||
return self._parent
|
|
||||||
|
|
||||||
parentStyle = property(_getParentStyle, _setParent,
|
|
||||||
doc="DEPRECATED: Use ``parent`` instead")
|
|
||||||
|
|
||||||
|
@ -7,9 +7,10 @@ TODO
|
|||||||
"""
|
"""
|
||||||
__all__ = ['Selector']
|
__all__ = ['Selector']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: selector.py 1741 2009-05-09 18:20:20Z cthedot $'
|
__version__ = '$Id: selector.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from cssutils.util import _SimpleNamespaces
|
from cssutils.util import _SimpleNamespaces
|
||||||
|
from cssutils.helper import Deprecated
|
||||||
import cssutils
|
import cssutils
|
||||||
import xml.dom
|
import xml.dom
|
||||||
|
|
||||||
@ -98,13 +99,13 @@ class Selector(cssutils.util.Base2):
|
|||||||
;
|
;
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, selectorText=None, parentList=None,
|
def __init__(self, selectorText=None, parent=None,
|
||||||
readonly=False):
|
readonly=False):
|
||||||
"""
|
"""
|
||||||
:Parameters:
|
:Parameters:
|
||||||
selectorText
|
selectorText
|
||||||
initial value of this selector
|
initial value of this selector
|
||||||
parentList
|
parent
|
||||||
a SelectorList
|
a SelectorList
|
||||||
readonly
|
readonly
|
||||||
default to False
|
default to False
|
||||||
@ -113,7 +114,7 @@ class Selector(cssutils.util.Base2):
|
|||||||
|
|
||||||
self.__namespaces = _SimpleNamespaces(log=self._log)
|
self.__namespaces = _SimpleNamespaces(log=self._log)
|
||||||
self._element = None
|
self._element = None
|
||||||
self._parent = parentList
|
self._parent = parent
|
||||||
self._specificity = (0, 0, 0, 0)
|
self._specificity = (0, 0, 0, 0)
|
||||||
|
|
||||||
if selectorText:
|
if selectorText:
|
||||||
@ -169,10 +170,10 @@ class Selector(cssutils.util.Base2):
|
|||||||
element = property(lambda self: self._element,
|
element = property(lambda self: self._element,
|
||||||
doc=u"Effective element target of this selector.")
|
doc=u"Effective element target of this selector.")
|
||||||
|
|
||||||
parentList = property(lambda self: self._parent,
|
parent = property(lambda self: self._parent,
|
||||||
doc="(DOM) The SelectorList that contains this Selector or\
|
doc="(DOM) The SelectorList that contains this Selector or\
|
||||||
None if this Selector is not attached to a SelectorList.")
|
None if this Selector is not attached to a SelectorList.")
|
||||||
|
|
||||||
def _getSelectorText(self):
|
def _getSelectorText(self):
|
||||||
"""Return serialized format."""
|
"""Return serialized format."""
|
||||||
return cssutils.ser.do_css_Selector(self)
|
return cssutils.ser.do_css_Selector(self)
|
||||||
@ -201,7 +202,7 @@ class Selector(cssutils.util.Base2):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# uses parent stylesheets namespaces if available, otherwise given ones
|
# uses parent stylesheets namespaces if available, otherwise given ones
|
||||||
namespaces = self.parentList.parentRule.parentStyleSheet.namespaces
|
namespaces = self.parent.parentRule.parentStyleSheet.namespaces
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
tokenizer = self._tokenize2(selectorText)
|
tokenizer = self._tokenize2(selectorText)
|
||||||
@ -787,3 +788,11 @@ class Selector(cssutils.util.Base2):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
wellformed = property(lambda self: bool(len(self.seq)))
|
wellformed = property(lambda self: bool(len(self.seq)))
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated('Use property parent instead')
|
||||||
|
def _getParentList(self):
|
||||||
|
return self.parent
|
||||||
|
|
||||||
|
parentList = property(_getParentList,
|
||||||
|
doc="DEPRECATED, see property parent instead")
|
||||||
|
@ -17,7 +17,7 @@ TODO
|
|||||||
"""
|
"""
|
||||||
__all__ = ['SelectorList']
|
__all__ = ['SelectorList']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: selectorlist.py 1638 2009-01-13 20:39:33Z cthedot $'
|
__version__ = '$Id: selectorlist.py 1868 2009-10-17 19:36:54Z cthedot $'
|
||||||
|
|
||||||
from selector import Selector
|
from selector import Selector
|
||||||
import cssutils
|
import cssutils
|
||||||
@ -73,7 +73,7 @@ class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
|||||||
self._checkReadonly()
|
self._checkReadonly()
|
||||||
if not isinstance(newSelector, Selector):
|
if not isinstance(newSelector, Selector):
|
||||||
newSelector = Selector((newSelector, namespaces),
|
newSelector = Selector((newSelector, namespaces),
|
||||||
parentList=self)
|
parent=self)
|
||||||
if newSelector.wellformed:
|
if newSelector.wellformed:
|
||||||
newSelector._parent = self # maybe set twice but must be!
|
newSelector._parent = self # maybe set twice but must be!
|
||||||
return newSelector
|
return newSelector
|
||||||
@ -88,6 +88,12 @@ class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
|||||||
namespaces.update(selector._namespaces)
|
namespaces.update(selector._namespaces)
|
||||||
return namespaces
|
return namespaces
|
||||||
|
|
||||||
|
def _absorb(self, other):
|
||||||
|
"""Replace all own data with data from other object."""
|
||||||
|
self._parentRule = other._parentRule
|
||||||
|
self.seq[:] = other.seq[:]
|
||||||
|
self._readonly = other._readonly
|
||||||
|
|
||||||
def _getUsedUris(self):
|
def _getUsedUris(self):
|
||||||
"Used by CSSStyleSheet to check if @namespace rules are needed"
|
"Used by CSSStyleSheet to check if @namespace rules are needed"
|
||||||
uris = set()
|
uris = set()
|
||||||
@ -191,7 +197,7 @@ class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
|||||||
expected = None
|
expected = None
|
||||||
|
|
||||||
selector = Selector((selectortokens, namespaces),
|
selector = Selector((selectortokens, namespaces),
|
||||||
parentList=self)
|
parent=self)
|
||||||
if selector.wellformed:
|
if selector.wellformed:
|
||||||
newseq.append(selector)
|
newseq.append(selector)
|
||||||
else:
|
else:
|
||||||
@ -212,8 +218,6 @@ class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
|
|||||||
self._valuestr(selectorText))
|
self._valuestr(selectorText))
|
||||||
if wellformed:
|
if wellformed:
|
||||||
self.seq = newseq
|
self.seq = newseq
|
||||||
# for selector in newseq:
|
|
||||||
# self.appendSelector(selector)
|
|
||||||
|
|
||||||
selectorText = property(_getSelectorText, _setSelectorText,
|
selectorText = property(_getSelectorText, _setSelectorText,
|
||||||
doc="""(cssutils) The textual representation of the selector for
|
doc="""(cssutils) The textual representation of the selector for
|
||||||
|
@ -12,7 +12,7 @@ open issues
|
|||||||
"""
|
"""
|
||||||
__all__ = ['CSSProductions', 'MACROS', 'PRODUCTIONS']
|
__all__ = ['CSSProductions', 'MACROS', 'PRODUCTIONS']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: cssproductions.py 1835 2009-08-02 16:47:27Z cthedot $'
|
__version__ = '$Id: cssproductions.py 1855 2009-10-07 17:03:19Z cthedot $'
|
||||||
|
|
||||||
# a complete list of css3 macros
|
# a complete list of css3 macros
|
||||||
MACROS = {
|
MACROS = {
|
||||||
@ -41,6 +41,7 @@ MACROS = {
|
|||||||
'nl': r'\n|\r\n|\r|\f',
|
'nl': r'\n|\r\n|\r|\f',
|
||||||
|
|
||||||
'A': r'A|a|\\0{0,4}(?:41|61)(?:\r\n|[ \t\r\n\f])?',
|
'A': r'A|a|\\0{0,4}(?:41|61)(?:\r\n|[ \t\r\n\f])?',
|
||||||
|
'B': r'B|b|\\0{0,4}(?:42|62)(?:\r\n|[ \t\r\n\f])?',
|
||||||
'C': r'C|c|\\0{0,4}(?:43|63)(?:\r\n|[ \t\r\n\f])?',
|
'C': r'C|c|\\0{0,4}(?:43|63)(?:\r\n|[ \t\r\n\f])?',
|
||||||
'D': r'D|d|\\0{0,4}(?:44|64)(?:\r\n|[ \t\r\n\f])?',
|
'D': r'D|d|\\0{0,4}(?:44|64)(?:\r\n|[ \t\r\n\f])?',
|
||||||
'E': r'E|e|\\0{0,4}(?:45|65)(?:\r\n|[ \t\r\n\f])?',
|
'E': r'E|e|\\0{0,4}(?:45|65)(?:\r\n|[ \t\r\n\f])?',
|
||||||
@ -58,6 +59,7 @@ MACROS = {
|
|||||||
'S': r'S|s|\\0{0,4}(?:53|73)(?:\r\n|[ \t\r\n\f])?|\\S|\\s',
|
'S': r'S|s|\\0{0,4}(?:53|73)(?:\r\n|[ \t\r\n\f])?|\\S|\\s',
|
||||||
'T': r'T|t|\\0{0,4}(?:54|74)(?:\r\n|[ \t\r\n\f])?|\\T|\\t',
|
'T': r'T|t|\\0{0,4}(?:54|74)(?:\r\n|[ \t\r\n\f])?|\\T|\\t',
|
||||||
'U': r'U|u|\\0{0,4}(?:55|75)(?:\r\n|[ \t\r\n\f])?|\\U|\\u',
|
'U': r'U|u|\\0{0,4}(?:55|75)(?:\r\n|[ \t\r\n\f])?|\\U|\\u',
|
||||||
|
'V': r'V|v|\\0{0,4}(?:56|76)(?:\r\n|[ \t\r\n\f])?|\\V|\\v',
|
||||||
'X': r'X|x|\\0{0,4}(?:58|78)(?:\r\n|[ \t\r\n\f])?|\\X|\\x',
|
'X': r'X|x|\\0{0,4}(?:58|78)(?:\r\n|[ \t\r\n\f])?|\\X|\\x',
|
||||||
'Z': r'Z|z|\\0{0,4}(?:5a|7a)(?:\r\n|[ \t\r\n\f])?|\\Z|\\z',
|
'Z': r'Z|z|\\0{0,4}(?:5a|7a)(?:\r\n|[ \t\r\n\f])?|\\Z|\\z',
|
||||||
}
|
}
|
||||||
@ -107,6 +109,7 @@ class CSSProductions(object):
|
|||||||
IMPORT_SYM = 'IMPORT_SYM'
|
IMPORT_SYM = 'IMPORT_SYM'
|
||||||
NAMESPACE_SYM = 'NAMESPACE_SYM'
|
NAMESPACE_SYM = 'NAMESPACE_SYM'
|
||||||
PAGE_SYM = 'PAGE_SYM'
|
PAGE_SYM = 'PAGE_SYM'
|
||||||
|
VARIABLES_SYM = 'VARIABLES_SYM'
|
||||||
|
|
||||||
for i, t in enumerate(PRODUCTIONS):
|
for i, t in enumerate(PRODUCTIONS):
|
||||||
setattr(CSSProductions, t[0].replace('-', '_'), t[0])
|
setattr(CSSProductions, t[0].replace('-', '_'), t[0])
|
||||||
|
@ -60,8 +60,8 @@ def pushtoken(token, tokens):
|
|||||||
``tokens``"""
|
``tokens``"""
|
||||||
# TODO: may use itertools.chain?
|
# TODO: may use itertools.chain?
|
||||||
yield token
|
yield token
|
||||||
for x in tokens:
|
for t in tokens:
|
||||||
yield x
|
yield t
|
||||||
|
|
||||||
def string(value):
|
def string(value):
|
||||||
"""
|
"""
|
||||||
|
@ -219,22 +219,31 @@ class Prod(object):
|
|||||||
"""Single Prod in Sequence or Choice."""
|
"""Single Prod in Sequence or Choice."""
|
||||||
def __init__(self, name, match, optional=False,
|
def __init__(self, name, match, optional=False,
|
||||||
toSeq=None, toStore=None,
|
toSeq=None, toStore=None,
|
||||||
stop=False, nextSor=False, mayEnd=False):
|
stop=False, stopAndKeep=False,
|
||||||
|
nextSor=False, mayEnd=False):
|
||||||
"""
|
"""
|
||||||
name
|
name
|
||||||
name used for error reporting
|
name used for error reporting
|
||||||
match callback
|
match callback
|
||||||
function called with parameters tokentype and tokenvalue
|
function called with parameters tokentype and tokenvalue
|
||||||
returning True, False or raising ParseError
|
returning True, False or raising ParseError
|
||||||
toSeq callback (optional)
|
toSeq callback (optional) or False
|
||||||
calling toSeq(token, tokens) returns (type_, val) == (token[0], token[1])
|
calling toSeq(token, tokens) returns (type_, val) == (token[0], token[1])
|
||||||
to be appended to seq else simply unaltered (type_, val)
|
to be appended to seq else simply unaltered (type_, val)
|
||||||
|
|
||||||
|
if False nothing is added
|
||||||
|
|
||||||
toStore (optional)
|
toStore (optional)
|
||||||
key to save util.Item to store or callback(store, util.Item)
|
key to save util.Item to store or callback(store, util.Item)
|
||||||
optional = False
|
optional = False
|
||||||
wether Prod is optional or not
|
wether Prod is optional or not
|
||||||
stop = False
|
stop = False
|
||||||
if True stop parsing of tokens here
|
if True stop parsing of tokens here
|
||||||
|
stopAndKeep
|
||||||
|
if True stop parsing of tokens here but return stopping
|
||||||
|
token in unused tokens
|
||||||
|
nextSor=False
|
||||||
|
next is S or other like , or / (CSSValue)
|
||||||
mayEnd = False
|
mayEnd = False
|
||||||
no token must follow even defined by Sequence.
|
no token must follow even defined by Sequence.
|
||||||
Used for operator ',/ ' currently only
|
Used for operator ',/ ' currently only
|
||||||
@ -243,6 +252,7 @@ class Prod(object):
|
|||||||
self.match = match
|
self.match = match
|
||||||
self.optional = optional
|
self.optional = optional
|
||||||
self.stop = stop
|
self.stop = stop
|
||||||
|
self.stopAndKeep = stopAndKeep
|
||||||
self.nextSor = nextSor
|
self.nextSor = nextSor
|
||||||
self.mayEnd = mayEnd
|
self.mayEnd = mayEnd
|
||||||
|
|
||||||
@ -256,7 +266,7 @@ class Prod(object):
|
|||||||
store[key] = item
|
store[key] = item
|
||||||
return toStore
|
return toStore
|
||||||
|
|
||||||
if toSeq:
|
if toSeq or toSeq is False:
|
||||||
# called: seq.append(toSeq(value))
|
# called: seq.append(toSeq(value))
|
||||||
self.toSeq = toSeq
|
self.toSeq = toSeq
|
||||||
else:
|
else:
|
||||||
@ -288,22 +298,27 @@ class Prod(object):
|
|||||||
self.__class__.__name__, self._name, id(self))
|
self.__class__.__name__, self._name, id(self))
|
||||||
|
|
||||||
|
|
||||||
|
# global tokenizer as there is only one!
|
||||||
|
tokenizer = cssutils.tokenize2.Tokenizer()
|
||||||
|
|
||||||
class ProdParser(object):
|
class ProdParser(object):
|
||||||
"""Productions parser."""
|
"""Productions parser."""
|
||||||
def __init__(self):
|
def __init__(self, clear=True):
|
||||||
self.types = cssutils.cssproductions.CSSProductions
|
self.types = cssutils.cssproductions.CSSProductions
|
||||||
self._log = cssutils.log
|
self._log = cssutils.log
|
||||||
self._tokenizer = cssutils.tokenize2.Tokenizer()
|
if clear:
|
||||||
|
tokenizer.clear()
|
||||||
|
|
||||||
def _texttotokens(self, text):
|
def _texttotokens(self, text):
|
||||||
"""Build a generator which is the only thing that is parsed!
|
"""Build a generator which is the only thing that is parsed!
|
||||||
old classes may use lists etc
|
old classes may use lists etc
|
||||||
"""
|
"""
|
||||||
if isinstance(text, basestring):
|
if isinstance(text, basestring):
|
||||||
# to tokenize strip space
|
# DEFAULT, to tokenize strip space
|
||||||
tokens = self._tokenizer.tokenize(text.strip())
|
return tokenizer.tokenize(text.strip())
|
||||||
|
|
||||||
elif isinstance(text, tuple):
|
elif isinstance(text, tuple):
|
||||||
# (token, tokens) or a single token
|
# OLD: (token, tokens) or a single token
|
||||||
if len(text) == 2:
|
if len(text) == 2:
|
||||||
# (token, tokens)
|
# (token, tokens)
|
||||||
def gen(token, tokens):
|
def gen(token, tokens):
|
||||||
@ -312,19 +327,19 @@ class ProdParser(object):
|
|||||||
for t in tokens:
|
for t in tokens:
|
||||||
yield t
|
yield t
|
||||||
|
|
||||||
tokens = (t for t in gen(*text))
|
return (t for t in gen(*text))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# single token
|
# single token
|
||||||
tokens = (t for t in [text])
|
return (t for t in [text])
|
||||||
|
|
||||||
elif isinstance(text, list):
|
elif isinstance(text, list):
|
||||||
# generator from list
|
# OLD: generator from list
|
||||||
tokens = (t for t in text)
|
return (t for t in text)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# already tokenized, assume generator
|
# DEFAULT, already tokenized, assume generator
|
||||||
tokens = text
|
return text
|
||||||
|
|
||||||
return tokens
|
|
||||||
|
|
||||||
def _SorTokens(self, tokens, until=',/'):
|
def _SorTokens(self, tokens, until=',/'):
|
||||||
"""New tokens generator which has S tokens removed,
|
"""New tokens generator which has S tokens removed,
|
||||||
@ -359,7 +374,7 @@ class ProdParser(object):
|
|||||||
|
|
||||||
return (token for token in removedS(tokens))
|
return (token for token in removedS(tokens))
|
||||||
|
|
||||||
def parse(self, text, name, productions, store=None):
|
def parse(self, text, name, productions, keepS=False, store=None):
|
||||||
"""
|
"""
|
||||||
text (or token generator)
|
text (or token generator)
|
||||||
to parse, will be tokenized if not a generator yet
|
to parse, will be tokenized if not a generator yet
|
||||||
@ -374,6 +389,8 @@ class ProdParser(object):
|
|||||||
used for logging
|
used for logging
|
||||||
productions
|
productions
|
||||||
used to parse tokens
|
used to parse tokens
|
||||||
|
keepS
|
||||||
|
if WS should be added to Seq or just be ignored
|
||||||
store UPDATED
|
store UPDATED
|
||||||
If a Prod defines ``toStore`` the key defined there
|
If a Prod defines ``toStore`` the key defined there
|
||||||
is a key in store to be set or if store[key] is a list
|
is a key in store to be set or if store[key] is a list
|
||||||
@ -389,7 +406,7 @@ class ProdParser(object):
|
|||||||
:store: filled keys defined by Prod.toStore
|
:store: filled keys defined by Prod.toStore
|
||||||
:unusedtokens: token generator containing tokens not used yet
|
:unusedtokens: token generator containing tokens not used yet
|
||||||
"""
|
"""
|
||||||
tokens = self._texttotokens(text)
|
tokens = self._texttotokens(text)
|
||||||
if not tokens:
|
if not tokens:
|
||||||
self._log.error(u'No content to parse.')
|
self._log.error(u'No content to parse.')
|
||||||
# TODO: return???
|
# TODO: return???
|
||||||
@ -411,7 +428,7 @@ class ProdParser(object):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
type_, val, line, col = token
|
type_, val, line, col = token
|
||||||
|
|
||||||
# default productions
|
# default productions
|
||||||
if type_ == self.types.COMMENT:
|
if type_ == self.types.COMMENT:
|
||||||
# always append COMMENT
|
# always append COMMENT
|
||||||
@ -419,10 +436,10 @@ class ProdParser(object):
|
|||||||
cssutils.css.CSSComment, line, col)
|
cssutils.css.CSSComment, line, col)
|
||||||
elif defaultS and type_ == self.types.S:
|
elif defaultS and type_ == self.types.S:
|
||||||
# append S (but ignore starting ones)
|
# append S (but ignore starting ones)
|
||||||
if started:
|
if not keepS or not started:
|
||||||
seq.append(val, type_, line, col)
|
|
||||||
else:
|
|
||||||
continue
|
continue
|
||||||
|
else:
|
||||||
|
seq.append(val, type_, line, col)
|
||||||
# elif type_ == self.types.ATKEYWORD:
|
# elif type_ == self.types.ATKEYWORD:
|
||||||
# # @rule
|
# # @rule
|
||||||
# r = cssutils.css.CSSUnknownRule(cssText=val)
|
# r = cssutils.css.CSSUnknownRule(cssText=val)
|
||||||
@ -465,15 +482,23 @@ class ProdParser(object):
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# process prod
|
# process prod
|
||||||
if prod.toSeq:
|
if prod.toSeq and not prod.stopAndKeep:
|
||||||
type_, val = prod.toSeq(token, tokens)
|
type_, val = prod.toSeq(token, tokens)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
seq.append(val, type_, line, col)
|
seq.append(val, type_, line, col)
|
||||||
if prod.toStore:
|
if prod.toStore:
|
||||||
prod.toStore(store, seq[-1])
|
prod.toStore(store, seq[-1])
|
||||||
|
|
||||||
if prod.stop: # EOF?
|
if prod.stop: # EOF?
|
||||||
# stop here and ignore following tokens
|
# stop here and ignore following tokens
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if prod.stopAndKeep: # e.g. ;
|
||||||
|
# stop here and ignore following tokens
|
||||||
|
# but keep this token for next run
|
||||||
|
tokenizer.push(token)
|
||||||
|
break
|
||||||
|
|
||||||
if prod.nextSor:
|
if prod.nextSor:
|
||||||
# following is S or other token (e.g. ",")?
|
# following is S or other token (e.g. ",")?
|
||||||
# remove S if
|
# remove S if
|
||||||
@ -533,12 +558,12 @@ class PreDef(object):
|
|||||||
types = cssutils.cssproductions.CSSProductions
|
types = cssutils.cssproductions.CSSProductions
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def char(name='char', char=u',', toSeq=None, stop=False,
|
def char(name='char', char=u',', toSeq=None,
|
||||||
nextSor=False):
|
stop=False, stopAndKeep=False,
|
||||||
|
optional=True, nextSor=False):
|
||||||
"any CHAR"
|
"any CHAR"
|
||||||
return Prod(name=name, match=lambda t, v: v == char,
|
return Prod(name=name, match=lambda t, v: v == char, toSeq=toSeq,
|
||||||
toSeq=toSeq,
|
stop=stop, stopAndKeep=stopAndKeep, optional=optional,
|
||||||
stop=stop,
|
|
||||||
nextSor=nextSor)
|
nextSor=nextSor)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -566,9 +591,10 @@ class PreDef(object):
|
|||||||
stop=stop)
|
stop=stop)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def ident(nextSor=False):
|
def ident(toStore=None, nextSor=False):
|
||||||
return Prod(name=u'ident',
|
return Prod(name=u'ident',
|
||||||
match=lambda t, v: t == PreDef.types.IDENT,
|
match=lambda t, v: t == PreDef.types.IDENT,
|
||||||
|
toStore=toStore,
|
||||||
nextSor=nextSor)
|
nextSor=nextSor)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -592,9 +618,11 @@ class PreDef(object):
|
|||||||
nextSor=nextSor)
|
nextSor=nextSor)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def S():
|
def S(toSeq=None, optional=False):
|
||||||
return Prod(name=u'whitespace',
|
return Prod(name=u'whitespace',
|
||||||
match=lambda t, v: t == PreDef.types.S,
|
match=lambda t, v: t == PreDef.types.S,
|
||||||
|
toSeq=toSeq,
|
||||||
|
optional=optional,
|
||||||
mayEnd=True)
|
mayEnd=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -628,3 +656,11 @@ class PreDef(object):
|
|||||||
toSeq=lambda t, tokens: (t[0], t[1].lower()),
|
toSeq=lambda t, tokens: (t[0], t[1].lower()),
|
||||||
nextSor=nextSor
|
nextSor=nextSor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def variable(toSeq=None, nextSor=False):
|
||||||
|
return Prod(name=u'variable',
|
||||||
|
match=lambda t, v: u'var(' == cssutils.helper.normalize(v),
|
||||||
|
toSeq=toSeq,
|
||||||
|
nextSor=nextSor)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class Profiles(object):
|
|||||||
'int': r'[-]?\d+',
|
'int': r'[-]?\d+',
|
||||||
'nmchar': r'[\w-]|{nonascii}|{escape}',
|
'nmchar': r'[\w-]|{nonascii}|{escape}',
|
||||||
'num': r'[-]?\d+|[-]?\d*\.\d+',
|
'num': r'[-]?\d+|[-]?\d*\.\d+',
|
||||||
'positivenum': r'\d+|[-]?\d*\.\d+',
|
'positivenum': r'\d+|\d*\.\d+',
|
||||||
'number': r'{num}',
|
'number': r'{num}',
|
||||||
'string': r'{string1}|{string2}',
|
'string': r'{string1}|{string2}',
|
||||||
'string1': r'"(\\\"|[^\"])*"',
|
'string1': r'"(\\\"|[^\"])*"',
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"""cssutils serializer"""
|
"""cssutils serializer"""
|
||||||
__all__ = ['CSSSerializer', 'Preferences']
|
__all__ = ['CSSSerializer', 'Preferences']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: serialize.py 1741 2009-05-09 18:20:20Z cthedot $'
|
__version__ = '$Id: serialize.py 1872 2009-10-17 21:00:40Z cthedot $'
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
import cssutils
|
import cssutils
|
||||||
@ -384,6 +384,32 @@ class CSSSerializer(object):
|
|||||||
else:
|
else:
|
||||||
return u''
|
return u''
|
||||||
|
|
||||||
|
def do_CSSVariablesRule(self, rule):
|
||||||
|
"""
|
||||||
|
serializes CSSVariablesRule
|
||||||
|
|
||||||
|
media
|
||||||
|
TODO
|
||||||
|
variables
|
||||||
|
CSSStyleDeclaration
|
||||||
|
|
||||||
|
+ CSSComments
|
||||||
|
"""
|
||||||
|
variablesText = rule.variables.cssText
|
||||||
|
|
||||||
|
if variablesText and rule.wellformed:
|
||||||
|
out = Out(self)
|
||||||
|
out.append(self._atkeyword(rule, u'@variables'))
|
||||||
|
for item in rule.seq:
|
||||||
|
# assume comments {
|
||||||
|
out.append(item.value, item.type)
|
||||||
|
out.append(u'{')
|
||||||
|
out.append(u'%s%s}' % (variablesText, self.prefs.lineSeparator),
|
||||||
|
indent=1)
|
||||||
|
return out.value()
|
||||||
|
else:
|
||||||
|
return u''
|
||||||
|
|
||||||
def do_CSSFontFaceRule(self, rule):
|
def do_CSSFontFaceRule(self, rule):
|
||||||
"""
|
"""
|
||||||
serializes CSSFontFaceRule
|
serializes CSSFontFaceRule
|
||||||
@ -712,11 +738,40 @@ class CSSSerializer(object):
|
|||||||
else:
|
else:
|
||||||
return u''
|
return u''
|
||||||
|
|
||||||
|
def do_css_CSSVariablesDeclaration(self, variables):
|
||||||
|
"""Variables of CSSVariableRule."""
|
||||||
|
if len(variables.seq) > 0:
|
||||||
|
out = Out(self)
|
||||||
|
|
||||||
|
lastitem = len(variables.seq) - 1
|
||||||
|
for i, item in enumerate(variables.seq):
|
||||||
|
type_, val = item.type, item.value
|
||||||
|
if u'var' == type_:
|
||||||
|
name, cssvalue = val
|
||||||
|
out.append(name)
|
||||||
|
out.append(u':')
|
||||||
|
out.append(cssvalue.cssText)
|
||||||
|
if i < lastitem or not self.prefs.omitLastSemicolon:
|
||||||
|
out.append(u';')
|
||||||
|
|
||||||
|
elif isinstance(val, cssutils.css.CSSComment):
|
||||||
|
# CSSComment
|
||||||
|
out.append(val, 'COMMENT')
|
||||||
|
out.append(self.prefs.lineSeparator)
|
||||||
|
else:
|
||||||
|
out.append(val.cssText, type_)
|
||||||
|
out.append(self.prefs.lineSeparator)
|
||||||
|
|
||||||
|
return out.value().strip()
|
||||||
|
|
||||||
|
else:
|
||||||
|
return u''
|
||||||
|
|
||||||
def do_css_CSSStyleDeclaration(self, style, separator=None):
|
def do_css_CSSStyleDeclaration(self, style, separator=None):
|
||||||
"""
|
"""
|
||||||
Style declaration of CSSStyleRule
|
Style declaration of CSSStyleRule
|
||||||
"""
|
"""
|
||||||
# # TODO: use Out()
|
# TODO: use Out()
|
||||||
|
|
||||||
# may be comments only
|
# may be comments only
|
||||||
if len(style.seq) > 0:
|
if len(style.seq) > 0:
|
||||||
@ -867,7 +922,19 @@ class CSSSerializer(object):
|
|||||||
out.append(val, type_)
|
out.append(val, type_)
|
||||||
|
|
||||||
return out.value()
|
return out.value()
|
||||||
|
|
||||||
|
def do_css_CSSVariable(self, variable):
|
||||||
|
"""Serializes a CSSVariable"""
|
||||||
|
if not variable:
|
||||||
|
return u''
|
||||||
|
else:
|
||||||
|
out = Out(self)
|
||||||
|
for item in variable.seq:
|
||||||
|
type_, val = item.type, item.value
|
||||||
|
out.append(val, type_)
|
||||||
|
|
||||||
|
return out.value()
|
||||||
|
|
||||||
def do_css_RGBColor(self, cssvalue):
|
def do_css_RGBColor(self, cssvalue):
|
||||||
"""Serialize a RGBColor value"""
|
"""Serialize a RGBColor value"""
|
||||||
if not cssvalue:
|
if not cssvalue:
|
||||||
@ -877,17 +944,6 @@ class CSSSerializer(object):
|
|||||||
unary = None
|
unary = None
|
||||||
for item in cssvalue.seq:
|
for item in cssvalue.seq:
|
||||||
type_, val = item.type, item.value
|
type_, val = item.type, item.value
|
||||||
|
|
||||||
# # prepare
|
|
||||||
# if 'CHAR' == type_ and val in u'+-':
|
|
||||||
# # save - for next round
|
|
||||||
# if u'-' == val:
|
|
||||||
# # omit +
|
|
||||||
# unary = val
|
|
||||||
# continue
|
|
||||||
# elif unary:
|
|
||||||
# val = unary + val.cssText
|
|
||||||
# unary = None
|
|
||||||
|
|
||||||
out.append(val, type_)
|
out.append(val, type_)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ TODO:
|
|||||||
"""
|
"""
|
||||||
__all__ = ['MediaList']
|
__all__ = ['MediaList']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: medialist.py 1605 2009-01-03 18:27:32Z cthedot $'
|
__version__ = '$Id: medialist.py 1871 2009-10-17 19:57:37Z cthedot $'
|
||||||
|
|
||||||
from cssutils.css import csscomment
|
from cssutils.css import csscomment
|
||||||
from mediaquery import MediaQuery
|
from mediaquery import MediaQuery
|
||||||
@ -56,6 +56,13 @@ class MediaList(cssutils.util.Base, cssutils.util.ListSeq):
|
|||||||
return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (
|
return "<cssutils.stylesheets.%s object mediaText=%r at 0x%x>" % (
|
||||||
self.__class__.__name__, self.mediaText, id(self))
|
self.__class__.__name__, self.mediaText, id(self))
|
||||||
|
|
||||||
|
def _absorb(self, other):
|
||||||
|
"""Replace all own data with data from other object."""
|
||||||
|
#self._parentRule = other._parentRule
|
||||||
|
self.seq[:] = other.seq[:]
|
||||||
|
self._readonly = other._readonly
|
||||||
|
|
||||||
|
|
||||||
length = property(lambda self: len(self),
|
length = property(lambda self: len(self),
|
||||||
doc="The number of media in the list (DOM readonly).")
|
doc="The number of media in the list (DOM readonly).")
|
||||||
|
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
"""
|
"""
|
||||||
__all__ = ['Tokenizer', 'CSSProductions']
|
__all__ = ['Tokenizer', 'CSSProductions']
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: tokenize2.py 1834 2009-08-02 12:20:21Z cthedot $'
|
__version__ = '$Id: tokenize2.py 1865 2009-10-11 15:23:11Z cthedot $'
|
||||||
|
|
||||||
from cssproductions import *
|
from cssproductions import *
|
||||||
from helper import normalize
|
from helper import normalize
|
||||||
|
import itertools
|
||||||
import re
|
import re
|
||||||
|
|
||||||
class Tokenizer(object):
|
class Tokenizer(object):
|
||||||
@ -20,7 +21,8 @@ class Tokenizer(object):
|
|||||||
u'@import': CSSProductions.IMPORT_SYM,
|
u'@import': CSSProductions.IMPORT_SYM,
|
||||||
u'@media': CSSProductions.MEDIA_SYM,
|
u'@media': CSSProductions.MEDIA_SYM,
|
||||||
u'@namespace': CSSProductions.NAMESPACE_SYM,
|
u'@namespace': CSSProductions.NAMESPACE_SYM,
|
||||||
u'@page': CSSProductions.PAGE_SYM
|
u'@page': CSSProductions.PAGE_SYM,
|
||||||
|
u'@variables': CSSProductions.VARIABLES_SYM
|
||||||
}
|
}
|
||||||
_linesep = u'\n'
|
_linesep = u'\n'
|
||||||
unicodesub = re.compile(r'\\[0-9a-fA-F]{1,6}(?:\r\n|[\t|\r|\n|\f|\x20])?').sub
|
unicodesub = re.compile(r'\\[0-9a-fA-F]{1,6}(?:\r\n|[\t|\r|\n|\f|\x20])?').sub
|
||||||
@ -40,6 +42,8 @@ class Tokenizer(object):
|
|||||||
productions))
|
productions))
|
||||||
self.commentmatcher = [x[1] for x in self.tokenmatches if x[0] == 'COMMENT'][0]
|
self.commentmatcher = [x[1] for x in self.tokenmatches if x[0] == 'COMMENT'][0]
|
||||||
self.urimatcher = [x[1] for x in self.tokenmatches if x[0] == 'URI'][0]
|
self.urimatcher = [x[1] for x in self.tokenmatches if x[0] == 'URI'][0]
|
||||||
|
|
||||||
|
self._pushed = []
|
||||||
|
|
||||||
def _expand_macros(self, macros, productions):
|
def _expand_macros(self, macros, productions):
|
||||||
"""returns macro expanded productions, order of productions is kept"""
|
"""returns macro expanded productions, order of productions is kept"""
|
||||||
@ -60,6 +64,13 @@ class Tokenizer(object):
|
|||||||
compiled.append((key, re.compile('^(?:%s)' % value, re.U).match))
|
compiled.append((key, re.compile('^(?:%s)' % value, re.U).match))
|
||||||
return compiled
|
return compiled
|
||||||
|
|
||||||
|
def push(self, *tokens):
|
||||||
|
"""Push back tokens which have been pulled but not processed."""
|
||||||
|
self._pushed = itertools.chain(tokens, self._pushed)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._pushed = []
|
||||||
|
|
||||||
def tokenize(self, text, fullsheet=False):
|
def tokenize(self, text, fullsheet=False):
|
||||||
"""Generator: Tokenize text and yield tokens, each token is a tuple
|
"""Generator: Tokenize text and yield tokens, each token is a tuple
|
||||||
of::
|
of::
|
||||||
@ -107,6 +118,11 @@ class Tokenizer(object):
|
|||||||
col += len(found)
|
col += len(found)
|
||||||
|
|
||||||
while text:
|
while text:
|
||||||
|
|
||||||
|
for pushed in self._pushed:
|
||||||
|
# do pushed tokens before new ones
|
||||||
|
yield pushed
|
||||||
|
|
||||||
# speed test for most used CHARs
|
# speed test for most used CHARs
|
||||||
c = text[0]
|
c = text[0]
|
||||||
if c in '{}:;,':
|
if c in '{}:;,':
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__docformat__ = 'restructuredtext'
|
__docformat__ = 'restructuredtext'
|
||||||
__version__ = '$Id: util.py 1781 2009-07-19 12:30:49Z cthedot $'
|
__version__ = '$Id: util.py 1872 2009-10-17 21:00:40Z cthedot $'
|
||||||
|
|
||||||
from helper import normalize
|
from helper import normalize
|
||||||
from itertools import ifilter
|
from itertools import ifilter
|
||||||
@ -488,6 +488,27 @@ class Seq(object):
|
|||||||
self._seq = []
|
self._seq = []
|
||||||
self._readonly = readonly
|
self._readonly = readonly
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"returns a repr same as a list of tuples of (value, type)"
|
||||||
|
return u'cssutils.%s.%s([\n %s], readonly=%r)' % (self.__module__,
|
||||||
|
self.__class__.__name__,
|
||||||
|
u',\n '.join([u'%r' % item for item in self._seq]
|
||||||
|
), self._readonly)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
vals = []
|
||||||
|
for v in self:
|
||||||
|
if isinstance(v.value, basestring):
|
||||||
|
vals.append(v.value)
|
||||||
|
elif type(v) == tuple:
|
||||||
|
vals.append(v.value[1])
|
||||||
|
else:
|
||||||
|
vals.append(str(v))
|
||||||
|
|
||||||
|
return "<cssutils.%s.%s object length=%r values=%r readonly=%r at 0x%x>" % (
|
||||||
|
self.__module__, self.__class__.__name__, len(self),
|
||||||
|
u', '.join(vals), self._readonly, id(self))
|
||||||
|
|
||||||
def __delitem__(self, i):
|
def __delitem__(self, i):
|
||||||
del self._seq[i]
|
del self._seq[i]
|
||||||
|
|
||||||
@ -503,8 +524,12 @@ class Seq(object):
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._seq)
|
return len(self._seq)
|
||||||
|
|
||||||
|
def absorb(self, other):
|
||||||
|
"Replace own data with data from other seq"
|
||||||
|
self._seq = other._seq
|
||||||
|
|
||||||
def append(self, val, typ, line=None, col=None):
|
def append(self, val, typ, line=None, col=None):
|
||||||
"if not readonly add new Item()"
|
"If not readonly add new Item()"
|
||||||
if self._readonly:
|
if self._readonly:
|
||||||
raise AttributeError('Seq is readonly.')
|
raise AttributeError('Seq is readonly.')
|
||||||
else:
|
else:
|
||||||
@ -517,7 +542,7 @@ class Seq(object):
|
|||||||
else:
|
else:
|
||||||
self._seq.append(item)
|
self._seq.append(item)
|
||||||
|
|
||||||
def replace(self, index= - 1, val=None, typ=None, line=None, col=None):
|
def replace(self, index=-1, val=None, typ=None, line=None, col=None):
|
||||||
"""
|
"""
|
||||||
if not readonly replace Item at index with new Item or
|
if not readonly replace Item at index with new Item or
|
||||||
simply replace value or type
|
simply replace value or type
|
||||||
@ -544,26 +569,6 @@ class Seq(object):
|
|||||||
self._seq[index] = Item(old.value + val, old.type,
|
self._seq[index] = Item(old.value + val, old.type,
|
||||||
old.line, old.col)
|
old.line, old.col)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
"returns a repr same as a list of tuples of (value, type)"
|
|
||||||
return u'cssutils.%s.%s([\n %s], readonly=%r)' % (self.__module__,
|
|
||||||
self.__class__.__name__,
|
|
||||||
u',\n '.join([u'%r' % item for item in self._seq]
|
|
||||||
), self._readonly)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
vals = []
|
|
||||||
for v in self:
|
|
||||||
if isinstance(v.value, basestring):
|
|
||||||
vals.append(v.value)
|
|
||||||
elif type(v) == tuple:
|
|
||||||
vals.append(v.value[1])
|
|
||||||
else:
|
|
||||||
vals.append(str(v))
|
|
||||||
|
|
||||||
return "<cssutils.%s.%s object length=%r values=%r readonly=%r at 0x%x>" % (
|
|
||||||
self.__module__, self.__class__.__name__, len(self),
|
|
||||||
u''.join(vals), self._readonly, id(self))
|
|
||||||
|
|
||||||
class Item(object):
|
class Item(object):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user