forked from openlp/openlp
Head r1030
This commit is contained in:
commit
23859bb241
17
openlp.pyw
17
openlp.pyw
@ -29,12 +29,14 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
from optparse import OptionParser
|
||||
from traceback import format_exception
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
from openlp.core.resources import qInitResources
|
||||
from openlp.core.ui.mainwindow import MainWindow
|
||||
from openlp.core.ui.exceptionform import ExceptionForm
|
||||
from openlp.core.ui import SplashScreen, ScreenList
|
||||
from openlp.core.utils import AppLocation, LanguageManager, VersionThread
|
||||
|
||||
@ -129,11 +131,11 @@ class OpenLP(QtGui.QApplication):
|
||||
screens = ScreenList()
|
||||
# Decide how many screens we have and their size
|
||||
for screen in xrange(0, self.desktop().numScreens()):
|
||||
size = self.desktop().screenGeometry(screen);
|
||||
screens.add_screen({u'number': screen,
|
||||
u'size': self.desktop().availableGeometry(screen),
|
||||
u'size': size,
|
||||
u'primary': (self.desktop().primaryScreen() == screen)})
|
||||
log.info(u'Screen %d found with resolution %s',
|
||||
screen, self.desktop().availableGeometry(screen))
|
||||
log.info(u'Screen %d found with resolution %s', screen, size)
|
||||
# start the main app window
|
||||
self.mainWindow = MainWindow(screens, app_version)
|
||||
self.mainWindow.show()
|
||||
@ -144,6 +146,13 @@ class OpenLP(QtGui.QApplication):
|
||||
VersionThread(self.mainWindow, app_version).start()
|
||||
return self.exec_()
|
||||
|
||||
def hookException(self, exctype, value, traceback):
|
||||
if not hasattr(self, u'exceptionForm'):
|
||||
self.exceptionForm = ExceptionForm(self.mainWindow)
|
||||
self.exceptionForm.exceptionTextEdit.setPlainText(
|
||||
''.join(format_exception(exctype, value, traceback)))
|
||||
self.exceptionForm.exec_()
|
||||
|
||||
def main():
|
||||
"""
|
||||
The main function which parses command line options and then runs
|
||||
@ -194,7 +203,7 @@ def main():
|
||||
language = LanguageManager.get_language()
|
||||
appTranslator = LanguageManager.get_translator(language)
|
||||
app.installTranslator(appTranslator)
|
||||
|
||||
sys.excepthook = app.hookException
|
||||
sys.exit(app.run())
|
||||
|
||||
if __name__ == u'__main__':
|
||||
|
@ -316,6 +316,7 @@ def expand_tags(text):
|
||||
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
||||
return text
|
||||
|
||||
from spelltextedit import SpellTextEdit
|
||||
from eventreceiver import Receiver
|
||||
from settingsmanager import SettingsManager
|
||||
from plugin import PluginStatus, Plugin
|
||||
@ -324,7 +325,8 @@ from settingstab import SettingsTab
|
||||
from serviceitem import ServiceItem
|
||||
from serviceitem import ServiceItemType
|
||||
from serviceitem import ItemCapabilities
|
||||
from htmlbuilder import build_html
|
||||
from htmlbuilder import build_html, build_lyrics_format_css, \
|
||||
build_lyrics_outline_css
|
||||
from toolbar import OpenLPToolbar
|
||||
from dockwidget import OpenLPDockWidget
|
||||
from theme import ThemeLevel, ThemeXML
|
||||
|
@ -24,10 +24,13 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
from PyQt4 import QtWebKit
|
||||
|
||||
from openlp.core.lib import image_to_byte
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HTMLSRC = u"""
|
||||
<html>
|
||||
<head>
|
||||
@ -37,9 +40,10 @@ HTMLSRC = u"""
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
%s;
|
||||
}
|
||||
.size {
|
||||
position: absolute;
|
||||
@ -77,17 +81,14 @@ body {
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var timer = null;
|
||||
var video_timer = null;
|
||||
var transition = %s;
|
||||
|
||||
function show_video(state, path, volume, loop){
|
||||
var vid = document.getElementById('video');
|
||||
if(path != null)
|
||||
if(path != null){
|
||||
vid.src = path;
|
||||
if(loop != null){
|
||||
if(loop)
|
||||
vid.loop = 'loop';
|
||||
else
|
||||
vid.loop = '';
|
||||
vid.load();
|
||||
}
|
||||
if(volume != null){
|
||||
vid.volume = volume;
|
||||
@ -96,23 +97,55 @@ body {
|
||||
case 'play':
|
||||
vid.play();
|
||||
vid.style.display = 'block';
|
||||
if(loop)
|
||||
video_timer = setInterval('video_loop()', 200);
|
||||
break;
|
||||
case 'pause':
|
||||
if(video_timer!=null){
|
||||
clearInterval(video_timer);
|
||||
video_timer = null;
|
||||
}
|
||||
vid.pause();
|
||||
vid.style.display = 'block';
|
||||
break;
|
||||
case 'stop':
|
||||
if(video_timer!=null){
|
||||
clearInterval(video_timer);
|
||||
video_timer = null;
|
||||
}
|
||||
vid.pause();
|
||||
vid.style.display = 'none';
|
||||
vid.load();
|
||||
break;
|
||||
case 'close':
|
||||
if(video_timer!=null){
|
||||
clearInterval(video_timer);
|
||||
video_timer = null;
|
||||
}
|
||||
vid.pause();
|
||||
vid.style.display = 'none';
|
||||
vid.src = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function video_loop(){
|
||||
// The preferred method would be to use the video tag loop attribute
|
||||
// But QtWebKit doesn't support this. Neither does it support the
|
||||
// onended event, hence the setInterval()
|
||||
// In addition, setting the currentTime attribute to zero to restart
|
||||
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
|
||||
// To complicate it further, sometimes vid.currentTime stops
|
||||
// slightly short of vid.duration and vid.ended is intermittent!
|
||||
//
|
||||
// Note, currently the background may go black between loops. Not
|
||||
// desirable. Need to investigate using two <video>'s, and hiding/
|
||||
// preloading one, and toggle between the two when looping.
|
||||
var vid = document.getElementById('video');
|
||||
if(vid.ended||vid.currentTime+0.2>=vid.duration){
|
||||
vid.load();
|
||||
vid.play();
|
||||
}
|
||||
}
|
||||
function show_image(src){
|
||||
var img = document.getElementById('image');
|
||||
img.src = src;
|
||||
@ -141,6 +174,7 @@ body {
|
||||
}
|
||||
document.getElementById('black').style.display = black;
|
||||
document.getElementById('lyricsmain').style.visibility = lyrics;
|
||||
document.getElementById('image').style.visibility = lyrics;
|
||||
outline = document.getElementById('lyricsoutline')
|
||||
if(outline!=null)
|
||||
outline.style.visibility = lyrics;
|
||||
@ -192,7 +226,7 @@ body {
|
||||
function show_text(newtext){
|
||||
if(timer != null)
|
||||
clearTimeout(timer);
|
||||
text_fade('lyricsmain', newtext);
|
||||
text_fade('lyricsmain', newtext);
|
||||
text_fade('lyricsoutline', newtext);
|
||||
text_fade('lyricsshadow', newtext);
|
||||
if(text_opacity()==1) return;
|
||||
@ -233,7 +267,7 @@ body {
|
||||
var text = document.getElementById('lyricsmain');
|
||||
return getComputedStyle(text, '').opacity;
|
||||
}
|
||||
|
||||
|
||||
function show_text_complete(){
|
||||
return (text_opacity()==1);
|
||||
}
|
||||
@ -263,58 +297,101 @@ def build_html(item, screen, alert, islive):
|
||||
`islive`
|
||||
Item is going live, rather than preview/theme building
|
||||
"""
|
||||
try:
|
||||
webkitvers = float(QtWebKit.qWebKitVersion())
|
||||
except AttributeError:
|
||||
webkitvers = 0
|
||||
width = screen[u'size'].width()
|
||||
height = screen[u'size'].height()
|
||||
theme = item.themedata
|
||||
webkitvers = webkit_version()
|
||||
if item.bg_frame:
|
||||
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
|
||||
else:
|
||||
image = u''
|
||||
html = HTMLSRC % (width, height,
|
||||
html = HTMLSRC % (build_background_css(item, width, height),
|
||||
width, height,
|
||||
build_alert_css(alert, width),
|
||||
build_footer_css(item),
|
||||
build_footer_css(item, height),
|
||||
build_lyrics_css(item, webkitvers),
|
||||
u'true' if theme and theme.display_slideTransition and islive \
|
||||
else u'false',
|
||||
image,
|
||||
image,
|
||||
build_lyrics_html(item, webkitvers))
|
||||
return html
|
||||
|
||||
def build_lyrics_css(item, webkitvers):
|
||||
def webkit_version():
|
||||
"""
|
||||
Build the video display css
|
||||
Return the Webkit version in use.
|
||||
Note method added relatively recently, so return 0 if prior to this
|
||||
"""
|
||||
try:
|
||||
webkitvers = float(QtWebKit.qWebKitVersion())
|
||||
log.debug(u'Webkit version = %s' % webkitvers)
|
||||
except AttributeError:
|
||||
webkitvers = 0
|
||||
return webkitvers
|
||||
|
||||
def build_background_css(item, width, height):
|
||||
"""
|
||||
Build the background css
|
||||
|
||||
`item`
|
||||
Service Item containing theme and location information
|
||||
|
||||
|
||||
"""
|
||||
width = int(width) / 2
|
||||
theme = item.themedata
|
||||
background = u'background-color: black'
|
||||
if theme:
|
||||
if theme.background_type == u'solid':
|
||||
background = u'background-color: %s' % theme.background_color
|
||||
else:
|
||||
if theme.background_direction == u'horizontal':
|
||||
background = \
|
||||
u'background: ' \
|
||||
u'-webkit-gradient(linear, left top, left bottom, ' \
|
||||
'from(%s), to(%s))' % (theme.background_startColor,
|
||||
theme.background_endColor)
|
||||
elif theme.background_direction == u'vertical':
|
||||
background = \
|
||||
u'background: -webkit-gradient(linear, left top, ' \
|
||||
u'right top, from(%s), to(%s))' % \
|
||||
(theme.background_startColor, theme.background_endColor)
|
||||
else:
|
||||
background = \
|
||||
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
|
||||
u'50%%, %s, from(%s), to(%s))' % (width, width, width,
|
||||
theme.background_startColor, theme.background_endColor)
|
||||
return background
|
||||
|
||||
def build_lyrics_css(item, webkitvers):
|
||||
"""
|
||||
Build the lyrics display css
|
||||
|
||||
`item`
|
||||
Service Item containing theme and location information
|
||||
|
||||
`webkitvers`
|
||||
The version of qtwebkit we're using
|
||||
|
||||
"""
|
||||
style = """
|
||||
.lyricstable {
|
||||
z-index:4;
|
||||
position: absolute;
|
||||
display: table;
|
||||
%s
|
||||
}
|
||||
.lyricscell {
|
||||
display:table-cell;
|
||||
word-wrap: break-word;
|
||||
.lyricstable {
|
||||
z-index:4;
|
||||
position: absolute;
|
||||
display: table;
|
||||
%s
|
||||
}
|
||||
.lyricsmain {
|
||||
%s
|
||||
.lyricscell {
|
||||
display:table-cell;
|
||||
word-wrap: break-word;
|
||||
%s
|
||||
}
|
||||
.lyricsoutline {
|
||||
%s
|
||||
.lyricsmain {
|
||||
%s
|
||||
}
|
||||
.lyricsshadow {
|
||||
%s
|
||||
.lyricsoutline {
|
||||
%s
|
||||
}
|
||||
.lyricsshadow {
|
||||
%s
|
||||
}
|
||||
"""
|
||||
theme = item.themedata
|
||||
@ -323,77 +400,120 @@ def build_lyrics_css(item, webkitvers):
|
||||
lyricsmain = u''
|
||||
outline = u''
|
||||
shadow = u''
|
||||
if theme:
|
||||
if theme and item.main:
|
||||
lyricstable = u'left: %spx; top: %spx;' % \
|
||||
(item.main.x(), item.main.y())
|
||||
if theme.display_horizontalAlign == 2:
|
||||
align = u'center'
|
||||
elif theme.display_horizontalAlign == 1:
|
||||
align = u'right'
|
||||
else:
|
||||
align = u'left'
|
||||
if theme.display_verticalAlign == 2:
|
||||
valign = u'bottom'
|
||||
elif theme.display_verticalAlign == 1:
|
||||
valign = u'middle'
|
||||
else:
|
||||
valign = u'top'
|
||||
lyrics = u'width: %spx; height: %spx; text-align: %s; ' \
|
||||
'vertical-align: %s; font-family: %s; font-size: %spt; ' \
|
||||
'color: %s; line-height: %d%%;' % \
|
||||
(item.main.width(), item.main.height(), align, valign,
|
||||
theme.font_main_name, theme.font_main_proportion,
|
||||
theme.font_main_color, 100 + int(theme.font_main_line_adjustment))
|
||||
lyrics = build_lyrics_format_css(theme, item.main.width(),
|
||||
item.main.height())
|
||||
# For performance reasons we want to show as few DIV's as possible,
|
||||
# especially when animating/transitions.
|
||||
# However some bugs in older versions of qtwebkit mean we need to
|
||||
# especially when animating/transitions.
|
||||
# However some bugs in older versions of qtwebkit mean we need to
|
||||
# perform workarounds and add extra divs. Only do these when needed.
|
||||
#
|
||||
# Before 533.3 the webkit-text-fill colour wasn't displayed, only the
|
||||
# Before 533.3 the webkit-text-fill colour wasn't displayed, only the
|
||||
# stroke (outline) color. So put stroke layer underneath the main text.
|
||||
#
|
||||
# Before 534.4 the webkit-text-stroke was sometimes out of alignment
|
||||
# Before 534.4 the webkit-text-stroke was sometimes out of alignment
|
||||
# with the fill, or normal text. letter-spacing=1 is workaround
|
||||
# https://bugs.webkit.org/show_bug.cgi?id=44403
|
||||
#
|
||||
# Before 534.4 the text-shadow didn't get displayed when
|
||||
# webkit-text-stroke was used. So use an offset text layer underneath.
|
||||
# Before 534.4 the text-shadow didn't get displayed when
|
||||
# webkit-text-stroke was used. So use an offset text layer underneath.
|
||||
# https://bugs.webkit.org/show_bug.cgi?id=19728
|
||||
if theme.display_outline:
|
||||
if webkitvers < 534.3:
|
||||
lyrics += u' letter-spacing: 1px;'
|
||||
outline = u' -webkit-text-stroke: %sem %s; ' \
|
||||
'-webkit-text-fill-color: %s; ' % \
|
||||
(float(theme.display_outline_size) / 16,
|
||||
theme.display_outline_color, theme.font_main_color)
|
||||
if webkitvers >= 533.3:
|
||||
lyricsmain += outline
|
||||
if theme.display_shadow and webkitvers < 534.3:
|
||||
shadow = u'-webkit-text-stroke: %sem %s; ' \
|
||||
u'-webkit-text-fill-color: %s; ' \
|
||||
u' padding-left: %spx; padding-top: %spx' % \
|
||||
(float(theme.display_outline_size) / 16,
|
||||
theme.display_shadow_color, theme.display_shadow_color,
|
||||
theme.display_shadow_size, theme.display_shadow_size)
|
||||
if theme.display_shadow and \
|
||||
(not theme.display_outline or webkitvers >= 534.3):
|
||||
lyricsmain += u' text-shadow: %s %spx %spx;' % \
|
||||
(theme.display_shadow_color, theme.display_shadow_size,
|
||||
theme.display_shadow_size)
|
||||
if webkitvers >= 533.3:
|
||||
lyricsmain += build_lyrics_outline_css(theme)
|
||||
else:
|
||||
outline = build_lyrics_outline_css(theme)
|
||||
if theme.display_shadow:
|
||||
if theme.display_outline and webkitvers < 534.3:
|
||||
shadow = u'padding-left: %spx; padding-top: %spx ' % \
|
||||
(theme.display_shadow_size, theme.display_shadow_size)
|
||||
shadow += build_lyrics_outline_css(theme, True)
|
||||
else:
|
||||
lyricsmain += u' text-shadow: %s %spx %spx;' % \
|
||||
(theme.display_shadow_color, theme.display_shadow_size,
|
||||
theme.display_shadow_size)
|
||||
lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow)
|
||||
return lyrics_css
|
||||
|
||||
|
||||
def build_lyrics_outline_css(theme, is_shadow=False):
|
||||
"""
|
||||
Build the css which controls the theme outline
|
||||
Also used by renderer for splitting verses
|
||||
|
||||
`theme`
|
||||
Object containing theme information
|
||||
|
||||
`is_shadow`
|
||||
If true, use the shadow colors instead
|
||||
"""
|
||||
if theme.display_outline:
|
||||
size = float(theme.display_outline_size) / 16
|
||||
if is_shadow:
|
||||
fill_color = theme.display_shadow_color
|
||||
outline_color = theme.display_shadow_color
|
||||
else:
|
||||
fill_color = theme.font_main_color
|
||||
outline_color = theme.display_outline_color
|
||||
return u' -webkit-text-stroke: %sem %s; ' \
|
||||
u'-webkit-text-fill-color: %s; ' % (size, outline_color, fill_color)
|
||||
else:
|
||||
return u''
|
||||
|
||||
def build_lyrics_format_css(theme, width, height):
|
||||
"""
|
||||
Build the css which controls the theme format
|
||||
Also used by renderer for splitting verses
|
||||
|
||||
`theme`
|
||||
Object containing theme information
|
||||
|
||||
`width`
|
||||
Width of the lyrics block
|
||||
|
||||
`height`
|
||||
Height of the lyrics block
|
||||
|
||||
"""
|
||||
if theme.display_horizontalAlign == 2:
|
||||
align = u'center'
|
||||
elif theme.display_horizontalAlign == 1:
|
||||
align = u'right'
|
||||
else:
|
||||
align = u'left'
|
||||
if theme.display_verticalAlign == 2:
|
||||
valign = u'bottom'
|
||||
elif theme.display_verticalAlign == 1:
|
||||
valign = u'middle'
|
||||
else:
|
||||
valign = u'top'
|
||||
lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
|
||||
'text-align: %s; vertical-align: %s; font-family: %s; ' \
|
||||
'font-size: %spt; color: %s; line-height: %d%%; ' \
|
||||
'margin:0; padding:0; width: %spx; height: %spx; ' % \
|
||||
(align, valign, theme.font_main_name, theme.font_main_proportion,
|
||||
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
|
||||
width, height)
|
||||
if theme.display_outline:
|
||||
if webkit_version() < 534.3:
|
||||
lyrics += u' letter-spacing: 1px;'
|
||||
if theme.font_main_italics:
|
||||
lyrics += u' font-style:italic; '
|
||||
if theme.font_main_weight == u'Bold':
|
||||
lyrics += u' font-weight:bold; '
|
||||
return lyrics
|
||||
|
||||
def build_lyrics_html(item, webkitvers):
|
||||
"""
|
||||
Build the HTML required to show the lyrics
|
||||
|
||||
`item`
|
||||
Service Item containing theme and location information
|
||||
|
||||
|
||||
`webkitvers`
|
||||
The version of qtwebkit we're using
|
||||
"""
|
||||
# Bugs in some versions of QtWebKit mean we sometimes need additional
|
||||
# Bugs in some versions of QtWebKit mean we sometimes need additional
|
||||
# divs for outline and shadow, since the CSS doesn't work.
|
||||
# To support vertical alignment middle and bottom, nested div's using
|
||||
# display:table/display:table-cell are required for each lyric block.
|
||||
@ -411,8 +531,8 @@ def build_lyrics_html(item, webkitvers):
|
||||
u'<div id="lyricsmain" style="opacity:1" ' \
|
||||
u'class="lyricscell lyricsmain"></div></div>'
|
||||
return lyrics
|
||||
|
||||
def build_footer_css(item):
|
||||
|
||||
def build_footer_css(item, height):
|
||||
"""
|
||||
Build the display of the item footer
|
||||
|
||||
@ -421,26 +541,21 @@ def build_footer_css(item):
|
||||
"""
|
||||
style = """
|
||||
left: %spx;
|
||||
top: %spx;
|
||||
bottom: %spx;
|
||||
width: %spx;
|
||||
height: %spx;
|
||||
font-family: %s;
|
||||
font-size: %spt;
|
||||
color: %s;
|
||||
text-align: %s;
|
||||
text-align: left;
|
||||
white-space:nowrap;
|
||||
"""
|
||||
theme = item.themedata
|
||||
if not theme:
|
||||
if not theme or not item.footer:
|
||||
return u''
|
||||
if theme.display_horizontalAlign == 2:
|
||||
align = u'center'
|
||||
elif theme.display_horizontalAlign == 1:
|
||||
align = u'right'
|
||||
else:
|
||||
align = u'left'
|
||||
lyrics_html = style % (item.footer.x(), item.footer.y(),
|
||||
item.footer.width(), item.footer.height(), theme.font_footer_name,
|
||||
theme.font_footer_proportion, theme.font_footer_color, align)
|
||||
bottom = height - int(item.footer.y()) - int(item.footer.height())
|
||||
lyrics_html = style % (item.footer.x(), bottom,
|
||||
item.footer.width(), theme.font_footer_name,
|
||||
theme.font_footer_proportion, theme.font_footer_color)
|
||||
return lyrics_html
|
||||
|
||||
def build_alert_css(alertTab, width):
|
||||
|
@ -29,9 +29,10 @@ format it for the output display.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.lib import resize_image, expand_tags
|
||||
from openlp.core.lib import resize_image, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -69,21 +70,11 @@ class Renderer(object):
|
||||
self.theme_name = theme.theme_name
|
||||
if theme.background_type == u'image':
|
||||
if theme.background_filename:
|
||||
self.set_bg_image(theme.background_filename)
|
||||
|
||||
def set_bg_image(self, filename):
|
||||
"""
|
||||
Set a background image.
|
||||
|
||||
``filename``
|
||||
The name of the image file.
|
||||
"""
|
||||
log.debug(u'set bg image %s', filename)
|
||||
self._bg_image_filename = unicode(filename)
|
||||
if self.frame:
|
||||
self.bg_image = resize_image(self._bg_image_filename,
|
||||
self.frame.width(),
|
||||
self.frame.height())
|
||||
self._bg_image_filename = unicode(theme.background_filename)
|
||||
if self.frame:
|
||||
self.bg_image = resize_image(self._bg_image_filename,
|
||||
self.frame.width(),
|
||||
self.frame.height())
|
||||
|
||||
def set_text_rectangle(self, rect_main, rect_footer):
|
||||
"""
|
||||
@ -99,7 +90,7 @@ class Renderer(object):
|
||||
self._rect = rect_main
|
||||
self._rect_footer = rect_footer
|
||||
|
||||
def set_frame_dest(self, frame_width, frame_height, preview=False):
|
||||
def set_frame_dest(self, frame_width, frame_height):
|
||||
"""
|
||||
Set the size of the slide.
|
||||
|
||||
@ -109,11 +100,7 @@ class Renderer(object):
|
||||
``frame_height``
|
||||
The height of the slide.
|
||||
|
||||
``preview``
|
||||
Defaults to *False*. Whether or not to generate a preview.
|
||||
"""
|
||||
if preview:
|
||||
self.bg_frame = None
|
||||
log.debug(u'set frame dest (frame) w %d h %d', frame_width,
|
||||
frame_height)
|
||||
self.frame = QtGui.QImage(frame_width, frame_height,
|
||||
@ -121,8 +108,17 @@ class Renderer(object):
|
||||
if self._bg_image_filename and not self.bg_image:
|
||||
self.bg_image = resize_image(self._bg_image_filename,
|
||||
self.frame.width(), self.frame.height())
|
||||
if self.bg_frame is None:
|
||||
self._generate_background_frame()
|
||||
if self._theme.background_type == u'image':
|
||||
self.bg_frame = QtGui.QImage(self.frame.width(),
|
||||
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter()
|
||||
painter.begin(self.bg_frame)
|
||||
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
|
||||
if self.bg_image:
|
||||
painter.drawImage(0, 0, self.bg_image)
|
||||
painter.end()
|
||||
else:
|
||||
self.bg_frame = None
|
||||
|
||||
def format_slide(self, words, line_break):
|
||||
"""
|
||||
@ -143,91 +139,33 @@ class Renderer(object):
|
||||
lines = verse.split(u'\n')
|
||||
for line in lines:
|
||||
text.append(line)
|
||||
doc = QtGui.QTextDocument()
|
||||
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
|
||||
df = doc.defaultFont()
|
||||
df.setPointSize(self._theme.font_main_proportion)
|
||||
df.setFamily(self._theme.font_main_name)
|
||||
main_weight = 50
|
||||
if self._theme.font_main_weight == u'Bold':
|
||||
main_weight = 75
|
||||
df.setWeight(main_weight)
|
||||
doc.setDefaultFont(df)
|
||||
layout = doc.documentLayout()
|
||||
web = QtWebKit.QWebView()
|
||||
web.resize(self._rect.width(), self._rect.height())
|
||||
web.setVisible(False)
|
||||
frame = web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css
|
||||
width = self._rect.width() - int(self._theme.display_shadow_size)
|
||||
height = self._rect.height() - int(self._theme.display_shadow_size)
|
||||
shell = u'<html><head><style>#main {%s %s}</style><body>' \
|
||||
u'<div id="main">' % \
|
||||
(build_lyrics_format_css(self._theme, width, height),
|
||||
build_lyrics_outline_css(self._theme))
|
||||
formatted = []
|
||||
if self._theme.font_main_weight == u'Bold' and \
|
||||
self._theme.font_main_italics:
|
||||
shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
|
||||
elif self._theme.font_main_weight == u'Bold' and \
|
||||
not self._theme.font_main_italics:
|
||||
shell = u'{p}{st}%s{/st}{/p}'
|
||||
elif self._theme.font_main_italics:
|
||||
shell = u'{p}{it}%s{/it}{/p}'
|
||||
else:
|
||||
shell = u'{p}%s{/p}'
|
||||
temp_text = u''
|
||||
old_html_text = u''
|
||||
html_text = u''
|
||||
styled_text = u''
|
||||
js_height = 'document.getElementById("main").scrollHeight'
|
||||
for line in text:
|
||||
# mark line ends
|
||||
temp_text = temp_text + line + line_end
|
||||
html_text = shell % expand_tags(temp_text)
|
||||
doc.setHtml(html_text)
|
||||
# Text too long so gone to next mage
|
||||
if layout.pageCount() != 1:
|
||||
formatted.append(shell % old_html_text)
|
||||
temp_text = line + line_end
|
||||
old_html_text = temp_text
|
||||
formatted.append(shell % old_html_text)
|
||||
styled_line = expand_tags(line) + line_end
|
||||
styled_text += styled_line
|
||||
html = shell + styled_text + u'</div></body></html>'
|
||||
web.setHtml(html)
|
||||
# Text too long so go to next page
|
||||
text_height = int(frame.evaluateJavaScript(js_height).toString())
|
||||
if text_height > height:
|
||||
formatted.append(html_text)
|
||||
html_text = u''
|
||||
styled_text = styled_line
|
||||
html_text += line + line_end
|
||||
formatted.append(html_text)
|
||||
log.debug(u'format_slide - End')
|
||||
return formatted
|
||||
|
||||
def _generate_background_frame(self):
|
||||
"""
|
||||
Generate a background frame to the same size as the frame to be used.
|
||||
Results are cached for performance reasons.
|
||||
"""
|
||||
assert(self._theme)
|
||||
self.bg_frame = QtGui.QImage(self.frame.width(),
|
||||
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
log.debug(u'render background %s start', self._theme.background_type)
|
||||
painter = QtGui.QPainter()
|
||||
painter.begin(self.bg_frame)
|
||||
if self._theme.background_type == u'solid':
|
||||
painter.fillRect(self.frame.rect(),
|
||||
QtGui.QColor(self._theme.background_color))
|
||||
elif self._theme.background_type == u'gradient':
|
||||
# gradient
|
||||
gradient = None
|
||||
if self._theme.background_direction == u'horizontal':
|
||||
w = int(self.frame.width()) / 2
|
||||
# vertical
|
||||
gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
|
||||
elif self._theme.background_direction == u'vertical':
|
||||
h = int(self.frame.height()) / 2
|
||||
# Horizontal
|
||||
gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
|
||||
else:
|
||||
w = int(self.frame.width()) / 2
|
||||
h = int(self.frame.height()) / 2
|
||||
# Circular
|
||||
gradient = QtGui.QRadialGradient(w, h, w)
|
||||
gradient.setColorAt(0,
|
||||
QtGui.QColor(self._theme.background_startColor))
|
||||
gradient.setColorAt(1,
|
||||
QtGui.QColor(self._theme.background_endColor))
|
||||
painter.setBrush(QtGui.QBrush(gradient))
|
||||
rect_path = QtGui.QPainterPath()
|
||||
max_x = self.frame.width()
|
||||
max_y = self.frame.height()
|
||||
rect_path.moveTo(0, 0)
|
||||
rect_path.lineTo(0, max_y)
|
||||
rect_path.lineTo(max_x, max_y)
|
||||
rect_path.lineTo(max_x, 0)
|
||||
rect_path.closeSubpath()
|
||||
painter.drawPath(rect_path)
|
||||
elif self._theme.background_type == u'image':
|
||||
# image
|
||||
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
|
||||
if self.bg_image:
|
||||
painter.drawImage(0, 0, self.bg_image)
|
||||
painter.end()
|
||||
|
@ -93,6 +93,8 @@ class RenderManager(object):
|
||||
"""
|
||||
self.global_theme = global_theme
|
||||
self.theme_level = theme_level
|
||||
self.global_theme_data = \
|
||||
self.theme_manager.getThemeData(self.global_theme)
|
||||
self.themedata = None
|
||||
|
||||
def set_service_theme(self, service_theme):
|
||||
|
@ -160,9 +160,9 @@ class ServiceItem(object):
|
||||
self.themedata = self.render_manager.renderer._theme
|
||||
for slide in self._raw_frames:
|
||||
before = time.time()
|
||||
formated = self.render_manager \
|
||||
formatted = self.render_manager \
|
||||
.format_slide(slide[u'raw_slide'], line_break)
|
||||
for page in formated:
|
||||
for page in formatted:
|
||||
self._display_frames.append(
|
||||
{u'title': clean_tags(page),
|
||||
u'text': clean_tags(page.rstrip()),
|
||||
@ -170,6 +170,7 @@ class ServiceItem(object):
|
||||
u'verseTag': slide[u'verseTag'] })
|
||||
log.log(15, u'Formatting took %4s' % (time.time() - before))
|
||||
elif self.service_item_type == ServiceItemType.Image:
|
||||
self.themedata = self.render_manager.global_theme_data
|
||||
for slide in self._raw_frames:
|
||||
slide[u'image'] = resize_image(slide[u'image'],
|
||||
self.render_manager.width, self.render_manager.height)
|
||||
|
155
openlp/core/lib/spelltextedit.py
Normal file
155
openlp/core/lib/spelltextedit.py
Normal file
@ -0,0 +1,155 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
# Carsten Tinggaard, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import re
|
||||
import sys
|
||||
try:
|
||||
import enchant
|
||||
enchant_available = True
|
||||
except ImportError:
|
||||
enchant_available = False
|
||||
|
||||
# based on code from
|
||||
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import html_expands, translate, context_menu_action
|
||||
|
||||
class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
def __init__(self, *args):
|
||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
||||
# Default dictionary based on the current locale.
|
||||
if enchant_available:
|
||||
self.dict = enchant.Dict()
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.setDict(self.dict)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
# Rewrite the mouse event to a left button event so the cursor is
|
||||
# moved to the location of the pointer.
|
||||
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
|
||||
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
|
||||
QtCore.Qt.NoModifier)
|
||||
QtGui.QPlainTextEdit.mousePressEvent(self, event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
popup_menu = self.createStandardContextMenu()
|
||||
# Select the word under the cursor.
|
||||
cursor = self.textCursor()
|
||||
# only select text if not already selected
|
||||
if not cursor.hasSelection():
|
||||
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
||||
self.setTextCursor(cursor)
|
||||
# Check if the selected word is misspelled and offer spelling
|
||||
# suggestions if it is.
|
||||
if enchant_available and self.textCursor().hasSelection():
|
||||
text = unicode(self.textCursor().selectedText())
|
||||
if not self.dict.check(text):
|
||||
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Spelling Suggestions'))
|
||||
for word in self.dict.suggest(text):
|
||||
action = SpellAction(word, spell_menu)
|
||||
action.correct.connect(self.correctWord)
|
||||
spell_menu.addAction(action)
|
||||
# Only add the spelling suggests to the menu if there are
|
||||
# suggestions.
|
||||
if len(spell_menu.actions()) != 0:
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
|
||||
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Formatting Tags'))
|
||||
for html in html_expands:
|
||||
action = SpellAction( html[u'desc'], tag_menu)
|
||||
action.correct.connect(self.htmlTag)
|
||||
tag_menu.addAction(action)
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
|
||||
popup_menu.exec_(event.globalPos())
|
||||
|
||||
def correctWord(self, word):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(word)
|
||||
cursor.endEditBlock()
|
||||
|
||||
def htmlTag(self, tag):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
for html in html_expands:
|
||||
if tag == html[u'desc']:
|
||||
cursor = self.textCursor()
|
||||
if self.textCursor().hasSelection():
|
||||
text = cursor.selectedText()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(text)
|
||||
cursor.insertText(html[u'end tag'])
|
||||
cursor.endEditBlock()
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(html[u'end tag'])
|
||||
|
||||
class Highlighter(QtGui.QSyntaxHighlighter):
|
||||
|
||||
WORDS = u'(?iu)[\w\']+'
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QSyntaxHighlighter.__init__(self, *args)
|
||||
self.dict = None
|
||||
|
||||
def setDict(self, dict):
|
||||
self.dict = dict
|
||||
|
||||
def highlightBlock(self, text):
|
||||
if not self.dict:
|
||||
return
|
||||
text = unicode(text)
|
||||
format = QtGui.QTextCharFormat()
|
||||
format.setUnderlineColor(QtCore.Qt.red)
|
||||
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
|
||||
for word_object in re.finditer(self.WORDS, text):
|
||||
if not self.dict.check(word_object.group()):
|
||||
self.setFormat(word_object.start(),
|
||||
word_object.end() - word_object.start(), format)
|
||||
|
||||
class SpellAction(QtGui.QAction):
|
||||
"""
|
||||
A special QAction that returns the text in a signal.
|
||||
"""
|
||||
correct = QtCore.pyqtSignal(unicode)
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QAction.__init__(self, *args)
|
||||
self.triggered.connect(lambda x: self.correct.emit(
|
||||
unicode(self.text())))
|
@ -56,7 +56,7 @@ BLANK_THEME_XML = \
|
||||
<weight>Normal</weight>
|
||||
<italics>False</italics>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location override="False" x="10" y="10" width="1004" height="730"/>
|
||||
<location override="False" x="10" y="10" width="1004" height="690"/>
|
||||
</font>
|
||||
<font type="footer">
|
||||
<name>Arial</name>
|
||||
@ -65,7 +65,7 @@ BLANK_THEME_XML = \
|
||||
<weight>Normal</weight>
|
||||
<italics>False</italics>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location override="False" x="10" y="730" width="1004" height="38"/>
|
||||
<location override="False" x="10" y="690" width="1004" height="78"/>
|
||||
</font>
|
||||
<display>
|
||||
<shadow color="#000000" size="5">True</shadow>
|
||||
|
@ -27,141 +27,6 @@
|
||||
The :mod:`ui` module provides the core user interface for OpenLP
|
||||
"""
|
||||
|
||||
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
|
||||
|
||||
import re
|
||||
import sys
|
||||
try:
|
||||
import enchant
|
||||
enchant_available = True
|
||||
except ImportError:
|
||||
enchant_available = False
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import html_expands, translate, context_menu_action
|
||||
|
||||
class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
def __init__(self, *args):
|
||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
||||
# Default dictionary based on the current locale.
|
||||
if enchant_available:
|
||||
self.dict = enchant.Dict()
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.setDict(self.dict)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.RightButton:
|
||||
# Rewrite the mouse event to a left button event so the cursor is
|
||||
# moved to the location of the pointer.
|
||||
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
|
||||
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
|
||||
QtCore.Qt.NoModifier)
|
||||
QtGui.QPlainTextEdit.mousePressEvent(self, event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
popup_menu = self.createStandardContextMenu()
|
||||
# Select the word under the cursor.
|
||||
cursor = self.textCursor()
|
||||
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
||||
self.setTextCursor(cursor)
|
||||
# Check if the selected word is misspelled and offer spelling
|
||||
# suggestions if it is.
|
||||
if enchant_available and self.textCursor().hasSelection():
|
||||
text = unicode(self.textCursor().selectedText())
|
||||
if not self.dict.check(text):
|
||||
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Spelling Suggestions'))
|
||||
for word in self.dict.suggest(text):
|
||||
action = SpellAction(word, spell_menu)
|
||||
action.correct.connect(self.correctWord)
|
||||
spell_menu.addAction(action)
|
||||
# Only add the spelling suggests to the menu if there are
|
||||
# suggestions.
|
||||
if len(spell_menu.actions()) != 0:
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
|
||||
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Formatting Tags'))
|
||||
for html in html_expands:
|
||||
action = SpellAction( html[u'desc'], tag_menu)
|
||||
action.correct.connect(self.htmlTag)
|
||||
tag_menu.addAction(action)
|
||||
popup_menu.insertSeparator(popup_menu.actions()[0])
|
||||
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
|
||||
|
||||
popup_menu.exec_(event.globalPos())
|
||||
|
||||
def correctWord(self, word):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
cursor = self.textCursor()
|
||||
cursor.beginEditBlock()
|
||||
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(word)
|
||||
|
||||
cursor.endEditBlock()
|
||||
|
||||
def htmlTag(self, tag):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
for html in html_expands:
|
||||
if tag == html[u'desc']:
|
||||
cursor = self.textCursor()
|
||||
if self.textCursor().hasSelection():
|
||||
text = cursor.selectedText()
|
||||
cursor.beginEditBlock()
|
||||
cursor.removeSelectedText()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(text)
|
||||
cursor.insertText(html[u'end tag'])
|
||||
cursor.endEditBlock()
|
||||
else:
|
||||
cursor = self.textCursor()
|
||||
cursor.insertText(html[u'start tag'])
|
||||
cursor.insertText(html[u'end tag'])
|
||||
|
||||
class Highlighter(QtGui.QSyntaxHighlighter):
|
||||
|
||||
WORDS = u'(?iu)[\w\']+'
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QSyntaxHighlighter.__init__(self, *args)
|
||||
|
||||
self.dict = None
|
||||
|
||||
def setDict(self, dict):
|
||||
self.dict = dict
|
||||
|
||||
def highlightBlock(self, text):
|
||||
if not self.dict:
|
||||
return
|
||||
|
||||
text = unicode(text)
|
||||
|
||||
format = QtGui.QTextCharFormat()
|
||||
format.setUnderlineColor(QtCore.Qt.red)
|
||||
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
|
||||
|
||||
for word_object in re.finditer(self.WORDS, text):
|
||||
if not self.dict.check(word_object.group()):
|
||||
self.setFormat(word_object.start(),
|
||||
word_object.end() - word_object.start(), format)
|
||||
|
||||
class SpellAction(QtGui.QAction):
|
||||
"""
|
||||
A special QAction that returns the text in a signal.
|
||||
"""
|
||||
correct = QtCore.pyqtSignal(unicode)
|
||||
|
||||
def __init__(self, *args):
|
||||
QtGui.QAction.__init__(self, *args)
|
||||
|
||||
self.triggered.connect(lambda x: self.correct.emit(
|
||||
unicode(self.text())))
|
||||
|
||||
class HideMode(object):
|
||||
"""
|
||||
This is basically an enumeration class which specifies the mode of a Bible.
|
||||
|
@ -199,18 +199,18 @@ class Ui_AboutDialog(object):
|
||||
'Preamble\n'
|
||||
'\n'
|
||||
'The licenses for most software are designed to take away your '
|
||||
'freedom to share and change it. By contrast, the GNU General '
|
||||
'freedom to share and change it. By contrast, the GNU General '
|
||||
'Public License is intended to guarantee your freedom to share '
|
||||
'and change free software--to make sure the software is free for '
|
||||
'all its users. This General Public License applies to most of '
|
||||
'all its users. This General Public License applies to most of '
|
||||
'the Free Software Foundation\'s software and to any other '
|
||||
'program whose authors commit to using it. (Some other Free '
|
||||
'program whose authors commit to using it. (Some other Free '
|
||||
'Software Foundation software is covered by the GNU Lesser '
|
||||
'General Public License instead.) You can apply it to your '
|
||||
'General Public License instead.) You can apply it to your '
|
||||
'programs, too.\n'
|
||||
'\n'
|
||||
'When we speak of free software, we are referring to freedom, not '
|
||||
'price. Our General Public Licenses are designed to make sure '
|
||||
'price. Our General Public Licenses are designed to make sure '
|
||||
'that you have the freedom to distribute copies of free software '
|
||||
'(and charge for this service if you wish), that you receive '
|
||||
'source code or can get it if you want it, that you can change '
|
||||
@ -225,8 +225,8 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'For example, if you distribute copies of such a program, whether '
|
||||
'gratis or for a fee, you must give the recipients all the rights '
|
||||
'that you have. You must make sure that they, too, receive or '
|
||||
'can get the source code. And you must show them these terms so '
|
||||
'that you have. You must make sure that they, too, receive or '
|
||||
'can get the source code. And you must show them these terms so '
|
||||
'they know their rights.\n'
|
||||
'\n'
|
||||
'We protect your rights with two steps: (1) copyright the '
|
||||
@ -235,15 +235,15 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'Also, for each author\'s protection and ours, we want to make '
|
||||
'certain that everyone understands that there is no warranty for '
|
||||
'this free software. If the software is modified by someone else '
|
||||
'this free software. If the software is modified by someone else '
|
||||
'and passed on, we want its recipients to know that what they '
|
||||
'have is not the original, so that any problems introduced by '
|
||||
'others will not reflect on the original authors\' reputations.\n'
|
||||
'\n'
|
||||
'Finally, any free program is threatened constantly by software '
|
||||
'patents. We wish to avoid the danger that redistributors of a '
|
||||
'patents. We wish to avoid the danger that redistributors of a '
|
||||
'free program will individually obtain patent licenses, in effect '
|
||||
'making the program proprietary. To prevent this, we have made '
|
||||
'making the program proprietary. To prevent this, we have made '
|
||||
'it clear that any patent must be licensed for everyone\'s free '
|
||||
'use or not licensed at all.\n'
|
||||
'\n'
|
||||
@ -255,17 +255,17 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'0. This License applies to any program or other work which '
|
||||
'contains a notice placed by the copyright holder saying it may '
|
||||
'be distributed under the terms of this General Public License. '
|
||||
'be distributed under the terms of this General Public License. '
|
||||
'The "Program", below, refers to any such program or work, and a '
|
||||
'"work based on the Program" means either the Program or any '
|
||||
'derivative work under copyright law: that is to say, a work '
|
||||
'containing the Program or a portion of it, either verbatim or '
|
||||
'with modifications and/or translated into another language. '
|
||||
'with modifications and/or translated into another language. '
|
||||
'(Hereinafter, translation is included without limitation in the '
|
||||
'term "modification".) Each licensee is addressed as "you".\n'
|
||||
'term "modification".) Each licensee is addressed as "you".\n'
|
||||
'\n'
|
||||
'Activities other than copying, distribution and modification are '
|
||||
'not covered by this License; they are outside its scope. The '
|
||||
'not covered by this License; they are outside its scope. The '
|
||||
'act of running the Program is not restricted, and the output '
|
||||
'from the Program is covered only if its contents constitute a '
|
||||
'work based on the Program (independent of having been made by '
|
||||
@ -305,17 +305,17 @@ class Ui_AboutDialog(object):
|
||||
'notice that there is no warranty (or else, saying that you '
|
||||
'provide a warranty) and that users may redistribute the program '
|
||||
'under these conditions, and telling the user how to view a copy '
|
||||
'of this License. (Exception: if the Program itself is '
|
||||
'of this License. (Exception: if the Program itself is '
|
||||
'interactive but does not normally print such an announcement, '
|
||||
'your work based on the Program is not required to print an '
|
||||
'announcement.)\n'
|
||||
'\n'
|
||||
'These requirements apply to the modified work as a whole. If '
|
||||
'These requirements apply to the modified work as a whole. If '
|
||||
'identifiable sections of that work are not derived from the '
|
||||
'Program, and can be reasonably considered independent and '
|
||||
'separate works in themselves, then this License, and its terms, '
|
||||
'do not apply to those sections when you distribute them as '
|
||||
'separate works. But when you distribute the same sections as '
|
||||
'separate works. But when you distribute the same sections as '
|
||||
'part of a whole which is a work based on the Program, the '
|
||||
'distribution of the whole must be on the terms of this License, '
|
||||
'whose permissions for other licensees extend to the entire '
|
||||
@ -350,17 +350,17 @@ class Ui_AboutDialog(object):
|
||||
'medium customarily used for software interchange; or,\n'
|
||||
'\n'
|
||||
'c) Accompany it with the information you received as to the '
|
||||
'offer to distribute corresponding source code. (This '
|
||||
'offer to distribute corresponding source code. (This '
|
||||
'alternative is allowed only for noncommercial distribution and '
|
||||
'only if you received the program in object code or executable '
|
||||
'form with such an offer, in accord with Subsection b above.)\n'
|
||||
'\n'
|
||||
'The source code for a work means the preferred form of the work '
|
||||
'for making modifications to it. For an executable work, '
|
||||
'for making modifications to it. For an executable work, '
|
||||
'complete source code means all the source code for all modules '
|
||||
'it contains, plus any associated interface definition files, '
|
||||
'plus the scripts used to control compilation and installation of '
|
||||
'the executable. However, as a special exception, the source '
|
||||
'the executable. However, as a special exception, the source '
|
||||
'code distributed need not include anything that is normally '
|
||||
'distributed (in either source or binary form) with the major '
|
||||
'components (compiler, kernel, and so on) of the operating system '
|
||||
@ -374,7 +374,7 @@ class Ui_AboutDialog(object):
|
||||
'not compelled to copy the source along with the object code.\n'
|
||||
'\n'
|
||||
'4. You may not copy, modify, sublicense, or distribute the '
|
||||
'Program except as expressly provided under this License. Any '
|
||||
'Program except as expressly provided under this License. Any '
|
||||
'attempt otherwise to copy, modify, sublicense or distribute the '
|
||||
'Program is void, and will automatically terminate your rights '
|
||||
'under this License. However, parties who have received copies, '
|
||||
@ -383,10 +383,10 @@ class Ui_AboutDialog(object):
|
||||
'compliance.\n'
|
||||
'\n'
|
||||
'5. You are not required to accept this License, since you have '
|
||||
'not signed it. However, nothing else grants you permission to '
|
||||
'modify or distribute the Program or its derivative works. These '
|
||||
'not signed it. However, nothing else grants you permission to '
|
||||
'modify or distribute the Program or its derivative works. These '
|
||||
'actions are prohibited by law if you do not accept this '
|
||||
'License. Therefore, by modifying or distributing the Program '
|
||||
'License. Therefore, by modifying or distributing the Program '
|
||||
'(or any work based on the Program), you indicate your acceptance '
|
||||
'of this License to do so, and all its terms and conditions for '
|
||||
'copying, distributing or modifying the Program or works based on '
|
||||
@ -395,7 +395,7 @@ class Ui_AboutDialog(object):
|
||||
'6. Each time you redistribute the Program (or any work based on '
|
||||
'the Program), the recipient automatically receives a license '
|
||||
'from the original licensor to copy, distribute or modify the '
|
||||
'Program subject to these terms and conditions. You may not '
|
||||
'Program subject to these terms and conditions. You may not '
|
||||
'impose any further restrictions on the recipients\' exercise of '
|
||||
'the rights granted herein. You are not responsible for enforcing '
|
||||
'compliance by third parties to this License.\n'
|
||||
@ -405,10 +405,10 @@ class Ui_AboutDialog(object):
|
||||
'patent issues), conditions are imposed on you (whether by court '
|
||||
'order, agreement or otherwise) that contradict the conditions of '
|
||||
'this License, they do not excuse you from the conditions of this '
|
||||
'License. If you cannot distribute so as to satisfy '
|
||||
'License. If you cannot distribute so as to satisfy '
|
||||
'simultaneously your obligations under this License and any other '
|
||||
'pertinent obligations, then as a consequence you may not '
|
||||
'distribute the Program at all. For example, if a patent license '
|
||||
'distribute the Program at all. For example, if a patent license '
|
||||
'would not permit royalty-free redistribution of the Program by '
|
||||
'all those who receive copies directly or indirectly through you, '
|
||||
'then the only way you could satisfy both it and this License '
|
||||
@ -423,7 +423,7 @@ class Ui_AboutDialog(object):
|
||||
'any patents or other property right claims or to contest '
|
||||
'validity of any such claims; this section has the sole purpose '
|
||||
'of protecting the integrity of the free software distribution '
|
||||
'system, which is implemented by public license practices. Many '
|
||||
'system, which is implemented by public license practices. Many '
|
||||
'people have made generous contributions to the wide range of '
|
||||
'software distributed through that system in reliance on '
|
||||
'consistent application of that system; it is up to the '
|
||||
@ -439,29 +439,29 @@ class Ui_AboutDialog(object):
|
||||
'interfaces, the original copyright holder who places the Program '
|
||||
'under this License may add an explicit geographical distribution '
|
||||
'limitation excluding those countries, so that distribution is '
|
||||
'permitted only in or among countries not thus excluded. In such '
|
||||
'permitted only in or among countries not thus excluded. In such '
|
||||
'case, this License incorporates the limitation as if written in '
|
||||
'the body of this License.\n'
|
||||
'\n'
|
||||
'9. The Free Software Foundation may publish revised and/or new '
|
||||
'versions of the General Public License from time to time. Such '
|
||||
'versions of the General Public License from time to time. Such '
|
||||
'new versions will be similar in spirit to the present version, '
|
||||
'but may differ in detail to address new problems or concerns.\n'
|
||||
'\n'
|
||||
'Each version is given a distinguishing version number. If the '
|
||||
'Each version is given a distinguishing version number. If the '
|
||||
'Program specifies a version number of this License which applies '
|
||||
'to it and \"any later version\', you have the option of '
|
||||
'to it and "any later version", you have the option of '
|
||||
'following the terms and conditions either of that version or of '
|
||||
'any later version published by the Free Software Foundation. If '
|
||||
'any later version published by the Free Software Foundation. If '
|
||||
'the Program does not specify a version number of this License, '
|
||||
'you may choose any version ever published by the Free Software '
|
||||
'Foundation.\n'
|
||||
'\n'
|
||||
'10. If you wish to incorporate parts of the Program into other '
|
||||
'free programs whose distribution conditions are different, write '
|
||||
'to the author to ask for permission. For software which is '
|
||||
'to the author to ask for permission. For software which is '
|
||||
'copyrighted by the Free Software Foundation, write to the Free '
|
||||
'Software Foundation; we sometimes make exceptions for this. Our '
|
||||
'Software Foundation; we sometimes make exceptions for this. Our '
|
||||
'decision will be guided by the two goals of preserving the free '
|
||||
'status of all derivatives of our free software and of promoting '
|
||||
'the sharing and reuse of software generally.\n'
|
||||
@ -470,12 +470,12 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO '
|
||||
'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE '
|
||||
'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
|
||||
'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
|
||||
'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT '
|
||||
'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, '
|
||||
'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY '
|
||||
'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
|
||||
'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
|
||||
'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
|
||||
'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
|
||||
'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY '
|
||||
'SERVICING, REPAIR OR CORRECTION.\n'
|
||||
'\n'
|
||||
@ -499,7 +499,7 @@ class Ui_AboutDialog(object):
|
||||
'this is to make it free software which everyone can redistribute '
|
||||
'and change under these terms.\n'
|
||||
'\n'
|
||||
'To do so, attach the following notices to the program. It is '
|
||||
'To do so, attach the following notices to the program. It is '
|
||||
'safest to attach them to the start of each source file to most '
|
||||
'effectively convey the exclusion of warranty; and each file '
|
||||
'should have at least the "copyright" line and a pointer to where '
|
||||
@ -507,7 +507,7 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'<one line to give the program\'s name and a brief idea of what '
|
||||
'it does.>\n'
|
||||
'Copyright (C) <year> <name of author>\n'
|
||||
'Copyright (C) <year> <name of author>\n'
|
||||
'\n'
|
||||
'This program is free software; you can redistribute it and/or '
|
||||
'modify it under the terms of the GNU General Public License as '
|
||||
@ -516,7 +516,7 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'This program is distributed in the hope that it will be useful, '
|
||||
'but WITHOUT ANY WARRANTY; without even the implied warranty of '
|
||||
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
|
||||
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
|
||||
'GNU General Public License for more details.\n'
|
||||
'\n'
|
||||
'You should have received a copy of the GNU General Public '
|
||||
@ -537,14 +537,14 @@ class Ui_AboutDialog(object):
|
||||
'under certain conditions; type "show c" for details.\n'
|
||||
'\n'
|
||||
'The hypothetical commands "show w" and "show c" should show '
|
||||
'the appropriate parts of the General Public License. Of course, '
|
||||
'the appropriate parts of the General Public License. Of course, '
|
||||
'the commands you use may be called something other than "show '
|
||||
'w" and "show c"; they could even be mouse-clicks or menu items--'
|
||||
'whatever suits your program.\n'
|
||||
'\n'
|
||||
'You should also get your employer (if you work as a programmer) '
|
||||
'or your school, if any, to sign a "copyright disclaimer" for the '
|
||||
'program, if necessary. Here is a sample; alter the names:\n'
|
||||
'program, if necessary. Here is a sample; alter the names:\n'
|
||||
'\n'
|
||||
'Yoyodyne, Inc., hereby disclaims all copyright interest in the '
|
||||
'program "Gnomovision" (which makes passes at compilers) written '
|
||||
@ -554,9 +554,9 @@ class Ui_AboutDialog(object):
|
||||
'Ty Coon, President of Vice\n'
|
||||
'\n'
|
||||
'This General Public License does not permit incorporating your '
|
||||
'program into proprietary programs. If your program is a '
|
||||
'program into proprietary programs. If your program is a '
|
||||
'subroutine library, you may consider it more useful to permit '
|
||||
'linking proprietary applications with the library. If this is '
|
||||
'linking proprietary applications with the library. If this is '
|
||||
'what you want to do, use the GNU Lesser General Public License '
|
||||
'instead of this License.'))
|
||||
self.aboutNotebook.setTabText(
|
||||
@ -565,3 +565,4 @@ class Ui_AboutDialog(object):
|
||||
self.contributeButton.setText(translate('OpenLP.AboutForm',
|
||||
'Contribute'))
|
||||
self.closeButton.setText(translate('OpenLP.AboutForm', 'Close'))
|
||||
|
||||
|
82
openlp/core/ui/exceptiondialog.py
Normal file
82
openlp/core/ui/exceptiondialog.py
Normal file
@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
# Carsten Tinggaard, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
||||
class Ui_ExceptionDialog(object):
|
||||
def setupUi(self, exceptionDialog):
|
||||
exceptionDialog.setObjectName(u'exceptionDialog')
|
||||
exceptionDialog.resize(580, 407)
|
||||
self.exceptionLayout = QtGui.QVBoxLayout(exceptionDialog)
|
||||
self.exceptionLayout.setSpacing(8)
|
||||
self.exceptionLayout.setMargin(8)
|
||||
self.exceptionLayout.setObjectName(u'exceptionLayout')
|
||||
self.messageLayout = QtGui.QHBoxLayout()
|
||||
self.messageLayout.setSpacing(0)
|
||||
self.messageLayout.setContentsMargins(0, -1, 0, -1)
|
||||
self.messageLayout.setObjectName(u'messageLayout')
|
||||
self.bugLabel = QtGui.QLabel(exceptionDialog)
|
||||
self.bugLabel.setMinimumSize(QtCore.QSize(64, 64))
|
||||
self.bugLabel.setMaximumSize(QtCore.QSize(64, 64))
|
||||
self.bugLabel.setText(u'')
|
||||
self.bugLabel.setPixmap(QtGui.QPixmap(u':/graphics/exception.png'))
|
||||
self.bugLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.bugLabel.setObjectName(u'bugLabel')
|
||||
self.messageLayout.addWidget(self.bugLabel)
|
||||
self.messageLabel = QtGui.QLabel(exceptionDialog)
|
||||
self.messageLabel.setWordWrap(True)
|
||||
self.messageLabel.setObjectName(u'messageLabel')
|
||||
self.messageLayout.addWidget(self.messageLabel)
|
||||
self.exceptionLayout.addLayout(self.messageLayout)
|
||||
self.exceptionTextEdit = QtGui.QPlainTextEdit(exceptionDialog)
|
||||
self.exceptionTextEdit.setReadOnly(True)
|
||||
self.exceptionTextEdit.setBackgroundVisible(False)
|
||||
self.exceptionTextEdit.setObjectName(u'exceptionTextEdit')
|
||||
self.exceptionLayout.addWidget(self.exceptionTextEdit)
|
||||
self.exceptionButtonBox = QtGui.QDialogButtonBox(exceptionDialog)
|
||||
self.exceptionButtonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.exceptionButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
|
||||
self.exceptionButtonBox.setObjectName(u'exceptionButtonBox')
|
||||
self.exceptionLayout.addWidget(self.exceptionButtonBox)
|
||||
|
||||
self.retranslateUi(exceptionDialog)
|
||||
QtCore.QObject.connect(self.exceptionButtonBox,
|
||||
QtCore.SIGNAL(u'accepted()'), exceptionDialog.accept)
|
||||
QtCore.QObject.connect(self.exceptionButtonBox,
|
||||
QtCore.SIGNAL(u'rejected()'), exceptionDialog.reject)
|
||||
QtCore.QMetaObject.connectSlotsByName(exceptionDialog)
|
||||
|
||||
def retranslateUi(self, exceptionDialog):
|
||||
exceptionDialog.setWindowTitle(
|
||||
translate('OpenLP.ExceptionDialog', 'Error Occured'))
|
||||
self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! '
|
||||
'OpenLP hit a problem, and couldn\'t recover. The text in the box '
|
||||
'below contains information that might be helpful to the OpenLP '
|
||||
'developers, so please e-mail it to bugs@openlp.org, along with a '
|
||||
'detailed description of what you were doing when the problem '
|
||||
'occurred.'))
|
38
openlp/core/ui/exceptionform.py
Normal file
38
openlp/core/ui/exceptionform.py
Normal file
@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
# Carsten Tinggaard, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from exceptiondialog import Ui_ExceptionDialog
|
||||
from openlp.core.lib import translate
|
||||
|
||||
class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
||||
"""
|
||||
The exception dialog
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
@ -195,6 +195,19 @@ class GeneralTab(SettingsTab):
|
||||
self.currentYValueLabel.setObjectName(u'currentYValueLabel')
|
||||
self.currentYLayout.addWidget(self.currentYValueLabel)
|
||||
self.currentLayout.addLayout(self.currentYLayout)
|
||||
self.currentWidthLayout = QtGui.QVBoxLayout()
|
||||
self.currentWidthLayout.setSpacing(0)
|
||||
self.currentWidthLayout.setMargin(0)
|
||||
self.currentWidthLayout.setObjectName(u'currentWidthLayout')
|
||||
self.currentWidthLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.currentWidthLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.currentWidthLabel.setObjectName(u'currentWidthLabel')
|
||||
self.currentWidthLayout.addWidget(self.currentWidthLabel)
|
||||
self.currentWidthValueLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.currentWidthValueLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.currentWidthValueLabel.setObjectName(u'currentWidthValueLabel')
|
||||
self.currentWidthLayout.addWidget(self.currentWidthValueLabel)
|
||||
self.currentLayout.addLayout(self.currentWidthLayout)
|
||||
self.currentHeightLayout = QtGui.QVBoxLayout()
|
||||
self.currentHeightLayout.setSpacing(0)
|
||||
self.currentHeightLayout.setMargin(0)
|
||||
@ -209,19 +222,6 @@ class GeneralTab(SettingsTab):
|
||||
self.currentHeightValueLabel.setObjectName(u'Height')
|
||||
self.currentHeightLayout.addWidget(self.currentHeightValueLabel)
|
||||
self.currentLayout.addLayout(self.currentHeightLayout)
|
||||
self.currentWidthLayout = QtGui.QVBoxLayout()
|
||||
self.currentWidthLayout.setSpacing(0)
|
||||
self.currentWidthLayout.setMargin(0)
|
||||
self.currentWidthLayout.setObjectName(u'currentWidthLayout')
|
||||
self.currentWidthLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.currentWidthLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.currentWidthLabel.setObjectName(u'currentWidthLabel')
|
||||
self.currentWidthLayout.addWidget(self.currentWidthLabel)
|
||||
self.currentWidthValueLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.currentWidthValueLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.currentWidthValueLabel.setObjectName(u'currentWidthValueLabel')
|
||||
self.currentWidthLayout.addWidget(self.currentWidthValueLabel)
|
||||
self.currentLayout.addLayout(self.currentWidthLayout)
|
||||
self.displayLayout.addLayout(self.currentLayout)
|
||||
self.overrideCheckBox = QtGui.QCheckBox(self.displayGroupBox)
|
||||
self.overrideCheckBox.setObjectName(u'overrideCheckBox')
|
||||
@ -256,18 +256,6 @@ class GeneralTab(SettingsTab):
|
||||
self.customYValueEdit.setObjectName(u'customYValueEdit')
|
||||
self.customYLayout.addWidget(self.customYValueEdit)
|
||||
self.customLayout.addLayout(self.customYLayout)
|
||||
self.customHeightLayout = QtGui.QVBoxLayout()
|
||||
self.customHeightLayout.setSpacing(0)
|
||||
self.customHeightLayout.setMargin(0)
|
||||
self.customHeightLayout.setObjectName(u'customHeightLayout')
|
||||
self.customHeightLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.customHeightLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.customHeightLabel.setObjectName(u'customHeightLabel')
|
||||
self.customHeightLayout.addWidget(self.customHeightLabel)
|
||||
self.customHeightValueEdit = QtGui.QLineEdit(self.displayGroupBox)
|
||||
self.customHeightValueEdit.setObjectName(u'customHeightValueEdit')
|
||||
self.customHeightLayout.addWidget(self.customHeightValueEdit)
|
||||
self.customLayout.addLayout(self.customHeightLayout)
|
||||
self.customWidthLayout = QtGui.QVBoxLayout()
|
||||
self.customWidthLayout.setSpacing(0)
|
||||
self.customWidthLayout.setMargin(0)
|
||||
@ -281,6 +269,18 @@ class GeneralTab(SettingsTab):
|
||||
self.customWidthValueEdit.setObjectName(u'customWidthValueEdit')
|
||||
self.customWidthLayout.addWidget(self.customWidthValueEdit)
|
||||
self.customLayout.addLayout(self.customWidthLayout)
|
||||
self.customHeightLayout = QtGui.QVBoxLayout()
|
||||
self.customHeightLayout.setSpacing(0)
|
||||
self.customHeightLayout.setMargin(0)
|
||||
self.customHeightLayout.setObjectName(u'customHeightLayout')
|
||||
self.customHeightLabel = QtGui.QLabel(self.displayGroupBox)
|
||||
self.customHeightLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.customHeightLabel.setObjectName(u'customHeightLabel')
|
||||
self.customHeightLayout.addWidget(self.customHeightLabel)
|
||||
self.customHeightValueEdit = QtGui.QLineEdit(self.displayGroupBox)
|
||||
self.customHeightValueEdit.setObjectName(u'customHeightValueEdit')
|
||||
self.customHeightLayout.addWidget(self.customHeightValueEdit)
|
||||
self.customLayout.addLayout(self.customHeightLayout)
|
||||
self.displayLayout.addLayout(self.customLayout)
|
||||
# Bottom spacer
|
||||
self.generalRightSpacer = QtGui.QSpacerItem(20, 40,
|
||||
@ -476,7 +476,6 @@ class GeneralTab(SettingsTab):
|
||||
# Order is important so be careful if you change
|
||||
if self.overrideChanged or postUpdate:
|
||||
Receiver.send_message(u'config_screen_changed')
|
||||
Receiver.send_message(u'config_updated')
|
||||
self.overrideChanged = False
|
||||
|
||||
def onOverrideCheckBoxToggled(self, checked):
|
||||
|
@ -97,6 +97,7 @@ class MainDisplay(DisplayWidget):
|
||||
self.screens = screens
|
||||
self.isLive = live
|
||||
self.alertTab = None
|
||||
self.hide_mode = None
|
||||
self.setWindowTitle(u'OpenLP Display')
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
|
||||
QtCore.Qt.WindowStaysOnTopHint)
|
||||
@ -125,6 +126,8 @@ class MainDisplay(DisplayWidget):
|
||||
self.frame = self.page.mainFrame()
|
||||
QtCore.QObject.connect(self.webView,
|
||||
QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
|
||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
|
||||
@ -138,7 +141,7 @@ class MainDisplay(DisplayWidget):
|
||||
painter_image = QtGui.QPainter()
|
||||
painter_image.begin(self.black)
|
||||
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
|
||||
#Build the initial frame.
|
||||
# Build the initial frame.
|
||||
initialFrame = QtGui.QImage(
|
||||
self.screens.current[u'size'].width(),
|
||||
self.screens.current[u'size'].height(),
|
||||
@ -338,6 +341,9 @@ class MainDisplay(DisplayWidget):
|
||||
self.webView.setHtml(html)
|
||||
if serviceItem.foot_text and serviceItem.foot_text:
|
||||
self.footer(serviceItem.foot_text)
|
||||
# if was hidden keep it hidden
|
||||
if self.hide_mode and self.isLive:
|
||||
self.hideDisplay(self.hide_mode)
|
||||
|
||||
def footer(self, text):
|
||||
"""
|
||||
@ -363,6 +369,7 @@ class MainDisplay(DisplayWidget):
|
||||
self.frame.evaluateJavaScript(u'show_blank("theme");')
|
||||
if mode != HideMode.Screen and self.isHidden():
|
||||
self.setVisible(True)
|
||||
self.hide_mode = mode
|
||||
|
||||
def showDisplay(self):
|
||||
"""
|
||||
@ -376,6 +383,7 @@ class MainDisplay(DisplayWidget):
|
||||
self.setVisible(True)
|
||||
# Trigger actions when display is active again
|
||||
Receiver.send_message(u'maindisplay_active')
|
||||
self.hide_mode = None
|
||||
|
||||
class AudioPlayer(QtCore.QObject):
|
||||
"""
|
||||
|
@ -93,7 +93,7 @@ class Ui_PluginViewDialog(object):
|
||||
self.pluginListButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
|
||||
self.pluginListButtonBox.setObjectName(u'pluginListButtonBox')
|
||||
self.pluginLayout.addWidget(self.pluginListButtonBox)
|
||||
|
||||
self.versionNumberLabel.setText(u'')
|
||||
self.retranslateUi(pluginViewDialog)
|
||||
QtCore.QObject.connect(self.pluginListButtonBox,
|
||||
QtCore.SIGNAL(u'accepted()'), pluginViewDialog.close)
|
||||
@ -106,8 +106,6 @@ class Ui_PluginViewDialog(object):
|
||||
translate('OpenLP.PluginForm', 'Plugin Details'))
|
||||
self.versionLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'Version:'))
|
||||
self.versionNumberLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'TextLabel'))
|
||||
self.aboutLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'About:'))
|
||||
self.statusLabel.setText(
|
||||
@ -116,3 +114,4 @@ class Ui_PluginViewDialog(object):
|
||||
translate('OpenLP.PluginForm', 'Active'))
|
||||
self.statusComboBox.setItemText(1,
|
||||
translate('OpenLP.PluginForm', 'Inactive'))
|
||||
|
||||
|
@ -58,6 +58,9 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
|
||||
Load the plugin details into the screen
|
||||
"""
|
||||
self.pluginListWidget.clear()
|
||||
self.programaticChange = True
|
||||
self._clearDetails()
|
||||
self.programaticChange = True
|
||||
for plugin in self.parent.plugin_manager.plugins:
|
||||
item = QtGui.QListWidgetItem(self.pluginListWidget)
|
||||
# We do this just to make 100% sure the status is an integer as
|
||||
|
@ -632,6 +632,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
|
||||
def onLoadService(self, lastService=False):
|
||||
if lastService:
|
||||
if not self.parent.recentFiles:
|
||||
return
|
||||
filename = self.parent.recentFiles[0]
|
||||
else:
|
||||
filename = QtGui.QFileDialog.getOpenFileName(
|
||||
|
@ -30,6 +30,7 @@ import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
|
||||
from settingsdialog import Ui_SettingsDialog
|
||||
|
||||
@ -87,6 +88,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
for tabIndex in range(0, self.settingsTabWidget.count()):
|
||||
self.settingsTabWidget.widget(tabIndex).save()
|
||||
# Must go after all settings are save
|
||||
Receiver.send_message(u'config_updated')
|
||||
return QtGui.QDialog.accept(self)
|
||||
|
||||
def postSetUp(self):
|
||||
|
@ -162,11 +162,6 @@ class SlideController(QtGui.QWidget):
|
||||
sizeToolbarPolicy.setHeightForWidth(
|
||||
self.Toolbar.sizePolicy().hasHeightForWidth())
|
||||
self.Toolbar.setSizePolicy(sizeToolbarPolicy)
|
||||
# if self.isLive:
|
||||
# self.Toolbar.addToolbarButton(
|
||||
# u'First Slide', u':/slides/slide_first.png',
|
||||
# translate('OpenLP.SlideController', 'Move to first'),
|
||||
# self.onSlideSelectedFirst)
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Previous Slide', u':/slides/slide_previous.png',
|
||||
translate('OpenLP.SlideController', 'Move to previous'),
|
||||
@ -175,11 +170,6 @@ class SlideController(QtGui.QWidget):
|
||||
u'Next Slide', u':/slides/slide_next.png',
|
||||
translate('OpenLP.SlideController', 'Move to next'),
|
||||
self.onSlideSelectedNext)
|
||||
# if self.isLive:
|
||||
# self.Toolbar.addToolbarButton(
|
||||
# u'Last Slide', u':/slides/slide_last.png',
|
||||
# translate('OpenLP.SlideController', 'Move to last'),
|
||||
# self.onSlideSelectedLast)
|
||||
if self.isLive:
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.HideMenu = QtGui.QToolButton(self.Toolbar)
|
||||
@ -219,7 +209,7 @@ class SlideController(QtGui.QWidget):
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Edit Song', u':/general/general_edit.png',
|
||||
translate('OpenLP.SlideController', 'Edit and re-preview Song'),
|
||||
translate('OpenLP.SlideController', 'Edit and re-preview song'),
|
||||
self.onEditSong)
|
||||
if isLive:
|
||||
self.Toolbar.addToolbarSeparator(u'Loop Separator')
|
||||
@ -279,11 +269,11 @@ class SlideController(QtGui.QWidget):
|
||||
if isLive:
|
||||
self.SongMenu = QtGui.QToolButton(self.Toolbar)
|
||||
self.SongMenu.setText(translate('OpenLP.SlideController',
|
||||
'Go to Verse'))
|
||||
'Go To'))
|
||||
self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
|
||||
self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
|
||||
self.SongMenu.setMenu(QtGui.QMenu(
|
||||
translate('OpenLP.SlideController', 'Go to Verse'),
|
||||
translate('OpenLP.SlideController', 'Go To'),
|
||||
self.Toolbar))
|
||||
self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
|
||||
# Screen preview area
|
||||
|
@ -576,7 +576,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
translate('OpenLP.ThemeManager', 'Theme Exists'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
'A theme with this name already '
|
||||
'exists. Would you like to overwrite it?'),
|
||||
'exists. Would you like to overwrite it?'),
|
||||
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.No)
|
||||
if self.saveThemeName != u'':
|
||||
|
@ -112,7 +112,11 @@ class AppLocation(object):
|
||||
The directory type you want, for instance the data directory.
|
||||
"""
|
||||
if dir_type == AppLocation.AppDir:
|
||||
return os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
if hasattr(sys, u'frozen') and sys.frozen == 1:
|
||||
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
else:
|
||||
app_path = os.path.split(openlp.__file__)[0]
|
||||
return app_path
|
||||
elif dir_type == AppLocation.ConfigDir:
|
||||
if sys.platform == u'win32':
|
||||
path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
|
||||
|
@ -55,7 +55,7 @@ class LanguageManager(object):
|
||||
if LanguageManager.AutoLanguage:
|
||||
language = QtCore.QLocale.system().name()
|
||||
lang_path = AppLocation.get_directory(AppLocation.AppDir)
|
||||
lang_path = os.path.join(lang_path, u'resources', u'i18n')
|
||||
lang_path = os.path.join(lang_path, u'i18n')
|
||||
app_translator = QtCore.QTranslator()
|
||||
if app_translator.load("openlp_" + language, lang_path):
|
||||
return app_translator
|
||||
@ -66,7 +66,7 @@ class LanguageManager(object):
|
||||
Find all available language files in this OpenLP install
|
||||
"""
|
||||
trans_dir = AppLocation.get_directory(AppLocation.AppDir)
|
||||
trans_dir = QtCore.QDir(os.path.join(trans_dir, u'resources', u'i18n'))
|
||||
trans_dir = QtCore.QDir(os.path.join(trans_dir, u'i18n'))
|
||||
file_names = trans_dir.entryList(QtCore.QStringList("*.qm"),
|
||||
QtCore.QDir.Files, QtCore.QDir.Name)
|
||||
for name in file_names:
|
||||
|
@ -196,10 +196,10 @@ class BiblesTab(SettingsTab):
|
||||
self.show_new_chapters = True
|
||||
|
||||
def onBibleDualCheckBox(self, check_state):
|
||||
self.duel_bibles = False
|
||||
self.dual_bibles = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.duel_bibles = True
|
||||
self.dual_bibles = True
|
||||
|
||||
def load(self):
|
||||
settings = QtCore.QSettings()
|
||||
@ -212,12 +212,12 @@ class BiblesTab(SettingsTab):
|
||||
u'verse layout style', QtCore.QVariant(0)).toInt()[0]
|
||||
self.bible_theme = unicode(
|
||||
settings.value(u'bible theme', QtCore.QVariant(u'')).toString())
|
||||
self.duel_bibles = settings.value(
|
||||
self.dual_bibles = settings.value(
|
||||
u'dual bibles', QtCore.QVariant(True)).toBool()
|
||||
self.NewChaptersCheckBox.setChecked(self.show_new_chapters)
|
||||
self.DisplayStyleComboBox.setCurrentIndex(self.display_style)
|
||||
self.LayoutStyleComboBox.setCurrentIndex(self.layout_style)
|
||||
self.BibleDualCheckBox.setChecked(self.duel_bibles)
|
||||
self.BibleDualCheckBox.setChecked(self.dual_bibles)
|
||||
settings.endGroup()
|
||||
|
||||
def save(self):
|
||||
@ -229,7 +229,7 @@ class BiblesTab(SettingsTab):
|
||||
QtCore.QVariant(self.display_style))
|
||||
settings.setValue(u'verse layout style',
|
||||
QtCore.QVariant(self.layout_style))
|
||||
settings.setValue(u'dual bibles', QtCore.QVariant(self.duel_bibles))
|
||||
settings.setValue(u'dual bibles', QtCore.QVariant(self.dual_bibles))
|
||||
settings.setValue(u'bible theme', QtCore.QVariant(self.bible_theme))
|
||||
settings.endGroup()
|
||||
|
||||
|
@ -353,7 +353,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
QtGui.QMessageBox.information(self.bible_plugin.mediaItem,
|
||||
translate('BiblesPlugin.BibleDB', 'Book not found'),
|
||||
translate('BiblesPlugin.BibleDB', 'The book you requested '
|
||||
'could not be found in this bible. Please check your '
|
||||
'could not be found in this bible. Please check your '
|
||||
'spelling and that this is a complete bible not just '
|
||||
'one testament.'))
|
||||
return verse_list
|
||||
|
@ -246,7 +246,7 @@ class BibleManager(object):
|
||||
translate('BiblesPlugin.BibleManager',
|
||||
'Scripture Reference Error'),
|
||||
translate('BiblesPlugin.BibleManager', 'Your scripture '
|
||||
'reference is either not supported by OpenLP or invalid. '
|
||||
'reference is either not supported by OpenLP or invalid. '
|
||||
'Please make sure your reference conforms to one of the '
|
||||
'following patterns:\n\n'
|
||||
'Book Chapter\n'
|
||||
|
@ -275,8 +275,9 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.SearchProgress.setObjectName(u'SearchProgress')
|
||||
|
||||
def configUpdated(self):
|
||||
log.debug(u'configUpdated')
|
||||
if QtCore.QSettings().value(self.settingsSection + u'/dual bibles',
|
||||
QtCore.QVariant(False)).toBool():
|
||||
QtCore.QVariant(True)).toBool():
|
||||
self.AdvancedSecondBibleLabel.setVisible(True)
|
||||
self.AdvancedSecondBibleComboBox.setVisible(True)
|
||||
self.QuickSecondVersionLabel.setVisible(True)
|
||||
|
@ -26,8 +26,7 @@
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, translate
|
||||
from openlp.core.ui import SpellTextEdit
|
||||
from openlp.core.lib import build_icon, translate, SpellTextEdit
|
||||
|
||||
class Ui_CustomEditDialog(object):
|
||||
def setupUi(self, customEditDialog):
|
||||
|
@ -212,7 +212,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self, translate('PresentationPlugin.MediaItem',
|
||||
'Unsupported File'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'This type of presentation is not supported'))
|
||||
'This type of presentation is not supported.'))
|
||||
continue
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
||||
|
@ -1,119 +1,57 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenLP Controller</title>
|
||||
<script type='text/javascript'>
|
||||
|
||||
function send_event(eventname, data){
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = function() {
|
||||
if(req.readyState==4)
|
||||
response(eventname, req);
|
||||
}
|
||||
var url = '';
|
||||
if(eventname.substr(-8) == '_request')
|
||||
url = 'request';
|
||||
else
|
||||
url = 'send';
|
||||
url += '/' + eventname;
|
||||
if(data!=null)
|
||||
url += '?q=' + escape(data);
|
||||
req.open('GET', url, true);
|
||||
req.send();
|
||||
}
|
||||
function failed_response(eventname, req){
|
||||
switch(eventname){
|
||||
case 'remotes_poll_request':
|
||||
if(req.status==408)
|
||||
send_event("remotes_poll_request");
|
||||
break;
|
||||
}
|
||||
}
|
||||
function response(eventname, req){
|
||||
if(req.status!=200){
|
||||
failed_response(eventname, req);
|
||||
return;
|
||||
}
|
||||
text = req.responseText;
|
||||
switch(eventname){
|
||||
case 'servicemanager_list_request':
|
||||
var data = eval('(' + text + ')');
|
||||
var html = '<table>';
|
||||
for(row in data){
|
||||
html += '<tr onclick="send_event('
|
||||
html += "'servicemanager_set_item', " + row + ')"';
|
||||
if(data[row]['selected'])
|
||||
html += ' style="font-weight: bold"';
|
||||
html += '>'
|
||||
html += '<td>' + (parseInt(row)+1) + '</td>'
|
||||
html += '<td>' + data[row]['title'] + '</td>'
|
||||
html += '<td>' + data[row]['plugin'] + '</td>'
|
||||
html += '<td>' + data[row]['notes'] + '</td>'
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
document.getElementById('service').innerHTML = html;
|
||||
break;
|
||||
case 'slidecontroller_live_text_request':
|
||||
var data = eval('(' + text + ')');
|
||||
var html = '<table>';
|
||||
for(row in data){
|
||||
html += '<tr onclick="send_event('
|
||||
html += "'slidecontroller_live_set', " + row + ')"';
|
||||
if(data[row]['selected'])
|
||||
html += ' style="font-weight: bold"';
|
||||
html += '>';
|
||||
html += '<td>' + data[row]['tag'] + '</td>';
|
||||
html += '<td>' + data[row]['text'].replace(/\n/g, '<br>');
|
||||
html += '</td></tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
document.getElementById('currentitem').innerHTML = html;
|
||||
break;
|
||||
case 'remotes_poll_request':
|
||||
send_event("remotes_poll_request");
|
||||
send_event("servicemanager_list_request");
|
||||
send_event("slidecontroller_live_text_request");
|
||||
break;
|
||||
}
|
||||
}
|
||||
send_event("servicemanager_list_request");
|
||||
send_event("slidecontroller_live_text_request");
|
||||
send_event("remotes_poll_request");
|
||||
</script>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>OpenLP Remote Controller</title>
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type='text/javascript' src="/files/openlp.js"></script>
|
||||
<script type='text/javascript' src="/files/init.js"></script>
|
||||
<link rel="stylesheet" href="/files/style.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>OpenLP Controller</h1>
|
||||
<input type='button' value='<- Previous Slide'
|
||||
onclick='send_event("slidecontroller_live_previous");' />
|
||||
<input type='button' value='Next Slide ->'
|
||||
onclick='send_event("slidecontroller_live_next");' />
|
||||
<br/>
|
||||
<input type='button' value='<- Previous Item'
|
||||
onclick='send_event("servicemanager_previous_item");' />
|
||||
<input type='button' value='Next Item ->'
|
||||
onclick='send_event("servicemanager_next_item");' />
|
||||
<br/>
|
||||
<input type='button' value='Blank'
|
||||
onclick='send_event("slidecontroller_live_blank");' />
|
||||
<input type='button' value='Unblank'
|
||||
onclick='send_event("slidecontroller_live_unblank");' />
|
||||
<br/>
|
||||
<label>Alert text</label><input id='alert' type='text' />
|
||||
<input type='button' value='Send'
|
||||
onclick='send_event("alerts_text",
|
||||
document.getElementById("alert").value);' />
|
||||
<p>Quick Links: <a href="#service-manager">Service Manager</a> | <a href="#slide-controller">Slide Controller</a> | <a href="#miscellaneous">Miscellaneous</a></p>
|
||||
<h2 id="service-manager">Service Manager</h2>
|
||||
<div id="service"></div>
|
||||
<p><em>(Click service item to go live.)</em></p>
|
||||
<fieldset>
|
||||
<legend>Controls</legend>
|
||||
<div id="service-buttons">
|
||||
<input type="button" value="Refresh Service" id="servicemanager_list_request" />
|
||||
</div>
|
||||
<div id="item-buttons">
|
||||
<input type="button" value="<- Previous Item" id="servicemanager_previous_item" />
|
||||
<input type="button" value="Next Item ->" id="servicemanager_next_item" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<input type='button' value='Order of service'
|
||||
onclick='send_event("servicemanager_list_request");'>
|
||||
<i>(Click service item to go live.)</i>
|
||||
<div id='service'></div>
|
||||
<h2 id="slide-controller">Slide Controller</h2>
|
||||
<div id="current-item"></div>
|
||||
<p><em>(Click verse to display.)</em></p>
|
||||
<fieldset>
|
||||
<legend>Controls</legend>
|
||||
<div id="item-buttons">
|
||||
<input type="button" value="Refresh Item" id="slidecontroller_live_text_request" />
|
||||
</div>
|
||||
<div id="slide-buttons">
|
||||
<input type="button" value="<- Previous Slide" id="slidecontroller_live_previous" />
|
||||
<input type="button" value="Next Slide ->" id="slidecontroller_live_next" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<input type='button' value='Current item'
|
||||
onclick='send_event("slidecontroller_live_text_request");'>
|
||||
<i>(Click verse to display.)</i>
|
||||
<div id='currentitem'></div>
|
||||
<h2 id="miscellaneous">Miscellaneous</h2>
|
||||
<div id="display-buttons">
|
||||
<input type="button" value="Blank" id="slidecontroller_live_blank" />
|
||||
<input type="button" value="Unblank" id="slidecontroller_live_unblank" />
|
||||
</div>
|
||||
<div id="alert-details">
|
||||
<label for="alert-text">Alert text:</label>
|
||||
<input type="text" id="alert-text" />
|
||||
<input type="button" value="Send" id="alert-send" />
|
||||
</div>
|
||||
<hr>
|
||||
<a href="http://www.openlp.org/">OpenLP website</a>
|
||||
<a href="http://openlp.org/">OpenLP website</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
32
openlp/plugins/remotes/html/init.js
Normal file
32
openlp/plugins/remotes/html/init.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*****************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2010 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
|
||||
* Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin *
|
||||
* Thompson, Jon Tibble, Carsten Tinggaard *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the *
|
||||
* Free Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||
* Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* init.js - In certain browsers (yes, IE, I'm looking at you!), DocumentReady
|
||||
* JavaScript functions can only be run very last on the page. This file is the
|
||||
* last JavaScript file to be included on the page, and provides a work-around
|
||||
* for this bug in certain browsers.
|
||||
*/
|
||||
|
||||
$(document).ready(function () {
|
||||
OpenLP.Events.init();
|
||||
});
|
154
openlp/plugins/remotes/html/jquery.js
vendored
Normal file
154
openlp/plugins/remotes/html/jquery.js
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
/*!
|
||||
* jQuery JavaScript Library v1.4.2
|
||||
* http://jquery.com/
|
||||
*
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* http://sizzlejs.com/
|
||||
* Copyright 2010, The Dojo Foundation
|
||||
* Released under the MIT, BSD, and GPL Licenses.
|
||||
*
|
||||
* Date: Sat Feb 13 22:33:48 2010 -0500
|
||||
*/
|
||||
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
|
||||
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
|
||||
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
|
||||
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
|
||||
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
|
||||
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
|
||||
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
|
||||
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
|
||||
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
|
||||
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
|
||||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
|
||||
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
|
||||
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
|
||||
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
|
||||
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
|
||||
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
|
||||
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
|
||||
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
||||
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
|
||||
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
|
||||
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
|
||||
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
|
||||
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
|
||||
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
|
||||
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
|
||||
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
|
||||
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
|
||||
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
|
||||
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
|
||||
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
|
||||
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
|
||||
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
|
||||
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
|
||||
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
|
||||
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
|
||||
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
|
||||
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
|
||||
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
|
||||
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
|
||||
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
|
||||
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
|
||||
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
|
||||
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
|
||||
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
|
||||
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
|
||||
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
|
||||
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
|
||||
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
|
||||
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
|
||||
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
|
||||
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
|
||||
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
||||
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
|
||||
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
|
||||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
|
||||
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
|
||||
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
||||
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
|
||||
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
|
||||
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
|
||||
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
|
||||
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
|
||||
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
|
||||
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
|
||||
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
|
||||
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
|
||||
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
|
||||
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
|
||||
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
|
||||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
|
||||
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
|
||||
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
|
||||
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
|
||||
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
|
||||
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
|
||||
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
|
||||
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
|
||||
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
|
||||
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
|
||||
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
|
||||
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
|
||||
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
|
||||
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
|
||||
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
|
||||
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
|
||||
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
|
||||
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
|
||||
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
|
||||
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
|
||||
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
|
||||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
|
||||
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
|
||||
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
|
||||
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
|
||||
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
|
||||
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
|
||||
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
|
||||
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
|
||||
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
|
||||
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
|
||||
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
|
||||
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
|
||||
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
|
||||
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
|
||||
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
|
||||
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
|
||||
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
|
||||
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
|
||||
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
|
||||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
|
||||
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
|
||||
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
|
||||
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
|
||||
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
|
||||
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
|
||||
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
|
||||
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
|
||||
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
|
||||
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
|
||||
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
|
||||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
|
||||
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
|
||||
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
|
||||
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
|
||||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
|
||||
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
|
||||
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
|
||||
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
|
||||
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
||||
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
|
||||
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
|
||||
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
|
||||
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
|
||||
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
|
||||
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
|
239
openlp/plugins/remotes/html/openlp.js
Normal file
239
openlp/plugins/remotes/html/openlp.js
Normal file
@ -0,0 +1,239 @@
|
||||
/*****************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2010 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
|
||||
* Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin *
|
||||
* Thompson, Jon Tibble, Carsten Tinggaard *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the *
|
||||
* Free Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||
* Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
*****************************************************************************/
|
||||
|
||||
window["OpenLP"] = {
|
||||
Namespace: {
|
||||
/**
|
||||
* Create a Javascript namespace.
|
||||
* Based on: http://code.google.com/p/namespacedotjs/
|
||||
* Idea behind this is to created nested namespaces that are not ugly.
|
||||
*/
|
||||
create: function (name, attributes) {
|
||||
var parts = name.split('.'),
|
||||
ns = window,
|
||||
i = 0;
|
||||
// find the deepest part of the namespace
|
||||
// that is already defined
|
||||
for(; i < parts.length && parts[i] in ns; i++)
|
||||
ns = ns[parts[i]];
|
||||
// initialize any remaining parts of the namespace
|
||||
for(; i < parts.length; i++)
|
||||
ns = ns[parts[i]] = {};
|
||||
// copy the attributes into the namespace
|
||||
for (var attr in attributes)
|
||||
ns[attr] = attributes[attr];
|
||||
},
|
||||
exists: function (namespace) {
|
||||
/**
|
||||
* Determine the namespace of a page
|
||||
*/
|
||||
page_namespace = $ScribeEngine.Namespace.get_page_namespace();
|
||||
return (namespace == page_namespace);
|
||||
},
|
||||
get_page_namespace: function () {
|
||||
return $("#content > h2").attr("id");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Array.prototype.append = function (elem) {
|
||||
this[this.length] = elem;
|
||||
}
|
||||
|
||||
OpenLP.Namespace.create("OpenLP.Events", {
|
||||
// Local variables
|
||||
onload_functions: Array(),
|
||||
// Functions
|
||||
bindLoad: function (func) {
|
||||
this.onload_functions.append(func);
|
||||
},
|
||||
bindClick: function (selector, func) {
|
||||
$(selector).bind("click", func);
|
||||
},
|
||||
bindChange: function (selector, func) {
|
||||
$(selector).bind("change", func);
|
||||
},
|
||||
bindSubmit: function (selector, func) {
|
||||
$(selector).bind("submit", func);
|
||||
},
|
||||
bindBlur: function (selector, func) {
|
||||
$(selector).bind("blur", func);
|
||||
},
|
||||
bindPaste: function (selector, func) {
|
||||
$(selector).bind("paste", func);
|
||||
},
|
||||
bindKeyUp: function (selector, func) {
|
||||
$(selector).bind("keyup", func);
|
||||
},
|
||||
bindKeyDown: function (selector, func) {
|
||||
$(selector).bind("keydown", func);
|
||||
},
|
||||
bindKeyPress: function (selector, func) {
|
||||
$(selector).bind("keypress", func);
|
||||
},
|
||||
bindMouseEnter: function (selector, func) {
|
||||
$(selector).bind("mouseenter", func);
|
||||
},
|
||||
bindMouseLeave: function (selector, func) {
|
||||
$(selector).bind("mouseleave", func);
|
||||
},
|
||||
liveClick: function (selector, func) {
|
||||
$(selector).live("click", func);
|
||||
},
|
||||
getElement: function(event) {
|
||||
var targ;
|
||||
if (!event) {
|
||||
var event = window.event;
|
||||
}
|
||||
if (event.target) {
|
||||
targ = event.target;
|
||||
}
|
||||
else if (event.srcElement) {
|
||||
targ = event.srcElement;
|
||||
}
|
||||
if (targ.nodeType == 3) {
|
||||
// defeat Safari bug
|
||||
targ = targ.parentNode;
|
||||
}
|
||||
return $(targ);
|
||||
},
|
||||
init: function () {
|
||||
for (idx in this.onload_functions) {
|
||||
func = this.onload_functions[idx];
|
||||
func();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OpenLP.Namespace.create("OpenLP.Remote", {
|
||||
sendEvent: function (eventName, eventData)
|
||||
{
|
||||
var url = "/";
|
||||
if (eventName.substr(-8) == "_request")
|
||||
{
|
||||
url += "request";
|
||||
}
|
||||
else
|
||||
{
|
||||
url += "send";
|
||||
}
|
||||
url += "/" + eventName;
|
||||
var args = {};
|
||||
if (eventData != null && eventData != "")
|
||||
{
|
||||
args.q = escape(eventData);
|
||||
}
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: "json",
|
||||
data: args,
|
||||
success: function (data)
|
||||
{
|
||||
OpenLP.Remote.handleEvent(eventName, data);
|
||||
},
|
||||
error: function (xhr, textStatus, errorThrown)
|
||||
{
|
||||
if (eventName == "remotes_poll_request")
|
||||
{
|
||||
OpenLP.Remote.handleEvent("remotes_poll_request");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleEvent: function (eventName, eventData)
|
||||
{
|
||||
switch (eventName)
|
||||
{
|
||||
case "servicemanager_list_request":
|
||||
var table = $("<table>");
|
||||
$.each(eventData, function (row, item) {
|
||||
var trow = $("<tr>")
|
||||
.attr("value", parseInt(row))
|
||||
.click(OpenLP.Remote.sendSetItem);
|
||||
if (item["selected"])
|
||||
{
|
||||
trow.addClass("selected");
|
||||
}
|
||||
trow.append($("<td>").text(parseInt(row) + 1));
|
||||
trow.append($("<td>").text(item["title"]));
|
||||
trow.append($("<td>").text(item["plugin"]));
|
||||
trow.append($("<td>").text("Notes: " + item["notes"]));
|
||||
table.append(trow);
|
||||
});
|
||||
$("#service").html(table);
|
||||
break;
|
||||
case "slidecontroller_live_text_request":
|
||||
var table = $("<table>");
|
||||
$.each(eventData, function (row, item) {
|
||||
var trow = $("<tr>")
|
||||
.attr("value", parseInt(row))
|
||||
.click(OpenLP.Remote.sendLiveSet);
|
||||
if (item["selected"])
|
||||
{
|
||||
trow.addClass("selected");
|
||||
}
|
||||
trow.append($("<td>").text(item["tag"]));
|
||||
trow.append($("<td>").html(item["text"] ? item["text"].replace(/\\n/g, "<br />") : ""));
|
||||
table.append(trow);
|
||||
});
|
||||
$("#current-item").html(table);
|
||||
break;
|
||||
case "remotes_poll_request":
|
||||
OpenLP.Remote.sendEvent("remotes_poll_request");
|
||||
OpenLP.Remote.sendEvent("servicemanager_list_request");
|
||||
OpenLP.Remote.sendEvent("slidecontroller_live_text_request");
|
||||
break;
|
||||
}
|
||||
},
|
||||
sendLiveSet: function (e)
|
||||
{
|
||||
var id = OpenLP.Events.getElement(e).parent().attr("value");
|
||||
OpenLP.Remote.sendEvent("slidecontroller_live_set", id);
|
||||
return false;
|
||||
},
|
||||
sendSetItem: function (e)
|
||||
{
|
||||
var id = OpenLP.Events.getElement(e).parent().attr("value");
|
||||
OpenLP.Remote.sendEvent("servicemanager_set_item", id);
|
||||
return false;
|
||||
},
|
||||
sendAlert: function (e)
|
||||
{
|
||||
var alert_text = $("#alert-text").val();
|
||||
OpenLP.Remote.sendEvent("alerts_text", alert_text);
|
||||
return false;
|
||||
},
|
||||
buttonClick: function (e)
|
||||
{
|
||||
var id = OpenLP.Events.getElement(e).attr("id");
|
||||
OpenLP.Remote.sendEvent(id);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
OpenLP.Events.bindLoad(function () {
|
||||
OpenLP.Events.bindClick("input[type=button][id!=alert-send]", OpenLP.Remote.buttonClick);
|
||||
OpenLP.Events.bindClick("#alert-send", OpenLP.Remote.sendAlert);
|
||||
OpenLP.Remote.sendEvent("servicemanager_list_request");
|
||||
OpenLP.Remote.sendEvent("slidecontroller_live_text_request");
|
||||
OpenLP.Remote.sendEvent("remotes_poll_request");
|
||||
});
|
44
openlp/plugins/remotes/html/openlp.service.js
Normal file
44
openlp/plugins/remotes/html/openlp.service.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*****************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2010 Raoul Snyman *
|
||||
* Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael *
|
||||
* Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin *
|
||||
* Thompson, Jon Tibble, Carsten Tinggaard *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the *
|
||||
* Free Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
|
||||
* Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
*****************************************************************************/
|
||||
|
||||
OpenLP.Namespace.create("OpenLP.Service", {
|
||||
addServiceItem: function (elem, item)
|
||||
{
|
||||
var trow = $("<tr>")
|
||||
.attr("id", "item-" + item.id)
|
||||
.addClass("item")
|
||||
.append($("<td>").text(item.tag))
|
||||
.append($("<td>").text(item.tag.replace(/\n/g, "<br />")));
|
||||
return false;
|
||||
},
|
||||
sendLive: function (e)
|
||||
{
|
||||
var elem = OpenLP.Events.getElement(e);
|
||||
var row = elem.attr("id").substr(5);
|
||||
elem.addStyle("font-weight", "bold");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
OpenLP.Events.load(function (){
|
||||
OpenLP.Events.liveClick(".item", OpenLP.Service.sendLive);
|
||||
});
|
45
openlp/plugins/remotes/html/style.css
Normal file
45
openlp/plugins/remotes/html/style.css
Normal file
@ -0,0 +1,45 @@
|
||||
body
|
||||
{
|
||||
background-color: #fff;
|
||||
font-family: Lucida Grande, Lucida Sans, Arial, Helvetica, sans-serif;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
color: #3c60a5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover
|
||||
{
|
||||
color: #304d85;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
fieldset
|
||||
{
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
td
|
||||
{
|
||||
cursor: pointer;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
tr:hover
|
||||
{
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
tr.selected:hover
|
||||
{
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.selected
|
||||
{
|
||||
background-color: #ddd;
|
||||
font-weight: bold;
|
||||
}
|
@ -26,9 +26,13 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import urlparse
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
from PyQt4 import QtCore, QtNetwork
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
@ -37,7 +41,7 @@ from openlp.core.utils import AppLocation
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
"""
|
||||
Ability to control OpenLP via a webbrowser
|
||||
e.g. http://localhost:4316/send/slidecontroller_live_next
|
||||
http://localhost:4316/send/alerts_text?q=your%20alert%20text
|
||||
@ -59,7 +63,7 @@ class HttpServer(object):
|
||||
def start_tcp(self):
|
||||
"""
|
||||
Start the http server, use the port in the settings default to 4316
|
||||
Listen out for slide and song changes so they can be broadcast to
|
||||
Listen out for slide and song changes so they can be broadcast to
|
||||
clients. Listen out for socket connections
|
||||
"""
|
||||
log.debug(u'Start TCP server')
|
||||
@ -67,13 +71,13 @@ class HttpServer(object):
|
||||
self.parent.settingsSection + u'/remote port',
|
||||
QtCore.QVariant(4316)).toInt()[0]
|
||||
self.server = QtNetwork.QTcpServer()
|
||||
self.server.listen(QtNetwork.QHostAddress(QtNetwork.QHostAddress.Any),
|
||||
self.server.listen(QtNetwork.QHostAddress(QtNetwork.QHostAddress.Any),
|
||||
port)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_changed'),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_changed'),
|
||||
self.slide_change)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_started'),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_started'),
|
||||
self.item_change)
|
||||
QtCore.QObject.connect(self.server,
|
||||
QtCore.SIGNAL(u'newConnection()'), self.new_connection)
|
||||
@ -92,7 +96,7 @@ class HttpServer(object):
|
||||
"""
|
||||
self.current_item = items[0].title
|
||||
self.send_poll()
|
||||
|
||||
|
||||
def send_poll(self):
|
||||
"""
|
||||
Tell the clients something has changed
|
||||
@ -100,7 +104,7 @@ class HttpServer(object):
|
||||
Receiver.send_message(u'remotes_poll_response',
|
||||
{'slide': self.current_slide,
|
||||
'item': self.current_item})
|
||||
|
||||
|
||||
def new_connection(self):
|
||||
"""
|
||||
A new http connection has been made. Create a client object to handle
|
||||
@ -110,7 +114,7 @@ class HttpServer(object):
|
||||
socket = self.server.nextPendingConnection()
|
||||
if socket:
|
||||
self.connections.append(HttpConnection(self, socket))
|
||||
|
||||
|
||||
def close_connection(self, connection):
|
||||
"""
|
||||
The connection has been closed. Clean up
|
||||
@ -124,9 +128,9 @@ class HttpServer(object):
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
self.server.close()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
"""
|
||||
"""
|
||||
A single connection, this handles communication between the server
|
||||
and the client
|
||||
"""
|
||||
@ -134,7 +138,7 @@ class HttpConnection(object):
|
||||
"""
|
||||
Initialise the http connection. Listen out for socket signals
|
||||
"""
|
||||
log.debug(u'Initialise HttpConnection: %s' %
|
||||
log.debug(u'Initialise HttpConnection: %s' %
|
||||
socket.peerAddress().toString())
|
||||
self.socket = socket
|
||||
self.parent = parent
|
||||
@ -180,13 +184,13 @@ class HttpConnection(object):
|
||||
def serve_file(self, filename):
|
||||
"""
|
||||
Send a file to the socket. For now, just a subset of file types
|
||||
and must be top level inside the html folder.
|
||||
and must be top level inside the html folder.
|
||||
If subfolders requested return 404, easier for security for the present.
|
||||
|
||||
Ultimately for i18n, this could first look for xx/file.html before
|
||||
falling back to file.html... where xx is the language, e.g. 'en'
|
||||
"""
|
||||
log.debug(u'serve file request %s' % filename)
|
||||
log.debug(u'serve file request %s' % filename)
|
||||
if not filename:
|
||||
filename = u'index.html'
|
||||
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
||||
@ -229,8 +233,8 @@ class HttpConnection(object):
|
||||
if not params:
|
||||
return None
|
||||
else:
|
||||
return params['q']
|
||||
|
||||
return params['q']
|
||||
|
||||
def process_event(self, event, params):
|
||||
"""
|
||||
Send a signal to openlp to perform an action.
|
||||
@ -239,21 +243,27 @@ class HttpConnection(object):
|
||||
"""
|
||||
log.debug(u'Processing event %s' % event)
|
||||
if params:
|
||||
Receiver.send_message(event, params)
|
||||
else:
|
||||
Receiver.send_message(event)
|
||||
return u'OK'
|
||||
Receiver.send_message(event, params)
|
||||
else:
|
||||
Receiver.send_message(event)
|
||||
return json.dumps([u'OK'])
|
||||
|
||||
def process_request(self, event, params):
|
||||
"""
|
||||
Client has requested data. Send the signal and parameters for openlp
|
||||
to handle, then listen out for a corresponding _request signal
|
||||
to handle, then listen out for a corresponding ``_request`` signal
|
||||
which will have the data to return.
|
||||
For most event timeout after 10 seconds (i.e. incase the signal
|
||||
recipient isn't listening)
|
||||
remotes_poll_request is a special case, this is a ajax long poll which
|
||||
is just waiting for slide change/song change activity. This can wait
|
||||
longer (one minute)
|
||||
|
||||
For most events, timeout after 10 seconds (i.e. in case the signal
|
||||
recipient isn't listening). ``remotes_poll_request`` is a special case
|
||||
however, this is a ajax long poll which is just waiting for slide
|
||||
change/song change activity. This can wait longer (one minute).
|
||||
|
||||
``event``
|
||||
The event from the web page.
|
||||
|
||||
``params``
|
||||
Parameters sent with the event.
|
||||
"""
|
||||
log.debug(u'Processing request %s' % event)
|
||||
if not event.endswith(u'_request'):
|
||||
@ -271,14 +281,14 @@ class HttpConnection(object):
|
||||
else:
|
||||
self.timer.start(10000)
|
||||
if params:
|
||||
Receiver.send_message(event, params)
|
||||
else:
|
||||
Receiver.send_message(event)
|
||||
Receiver.send_message(event, params)
|
||||
else:
|
||||
Receiver.send_message(event)
|
||||
return True
|
||||
|
||||
def process_response(self, data):
|
||||
"""
|
||||
The recipient of a _request signal has sent data. Convert this to
|
||||
The recipient of a _request signal has sent data. Convert this to
|
||||
json and return it to client
|
||||
"""
|
||||
log.debug(u'Processing response for %s' % self.event)
|
||||
@ -292,7 +302,7 @@ class HttpConnection(object):
|
||||
|
||||
def send_200_ok(self, mimetype='text/html; charset="utf-8"'):
|
||||
"""
|
||||
Successful request. Send OK headers. Assume html for now.
|
||||
Successful request. Send OK headers. Assume html for now.
|
||||
"""
|
||||
self.socket.write(u'HTTP/1.1 200 OK\r\n' + \
|
||||
u'Content-Type: %s\r\n\r\n' % mimetype)
|
||||
@ -307,11 +317,11 @@ class HttpConnection(object):
|
||||
|
||||
def send_408_timeout(self):
|
||||
"""
|
||||
A _request hasn't returned anything in the timeout period.
|
||||
A _request hasn't returned anything in the timeout period.
|
||||
Return timeout
|
||||
"""
|
||||
self.socket.write(u'HTTP/1.1 408 Request Timeout\r\n')
|
||||
|
||||
|
||||
def timeout(self):
|
||||
"""
|
||||
Listener for timeout signal
|
||||
@ -320,14 +330,14 @@ class HttpConnection(object):
|
||||
return
|
||||
self.send_408_timeout()
|
||||
self.close()
|
||||
|
||||
|
||||
def disconnected(self):
|
||||
"""
|
||||
The client has disconnected. Tidy up
|
||||
"""
|
||||
log.debug(u'socket disconnected')
|
||||
self.close()
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
The server has closed the connection. Tidy up
|
||||
|
@ -258,10 +258,11 @@ class Ui_EditSongDialog(object):
|
||||
self.TopicBookLayout.addWidget(self.TopicGroupBox)
|
||||
self.SongBookGroup = QtGui.QGroupBox(self.TopicBookWidget)
|
||||
self.SongBookGroup.setObjectName(u'SongBookGroup')
|
||||
self.SongbookLayout = QtGui.QGridLayout(self.SongBookGroup)
|
||||
self.SongbookLayout = QtGui.QFormLayout(self.SongBookGroup)
|
||||
self.SongbookLayout.setMargin(8)
|
||||
self.SongbookLayout.setSpacing(8)
|
||||
self.SongbookLayout.setObjectName(u'SongbookLayout')
|
||||
self.SongbookNameLabel = QtGui.QLabel(self.SongBookGroup)
|
||||
self.SongbookCombo = QtGui.QComboBox(self.SongBookGroup)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
|
||||
QtGui.QSizePolicy.Fixed)
|
||||
@ -272,13 +273,11 @@ class Ui_EditSongDialog(object):
|
||||
self.SongbookCombo.setEditable(True)
|
||||
self.SongbookCombo.setSizePolicy(sizePolicy)
|
||||
self.SongbookCombo.setObjectName(u'SongbookCombo')
|
||||
self.SongbookLayout.addWidget(self.SongbookCombo, 0, 0, 1, 1)
|
||||
self.SongbookLayout.addRow(self.SongbookNameLabel, self.SongbookCombo)
|
||||
self.songBookNumberLabel = QtGui.QLabel(self.SongBookGroup)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberLabel, 0, 1, 1, 1)
|
||||
self.songBookNumberEdit = QtGui.QLineEdit(self.SongBookGroup)
|
||||
self.songBookNumberLabel.setBuddy(self.songBookNumberEdit)
|
||||
self.songBookNumberEdit.setMaximumWidth(35)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberEdit, 0, 2, 1, 1)
|
||||
self.SongbookLayout.addRow(self.songBookNumberLabel,
|
||||
self.songBookNumberEdit)
|
||||
self.TopicBookLayout.addWidget(self.SongBookGroup)
|
||||
self.AuthorsTabLayout.addWidget(self.TopicBookWidget)
|
||||
self.SongTabWidget.addTab(self.AuthorsTab, u'')
|
||||
@ -446,8 +445,10 @@ class Ui_EditSongDialog(object):
|
||||
translate('SongsPlugin.EditSongForm', 'R&emove'))
|
||||
self.SongBookGroup.setTitle(
|
||||
translate('SongsPlugin.EditSongForm', 'Song Book'))
|
||||
self.SongbookNameLabel.setText(translate('SongsPlugin.EditSongForm',
|
||||
'Book:'))
|
||||
self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm',
|
||||
'Song No.:'))
|
||||
'Number:'))
|
||||
self.SongTabWidget.setTabText(
|
||||
self.SongTabWidget.indexOf(self.AuthorsTab),
|
||||
translate('SongsPlugin.EditSongForm',
|
||||
|
@ -274,7 +274,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
item = self.VerseListWidget.item(row, 0)
|
||||
data = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
bit = data.split(u':')
|
||||
rowTag = u'%s\n%s' % (bit[0][0:1], bit[1])
|
||||
rowTag = u'%s%s' % (bit[0][0:1], bit[1])
|
||||
rowLabel.append(rowTag)
|
||||
self.VerseListWidget.setVerticalHeaderLabels(rowLabel)
|
||||
|
||||
@ -395,9 +395,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.VerseDeleteButton.setEnabled(True)
|
||||
|
||||
def onVerseAddButtonClicked(self):
|
||||
# Allow insert button as you do not know if multiple verses will
|
||||
# be entered.
|
||||
self.verse_form.setVerse(u'')
|
||||
self.verse_form.setVerse(u'', True)
|
||||
if self.verse_form.exec_():
|
||||
afterText, verse, subVerse = self.verse_form.getVerse()
|
||||
data = u'%s:%s' % (verse, subVerse)
|
||||
|
@ -26,8 +26,7 @@
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import build_icon, translate
|
||||
from openlp.core.ui import SpellTextEdit
|
||||
from openlp.core.lib import build_icon, translate, SpellTextEdit
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
|
||||
class Ui_EditVerseDialog(object):
|
||||
|
@ -31,7 +31,6 @@ from PyQt4 import QtCore, QtGui
|
||||
|
||||
from songimportwizard import Ui_SongImportWizard
|
||||
from openlp.core.lib import Receiver, SettingsManager, translate
|
||||
#from openlp.core.utils import AppLocation
|
||||
from openlp.plugins.songs.lib.importer import SongFormat
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -136,7 +135,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
self.openLP2BrowseButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.OpenLP1:
|
||||
if self.openSongFilenameEdit.text().isEmpty():
|
||||
if self.openLP1FilenameEdit.text().isEmpty():
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'No openlp.org 1.x Song Database Selected'),
|
||||
@ -292,7 +291,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
|
||||
def onCCLIRemoveButtonClicked(self):
|
||||
self.removeSelectedItems(self.ccliFileListWidget)
|
||||
|
||||
|
||||
def onSongsOfFellowshipAddButtonClicked(self):
|
||||
self.getFiles(
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
@ -374,11 +373,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
importer = self.plugin.importSongs(SongFormat.OpenLP2,
|
||||
filename=unicode(self.openLP2FilenameEdit.text())
|
||||
)
|
||||
#elif source_format == SongFormat.OpenLP1:
|
||||
# # Import an openlp.org database
|
||||
# importer = self.plugin.importSongs(SongFormat.OpenLP1,
|
||||
# filename=unicode(self.field(u'openlp1_filename').toString())
|
||||
# )
|
||||
elif source_format == SongFormat.OpenLP1:
|
||||
# Import an openlp.org database
|
||||
importer = self.plugin.importSongs(SongFormat.OpenLP1,
|
||||
filename=unicode(self.openLP1FilenameEdit.text())
|
||||
)
|
||||
elif source_format == SongFormat.OpenLyrics:
|
||||
# Import OpenLyrics songs
|
||||
importer = self.plugin.importSongs(SongFormat.OpenLyrics,
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
from opensongimport import OpenSongImport
|
||||
from olpimport import OpenLPSongImport
|
||||
from olp1import import OpenLP1SongImport
|
||||
try:
|
||||
from sofimport import SofImport
|
||||
from oooimport import OooImport
|
||||
@ -61,6 +62,8 @@ class SongFormat(object):
|
||||
"""
|
||||
if format == SongFormat.OpenLP2:
|
||||
return OpenLPSongImport
|
||||
if format == SongFormat.OpenLP1:
|
||||
return OpenLP1SongImport
|
||||
elif format == SongFormat.OpenSong:
|
||||
return OpenSongImport
|
||||
elif format == SongFormat.SongsOfFellowship:
|
||||
@ -70,7 +73,7 @@ class SongFormat(object):
|
||||
elif format == SongFormat.Generic:
|
||||
return OooImport
|
||||
elif format == SongFormat.CCLI:
|
||||
return CCLIFileImport
|
||||
return CCLIFileImport
|
||||
# else:
|
||||
return None
|
||||
|
||||
|
@ -359,16 +359,13 @@ class SongMediaItem(MediaManagerItem):
|
||||
author_list = author_list + u', '
|
||||
author_list = author_list + unicode(author.display_name)
|
||||
author_audit.append(unicode(author.display_name))
|
||||
if song.ccli_number is None or len(song.ccli_number) == 0:
|
||||
ccli = QtCore.QSettings().value(u'general/ccli number',
|
||||
QtCore.QVariant(u'')).toString()
|
||||
else:
|
||||
ccli = unicode(song.ccli_number)
|
||||
raw_footer.append(song.title)
|
||||
raw_footer.append(author_list)
|
||||
raw_footer.append(song.copyright )
|
||||
raw_footer.append(unicode(
|
||||
translate('SongsPlugin.MediaItem', 'CCLI Licence: ') + ccli))
|
||||
translate('SongsPlugin.MediaItem', 'CCLI Licence: ') +
|
||||
QtCore.QSettings().value(u'general/ccli number',
|
||||
QtCore.QVariant(u'')).toString()))
|
||||
service_item.raw_footer = raw_footer
|
||||
service_item.audit = [
|
||||
song.title, author_audit, song.copyright, unicode(song.ccli_number)
|
||||
|
149
openlp/plugins/songs/lib/olp1import.py
Normal file
149
openlp/plugins/songs/lib/olp1import.py
Normal file
@ -0,0 +1,149 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
# Carsten Tinggaard, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`olp1import` module provides the functionality for importing
|
||||
openlp.org 1.x song databases into the current installation database.
|
||||
"""
|
||||
import logging
|
||||
import chardet
|
||||
try:
|
||||
import sqlite
|
||||
except:
|
||||
pass
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class OpenLP1SongImport(SongImport):
|
||||
"""
|
||||
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
|
||||
import song databases from installations of openlp.org 1.x.
|
||||
"""
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the import.
|
||||
|
||||
``manager``
|
||||
The song manager for the running OpenLP installation.
|
||||
|
||||
``filename``
|
||||
The database providing the data to import.
|
||||
"""
|
||||
SongImport.__init__(self, manager)
|
||||
self.import_source = kwargs[u'filename']
|
||||
|
||||
def decode_string(self, raw):
|
||||
"""
|
||||
Use chardet to detect the encoding of the raw string, and convert it
|
||||
to unicode.
|
||||
|
||||
``raw``
|
||||
The raw bytestring to decode.
|
||||
"""
|
||||
detection = chardet.detect(raw)
|
||||
if detection[u'confidence'] < 0.8:
|
||||
codec = u'windows-1252'
|
||||
else:
|
||||
codec = detection[u'encoding']
|
||||
return unicode(raw, codec)
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
Run the import for an openlp.org 1.x song database.
|
||||
"""
|
||||
# Connect to the database
|
||||
connection = sqlite.connect(self.import_source)
|
||||
cursor = connection.cursor()
|
||||
# Determine if we're using a new or an old DB
|
||||
cursor.execute(u'SELECT name FROM sqlite_master '
|
||||
u'WHERE type = \'table\' AND name = \'tracks\'')
|
||||
table_list = cursor.fetchall()
|
||||
new_db = len(table_list) > 0
|
||||
# Count the number of records we need to import, for the progress bar
|
||||
cursor.execute(u'SELECT COUNT(songid) FROM songs')
|
||||
count = int(cursor.fetchone()[0])
|
||||
success = True
|
||||
self.import_wizard.importProgressBar.setMaximum(count)
|
||||
# "cache" our list of authors
|
||||
cursor.execute(u'SELECT authorid, authorname FROM authors')
|
||||
authors = cursor.fetchall()
|
||||
if new_db:
|
||||
# "cache" our list of tracks
|
||||
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
|
||||
tracks = cursor.fetchall()
|
||||
# Import the songs
|
||||
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
|
||||
u'copyrightinfo FROM songs')
|
||||
songs = cursor.fetchall()
|
||||
for song in songs:
|
||||
self.set_defaults()
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
song_id = song[0]
|
||||
title = self.decode_string(song[1])
|
||||
lyrics = self.decode_string(song[2]).replace(u'\r', u'')
|
||||
copyright = self.decode_string(song[3])
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
'Importing "%s"...')) % title)
|
||||
self.title = title
|
||||
self.process_song_text(lyrics)
|
||||
self.add_copyright(copyright)
|
||||
cursor.execute(u'SELECT authorid FROM songauthors '
|
||||
u'WHERE songid = %s' % song_id)
|
||||
author_ids = cursor.fetchall()
|
||||
for author_id in author_ids:
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
for author in authors:
|
||||
if author[0] == author_id[0]:
|
||||
self.parse_author(self.decode_string(author[1]))
|
||||
break
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
if new_db:
|
||||
cursor.execute(u'SELECT trackid FROM songtracks '
|
||||
u'WHERE songid = %s ORDER BY listindex' % song_id)
|
||||
track_ids = cursor.fetchall()
|
||||
for track_id in track_ids:
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
for track in tracks:
|
||||
if track[0] == track_id[0]:
|
||||
self.add_media_file(self.decode_string(track[1]))
|
||||
break
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
self.finish()
|
||||
return success
|
||||
|
@ -28,6 +28,7 @@ import os
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
from songimport import SongImport
|
||||
|
||||
if os.name == u'nt':
|
||||
@ -43,23 +44,32 @@ else:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
class OooImport(object):
|
||||
class OooImport(SongImport):
|
||||
"""
|
||||
Import songs from Impress/Powerpoint docs using Impress
|
||||
"""
|
||||
def __init__(self, songmanager):
|
||||
def __init__(self, master_manager, **kwargs):
|
||||
"""
|
||||
Initialise the class. Requires a songmanager class which is passed
|
||||
to SongImport for writing song to disk
|
||||
"""
|
||||
SongImport.__init__(self, master_manager)
|
||||
self.song = None
|
||||
self.manager = songmanager
|
||||
self.master_manager = master_manager
|
||||
self.document = None
|
||||
self.process_started = False
|
||||
self.filenames = kwargs[u'filenames']
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'song_stop_import'), self.stop_import)
|
||||
|
||||
def import_docs(self, filenames):
|
||||
def do_import(self):
|
||||
self.abort = False
|
||||
self.import_wizard.importProgressBar.setMaximum(0)
|
||||
self.start_ooo()
|
||||
for filename in filenames:
|
||||
for filename in self.filenames:
|
||||
if self.abort:
|
||||
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
|
||||
return
|
||||
filename = unicode(filename)
|
||||
if os.path.isfile(filename):
|
||||
self.open_ooo_file(filename)
|
||||
@ -72,6 +82,12 @@ class OooImport(object):
|
||||
self.process_doc()
|
||||
self.close_ooo_file()
|
||||
self.close_ooo()
|
||||
self.import_wizard.importProgressBar.setMaximum(1)
|
||||
self.import_wizard.incrementProgressBar(u'', 1)
|
||||
return True
|
||||
|
||||
def stop_import(self):
|
||||
self.abort = True
|
||||
|
||||
def start_ooo(self):
|
||||
"""
|
||||
@ -135,6 +151,9 @@ class OooImport(object):
|
||||
"com.sun.star.presentation.PresentationDocument") and not \
|
||||
self.document.supportsService("com.sun.star.text.TextDocument"):
|
||||
self.close_ooo_file()
|
||||
else:
|
||||
self.import_wizard.incrementProgressBar(
|
||||
u'Processing file ' + filepath, 0)
|
||||
except:
|
||||
pass
|
||||
return
|
||||
@ -161,6 +180,9 @@ class OooImport(object):
|
||||
slides = doc.getDrawPages()
|
||||
text = u''
|
||||
for slide_no in range(slides.getCount()):
|
||||
if self.abort:
|
||||
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
|
||||
return
|
||||
slide = slides.getByIndex(slide_no)
|
||||
slidetext = u''
|
||||
for idx in range(slide.getCount()):
|
||||
|
@ -28,6 +28,7 @@ import logging
|
||||
import os
|
||||
from zipfile import ZipFile
|
||||
from lxml import objectify
|
||||
from lxml.etree import Error, LxmlError
|
||||
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
@ -36,119 +37,163 @@ log = logging.getLogger(__name__)
|
||||
class OpenSongImportError(Exception):
|
||||
pass
|
||||
|
||||
class OpenSongImport(object):
|
||||
class OpenSongImport(SongImport):
|
||||
"""
|
||||
Import songs exported from OpenSong - the format is described loosly here:
|
||||
http://www.opensong.org/d/manual/song_file_format_specification
|
||||
Import songs exported from OpenSong
|
||||
|
||||
However, it doesn't describe the <lyrics> section, so here's an attempt:
|
||||
The format is described loosly on the `OpenSong File Format Specification
|
||||
<http://www.opensong.org/d/manual/song_file_format_specification>`_ page on
|
||||
the OpenSong web site. However, it doesn't describe the <lyrics> section,
|
||||
so here's an attempt:
|
||||
|
||||
Verses can be expressed in one of 2 ways:
|
||||
<lyrics>
|
||||
[v1]List of words
|
||||
Another Line
|
||||
Verses can be expressed in one of 2 ways, either in complete verses, or by
|
||||
line grouping, i.e. grouping all line 1's of a verse together, all line 2's
|
||||
of a verse together, and so on.
|
||||
|
||||
[v2]Some words for the 2nd verse
|
||||
etc...
|
||||
</lyrics>
|
||||
An example of complete verses::
|
||||
|
||||
The 'v' can be left out - it is implied
|
||||
or:
|
||||
<lyrics>
|
||||
[V]
|
||||
1List of words
|
||||
2Some words for the 2nd Verse
|
||||
<lyrics>
|
||||
[v1]
|
||||
List of words
|
||||
Another Line
|
||||
|
||||
1Another Line
|
||||
2etc...
|
||||
</lyrics>
|
||||
[v2]
|
||||
Some words for the 2nd verse
|
||||
etc...
|
||||
</lyrics>
|
||||
|
||||
Either or both forms can be used in one song. The Number does not
|
||||
necessarily appear at the start of the line
|
||||
The 'v' in the verse specifiers above can be left out, it is implied.
|
||||
|
||||
An example of line grouping::
|
||||
|
||||
<lyrics>
|
||||
[V]
|
||||
1List of words
|
||||
2Some words for the 2nd Verse
|
||||
|
||||
1Another Line
|
||||
2etc...
|
||||
</lyrics>
|
||||
|
||||
Either or both forms can be used in one song. The number does not
|
||||
necessarily appear at the start of the line. Additionally, the [v1] labels
|
||||
can have either upper or lower case Vs.
|
||||
|
||||
The [v1] labels can have either upper or lower case Vs
|
||||
Other labels can be used also:
|
||||
C - Chorus
|
||||
B - Bridge
|
||||
|
||||
Guitar chords can be provided 'above' the lyrics (the line is
|
||||
preceeded by a'.') and _s can be used to signify long-drawn-out
|
||||
words:
|
||||
C
|
||||
Chorus
|
||||
|
||||
. A7 Bm
|
||||
1 Some____ Words
|
||||
B
|
||||
Bridge
|
||||
|
||||
Chords and _s are removed by this importer.
|
||||
All verses are imported and tagged appropriately.
|
||||
|
||||
The verses etc. are imported and tagged appropriately.
|
||||
Guitar chords can be provided "above" the lyrics (the line is preceeded by
|
||||
a period "."), and one or more "_" can be used to signify long-drawn-out
|
||||
words. Chords and "_" are removed by this importer. For example::
|
||||
|
||||
The <presentation> tag is used to populate the OpenLP verse
|
||||
display order field. The Author and Copyright tags are also
|
||||
imported to the appropriate places.
|
||||
. A7 Bm
|
||||
1 Some____ Words
|
||||
|
||||
The <presentation> tag is used to populate the OpenLP verse display order
|
||||
field. The Author and Copyright tags are also imported to the appropriate
|
||||
places.
|
||||
|
||||
"""
|
||||
def __init__(self, songmanager):
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the class. Requires a songmanager class which
|
||||
is passed to SongImport for writing song to disk
|
||||
Initialise the class.
|
||||
"""
|
||||
self.songmanager = songmanager
|
||||
SongImport.__init__(self, manager)
|
||||
self.filenames = kwargs[u'filenames']
|
||||
self.song = None
|
||||
self.commit = True
|
||||
|
||||
def do_import(self, filename, commit=True):
|
||||
def do_import(self):
|
||||
"""
|
||||
Import either a single opensong file, or a zipfile
|
||||
containing multiple opensong files If the commit parameter is
|
||||
set False, the import will not be committed to the database
|
||||
(useful for test scripts)
|
||||
Import either a single opensong file, or a zipfile containing multiple
|
||||
opensong files. If `self.commit` is set False, the import will not be
|
||||
committed to the database (useful for test scripts).
|
||||
"""
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == ".zip":
|
||||
log.info('Zipfile found %s', filename)
|
||||
z = ZipFile(filename, u'r')
|
||||
for song in z.infolist():
|
||||
parts = os.path.split(song.filename)
|
||||
if parts[-1] == u'':
|
||||
#No final part => directory
|
||||
continue
|
||||
songfile = z.open(song)
|
||||
self.do_import_file(songfile)
|
||||
if commit:
|
||||
success = True
|
||||
self.import_wizard.importProgressBar.setMaximum(len(self.filenames))
|
||||
for filename in self.filenames:
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == u'.zip':
|
||||
log.debug(u'Zipfile found %s', filename)
|
||||
z = ZipFile(filename, u'r')
|
||||
self.import_wizard.importProgressBar.setMaximum(
|
||||
self.import_wizard.importProgressBar.maximum() +
|
||||
len(z.infolist()))
|
||||
for song in z.infolist():
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
parts = os.path.split(song.filename)
|
||||
if parts[-1] == u'':
|
||||
#No final part => directory
|
||||
continue
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
'Importing %s...')) % parts[-1])
|
||||
songfile = z.open(song)
|
||||
self.do_import_file(songfile)
|
||||
if self.commit:
|
||||
self.finish()
|
||||
self.set_defaults()
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
else:
|
||||
log.info('Direct import %s', filename)
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
'Importing %s...')) % os.path.split(filename)[-1])
|
||||
file = open(filename)
|
||||
self.do_import_file(file)
|
||||
if self.commit:
|
||||
self.finish()
|
||||
else:
|
||||
log.info('Direct import %s', filename)
|
||||
file = open(filename)
|
||||
self.do_import_file(file)
|
||||
if commit:
|
||||
self.finish()
|
||||
self.set_defaults()
|
||||
if not self.commit:
|
||||
self.finish()
|
||||
return success
|
||||
|
||||
|
||||
def do_import_file(self, file):
|
||||
"""
|
||||
Process the OpenSong file - pass in a file-like object,
|
||||
not a filename
|
||||
"""
|
||||
self.song_import = SongImport(self.songmanager)
|
||||
tree = objectify.parse(file)
|
||||
"""
|
||||
self.authors = []
|
||||
try:
|
||||
tree = objectify.parse(file)
|
||||
except Error, LxmlError:
|
||||
log.exception(u'Error parsing XML')
|
||||
return
|
||||
root = tree.getroot()
|
||||
fields = dir(root)
|
||||
decode = {u'copyright':self.song_import.add_copyright,
|
||||
u'ccli':u'ccli_number',
|
||||
u'author':self.song_import.parse_author,
|
||||
u'title':u'title',
|
||||
u'aka':u'alternate_title',
|
||||
u'hymn_number':u'song_number'}
|
||||
for (attr, fn_or_string) in decode.items():
|
||||
decode = {
|
||||
u'copyright': self.add_copyright,
|
||||
u'ccli': u'ccli_number',
|
||||
u'author': self.parse_author,
|
||||
u'title': u'title',
|
||||
u'aka': u'alternate_title',
|
||||
u'hymn_number': u'song_number'
|
||||
}
|
||||
for attr, fn_or_string in decode.items():
|
||||
if attr in fields:
|
||||
ustring = unicode(root.__getattr__(attr))
|
||||
if type(fn_or_string) == type(u''):
|
||||
self.song_import.__setattr__(fn_or_string, ustring)
|
||||
if isinstance(fn_or_string, basestring):
|
||||
setattr(self, fn_or_string, ustring)
|
||||
else:
|
||||
fn_or_string(ustring)
|
||||
if u'theme' in fields:
|
||||
self.song_import.topics.append(unicode(root.theme))
|
||||
if u'alttheme' in fields:
|
||||
self.song_import.topics.append(unicode(root.alttheme))
|
||||
if u'theme' in fields and unicode(root.theme) not in self.topics:
|
||||
self.topics.append(unicode(root.theme))
|
||||
if u'alttheme' in fields and unicode(root.alttheme) not in self.topics:
|
||||
self.topics.append(unicode(root.alttheme))
|
||||
# data storage while importing
|
||||
verses = {}
|
||||
lyrics = unicode(root.lyrics)
|
||||
@ -158,6 +203,7 @@ class OpenSongImport(object):
|
||||
# in the absence of any other indication, verses are the default,
|
||||
# erm, versetype!
|
||||
versetype = u'V'
|
||||
versenum = None
|
||||
for thisline in lyrics.split(u'\n'):
|
||||
# remove comments
|
||||
semicolon = thisline.find(u';')
|
||||
@ -170,7 +216,6 @@ class OpenSongImport(object):
|
||||
if thisline[0] == u'.' or thisline.startswith(u'---') \
|
||||
or thisline.startswith(u'-!!'):
|
||||
continue
|
||||
|
||||
# verse/chorus/etc. marker
|
||||
if thisline[0] == u'[':
|
||||
versetype = thisline[1].upper()
|
||||
@ -186,7 +231,6 @@ class OpenSongImport(object):
|
||||
versenum = u'1'
|
||||
continue
|
||||
words = None
|
||||
|
||||
# number at start of line.. it's verse number
|
||||
if thisline[0].isdigit():
|
||||
versenum = thisline[0]
|
||||
@ -207,7 +251,7 @@ class OpenSongImport(object):
|
||||
our_verse_order.append(versetag)
|
||||
if words:
|
||||
# Tidy text and remove the ____s from extended words
|
||||
words = self.song_import.tidy_text(words)
|
||||
words = self.tidy_text(words)
|
||||
words = words.replace('_', '')
|
||||
verses[versetype][versenum].append(words)
|
||||
# done parsing
|
||||
@ -220,24 +264,23 @@ class OpenSongImport(object):
|
||||
for num in versenums:
|
||||
versetag = u'%s%s' % (versetype, num)
|
||||
lines = u'\n'.join(verses[versetype][num])
|
||||
self.song_import.verses.append([versetag, lines])
|
||||
self.verses.append([versetag, lines])
|
||||
# Keep track of what we have for error checking later
|
||||
versetags[versetag] = 1
|
||||
# now figure out the presentation order
|
||||
order = []
|
||||
if u'presentation' in fields and root.presentation != u'':
|
||||
order = unicode(root.presentation)
|
||||
order = order.split()
|
||||
else:
|
||||
assert len(our_verse_order)>0
|
||||
order = our_verse_order
|
||||
if len(our_verse_order) > 0:
|
||||
order = our_verse_order
|
||||
else:
|
||||
log.warn(u'No verse order available for %s, skipping.', self.title)
|
||||
for tag in order:
|
||||
if len(tag) == 1:
|
||||
tag = tag + u'1' # Assume it's no.1 if it's not there
|
||||
if not versetags.has_key(tag):
|
||||
log.warn(u'Got order %s but not in versetags, skipping', tag)
|
||||
else:
|
||||
self.song_import.verse_order_list.append(tag)
|
||||
|
||||
def finish(self):
|
||||
""" Separate function, allows test suite to not pollute database"""
|
||||
self.song_import.finish()
|
||||
self.verse_order_list.append(tag)
|
||||
|
@ -68,19 +68,30 @@ class SofImport(OooImport):
|
||||
It attempts to detect italiced verses, and treats these as choruses in
|
||||
the verse ordering. Again not perfect, but a start.
|
||||
"""
|
||||
def __init__(self, songmanager):
|
||||
def __init__(self, master_manager, **kwargs):
|
||||
"""
|
||||
Initialise the class. Requires a songmanager class which is passed
|
||||
to SongImport for writing song to disk
|
||||
"""
|
||||
OooImport.__init__(self, songmanager)
|
||||
OooImport.__init__(self, master_manager, **kwargs)
|
||||
|
||||
def import_sof(self, filename):
|
||||
def do_import(self):
|
||||
self.abort = False
|
||||
self.start_ooo()
|
||||
self.open_ooo_file(filename)
|
||||
self.process_sof_file()
|
||||
self.close_ooo_file()
|
||||
for filename in self.filenames:
|
||||
if self.abort:
|
||||
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
|
||||
return
|
||||
filename = unicode(filename)
|
||||
if os.path.isfile(filename):
|
||||
self.open_ooo_file(filename)
|
||||
if self.document:
|
||||
self.process_sof_file()
|
||||
self.close_ooo_file()
|
||||
self.close_ooo()
|
||||
self.import_wizard.importProgressBar.setMaximum(1)
|
||||
self.import_wizard.incrementProgressBar(u'', 1)
|
||||
return True
|
||||
|
||||
def process_sof_file(self):
|
||||
"""
|
||||
@ -90,6 +101,9 @@ class SofImport(OooImport):
|
||||
self.new_song()
|
||||
paragraphs = self.document.getText().createEnumeration()
|
||||
while paragraphs.hasMoreElements():
|
||||
if self.abort:
|
||||
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
|
||||
return
|
||||
paragraph = paragraphs.nextElement()
|
||||
if paragraph.supportsService("com.sun.star.text.Paragraph"):
|
||||
self.process_paragraph(paragraph)
|
||||
@ -244,6 +258,7 @@ class SofImport(OooImport):
|
||||
if title.endswith(u','):
|
||||
title = title[:-1]
|
||||
self.song.title = title
|
||||
self.import_wizard.incrementProgressBar(u'Processing song ' + title, 0)
|
||||
|
||||
def add_author(self, text):
|
||||
"""
|
||||
|
@ -30,7 +30,7 @@ from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
||||
from openlp.plugins.songs.lib.xml import SongXMLBuilder
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -66,17 +66,17 @@ class SongImport(QtCore.QObject):
|
||||
self.ccli_number = u''
|
||||
self.authors = []
|
||||
self.topics = []
|
||||
self.media_files = []
|
||||
self.song_book_name = u''
|
||||
self.song_book_pub = u''
|
||||
self.verse_order_list = []
|
||||
self.verses = []
|
||||
self.versecount = 0
|
||||
self.choruscount = 0
|
||||
self.versecounts = {}
|
||||
self.copyright_string = unicode(translate(
|
||||
'SongsPlugin.SongImport', 'copyright'))
|
||||
self.copyright_symbol = unicode(translate(
|
||||
'SongsPlugin.SongImport', '\xa9'))
|
||||
|
||||
|
||||
def stop_import(self):
|
||||
"""
|
||||
Sets the flag for importers to stop their import
|
||||
@ -129,13 +129,13 @@ class SongImport(QtCore.QObject):
|
||||
|
||||
def process_verse_text(self, text):
|
||||
lines = text.split(u'\n')
|
||||
if text.lower().find(COPYRIGHT_STRING) >= 0 \
|
||||
or text.lower().find(COPYRIGHT_SYMBOL) >= 0:
|
||||
if text.lower().find(self.copyright_string) >= 0 \
|
||||
or text.lower().find(self.copyright_symbol) >= 0:
|
||||
copyright_found = False
|
||||
for line in lines:
|
||||
if (copyright_found or
|
||||
line.lower().find(COPYRIGHT_STRING) >= 0 or
|
||||
line.lower().find(COPYRIGHT_SYMBOL) >= 0):
|
||||
line.lower().find(self.copyright_string) >= 0 or
|
||||
line.lower().find(self.copyright_symbol) >= 0):
|
||||
copyright_found = True
|
||||
self.add_copyright(line)
|
||||
else:
|
||||
@ -184,7 +184,15 @@ class SongImport(QtCore.QObject):
|
||||
return
|
||||
self.authors.append(author)
|
||||
|
||||
def add_verse(self, verse, versetag=None):
|
||||
def add_media_file(self, filename):
|
||||
"""
|
||||
Add a media file to the list
|
||||
"""
|
||||
if filename in self.media_files:
|
||||
return
|
||||
self.media_files.append(filename)
|
||||
|
||||
def add_verse(self, verse, versetag=u'V'):
|
||||
"""
|
||||
Add a verse. This is the whole verse, lines split by \n
|
||||
Verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the verses/
|
||||
@ -196,13 +204,14 @@ class SongImport(QtCore.QObject):
|
||||
if oldverse.strip() == verse.strip():
|
||||
self.verse_order_list.append(oldversetag)
|
||||
return
|
||||
if versetag == u'V' or not versetag:
|
||||
self.versecount += 1
|
||||
versetag = u'V' + unicode(self.versecount)
|
||||
if versetag.startswith(u'C'):
|
||||
self.choruscount += 1
|
||||
if versetag == u'C':
|
||||
versetag += unicode(self.choruscount)
|
||||
if versetag[0] in self.versecounts:
|
||||
self.versecounts[versetag[0]] += 1
|
||||
else:
|
||||
self.versecounts[versetag[0]] = 1
|
||||
if len(versetag) == 1:
|
||||
versetag += unicode(self.versecounts[versetag[0]])
|
||||
elif int(versetag[1:]) > self.versecounts[versetag[0]]:
|
||||
self.versecounts[versetag[0]] = int(versetag[1:])
|
||||
self.verses.append([versetag, verse.rstrip()])
|
||||
self.verse_order_list.append(versetag)
|
||||
if versetag.startswith(u'V') and self.contains_verse(u'C1'):
|
||||
@ -279,11 +288,16 @@ class SongImport(QtCore.QObject):
|
||||
for authortext in self.authors:
|
||||
author = self.manager.get_object_filtered(Author,
|
||||
Author.display_name == authortext)
|
||||
if author is None:
|
||||
if not author:
|
||||
author = Author.populate(display_name = authortext,
|
||||
last_name=authortext.split(u' ')[-1],
|
||||
first_name=u' '.join(authortext.split(u' ')[:-1]))
|
||||
song.authors.append(author)
|
||||
for filename in self.media_files:
|
||||
media_file = self.manager.get_object_filtered(MediaFile,
|
||||
MediaFile.file_name == filename)
|
||||
if not media_file:
|
||||
song.media_files.append(MediaFile.populate(file_name=filename))
|
||||
if self.song_book_name:
|
||||
song_book = self.manager.get_object_filtered(Book,
|
||||
Book.name == self.song_book_name)
|
||||
@ -300,7 +314,7 @@ class SongImport(QtCore.QObject):
|
||||
topic = Topic.populate(name=topictext)
|
||||
song.topics.append(topic)
|
||||
self.manager.save_object(song)
|
||||
self.setDefaults()
|
||||
self.set_defaults()
|
||||
|
||||
def print_song(self):
|
||||
"""
|
||||
|
130
resources/forms/exceptiondialog.ui
Normal file
130
resources/forms/exceptiondialog.ui
Normal file
@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExceptionDialog</class>
|
||||
<widget class="QDialog" name="ExceptionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>580</width>
|
||||
<height>407</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="exceptionLayout">
|
||||
<property name="spacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="messageLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="bugLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../images/openlp-2.qrc">:/graphics/exception.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="text">
|
||||
<string>Oops! OpenLP hit a problem, and couldn't recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@openlp.org, along with a detailed description of what you were doing when the problem occurred.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="exceptionTextEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="backgroundVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="exceptionButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../images/openlp-2.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>exceptionButtonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ExceptionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>exceptionButtonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ExceptionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
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
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
resources/images/exception.png
Normal file
BIN
resources/images/exception.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@ -62,6 +62,7 @@
|
||||
<file>openlp-logo-256x256.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="graphics">
|
||||
<file>exception.png</file>
|
||||
<file>openlp-about-logo.png</file>
|
||||
<file>openlp-splash-screen.png</file>
|
||||
</qresource>
|
||||
|
@ -1,6 +1,6 @@
|
||||
--- openlp/core/resources.py.old Mon Jun 21 23:16:19 2010
|
||||
+++ openlp/core/resources.py Mon Jun 21 23:27:48 2010
|
||||
@@ -1,10 +1,31 @@
|
||||
@@ -1,10 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
+# --------------------------------------------------------------------------- #
|
||||
+# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
+# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin #
|
||||
+# Thompson, Jon Tibble, Carsten Tinggaard #
|
||||
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
+# Carsten Tinggaard, Frode Woldsund #
|
||||
+# --------------------------------------------------------------------------- #
|
||||
+# This program is free software; you can redistribute it and/or modify it #
|
||||
+# under the terms of the GNU General Public License as published by the Free #
|
||||
|
@ -24,13 +24,31 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
# Short description
|
||||
# Steps for creating languages:
|
||||
# 1. make sure that the openlp_en.ts file exist
|
||||
# 2. go to scripts folder and start:
|
||||
# python translation_utils.py -a
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
This script is used to maintain the translation files in OpenLP. It downloads
|
||||
the latest translation files from the Pootle translation server, updates the
|
||||
local translation files from both the source code and the files from Pootle,
|
||||
and can also generate the compiled translation files.
|
||||
|
||||
Create New Language
|
||||
-------------------
|
||||
|
||||
To create a new language, simply run this script with the ``-a`` command line
|
||||
option::
|
||||
|
||||
@:~$ ./translation_utils.py -a
|
||||
|
||||
Update Translation Files
|
||||
------------------------
|
||||
|
||||
The best way to update the translations is to download the files from Pootle,
|
||||
and then update the local files using both the downloaded files and the source.
|
||||
This is done easily via the ``-d``, ``-p`` and ``-u`` options::
|
||||
|
||||
@:~$ ./translation_utils.py -dpu
|
||||
|
||||
"""
|
||||
import os
|
||||
import urllib
|
||||
import re
|
||||
@ -39,201 +57,277 @@ from optparse import OptionParser
|
||||
from PyQt4 import QtCore
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
|
||||
class TranslationUtils(object):
|
||||
SERVER_URL = u'http://pootle.projecthq.biz/export/openlp/'
|
||||
IGNORED_PATHS = [u'scripts']
|
||||
IGNORED_FILES = [u'setup.py']
|
||||
|
||||
verbose_mode = False
|
||||
|
||||
class Command(object):
|
||||
"""
|
||||
Provide an enumeration of commands.
|
||||
"""
|
||||
Download = 1
|
||||
Create = 2
|
||||
Prepare = 3
|
||||
Update = 4
|
||||
Generate = 5
|
||||
|
||||
class CommandStack(object):
|
||||
"""
|
||||
This class provides an iterable stack.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.ignore_paths = [u'./scripts']
|
||||
self.ignore_files = [u'setup.py']
|
||||
self.server_url = u'http://pootle.projecthq.biz/export/openlp/'
|
||||
self.cmd_stack = []
|
||||
self.stack_count = 0
|
||||
self.verbose = False
|
||||
|
||||
self.current_index = 0
|
||||
self.data = []
|
||||
|
||||
def process_stack(self):
|
||||
if len(self.cmd_stack) > 0:
|
||||
if len(self.cmd_stack) == self.stack_count:
|
||||
print u'Process %d commands' % self.stack_count
|
||||
print u'%d. ' % (self.stack_count-len(self.cmd_stack)+1),
|
||||
command = self.cmd_stack.pop(0)
|
||||
if len(command) > 1:
|
||||
command[0](command[1])
|
||||
else:
|
||||
command[0]()
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def __getitem__(self, index):
|
||||
if self.data[index].get(u'arguments'):
|
||||
return self.data[index][u'command'], self.data[index][u'arguments']
|
||||
else:
|
||||
print "Finished all commands"
|
||||
return self.data[index][u'command']
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if self.current_index == len(self.data):
|
||||
raise StopIteration
|
||||
else:
|
||||
current_item = self.data[self.current_index][u'command']
|
||||
self.current_index += 1
|
||||
return current_item
|
||||
|
||||
def append(self, command, **kwargs):
|
||||
data = {u'command': command}
|
||||
if u'arguments' in kwargs:
|
||||
data[u'arguments'] = kwargs[u'arguments']
|
||||
self.data.append(data)
|
||||
|
||||
def reset(self):
|
||||
self.current_index = 0
|
||||
|
||||
|
||||
def downloadTranslations(self):
|
||||
print 'Download Translation files from HQ-Server'
|
||||
page = urllib.urlopen(u'%s' % (self.server_url))
|
||||
soup = BeautifulSoup(page)
|
||||
languages = soup.findAll(text=re.compile(".*\.ts"))
|
||||
for language in languages:
|
||||
filename = os.path.join(u'..', u'resources', u'i18n',
|
||||
u'openlp_%s' % language)
|
||||
self.printVerbose(u'Get Translation File: %s' % filename)
|
||||
self.get_and_write_file(language, filename)
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
def print_verbose(text):
|
||||
"""
|
||||
This method checks to see if we are in verbose mode, and if so prints
|
||||
``text`` out.
|
||||
|
||||
def get_and_write_file(self, language, filename):
|
||||
page = urllib.urlopen(u'%s%s' % (self.server_url, language))
|
||||
content = page.read().decode('utf8')
|
||||
page.close()
|
||||
file = open(filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
|
||||
def creation(self, language):
|
||||
print "Create new Translation File"
|
||||
"""
|
||||
Use this option to create a new translation file
|
||||
this function:
|
||||
* create the new *.ts file
|
||||
"""
|
||||
filename = os.path.join(u'..', u'resources', u'i18n',
|
||||
u'openlp_%s.ts' % language)
|
||||
self.get_and_write_file(u'en.ts', filename)
|
||||
self.printVerbose("""
|
||||
Please remind: For permanent providing this language:
|
||||
this language name have to append to the global list
|
||||
variable "translations" in this file
|
||||
and this file have to be uploaded to the Pootle Server
|
||||
Please contact the developers!
|
||||
""")
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
|
||||
|
||||
def preparation(self):
|
||||
print u'Generating the openlp.pro file'
|
||||
stringlist = []
|
||||
start_dir = os.path.join(u'..')
|
||||
for root, dirs, files in os.walk(start_dir):
|
||||
for file in files:
|
||||
path = u'%s' % root
|
||||
path = path.replace('\\','/')
|
||||
path = path.replace('..','.')
|
||||
|
||||
if file.startswith(u'hook-') or file.startswith(u'test_'):
|
||||
continue
|
||||
``text``
|
||||
The text to print.
|
||||
"""
|
||||
global verbose_mode
|
||||
if verbose_mode:
|
||||
print u' %s' % text
|
||||
|
||||
cond = False
|
||||
for search in self.ignore_paths:
|
||||
if path.startswith(search):
|
||||
cond = True
|
||||
if cond:
|
||||
continue
|
||||
cond = False
|
||||
for search in self.ignore_files:
|
||||
if search == file:
|
||||
cond = True
|
||||
if cond:
|
||||
continue
|
||||
|
||||
if file.endswith(u'.py'):
|
||||
def run(command):
|
||||
"""
|
||||
This method runs an external application.
|
||||
|
||||
``command``
|
||||
The command to run.
|
||||
"""
|
||||
print_verbose(command)
|
||||
process = QtCore.QProcess()
|
||||
process.start(command)
|
||||
while (process.waitForReadyRead()):
|
||||
print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll()))
|
||||
print_verbose(u'Error(s):\n%s' % process.readAllStandardError())
|
||||
print_verbose(u'Output:\n%s' % process.readAllStandardOutput())
|
||||
print u' Done.'
|
||||
|
||||
def download_file(source_filename, dest_filename):
|
||||
"""
|
||||
Download a file and save it to disk.
|
||||
|
||||
``source_filename``
|
||||
The file to download.
|
||||
|
||||
``dest_filename``
|
||||
The new local file name.
|
||||
"""
|
||||
print_verbose(u'Downloading from: %s' % (SERVER_URL + source_filename))
|
||||
page = urllib.urlopen(SERVER_URL + source_filename)
|
||||
content = page.read().decode('utf8')
|
||||
page.close()
|
||||
file = open(dest_filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
|
||||
def download_translations():
|
||||
"""
|
||||
This method downloads the translation files from the Pootle server.
|
||||
"""
|
||||
print 'Download translation files from Pootle'
|
||||
page = urllib.urlopen(SERVER_URL)
|
||||
soup = BeautifulSoup(page)
|
||||
languages = soup.findAll(text=re.compile(r'.*\.ts'))
|
||||
for language in languages:
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n',
|
||||
language)
|
||||
print_verbose(u'Get Translation File: %s' % filename)
|
||||
download_file(language, filename)
|
||||
print u' Done.'
|
||||
|
||||
def prepare_project():
|
||||
"""
|
||||
This method creates the project file needed to update the translation files
|
||||
and compile them into .qm files.
|
||||
"""
|
||||
print u'Generating the openlp.pro file'
|
||||
lines = []
|
||||
start_dir = os.path.abspath(u'..')
|
||||
start_dir = start_dir + os.sep
|
||||
print_verbose(u'Starting directory: %s' % start_dir)
|
||||
for root, dirs, files in os.walk(start_dir):
|
||||
for file in files:
|
||||
path = root.replace(start_dir, u'').replace(u'\\', u'/') #.replace(u'..', u'.')
|
||||
if file.startswith(u'hook-') or file.startswith(u'test_'):
|
||||
continue
|
||||
ignore = False
|
||||
for ignored_path in IGNORED_PATHS:
|
||||
if path.startswith(ignored_path):
|
||||
ignore = True
|
||||
break
|
||||
if ignore:
|
||||
continue
|
||||
ignore = False
|
||||
for ignored_file in IGNORED_FILES:
|
||||
if file == ignored_file:
|
||||
ignore = True
|
||||
break
|
||||
if ignore:
|
||||
continue
|
||||
if file.endswith(u'.py') or file.endswith(u'.pyw'):
|
||||
if path:
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.pyw'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.ts'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'TRANSLATIONS += %s' % line)
|
||||
|
||||
stringlist.sort()
|
||||
self.write_file(os.path.join(start_dir, u'openlp.pro'), stringlist)
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
else:
|
||||
line = file
|
||||
print_verbose(u'Parsing "%s"' % line)
|
||||
lines.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.ts'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
print_verbose(u'Parsing "%s"' % line)
|
||||
lines.append(u'TRANSLATIONS += %s' % line)
|
||||
lines.sort()
|
||||
file = open(os.path.join(start_dir, u'openlp.pro'), u'w')
|
||||
file.write(u'\n'.join(lines).encode('utf8'))
|
||||
file.close()
|
||||
print u' Done.'
|
||||
|
||||
def update(self):
|
||||
print u'Update the translation files'
|
||||
cmd = u'pylupdate4 -verbose -noobsolete ../openlp.pro'
|
||||
self.start_cmd(cmd)
|
||||
def update_translations():
|
||||
print u'Update the translation files'
|
||||
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
|
||||
print u'You have no generated a project file yet, please run this ' + \
|
||||
u'script with the -p option.'
|
||||
return
|
||||
else:
|
||||
os.chdir(os.path.abspath(u'..'))
|
||||
run(u'pylupdate4 -verbose -noobsolete openlp.pro')
|
||||
|
||||
def generate(self):
|
||||
print u'Generate the related *.qm files'
|
||||
cmd = u'lrelease ../openlp.pro'
|
||||
self.start_cmd(cmd)
|
||||
|
||||
def write_file(self, filename, stringlist):
|
||||
content = u''
|
||||
for line in stringlist:
|
||||
content = u'%s%s\n' % (content, line)
|
||||
file = open(filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
def generate_binaries():
|
||||
print u'Generate the related *.qm files'
|
||||
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
|
||||
print u'You have no generated a project file yet, please run this ' + \
|
||||
u'script with the -p option. It is also recommended that you ' + \
|
||||
u'this script with the -u option to update the translation ' + \
|
||||
u'files as well.'
|
||||
return
|
||||
else:
|
||||
os.chdir(os.path.abspath(u'..'))
|
||||
run(u'lrelease openlp.pro')
|
||||
|
||||
def printVerbose(self, data):
|
||||
if self.verbose:
|
||||
print u' %s' % data
|
||||
def create_translation(language):
|
||||
"""
|
||||
This method creates a new translation file.
|
||||
|
||||
def start_cmd(self, command):
|
||||
self.printVerbose(command)
|
||||
self.process = QtCore.QProcess()
|
||||
self.process.start(command)
|
||||
while (self.process.waitForReadyRead()):
|
||||
self.printVerbose(u'ReadyRead: %s' % QtCore.QString(self.process.readAll()))
|
||||
self.printVerbose(self.process.readAllStandardError())
|
||||
self.printVerbose(self.process.readAllStandardOutput())
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
|
||||
``language``
|
||||
The language file to create.
|
||||
"""
|
||||
print "Create new Translation File"
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
|
||||
download_file(u'en.ts', filename)
|
||||
print u'\n** Please Note **\n'
|
||||
print u'In order to get this file into OpenLP and onto the Pootle ' + \
|
||||
u'translation server you will need to subscribe to the OpenLP' + \
|
||||
u'Translators mailing list, and request that your language file ' + \
|
||||
u'be added to the project.\n'
|
||||
print u' Done'
|
||||
|
||||
def process_stack(command_stack):
|
||||
"""
|
||||
This method looks at the commands in the command stack, and processes them
|
||||
in the order they are in the stack.
|
||||
|
||||
``command_stack``
|
||||
The command stack to process.
|
||||
"""
|
||||
if command_stack:
|
||||
print u'Processing %d commands...' % len(command_stack)
|
||||
for command in command_stack:
|
||||
print u'%d.' % (command_stack.current_index),
|
||||
if command == Command.Download:
|
||||
download_translations()
|
||||
elif command == Command.Prepare:
|
||||
prepare_project()
|
||||
elif command == Command.Update:
|
||||
update_translations()
|
||||
elif command == Command.Generate:
|
||||
generate_binaries()
|
||||
elif command == Command.Create:
|
||||
command, arguments = command_stack[command_stack.current_index]
|
||||
create_translation(*arguments)
|
||||
print u'Finished processing commands.'
|
||||
else:
|
||||
print u'No commands to process.'
|
||||
|
||||
def main():
|
||||
# start Main Class
|
||||
Util = TranslationUtils()
|
||||
|
||||
global verbose_mode
|
||||
# Set up command line options.
|
||||
usage = u'''
|
||||
This script handle the translation files for OpenLP.
|
||||
Usage: %prog [options]
|
||||
If no option will be used, options "-d -p -u -g" will be set automatically
|
||||
'''
|
||||
usage = u'%prog [options]\nOptions are parsed in the order they are ' + \
|
||||
u'listed below. If no options are given, "-dpug" will be used.\n\n' + \
|
||||
u'This script is used to manage OpenLP\'s translation files.'
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option('-d', '--download-ts', action='store_true',
|
||||
dest='download', help='Load languages from Pootle Server')
|
||||
parser.add_option('-c', '--create', metavar='lang',
|
||||
help='creation of new translation file, Parameter: language (e.g. "en_GB"')
|
||||
parser.add_option('-p', '--prepare', action='store_true', dest='prepare',
|
||||
help='preparation (generate pro file)')
|
||||
parser.add_option('-d', '--download-ts', dest='download',
|
||||
action='store_true', help='download language files from Pootle')
|
||||
parser.add_option('-c', '--create', dest=u'create', metavar='LANG',
|
||||
help='create a new translation file for language LANG, e.g. "en_GB"')
|
||||
parser.add_option('-p', '--prepare', dest='prepare', action='store_true',
|
||||
help='generate a project file, used to update the translations')
|
||||
parser.add_option('-u', '--update', action='store_true', dest='update',
|
||||
help='update translation files')
|
||||
parser.add_option('-g', '--generate', action='store_true', dest='generate',
|
||||
help='generate qm files')
|
||||
parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
|
||||
help='Give more informations while processing')
|
||||
|
||||
help='update translation files (needs a project file)')
|
||||
parser.add_option('-g', '--generate', dest='generate', action='store_true',
|
||||
help='compile .ts files into .qm files')
|
||||
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
||||
help='show extra information while processing translations')
|
||||
(options, args) = parser.parse_args()
|
||||
# Create and populate the command stack
|
||||
command_stack = CommandStack()
|
||||
if options.download:
|
||||
Util.cmd_stack.append([Util.downloadTranslations])
|
||||
command_stack.append(Command.Download)
|
||||
if options.create:
|
||||
Util.cmd_stack.append([Util.creation, u'%s' % options.create])
|
||||
command_stack.append(Command.Create, arguments=[options.create])
|
||||
if options.prepare:
|
||||
Util.cmd_stack.append([Util.preparation])
|
||||
command_stack.append(Command.Prepare)
|
||||
if options.update:
|
||||
Util.cmd_stack.append([Util.update])
|
||||
command_stack.append(Command.Update)
|
||||
if options.generate:
|
||||
Util.cmd_stack.append([Util.generate])
|
||||
if options.verbose:
|
||||
Util.verbose = True
|
||||
command_stack.append(Command.Generate)
|
||||
verbose_mode = options.verbose
|
||||
if not command_stack:
|
||||
command_stack.append(Command.Download)
|
||||
command_stack.append(Command.Prepare)
|
||||
command_stack.append(Command.Update)
|
||||
command_stack.append(Command.Generate)
|
||||
# Process the commands
|
||||
process_stack(command_stack)
|
||||
|
||||
if len(Util.cmd_stack) == 0:
|
||||
Util.cmd_stack.append([Util.downloadTranslations])
|
||||
Util.cmd_stack.append([Util.preparation])
|
||||
Util.cmd_stack.append([Util.update])
|
||||
Util.cmd_stack.append([Util.generate])
|
||||
|
||||
Util.stack_count = len(Util.cmd_stack)
|
||||
Util.process_stack()
|
||||
|
||||
|
||||
|
||||
if __name__ == u'__main__':
|
||||
if os.path.split(os.path.abspath(u'.'))[1] != u'scripts':
|
||||
print u'You need to run this script from the scripts directory.'
|
||||
else:
|
||||
main()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user