r1396
@ -17,3 +17,9 @@ Helper Classes & Functions
|
|||||||
|
|
||||||
.. automodule:: openlp.plugins.remotes.lib
|
.. automodule:: openlp.plugins.remotes.lib
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: openlp.plugins.remotes.lib.httpserver.HttpConnection
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: openlp.plugins.remotes.lib.httpserver.HttpResponse
|
||||||
|
:members:
|
||||||
|
@ -49,11 +49,13 @@ class MediaPlugin(Plugin):
|
|||||||
u'audio/ac3': [u'.ac3'],
|
u'audio/ac3': [u'.ac3'],
|
||||||
u'audio/flac': [u'.flac'],
|
u'audio/flac': [u'.flac'],
|
||||||
u'audio/x-m4a': [u'.m4a'],
|
u'audio/x-m4a': [u'.m4a'],
|
||||||
|
u'audio/midi': [u'.mid', u'.midi'],
|
||||||
u'audio/x-mp3': [u'.mp3'],
|
u'audio/x-mp3': [u'.mp3'],
|
||||||
u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'],
|
u'audio/mpeg': [u'.mp3', u'.mp2', u'.mpga', u'.mpega', u'.m4a'],
|
||||||
u'audio/qcelp': [u'.qcp'],
|
u'audio/qcelp': [u'.qcp'],
|
||||||
u'audio/x-wma': [u'.wma'],
|
u'audio/x-wma': [u'.wma'],
|
||||||
u'audio/x-ms-wma': [u'.wma'],
|
u'audio/x-ms-wma': [u'.wma'],
|
||||||
|
u'video/x-flv': [u'.flv'],
|
||||||
u'video/x-matroska': [u'.mpv', u'.mkv'],
|
u'video/x-matroska': [u'.mpv', u'.mkv'],
|
||||||
u'video/x-wmv': [u'.wmv'],
|
u'video/x-wmv': [u'.wmv'],
|
||||||
u'video/x-ms-wmv': [u'.wmv']}
|
u'video/x-ms-wmv': [u'.wmv']}
|
||||||
|
@ -26,4 +26,68 @@
|
|||||||
"""
|
"""
|
||||||
The :mod:`remotes` plugin allows OpenLP to be controlled from another machine
|
The :mod:`remotes` plugin allows OpenLP to be controlled from another machine
|
||||||
over a network connection.
|
over a network connection.
|
||||||
|
|
||||||
|
Routes:
|
||||||
|
|
||||||
|
``/``
|
||||||
|
Go to the web interface.
|
||||||
|
|
||||||
|
``/files/{filename}``
|
||||||
|
Serve a static file.
|
||||||
|
|
||||||
|
``/api/poll``
|
||||||
|
Poll to see if there are any changes. Returns a JSON-encoded dict of
|
||||||
|
any changes that occurred::
|
||||||
|
|
||||||
|
{"results": {"type": "controller"}}
|
||||||
|
|
||||||
|
Or, if there were no results, False::
|
||||||
|
|
||||||
|
{"results": False}
|
||||||
|
|
||||||
|
``/api/controller/{live|preview}/{action}``
|
||||||
|
Perform ``{action}`` on the live or preview controller. Valid actions
|
||||||
|
are:
|
||||||
|
|
||||||
|
``next``
|
||||||
|
Load the next slide.
|
||||||
|
|
||||||
|
``previous``
|
||||||
|
Load the previous slide.
|
||||||
|
|
||||||
|
``jump``
|
||||||
|
Jump to a specific slide. Requires an id return in a JSON-encoded
|
||||||
|
dict like so::
|
||||||
|
|
||||||
|
{"request": {"id": 1}}
|
||||||
|
|
||||||
|
``first``
|
||||||
|
Load the first slide.
|
||||||
|
|
||||||
|
``last``
|
||||||
|
Load the last slide.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
Request the text of the current slide.
|
||||||
|
|
||||||
|
``/api/service/{action}``
|
||||||
|
Perform ``{action}`` on the service manager (e.g. go live). Data is
|
||||||
|
passed as a json-encoded ``data`` parameter. Valid actions are:
|
||||||
|
|
||||||
|
``next``
|
||||||
|
Load the next item in the service.
|
||||||
|
|
||||||
|
``previous``
|
||||||
|
Load the previews item in the service.
|
||||||
|
|
||||||
|
``jump``
|
||||||
|
Jump to a specific item in the service. Requires an id returned in
|
||||||
|
a JSON-encoded dict like so::
|
||||||
|
|
||||||
|
{"request": {"id": 1}}
|
||||||
|
|
||||||
|
``list``
|
||||||
|
Request a list of items in the service.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
BIN
openlp/plugins/remotes/html/images/ajax-loader.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
openlp/plugins/remotes/html/images/form-check-off.png
Normal file
After Width: | Height: | Size: 364 B |
BIN
openlp/plugins/remotes/html/images/form-check-on.png
Normal file
After Width: | Height: | Size: 460 B |
BIN
openlp/plugins/remotes/html/images/form-radio-off.png
Normal file
After Width: | Height: | Size: 453 B |
BIN
openlp/plugins/remotes/html/images/form-radio-on.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
openlp/plugins/remotes/html/images/icon-search-black.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
openlp/plugins/remotes/html/images/icons-18-black.png
Normal file
After Width: | Height: | Size: 935 B |
BIN
openlp/plugins/remotes/html/images/icons-18-white.png
Normal file
After Width: | Height: | Size: 940 B |
BIN
openlp/plugins/remotes/html/images/icons-36-black.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
openlp/plugins/remotes/html/images/icons-36-white.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
openlp/plugins/remotes/html/images/ui-icon-blank.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
openlp/plugins/remotes/html/images/ui-icon-unblank.png
Normal file
After Width: | Height: | Size: 231 B |
@ -1,57 +1,73 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
<!DOCTYPE html>
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
|
<head>
|
||||||
<head>
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>OpenLP 2.0 Remote</title>
|
||||||
<title>OpenLP Remote Controller</title>
|
<link rel="stylesheet" href="/files/jquery.mobile.css" />
|
||||||
|
<link rel="stylesheet" href="/files/openlp.css" />
|
||||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||||
<script type='text/javascript' src="/files/openlp.js"></script>
|
<script type="text/javascript" src="/files/openlp.js"></script>
|
||||||
<script type='text/javascript' src="/files/init.js"></script>
|
<script type="text/javascript" src="/files/jquery.mobile.js"></script>
|
||||||
<link rel="stylesheet" href="/files/style.css" type="text/css" />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>OpenLP Controller</h1>
|
<div data-role="page" id="home">
|
||||||
<p>Quick Links: <a href="#service-manager">Service Manager</a> | <a href="#slide-controller">Slide Controller</a> | <a href="#miscellaneous">Miscellaneous</a></p>
|
<div data-role="header">
|
||||||
<h2 id="service-manager">Service Manager</h2>
|
<h1>OpenLP 2.0 Remote</h1>
|
||||||
<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>
|
||||||
<div id="item-buttons">
|
<div data-role="content">
|
||||||
<input type="button" value="<- Previous Item" id="servicemanager_previous_item" />
|
<div data-role="controlgroup">
|
||||||
<input type="button" value="Next Item ->" id="servicemanager_next_item" />
|
<a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a>
|
||||||
|
<a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a>
|
||||||
|
<a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
<hr>
|
|
||||||
<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>
|
||||||
<div id="slide-buttons">
|
</div>
|
||||||
<input type="button" value="<- Previous Slide" id="slidecontroller_live_previous" />
|
<div data-role="page" id="service-manager">
|
||||||
<input type="button" value="Next Slide ->" id="slidecontroller_live_next" />
|
<div data-role="header">
|
||||||
|
<a href="#" data-rel="back" data-icon="arrow-l">Back</a>
|
||||||
|
<h1>Service Manager</h1>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<div data-role="content">
|
||||||
<hr>
|
<ul data-role="listview" data-inset="true">
|
||||||
<h2 id="miscellaneous">Miscellaneous</h2>
|
</ul>
|
||||||
<div id="display-buttons">
|
|
||||||
<input type="button" value="Blank" id="slidecontroller_live_blank" />
|
|
||||||
<input type="button" value="Unblank" id="slidecontroller_live_unblank" />
|
|
||||||
</div>
|
</div>
|
||||||
<div id="alert-details">
|
<div data-role="footer" data-theme="b" class="ui-bar">
|
||||||
<label for="alert-text">Alert text:</label>
|
<a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a>
|
||||||
<input type="text" id="alert-text" />
|
<a href="#" id="service-unblank" data-role="button" data-icon="unblank">Unblank</a>
|
||||||
<input type="button" value="Send" id="alert-send" />
|
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a>
|
||||||
|
<a href="#" id="service-previous" data-role="button" data-icon="arrow-l">Previous</a>
|
||||||
|
<a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
</div>
|
||||||
<a href="http://openlp.org/">OpenLP website</a>
|
<div data-role="page" id="slide-controller">
|
||||||
|
<div data-role="header">
|
||||||
|
<a href="#" data-rel="back" data-icon="arrow-l">Back</a>
|
||||||
|
<h1>Slide Controller</h1>
|
||||||
|
</div>
|
||||||
|
<div data-role="content">
|
||||||
|
<ul data-role="listview" data-inset="true">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div data-role="footer" data-theme="b" class="ui-bar">
|
||||||
|
<a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a>
|
||||||
|
<a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Unblank</a>
|
||||||
|
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a>
|
||||||
|
<a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">Previous</a>
|
||||||
|
<a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-role="page" id="alerts">
|
||||||
|
<div data-role="header">
|
||||||
|
<a href="#" data-rel="back" data-icon="arrow-l">Back</a>
|
||||||
|
<h1>Alerts</h1>
|
||||||
|
</div>
|
||||||
|
<div data-role="content">
|
||||||
|
<div data-role="fieldcontain">
|
||||||
|
<label for="alert-text">Text:</label>
|
||||||
|
<input type="text" name="alert-text" id="alert-text" value="" />
|
||||||
|
</div>
|
||||||
|
<a href="#" id="alert-submit" data-role="button">Show Alert</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
148
openlp/plugins/remotes/html/jquery.js
vendored
16
openlp/plugins/remotes/html/jquery.mobile.css
Normal file
121
openlp/plugins/remotes/html/jquery.mobile.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/*!
|
||||||
|
* jQuery Mobile v1.0a3
|
||||||
|
* http://jquerymobile.com/
|
||||||
|
*
|
||||||
|
* Copyright 2010, jQuery Project
|
||||||
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||||
|
* http://jquery.org/license
|
||||||
|
*/
|
||||||
|
(function(a,d){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var g=0,e;(e=b[g])!=null;g++)a(e).triggerHandler("remove");c(b)}}else{var f=a.fn.remove;a.fn.remove=function(b,g){return this.each(function(){if(!g)if(!b||a.filter(b,[this]).length)a("*",this).add([this]).each(function(){a(this).triggerHandler("remove")});return f.call(a(this),b,g)})}}a.widget=function(b,g,e){var i=b.split(".")[0],h;b=b.split(".")[1];h=i+"-"+b;if(!e){e=g;g=a.Widget}a.expr[":"][h]=function(k){return!!a.data(k,
|
||||||
|
b)};a[i]=a[i]||{};a[i][b]=function(k,j){arguments.length&&this._createWidget(k,j)};g=new g;g.options=a.extend(true,{},g.options);a[i][b].prototype=a.extend(true,g,{namespace:i,widgetName:b,widgetEventPrefix:a[i][b].prototype.widgetEventPrefix||b,widgetBaseClass:h},e);a.widget.bridge(b,a[i][b])};a.widget.bridge=function(b,g){a.fn[b]=function(e){var i=typeof e==="string",h=Array.prototype.slice.call(arguments,1),k=this;e=!i&&h.length?a.extend.apply(null,[true,e].concat(h)):e;if(i&&e.charAt(0)==="_")return k;
|
||||||
|
i?this.each(function(){var j=a.data(this,b);if(!j)throw"cannot call methods on "+b+" prior to initialization; attempted to call method '"+e+"'";if(!a.isFunction(j[e]))throw"no such method '"+e+"' for "+b+" widget instance";var o=j[e].apply(j,h);if(o!==j&&o!==d){k=o;return false}}):this.each(function(){var j=a.data(this,b);j?j.option(e||{})._init():a.data(this,b,new g(e,this))});return k}};a.Widget=function(b,g){arguments.length&&this._createWidget(b,g)};a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",
|
||||||
|
options:{disabled:false},_createWidget:function(b,g){a.data(g,this.widgetName,this);this.element=a(g);this.options=a.extend(true,{},this.options,this._getCreateOptions(),b);var e=this;this.element.bind("remove."+this.widgetName,function(){e.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){var b={};if(a.metadata)b=a.metadata.get(element)[this.widgetName];return b},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);
|
||||||
|
this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(b,g){var e=b;if(arguments.length===0)return a.extend({},this.options);if(typeof b==="string"){if(g===d)return this.options[b];e={};e[b]=g}this._setOptions(e);return this},_setOptions:function(b){var g=this;a.each(b,function(e,i){g._setOption(e,i)});return this},_setOption:function(b,g){this.options[b]=g;if(b===
|
||||||
|
"disabled")this.widget()[g?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",g);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(b,g,e){var i=this.options[b];g=a.Event(g);g.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase();e=e||{};if(g.originalEvent){b=a.event.props.length;for(var h;b;){h=a.event.props[--b];g[h]=g.originalEvent[h]}}this.element.trigger(g,
|
||||||
|
e);return!(a.isFunction(i)&&i.call(this.element[0],g,e)===false||g.isDefaultPrevented())}}})(jQuery);(function(a,d){a.widget("mobile.widget",{_getCreateOptions:function(){var c=this.element,f={};a.each(this.options,function(b){var g=c.data(b.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()}));if(g!==d)f[b]=g});return f}})})(jQuery);
|
||||||
|
(function(a){function d(){var g=c.width(),e=[],i=[],h;f.removeClass("min-width-"+b.join("px min-width-")+"px max-width-"+b.join("px max-width-")+"px");a.each(b,function(k,j){g>=j&&e.push("min-width-"+j+"px");g<=j&&i.push("max-width-"+j+"px")});if(e.length)h=e.join(" ");if(i.length)h+=" "+i.join(" ");f.addClass(h)}var c=a(window),f=a("html"),b=[320,480,768,1024];a.mobile.media=function(){var g={},e=a("<div id='jquery-mediatest'>"),i=a("<body>").append(e);return function(h){if(!(h in g)){var k=document.createElement("style"),
|
||||||
|
j="@media "+h+" { #jquery-mediatest { position:absolute; } }";k.type="text/css";if(k.styleSheet)k.styleSheet.cssText=j;else k.appendChild(document.createTextNode(j));f.prepend(i).prepend(k);g[h]=e.css("position")==="absolute";i.add(k).remove()}return g[h]}}();a.mobile.addResolutionBreakpoints=function(g){if(a.type(g)==="array")b=b.concat(g);else b.push(g);b.sort(function(e,i){return e-i});d()};a(document).bind("mobileinit.htmlclass",function(){c.bind("orientationchange.htmlclass resize.htmlclass",
|
||||||
|
function(g){g.orientation&&f.removeClass("portrait landscape").addClass(g.orientation);d()})});a(function(){c.trigger("orientationchange.htmlclass")})})(jQuery);
|
||||||
|
(function(a,d){function c(h){var k=h.charAt(0).toUpperCase()+h.substr(1);h=(h+" "+g.join(k+" ")+k).split(" ");for(var j in h)if(b[j]!==d)return true}var f=a("<body>").prependTo("html"),b=f[0].style,g=["webkit","moz","o"],e=window.palmGetResource||window.PalmServiceBridge,i=window.blackberry;a.extend(a.support,{orientation:"orientation"in window,touch:"ontouchend"in document,cssTransitions:"WebKitTransitionEvent"in window,pushState:!!history.pushState,mediaquery:a.mobile.media("only all"),cssPseudoElement:!!c("content"),
|
||||||
|
boxShadow:!!c("boxShadow")&&!i,scrollTop:("pageXOffset"in window||"scrollTop"in document.documentElement||"scrollTop"in f[0])&&!e,dynamicBaseTag:function(){var h=location.protocol+"//"+location.host+location.pathname+"ui-dir/",k=a("head base"),j=null,o="";if(k.length)o=k.attr("href");else k=j=a("<base>",{href:h}).appendTo("head");var p=a("<a href='testurl'></a>").prependTo(f)[0].href;k[0].href=o?o:location.pathname;j&&j.remove();return p.indexOf(h)===0}()});f.remove();a.support.boxShadow||a("html").addClass("ui-mobile-nosupport-boxshadow")})(jQuery);
|
||||||
|
(function(a,d){a.each("touchstart touchmove touchend orientationchange tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(e,i){a.fn[i]=function(h){return h?this.bind(i,h):this.trigger(i)};a.attrFn[i]=true});var c=a.support.touch,f=c?"touchstart":"mousedown",b=c?"touchend":"mouseup",g=c?"touchmove":"mousemove";a.event.special.scrollstart={enabled:true,setup:function(){function e(j,o){h=o;var p=j.type;j.type=h?"scrollstart":"scrollstop";a.event.handle.call(i,j);j.type=
|
||||||
|
p}var i=this,h,k;a(i).bind("touchmove scroll",function(j){if(a.event.special.scrollstart.enabled){h||e(j,true);clearTimeout(k);k=setTimeout(function(){e(j,false)},50)}})}};a.event.special.tap={setup:function(){var e=this,i=a(e);i.bind("mousedown touchstart",function(h){function k(n){if(n.type=="scroll")j=true;else{n=n.type=="touchmove"?n.originalEvent.touches[0]:n;if(Math.abs(v[0]-n.pageX)>10||Math.abs(v[1]-n.pageY)>10)j=true}}if(h.which&&h.which!==1||i.data("prevEvent")&&i.data("prevEvent")!==h.type)return false;
|
||||||
|
i.data("prevEvent",h.type);setTimeout(function(){i.removeData("prevEvent")},800);var j=false,o=true,p=h.target,t=h.originalEvent,v=h.type=="touchstart"?[t.touches[0].pageX,t.touches[0].pageY]:[h.pageX,h.pageY],m,r;r=setTimeout(function(){if(o&&!j){m=h.type;h.type="taphold";a.event.handle.call(e,h);h.type=m}},750);a(window).one("scroll",k);i.bind("mousemove touchmove",k).one("mouseup touchend",function(n){i.unbind("mousemove touchmove",k);a(window).unbind("scroll",k);clearTimeout(r);o=false;if(!j&&
|
||||||
|
p==n.target){m=n.type;n.type="tap";a.event.handle.call(e,n);n.type=m}})})}};a.event.special.swipe={setup:function(){var e=a(this);e.bind(f,function(i){function h(p){if(j){var t=p.originalEvent.touches?p.originalEvent.touches[0]:p;o={time:(new Date).getTime(),coords:[t.pageX,t.pageY]};Math.abs(j.coords[0]-o.coords[0])>10&&p.preventDefault()}}var k=i.originalEvent.touches?i.originalEvent.touches[0]:i,j={time:(new Date).getTime(),coords:[k.pageX,k.pageY],origin:a(i.target)},o;e.bind(g,h).one(b,function(){e.unbind(g,
|
||||||
|
h);if(j&&o)if(o.time-j.time<1E3&&Math.abs(j.coords[0]-o.coords[0])>30&&Math.abs(j.coords[1]-o.coords[1])<75)j.origin.trigger("swipe").trigger(j.coords[0]>o.coords[0]?"swipeleft":"swiperight");j=o=d})})}};(function(e){function i(){var o=k();if(o!==j){j=o;h.trigger("orientationchange")}}var h=e(window),k,j;e.event.special.orientationchange={setup:function(){if(e.support.orientation)return false;j=k();h.bind("resize",i)},teardown:function(){if(e.support.orientation)return false;h.unbind("resize",i)},
|
||||||
|
add:function(o){var p=o.handler;o.handler=function(t){t.orientation=k();return p.apply(this,arguments)}}};k=function(){var o=document.documentElement;return o&&o.clientWidth/o.clientHeight<1.1?"portrait":"landscape"}})(jQuery);a.each({scrollstop:"scrollstart",taphold:"tap",swipeleft:"swipe",swiperight:"swipe"},function(e,i){a.event.special[e]={setup:function(){a(this).bind(i,a.noop)}}})})(jQuery);
|
||||||
|
(function(a,d,c){function f(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}var b="hashchange",g=document,e,i=a.event.special,h=g.documentMode,k="on"+b in d&&(h===c||h>7);a.fn[b]=function(j){return j?this.bind(b,j):this.trigger(b)};a.fn[b].delay=50;i[b]=a.extend(i[b],{setup:function(){if(k)return false;a(e.start)},teardown:function(){if(k)return false;a(e.stop)}});e=function(){function j(){var n=f(),u=r(t);if(n!==t){m(t=n,u);a(d).trigger(b)}else if(u!==t)location.href=location.href.replace(/#.*/,
|
||||||
|
"")+u;p=setTimeout(j,a.fn[b].delay)}var o={},p,t=f(),v=function(n){return n},m=v,r=v;o.start=function(){p||j()};o.stop=function(){p&&clearTimeout(p);p=c};a.browser.msie&&!k&&function(){var n,u;o.start=function(){if(!n){u=(u=a.fn[b].src)&&u+f();n=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){u||m(f());j()}).attr("src",u||"javascript:0").insertAfter("body")[0].contentWindow;g.onpropertychange=function(){try{if(event.propertyName==="title")n.document.title=g.title}catch(l){}}}};
|
||||||
|
o.stop=v;r=function(){return f(n.location.href)};m=function(l,s){var q=n.document,w=a.fn[b].domain;if(l!==s){q.title=g.title;q.open();w&&q.write('<script>document.domain="'+w+'"<\/script>');q.close();n.location.hash=l}}}();return o}()})(jQuery,this);
|
||||||
|
(function(a){a.widget("mobile.page",a.mobile.widget,{options:{backBtnText:"Back",addBackBtn:true,degradeInputs:{color:false,date:false,datetime:false,"datetime-local":false,email:false,month:false,number:false,range:"number",search:true,tel:false,time:false,url:false,week:false},keepNative:null},_create:function(){var d=this.element,c=this.options;this.keepNative="[data-role='none'], [data-role='nojs']"+(c.keepNative?", "+c.keepNative:"");if(this._trigger("beforeCreate")!==false){d.find("[data-role='page'], [data-role='content']").andSelf().each(function(){a(this).addClass("ui-"+
|
||||||
|
a(this).data("role"))});d.find("[data-role='nojs']").addClass("ui-nojs");d.find("[data-role]").andSelf().each(function(){var f=a(this),b=f.data("role"),g=f.data("theme");if(b==="header"||b==="footer"){f.addClass("ui-bar-"+(g||f.parent("[data-role=page]").data("theme")||"a"));f.attr("role",b==="header"?"banner":"contentinfo");g=f.children("a");var e=g.hasClass("ui-btn-left"),i=g.hasClass("ui-btn-right");if(!e)e=g.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length;i||g.eq(1).addClass("ui-btn-right");
|
||||||
|
c.addBackBtn&&b==="header"&&a(".ui-page").length>1&&d.data("url")!==a.mobile.path.stripHash(location.hash)&&!e&&f.data("backbtn")!==false&&a("<a href='#' class='ui-btn-left' data-rel='back' data-icon='arrow-l'>"+c.backBtnText+"</a>").prependTo(f);f.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({tabindex:"0",role:"heading","aria-level":"1"})}else if(b==="content"){g&&f.addClass("ui-body-"+g);f.attr("role","main")}else if(b==="page")f.addClass("ui-body-"+(g||"c"));switch(b){case "header":case "footer":case "page":case "content":f.addClass("ui-"+
|
||||||
|
b);break;case "collapsible":case "fieldcontain":case "navbar":case "listview":case "dialog":f[b]()}});this._enhanceControls();d.find("[data-role='button'], .ui-bar > a, .ui-header > a, .ui-footer > a").not(".ui-btn").not(this.keepNative).buttonMarkup();d.find("[data-role='controlgroup']").controlgroup();d.find("a:not(.ui-btn):not(.ui-link-inherit)").not(this.keepNative).addClass("ui-link");d.fixHeaderFooter()}},_enhanceControls:function(){var d=this.options;this.element.find("input").not(this.keepNative).each(function(){var b=
|
||||||
|
this.getAttribute("type"),g=d.degradeInputs[b]||"text";d.degradeInputs[b]&&a(this).replaceWith(a("<div>").html(a(this).clone()).html().replace(/type="([a-zA-Z]+)"/,"type="+g+" data-type='$1'"))});var c=this.element.find("input, textarea, select, button"),f=c.not(this.keepNative);c=c.filter("input[type=text]");c.length&&typeof c[0].autocorrect!=="undefined"&&c.each(function(){this.setAttribute("autocorrect","off");this.setAttribute("autocomplete","off")});f.filter("[type='radio'], [type='checkbox']").checkboxradio();
|
||||||
|
f.filter("button, [type='button'], [type='submit'], [type='reset'], [type='image']").button();f.filter("input, textarea").not("[type='radio'], [type='checkbox'], [type='button'], [type='submit'], [type='reset'], [type='image'], [type='hidden']").textinput();f.filter("input, select").filter("[data-role='slider'], [data-type='range']").slider();f.filter("select:not([data-role='slider'])").selectmenu()}})})(jQuery);
|
||||||
|
(function(a,d,c){a.extend(a.mobile,{subPageUrlKey:"ui-page",nonHistorySelectors:"dialog",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",ajaxEnabled:true,hashListeningEnabled:true,ajaxLinksEnabled:true,ajaxFormsEnabled:true,defaultTransition:"slide",loadingMessage:"loading",metaViewportContent:"width=device-width, minimum-scale=1, maximum-scale=1",gradeA:function(){return a.support.mediaquery},autoInitialize:true,keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,
|
||||||
|
COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});a(d.document).trigger("mobileinit");if(a.mobile.gradeA()){var f=a(d),b=a("html"),g=a("head"),e=a.mobile.loadingMessage?a("<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1>"+
|
||||||
|
a.mobile.loadingMessage+"</h1></div>"):c;b.addClass("ui-mobile ui-mobile-rendering");a.mobile.metaViewportContent&&a("<meta>",{name:"viewport",content:a.mobile.metaViewportContent}).prependTo(g);a.extend(a.mobile,{pageLoading:function(i){if(i)b.removeClass("ui-loading");else{if(a.mobile.loadingMessage){i=a("."+a.mobile.activeBtnClass).first();e.appendTo(a.mobile.pageContainer).css({top:a.support.scrollTop&&a(d).scrollTop()+a(d).height()/2||i.length&&i.offset().top||100})}b.addClass("ui-loading")}},
|
||||||
|
silentScroll:function(i){i=i||0;a.event.special.scrollstart.enabled=false;setTimeout(function(){d.scrollTo(0,i);a(document).trigger("silentscroll",{x:0,y:i})},20);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},initializePage:function(){var i=a("[data-role='page']");i.add("[data-role='dialog']").each(function(){a(this).attr("data-url",a(this).attr("id"))});a.mobile.firstPage=i.first();a.mobile.pageContainer=i.first().parent().addClass("ui-mobile-viewport");a.mobile.pageLoading();
|
||||||
|
!a.mobile.hashListeningEnabled||!a.mobile.path.stripHash(location.hash)?a.mobile.changePage(a.mobile.firstPage,false,true,false,true):f.trigger("hashchange",[true])}});a.mobile.autoInitialize&&a(a.mobile.initializePage);f.load(a.mobile.silentScroll)}})(jQuery,this);
|
||||||
|
(function(a,d){function c(l){if(i&&(!i.closest(".ui-page-active").length||l))i.removeClass(a.mobile.activeBtnClass);i=null}var f=a(window),b=a("html"),g=a("head"),e={get:function(l){if(l==d)l=location.hash;return e.stripHash(l).replace(/[^\/]*\.[^\/*]+$/,"")},getFilePath:function(l){var s="&"+a.mobile.subPageUrlKey;return l&&l.split(s)[0].split(t)[0]},set:function(l){location.hash=l},origin:"",setOrigin:function(){e.origin=e.get(location.protocol+"//"+location.host+location.pathname)},makeAbsolute:function(l){return e.get()+
|
||||||
|
l},clean:function(l){return l.replace(location.protocol+"//"+location.host,"")},stripHash:function(l){return l.replace(/^#/,"")},isExternal:function(l){return e.hasProtocol(e.clean(l))},hasProtocol:function(l){return/^(:?\w+:)/.test(l)},isRelative:function(l){return/^[^\/|#]/.test(l)&&!e.hasProtocol(l)},isEmbeddedPage:function(l){return/^#/.test(l)}},i=null,h={stack:[],activeIndex:0,getActive:function(){return h.stack[h.activeIndex]},getPrev:function(){return h.stack[h.activeIndex-1]},getNext:function(){return h.stack[h.activeIndex+
|
||||||
|
1]},addNew:function(l,s){h.getNext()&&h.clearForward();h.stack.push({url:l,transition:s});h.activeIndex=h.stack.length-1},clearForward:function(){h.stack=h.stack.slice(0,h.activeIndex+1)},ignoreNextHashChange:true},k="[tabindex],a,button:visible,select:visible,input",j=null,o=[],p=false,t="&ui-state=dialog",v=g.children("base"),m=location.protocol+"//"+location.host,r=e.get(m+location.pathname),n=r;if(v.length){var u=v.attr("href");if(u)n=u.search(/^[^:/]+:\/\/[^/]+\/?/)==-1?u.charAt(0)=="/"?m+u:
|
||||||
|
r+u:u;n+=n.charAt(n.length-1)=="/"?" ":"/"}base=a.support.dynamicBaseTag?{element:v.length?v:a("<base>",{href:n}).prependTo(g),set:function(l){base.element.attr("href",n+e.get(l))},reset:function(){base.element.attr("href",n)}}:d;e.setOrigin();a.fn.animationComplete=function(l){if(a.support.cssTransitions)return a(this).one("webkitAnimationEnd",l);else setTimeout(l,0)};a.mobile.updateHash=e.set;a.mobile.path=e;a.mobile.base=base;a.mobile.urlstack=h.stack;a.mobile.urlHistory=h;a.mobile.changePage=
|
||||||
|
function(l,s,q,w,z){function F(){function A(){if(w!==false&&x){h.ignoreNextHashChange=false;e.set(x)}!I&&!K&&h.addNew(x,s);c();a.mobile.silentScroll(l.data("lastScroll"));var G=l,P=G.find(".ui-title:eq(0)");P.length?P.focus():G.find(k).eq(0).focus();y&&y.data("page")._trigger("hide",null,{nextPage:l});l.data("page")._trigger("show",null,{prevPage:y||a("")});a.mobile.activePage=l;L!=null&&L.remove();b.removeClass("ui-mobile-rendering");p=false;o.length>0&&a.mobile.changePage.apply(a.mobile,o.pop())}
|
||||||
|
function B(G){a.mobile.pageContainer.addClass(G);D.push(G)}a.mobile.silentScroll();var M=f.scrollTop(),J=["flip"],D=[];if(x.indexOf("&"+a.mobile.subPageUrlKey)>-1)l=a("[data-url='"+x+"']");if(y){y.data("lastScroll",M);y.data("page")._trigger("beforehide",{nextPage:l})}l.data("page")._trigger("beforeshow",{prevPage:y||a("")});if(s&&s!=="none"){a.mobile.pageLoading(true);a.inArray(s,J)>=0&&B("ui-mobile-viewport-perspective");B("ui-mobile-viewport-transitioning");if(y)y.addClass(s+" out "+(q?"reverse":
|
||||||
|
""));l.addClass(a.mobile.activePageClass+" "+s+" in "+(q?"reverse":""));l.animationComplete(function(){y.add(l).removeClass("out in reverse "+s);y&&y.removeClass(a.mobile.activePageClass);A();a.mobile.pageContainer.removeClass(D.join(" "));D=[]})}else{a.mobile.pageLoading(true);y&&y.removeClass(a.mobile.activePageClass);l.addClass(a.mobile.activePageClass);A()}}function Q(){if(j||l.data("role")=="dialog"){x=h.getActive().url+t;if(j){l.attr("data-role",j);j=null}}l.page()}var E=a.type(l)==="array",
|
||||||
|
H=a.type(l)==="object",y=E?l[0]:a.mobile.activePage;l=E?l[1]:l;var x=fileUrl=a.type(l)==="string"?e.stripHash(l):"",C=d,N="get",R=false,L=null,O=h.getActive(),I=false,K=false;if(!(O&&h.stack.length>1&&O.url===x&&!E&&!H))if(p)o.unshift(arguments);else{p=true;if(z){a.each(h.stack,function(A){if(this.url==x){urlIndex=A;I=A<h.activeIndex;K=!I;h.activeIndex=A}});if(I){q=true;s=s||O.transition}else if(K)s=s||h.getActive().transition}if(H&&l.url){x=l.url;C=l.data;N=l.type;R=true;if(C&&N=="get"){if(a.type(C)==
|
||||||
|
"object")C=a.param(C);x+="?"+C;C=d}}base&&base.reset();a(window.document.activeElement).add("input:focus, textarea:focus, select:focus").blur();if(x){l=a("[data-url='"+x+"']");fileUrl=e.getFilePath(x)}else{E=l.attr("data-url");H=e.getFilePath(E);if(E!=H)fileUrl=H}if(s===d)s=a.mobile.defaultTransition;if(l.length&&!R){fileUrl&&base&&base.set(fileUrl);Q();F()}else{if(l.length)L=l;a.mobile.pageLoading();a.ajax({url:fileUrl,type:N,data:C,success:function(A){var B=/ data-url="(.*)"/.test(A)&&RegExp.$1;
|
||||||
|
if(B){base&&base.set(B);x=fileUrl=e.getFilePath(B)}else base&&base.set(fileUrl);B=a("<div></div>");B.get(0).innerHTML=A;l=B.find('[data-role="page"], [data-role="dialog"]').first();if(!a.support.dynamicBaseTag){var M=e.get(fileUrl);l.find("[src],link[href]").each(function(){var J=a(this).is("[href]")?"href":"src",D=a(this).attr(J);D.replace(location.protocol+"//"+location.host+location.pathname,"");/^(\w+:|#|\/)/.test(D)||a(this).attr(J,M+D)})}l.attr("data-url",fileUrl).appendTo(a.mobile.pageContainer);
|
||||||
|
Q();setTimeout(function(){F()},0)},error:function(){a.mobile.pageLoading(true);c(true);base&&base.set(e.get());a("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>Error Loading Page</h1></div>").css({display:"block",opacity:0.96,top:a(window).scrollTop()+100}).appendTo(a.mobile.pageContainer).delay(800).fadeOut(400,function(){a(this).remove()})}})}}};a("form[data-ajax!='false']").live("submit",function(l){if(a.mobile.ajaxEnabled&&a.mobile.ajaxFormsEnabled){var s=a(this).attr("method"),
|
||||||
|
q=e.clean(a(this).attr("action"));if(!e.isExternal(q)){if(e.isRelative(q))q=e.makeAbsolute(q);a.mobile.changePage({url:q,type:s,data:a(this).serialize()},d,d,true);l.preventDefault()}}});a("a").live("click",function(l){var s=a(this),q=s.attr("href")||"#";q=e.clean(q);var w=s.is("[rel='external']"),z=e.isEmbeddedPage(q);w=e.isExternal(q)||w&&!z;z=s.is("[target]");var F=s.is("[data-ajax='false']");if(s.is("[data-rel='back']")){window.history.back();return false}if(q==="#")return false;i=s.closest(".ui-btn").addClass(a.mobile.activeBtnClass);
|
||||||
|
if(w||F||z||!a.mobile.ajaxEnabled||!a.mobile.ajaxLinksEnabled){c(true);if(z)window.open(q);else if(F)return;else location.href=q}else{w=s.data("transition");z=(z=s.data("direction"))&&z=="reverse"||s.data("back");j=s.attr("data-rel");if(e.isRelative(q))q=e.makeAbsolute(q);q=e.stripHash(q);a.mobile.changePage(q,w,z)}l.preventDefault()});f.bind("hashchange",function(){var l=e.stripHash(location.hash),s=a.mobile.urlHistory.stack.length===0?false:d;if(!a.mobile.hashListeningEnabled||!h.ignoreNextHashChange||
|
||||||
|
h.stack.length>1&&l.indexOf(t)>-1&&!a.mobile.activePage.is(".ui-dialog")){if(!h.ignoreNextHashChange)h.ignoreNextHashChange=true}else l?a.mobile.changePage(l,s,d,false,true):a.mobile.changePage(a.mobile.firstPage,s,true,false,true)})})(jQuery);
|
||||||
|
(function(a,d){a.fn.fixHeaderFooter=function(){if(!a.support.scrollTop)return this;return this.each(function(){var c=a(this);c.data("fullscreen")&&c.addClass("ui-page-fullscreen");c.find('.ui-header[data-position="fixed"]').addClass("ui-header-fixed ui-fixed-inline fade");c.find('.ui-footer[data-position="fixed"]').addClass("ui-footer-fixed ui-fixed-inline fade")})};a.fixedToolbars=function(){function c(){if(!e&&g=="overlay"){i||a.fixedToolbars.hide(true);a.fixedToolbars.startShowTimer()}}function f(m){var r=
|
||||||
|
0;if(m){var n=m.offsetParent,u=document.body;for(r=m.offsetTop;m&&m!=u;){r+=m.scrollTop||0;if(m==n){r+=n.offsetTop;n=m.offsetParent}m=m.parentNode}}return r}function b(m){var r=a(window).scrollTop(),n=f(m[0]),u=m.css("top")=="auto"?0:parseFloat(m.css("top")),l=window.innerHeight,s=m.outerHeight(),q=m.parents(".ui-page:not(.ui-page-fullscreen)").length;if(m.is(".ui-header-fixed")){u=r-n+u;if(u<n)u=0;return m.css("top",q?u:r)}else{u=r+l-s-(n-u);return m.css("top",q?u:r+l-s)}}if(a.support.scrollTop){var g=
|
||||||
|
"inline",e=false,i,h,k=a.support.touch,j=k?"touchstart":"mousedown",o=k?"touchend":"mouseup",p=null,t=false,v=true;a(function(){a(document).bind(j,function(m){if(v)a(m.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length||(p=g)}).bind(o,function(m){if(v)if(!a(m.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length)if(!t){a.fixedToolbars.toggle(p);p=null}}).bind("scrollstart",function(m){if(!a(m.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length){t=
|
||||||
|
true;if(p==null)p=g;if(e=(m=p=="overlay")||!!i){a.fixedToolbars.clearShowTimer();m&&a.fixedToolbars.hide(true)}}}).bind("scrollstop",function(m){if(!a(m.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length){t=false;if(e){e=false;a.fixedToolbars.startShowTimer()}p=null}}).bind("silentscroll",c);a(window).bind("resize",c)});a(".ui-page").live("pagebeforeshow",function(m){m=a(m.target).find('[data-role="footer"]:not(.ui-sticky-footer)');var r=m.data("id");
|
||||||
|
h=null;if(r){h=a('.ui-footer[data-id="'+r+'"].ui-sticky-footer');if(h.length==0){h=m;m=h.clone();h.addClass("ui-sticky-footer").before(m)}m.addClass("ui-footer-duplicate");h.appendTo(a.pageContainer).css("top",0);b(h)}});a(".ui-page").live("pageshow",function(m){h&&h.length&&h.appendTo(m.target).css("top",0);a.fixedToolbars.show(true,this)});return{show:function(m,r){a.fixedToolbars.clearShowTimer();g="overlay";return(r?a(r):a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var n=
|
||||||
|
a(this),u=a(window).scrollTop(),l=f(n[0]),s=window.innerHeight,q=n.outerHeight();u=n.is(".ui-header-fixed")&&u<=l+q||n.is(".ui-footer-fixed")&&l<=u+s;n.addClass("ui-fixed-overlay").removeClass("ui-fixed-inline");!u&&!m&&n.animationComplete(function(){n.removeClass("in")}).addClass("in");b(n)})},hide:function(m){g="inline";return(a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var r=a(this),
|
||||||
|
n=r.css("top");n=n=="auto"?0:parseFloat(n);r.addClass("ui-fixed-inline").removeClass("ui-fixed-overlay");if(n<0||r.is(".ui-header-fixed")&&n!=0)if(m)r.css("top",0);else r.css("top")!=="auto"&&parseFloat(r.css("top"))!==0&&r.animationComplete(function(){r.removeClass("out reverse");r.css("top",0)}).addClass("out reverse")})},startShowTimer:function(){a.fixedToolbars.clearShowTimer();var m=a.makeArray(arguments);i=setTimeout(function(){i=d;a.fixedToolbars.show.apply(null,m)},100)},clearShowTimer:function(){i&&
|
||||||
|
clearTimeout(i);i=d},toggle:function(m){if(m)g=m;return g=="overlay"?a.fixedToolbars.hide():a.fixedToolbars.show()},setTouchToggleEnabled:function(m){v=m}}}}()})(jQuery);
|
||||||
|
(function(a,d){a.widget("mobile.checkboxradio",a.mobile.widget,{options:{theme:null},_create:function(){var c=this,f=this.element,b=f.closest("form,fieldset,[data-role='page']").find("label[for='"+f.attr("id")+"']"),g=f.attr("type"),e="ui-icon-"+g+"-off";if(!(g!="checkbox"&&g!="radio")){if(!this.options.theme)this.options.theme=this.element.data("theme");b.buttonMarkup({theme:this.options.theme,icon:this.element.parents("[data-type='horizontal']").length?d:e,shadow:false});f.add(b).wrapAll("<div class='ui-"+
|
||||||
|
g+"'></div>");b.bind({mouseover:function(){if(a(this).parent().is(".ui-disabled"))return false},touchmove:function(i){i=i.originalEvent.touches[0];if(b.data("movestart")){if(Math.abs(b.data("movestart")[0]-i.pageX)>10||Math.abs(abel.data("movestart")[1]-i.pageY)>10)b.data("moved",true)}else b.data("movestart",[parseFloat(i.pageX),parseFloat(i.pageY)])},"touchend mouseup":function(i){b.removeData("movestart");if(b.data("etype")&&b.data("etype")!==i.type||b.data("moved")){b.removeData("etype").removeData("moved");
|
||||||
|
b.data("moved")&&b.removeData("moved");return false}b.data("etype",i.type);c._cacheVals();f.attr("checked",g==="radio"&&true||!f.is(":checked"));c._updateAll();i.preventDefault()},click:false});f.bind({mousedown:function(){this._cacheVals()},click:function(){c._updateAll()},focus:function(){b.addClass("ui-focus")},blur:function(){b.removeClass("ui-focus")}});this.refresh()}},_cacheVals:function(){this._getInputSet().each(function(){a(this).data("cacheVal",a(this).is(":checked"))})},_getInputSet:function(){return this.element.closest("form,fieldset,[data-role='page']").find("input[name='"+
|
||||||
|
this.element.attr("name")+"'][type='"+this.element.attr("type")+"']")},_updateAll:function(){this._getInputSet().each(function(){var c=a(this).data("cacheVal");if(c&&c!==a(this).is(":checked")||a(this).is("[type='checkbox']"))a(this).trigger("change")}).checkboxradio("refresh")},refresh:function(){var c=this.element,f=c.closest("form,fieldset,[data-role='page']").find("label[for='"+c.attr("id")+"']"),b=c.attr("type"),g=f.find(".ui-icon"),e="ui-icon-"+b+"-on";b="ui-icon-"+b+"-off";if(c[0].checked){f.addClass("ui-btn-active");
|
||||||
|
g.addClass(e);g.removeClass(b)}else{f.removeClass("ui-btn-active");g.removeClass(e);g.addClass(b)}c.is(":disabled")?this.disable():this.enable()},disable:function(){this.element.attr("disabled",true).parent().addClass("ui-disabled")},enable:function(){this.element.attr("disabled",false).parent().removeClass("ui-disabled")}})})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.textinput",a.mobile.widget,{options:{theme:null},_create:function(){var d=this.element,c=this.options,f=c.theme;if(!f){f=this.element.closest("[class*='ui-bar-'],[class*='ui-body-']");f=f.length?/ui-(bar|body)-([a-z])/.exec(f.attr("class"))[2]:"c"}f=" ui-body-"+f;a("label[for="+d.attr("id")+"]").addClass("ui-input-text");d.addClass("ui-input-text ui-body-"+c.theme);var b=d;if(d.is('[type="search"],[data-type="search"]')){b=d.wrap('<div class="ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield'+
|
||||||
|
f+'"></div>').parent();var g=a('<a href="#" class="ui-input-clear" title="clear text">clear text</a>').tap(function(h){d.val("").focus();d.trigger("change");g.addClass("ui-input-clear-hidden");h.preventDefault()}).appendTo(b).buttonMarkup({icon:"delete",iconpos:"notext",corners:true,shadow:true});c=function(){d.val()==""?g.addClass("ui-input-clear-hidden"):g.removeClass("ui-input-clear-hidden")};c();d.keyup(c)}else d.addClass("ui-corner-all ui-shadow-inset"+f);d.focus(function(){b.addClass("ui-focus")}).blur(function(){b.removeClass("ui-focus")});
|
||||||
|
if(d.is("textarea")){var e=function(){var h=d[0].scrollHeight;d[0].clientHeight<h&&d.css({height:h+15})},i;d.keyup(function(){clearTimeout(i);i=setTimeout(e,100)})}},disable:function(){(this.element.attr("disabled",true).is('[type="search"],[data-type="search"]')?this.element.parent():this.element).addClass("ui-disabled")},enable:function(){(this.element.attr("disabled",false).is('[type="search"],[data-type="search"]')?this.element.parent():this.element).removeClass("ui-disabled")}})})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.selectmenu",a.mobile.widget,{options:{theme:null,disabled:false,icon:"arrow-d",iconpos:"right",inline:null,corners:true,shadow:true,iconshadow:true,menuPageTheme:"b",overlayTheme:"a",hidePlaceholderMenuItems:true,closeText:"Close",nativeMenu:false},_create:function(){var d=this,c=this.options,f=this.element.wrap("<div class='ui-select'>"),b=f.attr("id"),g=a("label[for="+b+"]").addClass("ui-select"),e=(d.options.nativeMenu?a("<div/>"):a("<a>",{href:"#",role:"button",id:k,
|
||||||
|
"aria-haspopup":"true","aria-owns":j})).text(a(f[0].options.item(f[0].selectedIndex)).text()).insertBefore(f).buttonMarkup({theme:c.theme,icon:c.icon,iconpos:c.iconpos,inline:c.inline,corners:c.corners,shadow:c.shadow,iconshadow:c.iconshadow}),i=d.isMultiple=f[0].multiple;c.nativeMenu&&window.opera&&window.opera.version&&f.addClass("ui-select-nativeonly");if(!c.nativeMenu){var h=f.find("option"),k=b+"-button",j=b+"-menu",o=f.closest(".ui-page"),p=/ui-btn-up-([a-z])/.exec(e.attr("class"))[1],t=a("<div data-role='dialog' data-theme='"+
|
||||||
|
c.menuPageTheme+"'><div data-role='header'><div class='ui-title'>"+g.text()+"</div></div><div data-role='content'></div></div>").appendTo(a.mobile.pageContainer).page(),v=t.find(".ui-content"),m=t.find(".ui-header a"),r=a("<div>",{"class":"ui-selectmenu-screen ui-screen-hidden"}).appendTo(o),n=a("<div>",{"class":"ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all pop ui-body-"+c.overlayTheme}).insertAfter(r),u=a("<ul>",{"class":"ui-selectmenu-list",id:j,role:"listbox","aria-labelledby":k,
|
||||||
|
"data-theme":p}).appendTo(n);p=a("<div>",{"class":"ui-header ui-bar-"+p}).prependTo(n);var l=a("<h1>",{"class":"ui-title"}).appendTo(p),s=a("<a>",{"data-iconpos":"notext","data-icon":"delete",text:c.closeText,href:"#","class":"ui-btn-left"}).appendTo(p).buttonMarkup()}if(i)d.buttonCount=a("<span>").addClass("ui-li-count ui-btn-up-c ui-btn-corner-all").hide().appendTo(e);c.disabled&&this.disable();f.change(function(){d.refresh()});a.extend(d,{select:f,optionElems:h,selectID:b,label:g,buttonId:k,menuId:j,
|
||||||
|
thisPage:o,button:e,menuPage:t,menuPageContent:v,screen:r,listbox:n,list:u,menuType:void 0,header:p,headerClose:s,headerTitle:l,placeholder:""});if(c.nativeMenu){f.appendTo(e).bind("touchstart mousedown",function(){e.addClass(a.mobile.activeBtnClass)}).bind("focus mouseover",function(){e.trigger("mouseover")}).bind("touchmove",function(){e.removeClass(a.mobile.activeBtnClass)}).bind("change blur mouseout",function(){e.trigger("mouseout").removeClass(a.mobile.activeBtnClass)});e.attr("tabindex","-1")}else{d.refresh();
|
||||||
|
f.attr("tabindex","-1").focus(function(){a(this).blur();e.focus()});e.bind("touchstart",function(q){a(this).data("startTouches",a.extend({},q.originalEvent.touches[0]))}).bind(a.support.touch?"touchend":"mouseup",function(q){a(this).data("moved")?a(this).removeData("moved"):d.open();q.preventDefault()}).bind("touchmove",function(q){q=q.originalEvent.touches[0];var w=a(this).data("startTouches"),z=Math.abs(q.pageY-w.pageY);if(Math.abs(q.pageX-w.pageX)>10||z>10)a(this).data("moved",true)});u.delegate("li:not(.ui-disabled, .ui-li-divider)",
|
||||||
|
"click",function(q){if(a(q.target).is("a")){var w=u.find("li:not(.ui-li-divider)").index(this);w=d.optionElems.eq(w)[0];w.selected=i?!w.selected:true;i&&a(this).find(".ui-icon").toggleClass("ui-icon-checkbox-on",w.selected).toggleClass("ui-icon-checkbox-off",!w.selected);f.trigger("change");i||d.close();q.preventDefault()}});r.add(s).add(m).bind("click",function(q){d.close();q.preventDefault();a.contains(m[0],q.target)&&q.stopImmediatePropagation()})}},_buildList:function(){var d=this,c=this.options,
|
||||||
|
f=this.placeholder,b=[],g=[],e=d.isMultiple?"checkbox-off":"false";d.list.empty().filter(".ui-listview").listview("destroy");d.select.find("option").each(function(){var i=a(this),h=i.parent(),k=i.text(),j="<a href='#'>"+k+"</a>",o=[],p=[];if(h.is("optgroup")){h=h.attr("label");if(a.inArray(h,b)===-1){g.push("<li data-role='list-divider'>"+h+"</li>");b.push(h)}}if(!this.getAttribute("value")||k.length==0||i.data("placeholder")){c.hidePlaceholderMenuItems&&o.push("ui-selectmenu-placeholder");f=d.placeholder=
|
||||||
|
k}if(this.disabled){o.push("ui-disabled");p.push("aria-disabled='true'")}g.push("<li data-icon='"+e+"' class='"+o.join(" ")+"' "+p.join(" ")+">"+j+"</li>")});d.list.html(g.join(" "));this.isMultiple||this.headerClose.hide();!this.isMultiple&&!f.length?this.header.hide():this.headerTitle.text(this.placeholder);d.list.listview()},refresh:function(d){var c=this,f=this.element,b=this.isMultiple,g=this.optionElems=f.find("option"),e=g.filter(":selected"),i=e.map(function(){return g.index(this)}).get();
|
||||||
|
if(!c.options.nativeMenu&&(d||f[0].options.length>c.list.find("li").length))c._buildList();c.button.find(".ui-btn-text").text(function(){if(!b)return e.text();return e.length?e.map(function(){return a(this).text()}).get().join(", "):c.placeholder});if(b)c.buttonCount[e.length>1?"show":"hide"]().text(e.length);c.options.nativeMenu||c.list.find("li:not(.ui-li-divider)").removeClass(a.mobile.activeBtnClass).attr("aria-selected",false).each(function(h){if(a.inArray(h,i)>-1){h=a(this).addClass(a.mobile.activeBtnClass);
|
||||||
|
h.find("a").attr("aria-selected",true);b&&h.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on")}})},open:function(){function d(){c.list.find(".ui-btn-active").focus()}if(!(this.options.disabled||this.options.nativeMenu)){var c=this,f=c.list.outerHeight(),b=c.list.outerWidth(),g=a(window).scrollTop(),e=c.button.offset().top,i=window.innerHeight,h=c.list.parents(".ui-dialog").length;c.button.addClass(a.mobile.activeBtnClass);if(h||f>i-80||!a.support.scrollTop){g==0&&
|
||||||
|
e>i&&c.thisPage.one("pagehide",function(){a(this).data("lastScroll",e)});c.menuPage.one("pageshow",function(){a(window).one("silentscroll",function(){d()})});c.menuType="page";c.menuPageContent.append(c.list);a.mobile.changePage(c.menuPage,"pop",false,true)}else{c.menuType="overlay";c.screen.height(a(document).height()).removeClass("ui-screen-hidden");h=e-g;var k=g+i-e,j=f/2;f=h>f/2&&k>f/2?e+c.button.outerHeight()/2-j:h>k?g+i-f-30:g+30;b=c.button.offset().left+c.button.outerWidth()/2-b/2;c.listbox.append(c.list).removeClass("ui-selectmenu-hidden").css({top:f,
|
||||||
|
left:b}).addClass("in");d()}setTimeout(function(){c.isOpen=true},400)}},close:function(){function d(){setTimeout(function(){c.button.focus();c.button.removeClass(a.mobile.activeBtnClass)},40);c.listbox.removeAttr("style").append(c.list)}if(!(this.options.disabled||!this.isOpen||this.options.nativeMenu)){var c=this;if(c.menuType=="page"){a.mobile.changePage([c.menuPage,c.thisPage],"pop",true,false);c.menuPage.one("pagehide",d)}else{c.screen.addClass("ui-screen-hidden");c.listbox.addClass("ui-selectmenu-hidden").removeAttr("style").removeClass("in");
|
||||||
|
d()}this.isOpen=false}},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)}})})(jQuery);
|
||||||
|
(function(a){a.fn.buttonMarkup=function(c){return this.each(function(){var f=a(this),b=a.extend({},a.fn.buttonMarkup.defaults,f.data(),c),g,e="ui-btn-inner",i;d&&d();if(!b.theme){g=f.closest("[class*='ui-bar-'],[class*='ui-body-']");b.theme=g.length?/ui-(bar|body)-([a-z])/.exec(g.attr("class"))[2]:"c"}g="ui-btn ui-btn-up-"+b.theme;if(b.inline)g+=" ui-btn-inline";if(b.icon){b.icon="ui-icon-"+b.icon;b.iconpos=b.iconpos||"left";i="ui-icon "+b.icon;if(b.shadow)i+=" ui-icon-shadow"}if(b.iconpos){g+=" ui-btn-icon-"+
|
||||||
|
b.iconpos;b.iconpos=="notext"&&!f.attr("title")&&f.attr("title",f.text())}if(b.corners){g+=" ui-btn-corner-all";e+=" ui-btn-corner-all"}if(b.shadow)g+=" ui-shadow";f.attr("data-theme",b.theme).addClass(g);b=("<D class='"+e+"'><D class='ui-btn-text'></D>"+(b.icon?"<span class='"+i+"'></span>":"")+"</D>").replace(/D/g,b.wrapperEls);f.wrapInner(b)})};a.fn.buttonMarkup.defaults={corners:true,shadow:true,iconshadow:true,wrapperEls:"span"};var d=function(){a(".ui-btn:not(.ui-disabled)").live({"touchstart mousedown":function(){var c=
|
||||||
|
a(this).attr("data-theme");a(this).removeClass("ui-btn-up-"+c).addClass("ui-btn-down-"+c)},"touchmove touchend mouseup":function(){var c=a(this).attr("data-theme");a(this).removeClass("ui-btn-down-"+c).addClass("ui-btn-up-"+c)},"mouseover focus":function(){var c=a(this).attr("data-theme");a(this).removeClass("ui-btn-up-"+c).addClass("ui-btn-hover-"+c)},"mouseout blur":function(){var c=a(this).attr("data-theme");a(this).removeClass("ui-btn-hover-"+c).addClass("ui-btn-up-"+c)}});d=null}})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.button",a.mobile.widget,{options:{theme:null,icon:null,iconpos:null,inline:null,corners:true,shadow:true,iconshadow:true},_create:function(){var d=this.element,c=this.options;this.button=a("<div></div>").text(d.text()||d.val()).buttonMarkup({theme:c.theme,icon:c.icon,iconpos:c.iconpos,inline:c.inline,corners:c.corners,shadow:c.shadow,iconshadow:c.iconshadow}).insertBefore(d).append(d.addClass("ui-btn-hidden"));d.attr("type")!=="reset"&&d.click(function(){var f=a("<input>",
|
||||||
|
{type:"hidden",name:d.attr("name"),value:d.attr("value")}).insertBefore(d);a(document).submit(function(){f.remove()})})},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)}})})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.slider",a.mobile.widget,{options:{theme:null,trackTheme:null,disabled:false},_create:function(){var d=this,c=this.element,f=c.parents("[class*=ui-bar-],[class*=ui-body-]").eq(0);f=f.length?f.attr("class").match(/ui-(bar|body)-([a-z])/)[2]:"c";var b=this.options.theme?this.options.theme:f,g=this.options.trackTheme?this.options.trackTheme:f,e=c[0].nodeName.toLowerCase();f=e=="select"?"ui-slider-switch":"";var i=c.attr("id"),h=i+"-label";i=a("[for="+i+"]").attr("id",h);
|
||||||
|
var k=function(){return e=="input"?parseFloat(c.val()):c[0].selectedIndex},j=e=="input"?parseFloat(c.attr("min")):0,o=e=="input"?parseFloat(c.attr("max")):c.find("option").length-1,p=window.parseFloat(c.attr("step")||1),t=a('<div class="ui-slider '+f+" ui-btn-down-"+g+' ui-btn-corner-all" role="application"></div>'),v=a('<a href="#" class="ui-slider-handle"></a>').appendTo(t).buttonMarkup({corners:true,theme:b,shadow:true}).attr({role:"slider","aria-valuemin":j,"aria-valuemax":o,"aria-valuenow":k(),
|
||||||
|
"aria-valuetext":k(),title:k(),"aria-labelledby":h});a.extend(this,{slider:t,handle:v,dragging:false,beforeStart:null});if(e=="select"){t.wrapInner('<div class="ui-slider-inneroffset"></div>');c.find("option");c.find("option").each(function(m){var r=m==0?"b":"a",n=m==0?"right":"left";m=m==0?" ui-btn-down-"+g:" ui-btn-active";a('<div class="ui-slider-labelbg ui-slider-labelbg-'+r+m+" ui-btn-corner-"+n+'"></div>').prependTo(t);a('<span class="ui-slider-label ui-slider-label-'+r+m+" ui-btn-corner-"+
|
||||||
|
n+'" role="img">'+a(this).text()+"</span>").prependTo(v)})}i.addClass("ui-slider");c.addClass(e=="input"?"ui-slider-input":"ui-slider-switch").change(function(){d.refresh(k(),true)}).keyup(function(){d.refresh(k(),true,true)}).blur(function(){d.refresh(k(),true)});a(document).bind("touchmove mousemove",function(m){if(d.dragging){d.refresh(m);return false}});t.bind("touchstart mousedown",function(m){d.dragging=true;if(e==="select")d.beforeStart=c[0].selectedIndex;d.refresh(m);return false});t.add(document).bind("touchend mouseup",
|
||||||
|
function(){if(d.dragging){d.dragging=false;if(e==="select"){if(d.beforeStart===c[0].selectedIndex)d.refresh(d.beforeStart===0?1:0);var m=k();m=Math.round(m/(o-j)*100);v.addClass("ui-slider-handle-snapping").css("left",m+"%").animationComplete(function(){v.removeClass("ui-slider-handle-snapping")})}return false}});t.insertAfter(c);this.handle.bind("touchstart mousedown",function(){a(this).focus()});this.handle.bind("keydown",function(m){var r=k();if(!d.options.disabled){switch(m.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:m.preventDefault();
|
||||||
|
if(!d._keySliding){d._keySliding=true;a(this).addClass("ui-state-active")}}switch(m.keyCode){case a.mobile.keyCode.HOME:d.refresh(j);break;case a.mobile.keyCode.END:d.refresh(o);break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:d.refresh(r+p);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:d.refresh(r-p)}}}).keyup(function(){if(d._keySliding){d._keySliding=false;a(this).removeClass("ui-state-active")}});this.refresh()},
|
||||||
|
refresh:function(d,c,f){if(!this.options.disabled){var b=this.element,g=b[0].nodeName.toLowerCase(),e=g==="input"?parseFloat(b.attr("min")):0,i=g==="input"?parseFloat(b.attr("max")):b.find("option").length-1;if(typeof d==="object"){d=d.originalEvent.touches?d.originalEvent.touches[0]:d;if(!this.dragging||d.pageX<this.slider.offset().left-8||d.pageX>this.slider.offset().left+this.slider.width()+8)return;d=Math.round((d.pageX-this.slider.offset().left)/this.slider.width()*100)}else{if(d==null)d=g===
|
||||||
|
"input"?parseFloat(b.val()):b[0].selectedIndex;d=(parseFloat(d)-e)/(i-e)*100}if(!isNaN(d)){if(d<0)d=0;if(d>100)d=100;var h=Math.round(d/100*(i-e))+e;if(h<e)h=e;if(h>i)h=i;this.handle.css("left",d+"%");this.handle.attr({"aria-valuenow":g==="input"?h:b.find("option").eq(h).attr("value"),"aria-valuetext":g==="input"?h:b.find("option").eq(h).text(),title:h});if(g==="select")h===0?this.slider.addClass("ui-slider-switch-a").removeClass("ui-slider-switch-b"):this.slider.addClass("ui-slider-switch-b").removeClass("ui-slider-switch-a");
|
||||||
|
if(!f){if(g==="input")b.val(h);else b[0].selectedIndex=h;c||b.trigger("change")}}}},enable:function(){this.element.attr("disabled",false);this.slider.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.slider.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)}})})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.collapsible",a.mobile.widget,{options:{expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsed:false,heading:">:header,>legend",theme:null,iconTheme:"d"},_create:function(){var d=this.element,c=this.options,f=d.addClass("ui-collapsible-contain"),b=d.find(c.heading).eq(0),g=f.wrapInner('<div class="ui-collapsible-content"></div>').find(".ui-collapsible-content");d=d.closest('[data-role="collapsible-set"]').addClass("ui-collapsible-set");
|
||||||
|
if(b.is("legend")){b=a('<div role="heading">'+b.html()+"</div>").insertBefore(b);b.next().remove()}b.insertBefore(g);b.addClass("ui-collapsible-heading").append('<span class="ui-collapsible-heading-status"></span>').wrapInner('<a href="#" class="ui-collapsible-heading-toggle"></a>').find("a:eq(0)").buttonMarkup({shadow:!!!d.length,corners:false,iconPos:"left",icon:"plus",theme:c.theme}).find(".ui-icon").removeAttr("class").buttonMarkup({shadow:true,corners:true,iconPos:"notext",icon:"plus",theme:c.iconTheme});
|
||||||
|
if(d.length)f.data("collapsible-last")&&b.find("a:eq(0), .ui-btn-inner").addClass("ui-corner-bottom");else b.find("a:eq(0)").addClass("ui-corner-all").find(".ui-btn-inner").addClass("ui-corner-all");f.bind("collapse",function(e){if(!e.isDefaultPrevented()){e.preventDefault();b.addClass("ui-collapsible-heading-collapsed").find(".ui-collapsible-heading-status").text(c.expandCueText);b.find(".ui-icon").removeClass("ui-icon-minus").addClass("ui-icon-plus");g.addClass("ui-collapsible-content-collapsed").attr("aria-hidden",
|
||||||
|
true);f.data("collapsible-last")&&b.find("a:eq(0), .ui-btn-inner").addClass("ui-corner-bottom")}}).bind("expand",function(e){if(!e.isDefaultPrevented()){e.preventDefault();b.removeClass("ui-collapsible-heading-collapsed").find(".ui-collapsible-heading-status").text(c.collapseCueText);b.find(".ui-icon").removeClass("ui-icon-plus").addClass("ui-icon-minus");g.removeClass("ui-collapsible-content-collapsed").attr("aria-hidden",false);f.data("collapsible-last")&&b.find("a:eq(0), .ui-btn-inner").removeClass("ui-corner-bottom")}}).trigger(c.collapsed?
|
||||||
|
"collapse":"expand");if(d.length&&!d.data("collapsiblebound")){d.data("collapsiblebound",true).bind("expand",function(e){a(this).find(".ui-collapsible-contain").not(a(e.target).closest(".ui-collapsible-contain")).not("> .ui-collapsible-contain .ui-collapsible-contain").trigger("collapse")});d=d.find("[data-role=collapsible]");d.first().find("a:eq(0)").addClass("ui-corner-top").find(".ui-btn-inner").addClass("ui-corner-top");d.last().data("collapsible-last",true)}b.bind(a.support.touch?"touchstart":
|
||||||
|
"click",function(){b.is(".ui-collapsible-heading-collapsed")?f.trigger("expand"):f.trigger("collapse");return false})}})})(jQuery);
|
||||||
|
(function(a){a.fn.controlgroup=function(d){return this.each(function(){function c(e){e.removeClass("ui-btn-corner-all ui-shadow").eq(0).addClass(g[0]).end().filter(":last").addClass(g[1]).addClass("ui-controlgroup-last")}var f=a.extend({direction:a(this).data("type")||"vertical",shadow:false},d),b=a(this).find(">legend"),g=f.direction=="horizontal"?["ui-corner-left","ui-corner-right"]:["ui-corner-top","ui-corner-bottom"];a(this).find("input:eq(0)").attr("type");if(b.length){a(this).wrapInner('<div class="ui-controlgroup-controls"></div>');
|
||||||
|
a('<div role="heading" class="ui-controlgroup-label">'+b.html()+"</div>").insertBefore(a(this).children(0));b.remove()}a(this).addClass("ui-corner-all ui-controlgroup ui-controlgroup-"+f.direction);c(a(this).find(".ui-btn"));c(a(this).find(".ui-btn-inner"));f.shadow&&a(this).addClass("ui-shadow")})}})(jQuery);(function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain ui-body ui-br")}})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.listview",a.mobile.widget,{options:{theme:"c",countTheme:"c",headerTheme:"b",dividerTheme:"b",splitIcon:"arrow-r",splitTheme:"b",inset:false},_create:function(){var d=this.element,c=this.options;d.addClass("ui-listview").attr("role","listbox");c.inset&&d.addClass("ui-listview-inset ui-corner-all ui-shadow");d.delegate(".ui-li","focusin",function(){a(this).attr("tabindex","0")});this._itemApply(d,d);this.refresh(true);d.keydown(function(f){var b=a(f.target),g=b.closest("li");
|
||||||
|
switch(f.keyCode){case 38:f=g.prev();if(f.length){b.blur().attr("tabindex","-1");f.find("a").first().focus()}return false;case 40:f=g.next();if(f.length){b.blur().attr("tabindex","-1");f.find("a").first().focus()}return false;case 39:f=g.find("a.ui-li-link-alt");if(f.length){b.blur();f.first().focus()}return false;case 37:f=g.find("a.ui-link-inherit");if(f.length){b.blur();f.first().focus()}return false;case 13:case 32:b.trigger("click");return false}});d.delegate("li","click",function(f){if(!a(f.target).closest("a").length){a(this).find("a").first().trigger("click");
|
||||||
|
return false}})},_itemApply:function(d,c){c.find(".ui-li-count").addClass("ui-btn-up-"+(d.data("counttheme")||this.options.countTheme)+" ui-btn-corner-all");c.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading");c.find("p, dl").addClass("ui-li-desc");c.find("li").find("img:eq(0)").addClass("ui-li-thumb").each(function(){a(this).closest("li").addClass(a(this).is(".ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb")});var f=c.find(".ui-li-aside");f.length&&f.each(function(b,g){a(g).prependTo(a(g).parent())});
|
||||||
|
a.support.cssPseudoElement||a.nodeName(c[0],"ol")},_removeCorners:function(d){d.add(d.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb")).removeClass("ui-corner-top ui-corner-bottom ui-corner-br ui-corner-bl ui-corner-tr ui-corner-tl")},refresh:function(d){this._createSubPages();var c=this.options,f=this.element,b=this,g=f.data("dividertheme")||c.dividerTheme,e=f.children("li"),i=a.support.cssPseudoElement||!a.nodeName(f[0],"ol")?0:1;i&&f.find(".ui-li-dec").remove();e.attr({role:"option",tabindex:"-1"});
|
||||||
|
e.first().attr("tabindex","0");e.each(function(h){var k=a(this),j="ui-li";if(!(!d&&k.hasClass("ui-li"))){var o=k.data("theme")||c.theme,p=k.find("a");if(p.length){var t=k.data("icon");k.buttonMarkup({wrapperEls:"div",shadow:false,corners:false,iconpos:"right",icon:p.length>1||t===false?false:t||"arrow-r",theme:o});p.first().addClass("ui-link-inherit");if(p.length>1){j+=" ui-li-has-alt";p=p.last();t=f.data("splittheme")||p.data("theme")||c.splitTheme;p.appendTo(k).attr("title",p.text()).addClass("ui-li-link-alt").empty().buttonMarkup({shadow:false,
|
||||||
|
corners:false,theme:o,icon:false,iconpos:false}).find(".ui-btn-inner").append(a("<span>").buttonMarkup({shadow:true,corners:true,theme:t,iconpos:"notext",icon:f.data("spliticon")||p.data("icon")||c.splitIcon}))}}else if(k.data("role")==="list-divider"){j+=" ui-li-divider ui-btn ui-bar-"+g;k.attr("role","heading");if(i)i=1}else j+=" ui-li-static ui-btn-up-"+o;if(c.inset){if(h===0){j+=" ui-corner-top";k.add(k.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-tr").end().find(".ui-li-thumb").addClass("ui-corner-tl");
|
||||||
|
k.next().next().length&&b._removeCorners(k.next())}if(h===e.length-1){j+=" ui-corner-bottom";k.add(k.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-br").end().find(".ui-li-thumb").addClass("ui-corner-bl");k.prev().prev().length&&b._removeCorners(k.prev())}}i&&j.indexOf("ui-li-divider")<0&&k.find(".ui-link-inherit").first().addClass("ui-li-jsnumbering").prepend("<span class='ui-li-dec'>"+i++ +". </span>");k.addClass(j);d||b._itemApply(f,k)}})},_idStringEscape:function(d){return d.replace(/[^a-zA-Z0-9]/g,
|
||||||
|
"-")},_createSubPages:function(){var d=this.element,c=d.closest(".ui-page"),f=c.data("url"),b=this.options,g=this,e=c.find("[data-role='footer']").data("id");a(d.find("ul, ol").toArray().reverse()).each(function(i){var h=a(this),k=h.parent(),j=a.trim(k.contents()[0].nodeValue)||k.find("a:first").text();i=f+"&"+a.mobile.subPageUrlKey+"="+g._idStringEscape(j+" "+i);var o=h.data("theme")||b.theme,p=h.data("counttheme")||d.data("counttheme")||b.countTheme;h.wrap("<div data-role='page'><div data-role='content'></div></div>").parent().before("<div data-role='header' data-theme='"+
|
||||||
|
b.headerTheme+"'><div class='ui-title'>"+j+"</div></div>").after(e?a("<div>",{"data-role":"footer","data-id":e,"class":"ui-footer-duplicate"}):"").parent().attr({"data-url":i,"data-theme":o,"data-count-theme":p}).appendTo(a.mobile.pageContainer).page();h=k.find("a:first");h.length||(h=a("<a></a>").html(j).prependTo(k.empty()));h.attr("href","#"+i)}).listview()}})})(jQuery);
|
||||||
|
(function(a){a.mobile.listview.prototype.options.filter=false;a("[data-role='listview']").live("listviewcreate",function(){var d=a(this);if(d.data("listview").options.filter){var c=a("<form>",{"class":"ui-listview-filter ui-bar-c",role:"search"});a("<input>",{placeholder:"Filter results...","data-type":"search"}).bind("keyup change",function(){var f=this.value.toLowerCase();d.children().show();f&&d.children().filter(function(){return a(this).text().toLowerCase().indexOf(f)===-1}).hide()}).appendTo(c).textinput();
|
||||||
|
c.insertBefore(d)}})})(jQuery);
|
||||||
|
(function(a){a.widget("mobile.dialog",a.mobile.widget,{options:{},_create:function(){this.element.attr("role","dialog").addClass("ui-page ui-dialog ui-body-a").find("[data-role=header]").addClass("ui-corner-top ui-overlay-shadow").prepend('<a href="#" data-icon="delete" data-rel="back" data-iconpos="notext">Close</a>').end().find('.ui-content:not([class*="ui-body-"])').addClass("ui-body-c").end().find(".ui-content,[data-role=footer]").last().addClass("ui-corner-bottom ui-overlay-shadow");this.element.bind("click submit",
|
||||||
|
function(d){d=d.type=="click"?a(d.target).closest("a"):a(d.target).closest("form");d.length&&!d.data("transition")&&d.attr("data-transition",a.mobile.urlHistory.getActive().transition).attr("data-direction","reverse")})},close:function(){window.history.back()}})})(jQuery);
|
||||||
|
(function(a,d){a.widget("mobile.navbar",a.mobile.widget,{options:{iconpos:"top",grid:null},_create:function(){var c=this.element,f=c.find("a"),b=f.filter("[data-icon]").length?this.options.iconpos:d;c.addClass("ui-navbar").attr("role","navigation").find("ul").grid({grid:this.options.grid});b||c.addClass("ui-navbar-noicons");f.buttonMarkup({corners:false,shadow:false,iconpos:b});c.delegate("a","click",function(){f.removeClass("ui-btn-active");a(this).addClass("ui-btn-active")})}})})(jQuery);
|
||||||
|
(function(a){a.fn.grid=function(d){return this.each(function(){var c=a.extend({grid:null},d),f=a(this).children(),b={a:2,b:3,c:4,d:5};c=c.grid;if(!c)if(f.length<=5)for(var g in b){if(b[g]==f.length)c=g}else c="a";b=b[c];a(this).addClass("ui-grid-"+c);f.filter(":nth-child("+b+"n+1)").addClass("ui-block-a");f.filter(":nth-child("+b+"n+2)").addClass("ui-block-b");b>2&&f.filter(":nth-child(3n+3)").addClass("ui-block-c");b>3&&f.filter(":nth-child(4n+4)").addClass("ui-block-d");b>4&&f.filter(":nth-child(5n+5)").addClass("ui-block-e")})}})(jQuery);
|
480
openlp/plugins/remotes/html/json2.js
Executable file
@ -0,0 +1,480 @@
|
|||||||
|
/*
|
||||||
|
http://www.JSON.org/json2.js
|
||||||
|
2011-02-23
|
||||||
|
|
||||||
|
Public Domain.
|
||||||
|
|
||||||
|
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||||
|
|
||||||
|
See http://www.JSON.org/js.html
|
||||||
|
|
||||||
|
|
||||||
|
This code should be minified before deployment.
|
||||||
|
See http://javascript.crockford.com/jsmin.html
|
||||||
|
|
||||||
|
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
||||||
|
NOT CONTROL.
|
||||||
|
|
||||||
|
|
||||||
|
This file creates a global JSON object containing two methods: stringify
|
||||||
|
and parse.
|
||||||
|
|
||||||
|
JSON.stringify(value, replacer, space)
|
||||||
|
value any JavaScript value, usually an object or array.
|
||||||
|
|
||||||
|
replacer an optional parameter that determines how object
|
||||||
|
values are stringified for objects. It can be a
|
||||||
|
function or an array of strings.
|
||||||
|
|
||||||
|
space an optional parameter that specifies the indentation
|
||||||
|
of nested structures. If it is omitted, the text will
|
||||||
|
be packed without extra whitespace. If it is a number,
|
||||||
|
it will specify the number of spaces to indent at each
|
||||||
|
level. If it is a string (such as '\t' or ' '),
|
||||||
|
it contains the characters used to indent at each level.
|
||||||
|
|
||||||
|
This method produces a JSON text from a JavaScript value.
|
||||||
|
|
||||||
|
When an object value is found, if the object contains a toJSON
|
||||||
|
method, its toJSON method will be called and the result will be
|
||||||
|
stringified. A toJSON method does not serialize: it returns the
|
||||||
|
value represented by the name/value pair that should be serialized,
|
||||||
|
or undefined if nothing should be serialized. The toJSON method
|
||||||
|
will be passed the key associated with the value, and this will be
|
||||||
|
bound to the value
|
||||||
|
|
||||||
|
For example, this would serialize Dates as ISO strings.
|
||||||
|
|
||||||
|
Date.prototype.toJSON = function (key) {
|
||||||
|
function f(n) {
|
||||||
|
// Format integers to have at least two digits.
|
||||||
|
return n < 10 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getUTCFullYear() + '-' +
|
||||||
|
f(this.getUTCMonth() + 1) + '-' +
|
||||||
|
f(this.getUTCDate()) + 'T' +
|
||||||
|
f(this.getUTCHours()) + ':' +
|
||||||
|
f(this.getUTCMinutes()) + ':' +
|
||||||
|
f(this.getUTCSeconds()) + 'Z';
|
||||||
|
};
|
||||||
|
|
||||||
|
You can provide an optional replacer method. It will be passed the
|
||||||
|
key and value of each member, with this bound to the containing
|
||||||
|
object. The value that is returned from your method will be
|
||||||
|
serialized. If your method returns undefined, then the member will
|
||||||
|
be excluded from the serialization.
|
||||||
|
|
||||||
|
If the replacer parameter is an array of strings, then it will be
|
||||||
|
used to select the members to be serialized. It filters the results
|
||||||
|
such that only members with keys listed in the replacer array are
|
||||||
|
stringified.
|
||||||
|
|
||||||
|
Values that do not have JSON representations, such as undefined or
|
||||||
|
functions, will not be serialized. Such values in objects will be
|
||||||
|
dropped; in arrays they will be replaced with null. You can use
|
||||||
|
a replacer function to replace those with JSON values.
|
||||||
|
JSON.stringify(undefined) returns undefined.
|
||||||
|
|
||||||
|
The optional space parameter produces a stringification of the
|
||||||
|
value that is filled with line breaks and indentation to make it
|
||||||
|
easier to read.
|
||||||
|
|
||||||
|
If the space parameter is a non-empty string, then that string will
|
||||||
|
be used for indentation. If the space parameter is a number, then
|
||||||
|
the indentation will be that many spaces.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
||||||
|
// text is '["e",{"pluribus":"unum"}]'
|
||||||
|
|
||||||
|
|
||||||
|
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
||||||
|
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
||||||
|
|
||||||
|
text = JSON.stringify([new Date()], function (key, value) {
|
||||||
|
return this[key] instanceof Date ?
|
||||||
|
'Date(' + this[key] + ')' : value;
|
||||||
|
});
|
||||||
|
// text is '["Date(---current time---)"]'
|
||||||
|
|
||||||
|
|
||||||
|
JSON.parse(text, reviver)
|
||||||
|
This method parses a JSON text to produce an object or array.
|
||||||
|
It can throw a SyntaxError exception.
|
||||||
|
|
||||||
|
The optional reviver parameter is a function that can filter and
|
||||||
|
transform the results. It receives each of the keys and values,
|
||||||
|
and its return value is used instead of the original value.
|
||||||
|
If it returns what it received, then the structure is not modified.
|
||||||
|
If it returns undefined then the member is deleted.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
// Parse the text. Values that look like ISO date strings will
|
||||||
|
// be converted to Date objects.
|
||||||
|
|
||||||
|
myData = JSON.parse(text, function (key, value) {
|
||||||
|
var a;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
a =
|
||||||
|
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
||||||
|
if (a) {
|
||||||
|
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
||||||
|
+a[5], +a[6]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
||||||
|
var d;
|
||||||
|
if (typeof value === 'string' &&
|
||||||
|
value.slice(0, 5) === 'Date(' &&
|
||||||
|
value.slice(-1) === ')') {
|
||||||
|
d = new Date(value.slice(5, -1));
|
||||||
|
if (d) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
This is a reference implementation. You are free to copy, modify, or
|
||||||
|
redistribute.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*jslint evil: true, strict: false, regexp: false */
|
||||||
|
|
||||||
|
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
||||||
|
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||||
|
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
||||||
|
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
||||||
|
test, toJSON, toString, valueOf
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Create a JSON object only if one does not already exist. We create the
|
||||||
|
// methods in a closure to avoid creating global variables.
|
||||||
|
|
||||||
|
var JSON;
|
||||||
|
if (!JSON) {
|
||||||
|
JSON = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function f(n) {
|
||||||
|
// Format integers to have at least two digits.
|
||||||
|
return n < 10 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof Date.prototype.toJSON !== 'function') {
|
||||||
|
|
||||||
|
Date.prototype.toJSON = function (key) {
|
||||||
|
|
||||||
|
return isFinite(this.valueOf()) ?
|
||||||
|
this.getUTCFullYear() + '-' +
|
||||||
|
f(this.getUTCMonth() + 1) + '-' +
|
||||||
|
f(this.getUTCDate()) + 'T' +
|
||||||
|
f(this.getUTCHours()) + ':' +
|
||||||
|
f(this.getUTCMinutes()) + ':' +
|
||||||
|
f(this.getUTCSeconds()) + 'Z' : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
String.prototype.toJSON =
|
||||||
|
Number.prototype.toJSON =
|
||||||
|
Boolean.prototype.toJSON = function (key) {
|
||||||
|
return this.valueOf();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||||
|
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||||
|
gap,
|
||||||
|
indent,
|
||||||
|
meta = { // table of character substitutions
|
||||||
|
'\b': '\\b',
|
||||||
|
'\t': '\\t',
|
||||||
|
'\n': '\\n',
|
||||||
|
'\f': '\\f',
|
||||||
|
'\r': '\\r',
|
||||||
|
'"' : '\\"',
|
||||||
|
'\\': '\\\\'
|
||||||
|
},
|
||||||
|
rep;
|
||||||
|
|
||||||
|
|
||||||
|
function quote(string) {
|
||||||
|
|
||||||
|
// If the string contains no control characters, no quote characters, and no
|
||||||
|
// backslash characters, then we can safely slap some quotes around it.
|
||||||
|
// Otherwise we must also replace the offending characters with safe escape
|
||||||
|
// sequences.
|
||||||
|
|
||||||
|
escapable.lastIndex = 0;
|
||||||
|
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||||
|
var c = meta[a];
|
||||||
|
return typeof c === 'string' ? c :
|
||||||
|
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||||
|
}) + '"' : '"' + string + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function str(key, holder) {
|
||||||
|
|
||||||
|
// Produce a string from holder[key].
|
||||||
|
|
||||||
|
var i, // The loop counter.
|
||||||
|
k, // The member key.
|
||||||
|
v, // The member value.
|
||||||
|
length,
|
||||||
|
mind = gap,
|
||||||
|
partial,
|
||||||
|
value = holder[key];
|
||||||
|
|
||||||
|
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||||
|
|
||||||
|
if (value && typeof value === 'object' &&
|
||||||
|
typeof value.toJSON === 'function') {
|
||||||
|
value = value.toJSON(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we were called with a replacer function, then call the replacer to
|
||||||
|
// obtain a replacement value.
|
||||||
|
|
||||||
|
if (typeof rep === 'function') {
|
||||||
|
value = rep.call(holder, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// What happens next depends on the value's type.
|
||||||
|
|
||||||
|
switch (typeof value) {
|
||||||
|
case 'string':
|
||||||
|
return quote(value);
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
|
||||||
|
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||||
|
|
||||||
|
return isFinite(value) ? String(value) : 'null';
|
||||||
|
|
||||||
|
case 'boolean':
|
||||||
|
case 'null':
|
||||||
|
|
||||||
|
// If the value is a boolean or null, convert it to a string. Note:
|
||||||
|
// typeof null does not produce 'null'. The case is included here in
|
||||||
|
// the remote chance that this gets fixed someday.
|
||||||
|
|
||||||
|
return String(value);
|
||||||
|
|
||||||
|
// If the type is 'object', we might be dealing with an object or an array or
|
||||||
|
// null.
|
||||||
|
|
||||||
|
case 'object':
|
||||||
|
|
||||||
|
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||||
|
// so watch out for that case.
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make an array to hold the partial results of stringifying this object value.
|
||||||
|
|
||||||
|
gap += indent;
|
||||||
|
partial = [];
|
||||||
|
|
||||||
|
// Is the value an array?
|
||||||
|
|
||||||
|
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||||
|
|
||||||
|
// The value is an array. Stringify every element. Use null as a placeholder
|
||||||
|
// for non-JSON values.
|
||||||
|
|
||||||
|
length = value.length;
|
||||||
|
for (i = 0; i < length; i += 1) {
|
||||||
|
partial[i] = str(i, value) || 'null';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join all of the elements together, separated with commas, and wrap them in
|
||||||
|
// brackets.
|
||||||
|
|
||||||
|
v = partial.length === 0 ? '[]' : gap ?
|
||||||
|
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
|
||||||
|
'[' + partial.join(',') + ']';
|
||||||
|
gap = mind;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the replacer is an array, use it to select the members to be stringified.
|
||||||
|
|
||||||
|
if (rep && typeof rep === 'object') {
|
||||||
|
length = rep.length;
|
||||||
|
for (i = 0; i < length; i += 1) {
|
||||||
|
if (typeof rep[i] === 'string') {
|
||||||
|
k = rep[i];
|
||||||
|
v = str(k, value);
|
||||||
|
if (v) {
|
||||||
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Otherwise, iterate through all of the keys in the object.
|
||||||
|
|
||||||
|
for (k in value) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||||
|
v = str(k, value);
|
||||||
|
if (v) {
|
||||||
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join all of the member texts together, separated with commas,
|
||||||
|
// and wrap them in braces.
|
||||||
|
|
||||||
|
v = partial.length === 0 ? '{}' : gap ?
|
||||||
|
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
|
||||||
|
'{' + partial.join(',') + '}';
|
||||||
|
gap = mind;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the JSON object does not yet have a stringify method, give it one.
|
||||||
|
|
||||||
|
if (typeof JSON.stringify !== 'function') {
|
||||||
|
JSON.stringify = function (value, replacer, space) {
|
||||||
|
|
||||||
|
// The stringify method takes a value and an optional replacer, and an optional
|
||||||
|
// space parameter, and returns a JSON text. The replacer can be a function
|
||||||
|
// that can replace values, or an array of strings that will select the keys.
|
||||||
|
// A default replacer method can be provided. Use of the space parameter can
|
||||||
|
// produce text that is more easily readable.
|
||||||
|
|
||||||
|
var i;
|
||||||
|
gap = '';
|
||||||
|
indent = '';
|
||||||
|
|
||||||
|
// If the space parameter is a number, make an indent string containing that
|
||||||
|
// many spaces.
|
||||||
|
|
||||||
|
if (typeof space === 'number') {
|
||||||
|
for (i = 0; i < space; i += 1) {
|
||||||
|
indent += ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the space parameter is a string, it will be used as the indent string.
|
||||||
|
|
||||||
|
} else if (typeof space === 'string') {
|
||||||
|
indent = space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a replacer, it must be a function or an array.
|
||||||
|
// Otherwise, throw an error.
|
||||||
|
|
||||||
|
rep = replacer;
|
||||||
|
if (replacer && typeof replacer !== 'function' &&
|
||||||
|
(typeof replacer !== 'object' ||
|
||||||
|
typeof replacer.length !== 'number')) {
|
||||||
|
throw new Error('JSON.stringify');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a fake root object containing our value under the key of ''.
|
||||||
|
// Return the result of stringifying the value.
|
||||||
|
|
||||||
|
return str('', {'': value});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If the JSON object does not yet have a parse method, give it one.
|
||||||
|
|
||||||
|
if (typeof JSON.parse !== 'function') {
|
||||||
|
JSON.parse = function (text, reviver) {
|
||||||
|
|
||||||
|
// The parse method takes a text and an optional reviver function, and returns
|
||||||
|
// a JavaScript value if the text is a valid JSON text.
|
||||||
|
|
||||||
|
var j;
|
||||||
|
|
||||||
|
function walk(holder, key) {
|
||||||
|
|
||||||
|
// The walk method is used to recursively walk the resulting structure so
|
||||||
|
// that modifications can be made.
|
||||||
|
|
||||||
|
var k, v, value = holder[key];
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
for (k in value) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||||
|
v = walk(value, k);
|
||||||
|
if (v !== undefined) {
|
||||||
|
value[k] = v;
|
||||||
|
} else {
|
||||||
|
delete value[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reviver.call(holder, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parsing happens in four stages. In the first stage, we replace certain
|
||||||
|
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||||
|
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||||
|
|
||||||
|
text = String(text);
|
||||||
|
cx.lastIndex = 0;
|
||||||
|
if (cx.test(text)) {
|
||||||
|
text = text.replace(cx, function (a) {
|
||||||
|
return '\\u' +
|
||||||
|
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the second stage, we run the text against regular expressions that look
|
||||||
|
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||||
|
// because they can cause invocation, and '=' because it can cause mutation.
|
||||||
|
// But just to be safe, we want to reject all unexpected forms.
|
||||||
|
|
||||||
|
// We split the second stage into 4 regexp operations in order to work around
|
||||||
|
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||||
|
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||||
|
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||||
|
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||||
|
// we look to see that the remaining characters are only whitespace or ']' or
|
||||||
|
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||||
|
|
||||||
|
if (/^[\],:{}\s]*$/
|
||||||
|
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||||
|
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||||
|
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||||
|
|
||||||
|
// In the third stage we use the eval function to compile the text into a
|
||||||
|
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||||
|
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||||
|
// in parens to eliminate the ambiguity.
|
||||||
|
|
||||||
|
j = eval('(' + text + ')');
|
||||||
|
|
||||||
|
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||||
|
// each name/value pair to a reviver function for possible transformation.
|
||||||
|
|
||||||
|
return typeof reviver === 'function' ?
|
||||||
|
walk({'': j}, '') : j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||||
|
|
||||||
|
throw new SyntaxError('JSON.parse');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}());
|
@ -20,13 +20,10 @@
|
|||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/**
|
.ui-icon-blank {
|
||||||
* init.js - In certain browsers (yes, IE, I'm looking at you!), DocumentReady
|
background-image: url(images/ui-icon-blank.png);
|
||||||
* 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 () {
|
.ui-icon-unblank {
|
||||||
OpenLP.Events.init();
|
background-image: url(images/ui-icon-unblank.png);
|
||||||
});
|
}
|
@ -20,85 +20,7 @@
|
|||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
window["OpenLP"] = {
|
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) {
|
getElement: function(event) {
|
||||||
var targ;
|
var targ;
|
||||||
if (!event) {
|
if (!event) {
|
||||||
@ -116,128 +38,167 @@ OpenLP.Namespace.create("OpenLP.Events", {
|
|||||||
}
|
}
|
||||||
return $(targ);
|
return $(targ);
|
||||||
},
|
},
|
||||||
init: function () {
|
loadService: function (event) {
|
||||||
for (idx in this.onload_functions) {
|
$.getJSON(
|
||||||
func = this.onload_functions[idx];
|
"/api/service/list",
|
||||||
func();
|
function (data, status) {
|
||||||
|
var ul = $("#service-manager > div[data-role=content] > ul[data-role=listview]");
|
||||||
|
ul.html("");
|
||||||
|
$.each(data.results.items, function (idx, value) {
|
||||||
|
var li = $("<li data-icon=\"false\">").append(
|
||||||
|
$("<a href=\"#\">").attr("value", parseInt(idx, 10)).text(value["title"]));
|
||||||
|
li.children("a").click(OpenLP.setItem);
|
||||||
|
ul.append(li);
|
||||||
|
});
|
||||||
|
ul.listview("refresh");
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
});
|
|
||||||
|
|
||||||
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)
|
loadController: function (event) {
|
||||||
{
|
$.getJSON(
|
||||||
if (eventName == "remotes_poll_request")
|
"/api/controller/live/text",
|
||||||
{
|
function (data, status) {
|
||||||
OpenLP.Remote.handleEvent("remotes_poll_request");
|
var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]");
|
||||||
|
ul.html("");
|
||||||
|
for (idx in data.results.slides) {
|
||||||
|
var li = $("<li data-icon=\"false\">").append(
|
||||||
|
$("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(data.results.slides[idx]["text"]));
|
||||||
|
if (data.results.slides[idx]["selected"]) {
|
||||||
|
li.attr("data-theme", "e");
|
||||||
}
|
}
|
||||||
|
li.children("a").click(OpenLP.setSlide);
|
||||||
|
ul.append(li);
|
||||||
|
}
|
||||||
|
ul.listview("refresh");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setItem: function (event) {
|
||||||
|
var item = OpenLP.getElement(event);
|
||||||
|
var id = item.attr("value");
|
||||||
|
var text = JSON.stringify({"request": {"id": id}});
|
||||||
|
$.getJSON(
|
||||||
|
"/api/service/set",
|
||||||
|
{"data": text},
|
||||||
|
function (data, status) {
|
||||||
|
$("#service-manager > div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c");
|
||||||
|
while (item[0].tagName != "LI") {
|
||||||
|
item = item.parent();
|
||||||
|
}
|
||||||
|
item.attr("data-theme", "e").removeClass("ui-btn-up-c").addClass("ui-btn-up-e");
|
||||||
|
$("#service-manager > div[data-role=content] ul[data-role=listview]").listview("refresh");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setSlide: function (event) {
|
||||||
|
var slide = OpenLP.getElement(event);
|
||||||
|
console.log(slide);
|
||||||
|
var id = slide.attr("value");
|
||||||
|
var text = JSON.stringify({"request": {"id": id}});
|
||||||
|
$.getJSON(
|
||||||
|
"/api/controller/live/set",
|
||||||
|
{"data": text},
|
||||||
|
function (data, status) {
|
||||||
|
$("#slide-controller div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c");
|
||||||
|
while (slide[0].tagName != "LI") {
|
||||||
|
slide = slide.parent();
|
||||||
|
}
|
||||||
|
slide.attr("data-theme", "e").removeClass("ui-btn-up-c").addClass("ui-btn-up-e");
|
||||||
|
$("#slide-controller div[data-role=content] ul[data-role=listview]").listview("refresh");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
pollServer: function () {
|
||||||
|
$.getJSON(
|
||||||
|
"/api/poll",
|
||||||
|
function (data, status) {
|
||||||
|
OpenLP.currentSlide = data.results.slide;
|
||||||
|
OpenLP.currentItem = data.results.item;
|
||||||
|
if ($("#service-manager").is(":visible")) {
|
||||||
|
$("#service-manager div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c");
|
||||||
|
$("#service-manager div[data-role=content] ul[data-role=listview] li a").each(function () {
|
||||||
|
var item = $(this);
|
||||||
|
if (item.text() == OpenLP.currentItem) {
|
||||||
|
while (item[0].tagName != "LI") {
|
||||||
|
item = item.parent();
|
||||||
|
}
|
||||||
|
item.attr("data-theme", "e").removeClass("ui-btn-up-c").addClass("ui-btn-up-e");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
$("#service-manager div[data-role=content] ul[data-role=listview]").listview("refresh");
|
||||||
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));
|
if ($("#slide-controller").is(":visible")) {
|
||||||
trow.append($("<td>").text(item["title"]));
|
var idx = 0;
|
||||||
trow.append($("<td>").text(item["plugin"]));
|
$("#slide-controller div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c");
|
||||||
trow.append($("<td>").text("Notes: " + item["notes"]));
|
$("#slide-controller div[data-role=content] ul[data-role=listview] li a").each(function () {
|
||||||
table.append(trow);
|
var item = $(this);
|
||||||
|
if (idx == OpenLP.currentSlide) {
|
||||||
|
while (item[0].tagName != "LI") {
|
||||||
|
item = item.parent();
|
||||||
|
}
|
||||||
|
item.attr("data-theme", "e").removeClass("ui-btn-up-c").addClass("ui-btn-up-e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
});
|
});
|
||||||
$("#service").html(table);
|
$("#slide-controller div[data-role=content] ul[data-role=listview]").listview("refresh");
|
||||||
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)
|
nextItem: function (event) {
|
||||||
{
|
$.getJSON("/api/service/next");
|
||||||
var tr = OpenLP.Events.getElement(e).parent();
|
|
||||||
if (tr[0].tagName != "TR")
|
|
||||||
{
|
|
||||||
tr = tr.parent();
|
|
||||||
}
|
|
||||||
OpenLP.Remote.sendEvent("slidecontroller_live_set", tr.attr("value"));
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
sendSetItem: function (e)
|
previousItem: function (event) {
|
||||||
{
|
$.getJSON("/api/service/previous");
|
||||||
var id = OpenLP.Events.getElement(e).parent().attr("value");
|
|
||||||
OpenLP.Remote.sendEvent("servicemanager_set_item", id);
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
sendAlert: function (e)
|
nextSlide: function (event) {
|
||||||
{
|
$.getJSON("/api/controller/live/next");
|
||||||
var alert_text = $("#alert-text").val();
|
|
||||||
OpenLP.Remote.sendEvent("alerts_text", alert_text);
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
buttonClick: function (e)
|
previousSlide: function (event) {
|
||||||
{
|
$.getJSON("/api/controller/live/previous");
|
||||||
var id = OpenLP.Events.getElement(e).attr("id");
|
return false;
|
||||||
OpenLP.Remote.sendEvent(id);
|
},
|
||||||
|
blankDisplay: function (event) {
|
||||||
|
$.getJSON("/api/display/hide");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
unblankDisplay: function (event) {
|
||||||
|
$.getJSON("/api/display/show");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
showAlert: function (event) {
|
||||||
|
var text = JSON.stringify({"request": {"text": $("#alert-text").val()}});
|
||||||
|
$.getJSON(
|
||||||
|
"/api/alert",
|
||||||
|
{"data": text},
|
||||||
|
function () {
|
||||||
|
$("#alert-text").val("");
|
||||||
|
}
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
// Service Manager
|
||||||
OpenLP.Events.bindLoad(function () {
|
$("#service-manager").live("pagebeforeshow", OpenLP.loadService);
|
||||||
OpenLP.Events.bindClick("input[type=button][id!=alert-send]", OpenLP.Remote.buttonClick);
|
$("#service-refresh").live("click", OpenLP.loadService);
|
||||||
OpenLP.Events.bindClick("#alert-send", OpenLP.Remote.sendAlert);
|
$("#service-next").live("click", OpenLP.nextItem);
|
||||||
OpenLP.Remote.sendEvent("servicemanager_list_request");
|
$("#service-previous").live("click", OpenLP.previousItem);
|
||||||
OpenLP.Remote.sendEvent("slidecontroller_live_text_request");
|
$("#service-blank").live("click", OpenLP.blankDisplay);
|
||||||
OpenLP.Remote.sendEvent("remotes_poll_request");
|
$("#service-unblank").live("click", OpenLP.unblankDisplay);
|
||||||
});
|
// Slide Controller
|
||||||
|
$("#slide-controller").live("pagebeforeshow", OpenLP.loadController);
|
||||||
|
$("#controller-refresh").live("click", OpenLP.loadController);
|
||||||
|
$("#controller-next").live("click", OpenLP.nextSlide);
|
||||||
|
$("#controller-previous").live("click", OpenLP.previousSlide);
|
||||||
|
$("#controller-blank").live("click", OpenLP.blankDisplay);
|
||||||
|
$("#controller-unblank").live("click", OpenLP.unblankDisplay);
|
||||||
|
// Alerts
|
||||||
|
$("#alert-submit").live("click", OpenLP.showAlert);
|
||||||
|
// Poll the server twice a second to get any updates.
|
||||||
|
setInterval("OpenLP.pollServer();", 500);
|
||||||
|
OpenLP.pollServer();
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* 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);
|
|
||||||
});
|
|
@ -1,45 +0,0 @@
|
|||||||
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,3 +26,5 @@
|
|||||||
|
|
||||||
from remotetab import RemoteTab
|
from remotetab import RemoteTab
|
||||||
from httpserver import HttpServer
|
from httpserver import HttpServer
|
||||||
|
|
||||||
|
__all__ = [u'RemoteTab', u'HttpServer']
|
||||||
|
@ -24,9 +24,94 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
The :mod:`http` module contains the API web server. This is a lightweight web
|
||||||
|
server used by remotes to interact with OpenLP. It uses JSON to communicate with
|
||||||
|
the remotes.
|
||||||
|
|
||||||
|
*Routes:*
|
||||||
|
|
||||||
|
``/``
|
||||||
|
Go to the web interface.
|
||||||
|
|
||||||
|
``/files/{filename}``
|
||||||
|
Serve a static file.
|
||||||
|
|
||||||
|
``/api/poll``
|
||||||
|
Poll to see if there are any changes. Returns a JSON-encoded dict of
|
||||||
|
any changes that occurred::
|
||||||
|
|
||||||
|
{"results": {"type": "controller"}}
|
||||||
|
|
||||||
|
Or, if there were no results, False::
|
||||||
|
|
||||||
|
{"results": False}
|
||||||
|
|
||||||
|
``/api/display/{hide|show}``
|
||||||
|
Blank or unblank the screen.
|
||||||
|
|
||||||
|
``/api/alert``
|
||||||
|
Sends an alert message to the alerts plugin. This method expects a
|
||||||
|
JSON-encoded dict like this::
|
||||||
|
|
||||||
|
{"request": {"text": "<your alert text>"}}
|
||||||
|
|
||||||
|
``/api/controller/{live|preview}/{action}``
|
||||||
|
Perform ``{action}`` on the live or preview controller. Valid actions
|
||||||
|
are:
|
||||||
|
|
||||||
|
``next``
|
||||||
|
Load the next slide.
|
||||||
|
|
||||||
|
``previous``
|
||||||
|
Load the previous slide.
|
||||||
|
|
||||||
|
``set``
|
||||||
|
Set a specific slide. Requires an id return in a JSON-encoded dict like
|
||||||
|
this::
|
||||||
|
|
||||||
|
{"request": {"id": 1}}
|
||||||
|
|
||||||
|
``first``
|
||||||
|
Load the first slide.
|
||||||
|
|
||||||
|
``last``
|
||||||
|
Load the last slide.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
Fetches the text of the current song. The output is a JSON-encoded
|
||||||
|
dict which looks like this::
|
||||||
|
|
||||||
|
{"result": {"slides": ["...", "..."]}}
|
||||||
|
|
||||||
|
``/api/service/{action}``
|
||||||
|
Perform ``{action}`` on the service manager (e.g. go live). Data is
|
||||||
|
passed as a json-encoded ``data`` parameter. Valid actions are:
|
||||||
|
|
||||||
|
``next``
|
||||||
|
Load the next item in the service.
|
||||||
|
|
||||||
|
``previous``
|
||||||
|
Load the previews item in the service.
|
||||||
|
|
||||||
|
``set``
|
||||||
|
Set a specific item in the service. Requires an id returned in a
|
||||||
|
JSON-encoded dict like this::
|
||||||
|
|
||||||
|
{"request": {"id": 1}}
|
||||||
|
|
||||||
|
``list``
|
||||||
|
Request a list of items in the service. Returns a list of items in the
|
||||||
|
current service in a JSON-encoded dict like this::
|
||||||
|
|
||||||
|
{"results": {"items": [{...}, {...}]}}
|
||||||
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import urlparse
|
import urlparse
|
||||||
|
import re
|
||||||
|
from pprint import pformat
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
@ -36,10 +121,29 @@ except ImportError:
|
|||||||
from PyQt4 import QtCore, QtNetwork
|
from PyQt4 import QtCore, QtNetwork
|
||||||
|
|
||||||
from openlp.core.lib import Receiver
|
from openlp.core.lib import Receiver
|
||||||
|
from openlp.core.ui import HideMode
|
||||||
from openlp.core.utils import AppLocation
|
from openlp.core.utils import AppLocation
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class HttpResponse(object):
|
||||||
|
"""
|
||||||
|
A simple object to encapsulate a pseudo-http response.
|
||||||
|
"""
|
||||||
|
code = '200 OK'
|
||||||
|
content = ''
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'text/html; charset="utf-8"\r\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, content='', headers={}, code=None):
|
||||||
|
self.content = content
|
||||||
|
for key, value in headers.iteritems():
|
||||||
|
self.headers[key] = value
|
||||||
|
if code:
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
|
||||||
class HttpServer(object):
|
class HttpServer(object):
|
||||||
"""
|
"""
|
||||||
Ability to control OpenLP via a webbrowser
|
Ability to control OpenLP via a webbrowser
|
||||||
@ -90,22 +194,12 @@ class HttpServer(object):
|
|||||||
Slide change listener. Store the item and tell the clients
|
Slide change listener. Store the item and tell the clients
|
||||||
"""
|
"""
|
||||||
self.current_slide = row
|
self.current_slide = row
|
||||||
self.send_poll()
|
|
||||||
|
|
||||||
def item_change(self, items):
|
def item_change(self, items):
|
||||||
"""
|
"""
|
||||||
Item (song) change listener. Store the slide and tell the clients
|
Item (song) change listener. Store the slide and tell the clients
|
||||||
"""
|
"""
|
||||||
self.current_item = items[0].title
|
self.current_item = items[0]
|
||||||
self.send_poll()
|
|
||||||
|
|
||||||
def send_poll(self):
|
|
||||||
"""
|
|
||||||
Tell the clients something has changed
|
|
||||||
"""
|
|
||||||
Receiver.send_message(u'remotes_poll_response',
|
|
||||||
{'slide': self.current_slide,
|
|
||||||
'item': self.current_item})
|
|
||||||
|
|
||||||
def new_connection(self):
|
def new_connection(self):
|
||||||
"""
|
"""
|
||||||
@ -122,6 +216,7 @@ class HttpServer(object):
|
|||||||
The connection has been closed. Clean up
|
The connection has been closed. Clean up
|
||||||
"""
|
"""
|
||||||
log.debug(u'close http connection')
|
log.debug(u'close http connection')
|
||||||
|
if connection in self.connections:
|
||||||
self.connections.remove(connection)
|
self.connections.remove(connection)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -138,17 +233,44 @@ class HttpConnection(object):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, parent, socket):
|
def __init__(self, parent, socket):
|
||||||
"""
|
"""
|
||||||
Initialise the http connection. Listen out for socket signals
|
Initialise the http connection. Listen out for socket signals.
|
||||||
"""
|
"""
|
||||||
log.debug(u'Initialise HttpConnection: %s' %
|
log.debug(u'Initialise HttpConnection: %s' %
|
||||||
socket.peerAddress().toString())
|
socket.peerAddress().toString())
|
||||||
self.socket = socket
|
self.socket = socket
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.routes = [
|
||||||
|
(u'^/$', self.serve_file),
|
||||||
|
(r'^/files/(.*)$', self.serve_file),
|
||||||
|
(r'^/api/poll$', self.poll),
|
||||||
|
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||||
|
(r'^/api/service/(.*)$', self.service),
|
||||||
|
(r'^/api/display/(hide|show)$', self.display),
|
||||||
|
(r'^/api/alert$', self.alert)
|
||||||
|
]
|
||||||
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
|
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
|
||||||
self.ready_read)
|
self.ready_read)
|
||||||
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'),
|
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'),
|
||||||
self.disconnected)
|
self.disconnected)
|
||||||
|
|
||||||
|
def _get_service_items(self):
|
||||||
|
service_items = []
|
||||||
|
service_manager = self.parent.parent.serviceManager
|
||||||
|
item = service_manager.findServiceItem()[0]
|
||||||
|
if item >= 0 and item < len(service_manager.serviceItems):
|
||||||
|
curitem = service_manager.serviceItems[item]
|
||||||
|
else:
|
||||||
|
curitem = None
|
||||||
|
for item in service_manager.serviceItems:
|
||||||
|
service_item = item[u'service_item']
|
||||||
|
service_items.append({
|
||||||
|
u'title': unicode(service_item.get_display_title()),
|
||||||
|
u'plugin': unicode(service_item.name),
|
||||||
|
u'notes': unicode(service_item.notes),
|
||||||
|
u'selected': (item == curitem)
|
||||||
|
})
|
||||||
|
return service_items
|
||||||
|
|
||||||
def ready_read(self):
|
def ready_read(self):
|
||||||
"""
|
"""
|
||||||
Data has been sent from the client. Respond to it
|
Data has been sent from the client. Respond to it
|
||||||
@ -158,32 +280,27 @@ class HttpConnection(object):
|
|||||||
data = unicode(self.socket.readLine())
|
data = unicode(self.socket.readLine())
|
||||||
log.debug(u'received: ' + data)
|
log.debug(u'received: ' + data)
|
||||||
words = data.split(u' ')
|
words = data.split(u' ')
|
||||||
html = None
|
response = None
|
||||||
mimetype = None
|
|
||||||
if words[0] == u'GET':
|
if words[0] == u'GET':
|
||||||
url = urlparse.urlparse(words[1])
|
url = urlparse.urlparse(words[1])
|
||||||
params = self.load_params(url.query)
|
self.url_params = urlparse.parse_qs(url.query)
|
||||||
folders = url.path.split(u'/')
|
# Loop through the routes we set up earlier and execute them
|
||||||
if folders[1] == u'':
|
for route, func in self.routes:
|
||||||
mimetype, html = self.serve_file(u'')
|
match = re.match(route, url.path)
|
||||||
elif folders[1] == u'files':
|
if match:
|
||||||
mimetype, html = self.serve_file(os.sep.join(folders[2:]))
|
log.debug('Route "%s" matched "%s"', route, url.path)
|
||||||
elif folders[1] == u'send':
|
args = []
|
||||||
html = self.process_event(folders[2], params)
|
for param in match.groups():
|
||||||
elif folders[1] == u'request':
|
args.append(param)
|
||||||
if self.process_request(folders[2], params):
|
response = func(*args)
|
||||||
return
|
break
|
||||||
if html:
|
if response:
|
||||||
if mimetype:
|
self.send_response(response)
|
||||||
self.send_200_ok(mimetype)
|
|
||||||
else:
|
else:
|
||||||
self.send_200_ok()
|
self.send_response(HttpResponse(code='404 Not Found'))
|
||||||
self.socket.write(html)
|
|
||||||
else:
|
|
||||||
self.send_404_not_found()
|
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def serve_file(self, filename):
|
def serve_file(self, filename=None):
|
||||||
"""
|
"""
|
||||||
Send a file to the socket. For now, just a subset of file types
|
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.
|
||||||
@ -197,7 +314,7 @@ class HttpConnection(object):
|
|||||||
filename = u'index.html'
|
filename = u'index.html'
|
||||||
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
||||||
if not path.startswith(self.parent.html_dir):
|
if not path.startswith(self.parent.html_dir):
|
||||||
return None
|
return HttpResponse(code=u'404 Not Found')
|
||||||
ext = os.path.splitext(filename)[1]
|
ext = os.path.splitext(filename)[1]
|
||||||
if ext == u'.html':
|
if ext == u'.html':
|
||||||
mimetype = u'text/html'
|
mimetype = u'text/html'
|
||||||
@ -212,126 +329,116 @@ class HttpConnection(object):
|
|||||||
elif ext == u'.png':
|
elif ext == u'.png':
|
||||||
mimetype = u'image/png'
|
mimetype = u'image/png'
|
||||||
else:
|
else:
|
||||||
return (None, None)
|
mimetype = u'text/plain'
|
||||||
file_handle = None
|
file_handle = None
|
||||||
try:
|
try:
|
||||||
file_handle = open(path, u'rb')
|
file_handle = open(path, u'rb')
|
||||||
log.debug(u'Opened %s' % path)
|
log.debug(u'Opened %s' % path)
|
||||||
html = file_handle.read()
|
content = file_handle.read()
|
||||||
except IOError:
|
except IOError:
|
||||||
log.exception(u'Failed to open %s' % path)
|
log.exception(u'Failed to open %s' % path)
|
||||||
return None
|
return HttpResponse(code=u'404 Not Found')
|
||||||
finally:
|
finally:
|
||||||
if file_handle:
|
if file_handle:
|
||||||
file_handle.close()
|
file_handle.close()
|
||||||
return (mimetype, html)
|
return HttpResponse(content, {u'Content-Type': mimetype})
|
||||||
|
|
||||||
def load_params(self, query):
|
def poll(self):
|
||||||
"""
|
"""
|
||||||
Decode the query string parameters sent from the browser
|
Poll OpenLP to determine the current slide number and item name.
|
||||||
"""
|
"""
|
||||||
log.debug(u'loading params %s' % query)
|
result = {
|
||||||
params = urlparse.parse_qs(query)
|
u'slide': self.parent.current_slide or 0,
|
||||||
if not params:
|
u'item': self.parent.current_item.title \
|
||||||
return None
|
if self.parent.current_item else u''
|
||||||
|
}
|
||||||
|
return HttpResponse(json.dumps({u'results': result}),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
|
|
||||||
|
def display(self, action):
|
||||||
|
"""
|
||||||
|
Hide or show the display screen.
|
||||||
|
|
||||||
|
``action``
|
||||||
|
This is the action, either ``hide`` or ``show``.
|
||||||
|
"""
|
||||||
|
event = u'maindisplay_%s' % action
|
||||||
|
Receiver.send_message(event, HideMode.Blank)
|
||||||
|
return HttpResponse(json.dumps({u'results': {u'success': True}}),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
|
|
||||||
|
def alert(self):
|
||||||
|
"""
|
||||||
|
Send an alert.
|
||||||
|
"""
|
||||||
|
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||||
|
Receiver.send_message(u'alerts_text', [text])
|
||||||
|
return HttpResponse(json.dumps({u'results': {u'success': True}}),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
|
|
||||||
|
def controller(self, type, action):
|
||||||
|
"""
|
||||||
|
Perform an action on the slide controller.
|
||||||
|
|
||||||
|
``type``
|
||||||
|
This is the type of slide controller, either ``preview`` or
|
||||||
|
``live``.
|
||||||
|
|
||||||
|
``action``
|
||||||
|
The action to perform.
|
||||||
|
"""
|
||||||
|
event = u'slidecontroller_%s_%s' % (type, action)
|
||||||
|
if action == u'text':
|
||||||
|
current_item = self.parent.current_item
|
||||||
|
data = []
|
||||||
|
if current_item:
|
||||||
|
for index, frame in enumerate(current_item.get_frames()):
|
||||||
|
item = {}
|
||||||
|
if current_item.is_text():
|
||||||
|
item[u'tag'] = unicode(frame[u'verseTag'])
|
||||||
|
item[u'text'] = unicode(frame[u'html'])
|
||||||
else:
|
else:
|
||||||
return params['q']
|
item[u'tag'] = unicode(index)
|
||||||
|
item[u'text'] = u''
|
||||||
def process_event(self, event, params):
|
item[u'selected'] = (self.parent.current_slide == index)
|
||||||
"""
|
data.append(item)
|
||||||
Send a signal to openlp to perform an action.
|
json_data = {u'results': {u'slides': data}}
|
||||||
Currently lets anything through. Later we should restrict and perform
|
else:
|
||||||
basic parameter checking, otherwise rogue clients could crash openlp
|
if self.url_params and self.url_params.get(u'data'):
|
||||||
"""
|
data = json.loads(self.url_params[u'data'][0])
|
||||||
log.debug(u'Processing event %s' % event)
|
log.info(data)
|
||||||
if params:
|
# This slot expects an int within a list.
|
||||||
Receiver.send_message(event, params)
|
id = data[u'request'][u'id']
|
||||||
|
Receiver.send_message(event, [id])
|
||||||
else:
|
else:
|
||||||
Receiver.send_message(event)
|
Receiver.send_message(event)
|
||||||
return json.dumps([u'OK'])
|
json_data = {u'results': {u'success': True}}
|
||||||
|
return HttpResponse(json.dumps(json_data),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
|
|
||||||
def process_request(self, event, params):
|
def service(self, action):
|
||||||
"""
|
event = u'servicemanager_%s' % action
|
||||||
Client has requested data. Send the signal and parameters for openlp
|
if action == u'list':
|
||||||
to handle, then listen out for a corresponding ``_request`` signal
|
return HttpResponse(
|
||||||
which will have the data to return.
|
json.dumps({u'results': {u'items': self._get_service_items()}}),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
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'):
|
|
||||||
return False
|
|
||||||
self.event = event
|
|
||||||
response = event.replace(u'_request', u'_response')
|
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
||||||
QtCore.SIGNAL(response), self.process_response)
|
|
||||||
self.timer = QtCore.QTimer()
|
|
||||||
self.timer.setSingleShot(True)
|
|
||||||
QtCore.QObject.connect(self.timer,
|
|
||||||
QtCore.SIGNAL(u'timeout()'), self.timeout)
|
|
||||||
if event == 'remotes_poll_request':
|
|
||||||
self.timer.start(60000)
|
|
||||||
else:
|
else:
|
||||||
self.timer.start(10000)
|
event += u'_item'
|
||||||
if params:
|
if self.url_params and self.url_params.get(u'data'):
|
||||||
Receiver.send_message(event, params)
|
data = json.loads(self.url_params[u'data'][0])
|
||||||
|
Receiver.send_message(event, data[u'request'][u'id'])
|
||||||
else:
|
else:
|
||||||
Receiver.send_message(event)
|
Receiver.send_message(event)
|
||||||
return True
|
return HttpResponse(json.dumps({u'results': {u'success': True}}),
|
||||||
|
{u'Content-Type': u'application/json'})
|
||||||
|
|
||||||
def process_response(self, data):
|
def send_response(self, response):
|
||||||
"""
|
http = u'HTTP/1.1 %s\r\n' % response.code
|
||||||
The recipient of a _request signal has sent data. Convert this to
|
for header, value in response.headers.iteritems():
|
||||||
json and return it to client
|
http += '%s: %s\r\n' % (header, value)
|
||||||
"""
|
http += '\r\n'
|
||||||
log.debug(u'Processing response for %s' % self.event)
|
self.socket.write(http)
|
||||||
if not self.socket:
|
self.socket.write(response.content)
|
||||||
return
|
|
||||||
self.timer.stop()
|
|
||||||
html = json.dumps(data)
|
|
||||||
self.send_200_ok()
|
|
||||||
self.socket.write(html)
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def send_200_ok(self, mimetype='text/html; charset="utf-8"'):
|
|
||||||
"""
|
|
||||||
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)
|
|
||||||
|
|
||||||
def send_404_not_found(self):
|
|
||||||
"""
|
|
||||||
Invalid url. Say so
|
|
||||||
"""
|
|
||||||
self.socket.write(u'HTTP/1.1 404 Not Found\r\n'+ \
|
|
||||||
u'Content-Type: text/html; charset="utf-8"\r\n' + \
|
|
||||||
u'\r\n')
|
|
||||||
|
|
||||||
def send_408_timeout(self):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
if not self.socket:
|
|
||||||
return
|
|
||||||
self.send_408_timeout()
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def disconnected(self):
|
def disconnected(self):
|
||||||
"""
|
"""
|
||||||
|
@ -33,7 +33,7 @@ from openlp.core.lib import Receiver, translate
|
|||||||
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
|
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
|
||||||
critical_error_message_box
|
critical_error_message_box
|
||||||
from openlp.plugins.songs.forms import EditVerseForm
|
from openlp.plugins.songs.forms import EditVerseForm
|
||||||
from openlp.plugins.songs.lib import SongXML, VerseType
|
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
|
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from editsongdialog import Ui_EditSongDialog
|
from editsongdialog import Ui_EditSongDialog
|
||||||
@ -728,17 +728,15 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
self.song.title = unicode(self.titleEdit.text())
|
self.song.title = unicode(self.titleEdit.text())
|
||||||
self.song.alternate_title = unicode(self.alternativeEdit.text())
|
self.song.alternate_title = unicode(self.alternativeEdit.text())
|
||||||
self.song.copyright = unicode(self.copyrightEdit.text())
|
self.song.copyright = unicode(self.copyrightEdit.text())
|
||||||
if self.song.alternate_title:
|
# Values will be set when cleaning the song.
|
||||||
self.song.search_title = self.song.title + u'@' + \
|
self.song.search_title = u''
|
||||||
self.song.alternate_title
|
self.song.search_lyrics = u''
|
||||||
else:
|
self.song.verse_order = u''
|
||||||
self.song.search_title = self.song.title
|
|
||||||
self.song.comments = unicode(self.commentsEdit.toPlainText())
|
self.song.comments = unicode(self.commentsEdit.toPlainText())
|
||||||
ordertext = unicode(self.verseOrderEdit.text())
|
ordertext = unicode(self.verseOrderEdit.text())
|
||||||
order = []
|
order = []
|
||||||
for item in ordertext.split():
|
for item in ordertext.split():
|
||||||
verse_tag = VerseType.Tags[
|
verse_tag = VerseType.Tags[VerseType.from_translated_tag(item[0])]
|
||||||
VerseType.from_translated_tag(item[0])]
|
|
||||||
verse_num = item[1:].lower()
|
verse_num = item[1:].lower()
|
||||||
order.append(u'%s%s' % (verse_tag, verse_num))
|
order.append(u'%s%s' % (verse_tag, verse_num))
|
||||||
self.song.verse_order = u' '.join(order)
|
self.song.verse_order = u' '.join(order)
|
||||||
@ -756,7 +754,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
else:
|
else:
|
||||||
self.song.theme_name = None
|
self.song.theme_name = None
|
||||||
self.processLyrics()
|
self.processLyrics()
|
||||||
self.processTitle()
|
|
||||||
self.song.authors = []
|
self.song.authors = []
|
||||||
for row in range(self.authorsListView.count()):
|
for row in range(self.authorsListView.count()):
|
||||||
item = self.authorsListView.item(row)
|
item = self.authorsListView.item(row)
|
||||||
@ -767,6 +764,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
item = self.topicsListView.item(row)
|
item = self.topicsListView.item(row)
|
||||||
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
self.song.topics.append(self.manager.get_object(Topic, topicId))
|
self.song.topics.append(self.manager.get_object(Topic, topicId))
|
||||||
|
clean_song(self.manager, self.song)
|
||||||
self.manager.save_object(self.song)
|
self.manager.save_object(self.song)
|
||||||
if not preview:
|
if not preview:
|
||||||
self.song = None
|
self.song = None
|
||||||
@ -779,7 +777,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
log.debug(u'processLyrics')
|
log.debug(u'processLyrics')
|
||||||
try:
|
try:
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
text = u''
|
|
||||||
multiple = []
|
multiple = []
|
||||||
for i in range(0, self.verseListWidget.rowCount()):
|
for i in range(0, self.verseListWidget.rowCount()):
|
||||||
item = self.verseListWidget.item(i, 0)
|
item = self.verseListWidget.item(i, 0)
|
||||||
@ -788,11 +785,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
verse_num = verseId[1:]
|
verse_num = verseId[1:]
|
||||||
sxml.add_verse_to_lyrics(verse_tag, verse_num,
|
sxml.add_verse_to_lyrics(verse_tag, verse_num,
|
||||||
unicode(item.text()))
|
unicode(item.text()))
|
||||||
text = text + self.whitespace.sub(u' ',
|
if verse_num > u'1' and verse_tag not in multiple:
|
||||||
unicode(self.verseListWidget.item(i, 0).text())) + u' '
|
|
||||||
if (verse_num > u'1') and (verse_tag not in multiple):
|
|
||||||
multiple.append(verse_tag)
|
multiple.append(verse_tag)
|
||||||
self.song.search_lyrics = text.lower()
|
|
||||||
self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
for verse in multiple:
|
for verse in multiple:
|
||||||
self.song.verse_order = re.sub(u'([' + verse.upper() +
|
self.song.verse_order = re.sub(u'([' + verse.upper() +
|
||||||
@ -801,13 +795,3 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
except:
|
except:
|
||||||
log.exception(u'Problem processing song Lyrics \n%s',
|
log.exception(u'Problem processing song Lyrics \n%s',
|
||||||
sxml.dump_xml())
|
sxml.dump_xml())
|
||||||
|
|
||||||
def processTitle(self):
|
|
||||||
"""
|
|
||||||
Process the song title entered by the user to remove stray punctuation
|
|
||||||
characters.
|
|
||||||
"""
|
|
||||||
# This method must only be run after the self.song = Song() assignment.
|
|
||||||
log.debug(u'processTitle')
|
|
||||||
self.song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
|
|
||||||
unicode(self.song.search_title)).lower().strip()
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
import re
|
||||||
|
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
@ -244,9 +245,11 @@ def retrieve_windows_encoding(recommendation=None):
|
|||||||
return None
|
return None
|
||||||
return filter(lambda item: item[1] == choice[0], encodings)[0][0]
|
return filter(lambda item: item[1] == choice[0], encodings)[0][0]
|
||||||
|
|
||||||
def add_author_unknown(manager, song):
|
def clean_song(manager, song):
|
||||||
"""
|
"""
|
||||||
Add the default author *Author Unknown* to the song.
|
Cleans the search title, rebuilds the search lyrics, adds a default author
|
||||||
|
if the song does not have one and other clean ups. This should always
|
||||||
|
called when a new song is added or changed.
|
||||||
|
|
||||||
``manager``
|
``manager``
|
||||||
The song's manager.
|
The song's manager.
|
||||||
@ -254,8 +257,25 @@ def add_author_unknown(manager, song):
|
|||||||
``song``
|
``song``
|
||||||
The song object.
|
The song object.
|
||||||
"""
|
"""
|
||||||
|
song.title = song.title.strip() if song.title else u''
|
||||||
|
if song.alternate_title is None:
|
||||||
|
song.alternate_title = u''
|
||||||
|
song.alternate_title = song.alternate_title.strip()
|
||||||
|
whitespace = re.compile(r'\W+', re.UNICODE)
|
||||||
|
song.search_title = (whitespace.sub(u' ', song.title).strip() + u'@' +
|
||||||
|
whitespace.sub(u' ', song.alternate_title).strip()).strip().lower()
|
||||||
|
# Remove the old "language" attribute from lyrics tag (prior to 1.9.5). This
|
||||||
|
# is not very important, but this keeps the database clean. This can be
|
||||||
|
# removed when everybody has cleaned his songs.
|
||||||
|
song.lyrics = song.lyrics.replace(u'<lyrics language="en">', u'<lyrics>')
|
||||||
|
verses = SongXML().get_verses(song.lyrics)
|
||||||
|
lyrics = u' '.join([whitespace.sub(u' ', verse[1]) for verse in verses])
|
||||||
|
song.search_lyrics = lyrics.lower()
|
||||||
|
# The song does not have any author, add one.
|
||||||
|
if not song.authors:
|
||||||
name = SongStrings.AuthorUnknown
|
name = SongStrings.AuthorUnknown
|
||||||
author = manager.get_object_filtered(Author, Author.display_name == name)
|
author = manager.get_object_filtered(
|
||||||
|
Author, Author.display_name == name)
|
||||||
if author is None:
|
if author is None:
|
||||||
author = Author.populate(
|
author = Author.populate(
|
||||||
display_name=name, last_name=u'', first_name=u'')
|
display_name=name, last_name=u'', first_name=u'')
|
||||||
|
@ -94,7 +94,7 @@ import os
|
|||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
|
||||||
from openlp.core.ui.wizard import WizardStrings
|
from openlp.core.ui.wizard import WizardStrings
|
||||||
from openlp.plugins.songs.lib import add_author_unknown, VerseType
|
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||||
from openlp.plugins.songs.lib.songimport import SongImport
|
from openlp.plugins.songs.lib.songimport import SongImport
|
||||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
||||||
from openlp.plugins.songs.lib.xml import SongXML
|
from openlp.plugins.songs.lib.xml import SongXML
|
||||||
@ -212,9 +212,13 @@ class FoilPresenter(object):
|
|||||||
# No xml get out of here.
|
# No xml get out of here.
|
||||||
if not xml:
|
if not xml:
|
||||||
return None
|
return None
|
||||||
song = Song()
|
|
||||||
if xml[:5] == u'<?xml':
|
if xml[:5] == u'<?xml':
|
||||||
xml = xml[38:]
|
xml = xml[38:]
|
||||||
|
song = Song()
|
||||||
|
# Values will be set when cleaning the song.
|
||||||
|
song.search_lyrics = u''
|
||||||
|
song.verse_order = u''
|
||||||
|
song.search_title = u''
|
||||||
# Because "text" seems to be an reserverd word, we have to recompile it.
|
# Because "text" seems to be an reserverd word, we have to recompile it.
|
||||||
xml = re.compile(u'<text>').sub(u'<text_>', xml)
|
xml = re.compile(u'<text>').sub(u'<text_>', xml)
|
||||||
xml = re.compile(u'</text>').sub(u'</text_>', xml)
|
xml = re.compile(u'</text>').sub(u'</text_>', xml)
|
||||||
@ -229,6 +233,7 @@ class FoilPresenter(object):
|
|||||||
self._process_authors(foilpresenterfolie, song)
|
self._process_authors(foilpresenterfolie, song)
|
||||||
self._process_songbooks(foilpresenterfolie, song)
|
self._process_songbooks(foilpresenterfolie, song)
|
||||||
self._process_topics(foilpresenterfolie, song)
|
self._process_topics(foilpresenterfolie, song)
|
||||||
|
clean_song(self.manager, song)
|
||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
return song.id
|
return song.id
|
||||||
|
|
||||||
@ -348,8 +353,6 @@ class FoilPresenter(object):
|
|||||||
first_name = u' '.join(display_name.split(u' ')[:-1]))
|
first_name = u' '.join(display_name.split(u' ')[:-1]))
|
||||||
self.manager.save_object(author)
|
self.manager.save_object(author)
|
||||||
song.authors.append(author)
|
song.authors.append(author)
|
||||||
if not song.authors:
|
|
||||||
add_author_unknown(self.manager, song)
|
|
||||||
|
|
||||||
def _process_cclinumber(self, foilpresenterfolie, song):
|
def _process_cclinumber(self, foilpresenterfolie, song):
|
||||||
"""
|
"""
|
||||||
@ -407,7 +410,6 @@ class FoilPresenter(object):
|
|||||||
The song object.
|
The song object.
|
||||||
"""
|
"""
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
search_text = u''
|
|
||||||
temp_verse_order = {}
|
temp_verse_order = {}
|
||||||
temp_verse_order_backup = []
|
temp_verse_order_backup = []
|
||||||
temp_sortnr_backup = 1
|
temp_sortnr_backup = 1
|
||||||
@ -452,7 +454,6 @@ class FoilPresenter(object):
|
|||||||
else:
|
else:
|
||||||
verse_type = u'O'
|
verse_type = u'O'
|
||||||
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
|
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
|
||||||
#verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
|
|
||||||
# Foilpresenter allows e. g. "C", but we need "C1".
|
# Foilpresenter allows e. g. "C", but we need "C1".
|
||||||
if not verse_number:
|
if not verse_number:
|
||||||
verse_number = unicode(versenumber[verse_type])
|
verse_number = unicode(versenumber[verse_type])
|
||||||
@ -470,8 +471,6 @@ class FoilPresenter(object):
|
|||||||
temp_verse_order_backup.append(u''.join((verse_type[0],
|
temp_verse_order_backup.append(u''.join((verse_type[0],
|
||||||
verse_number)))
|
verse_number)))
|
||||||
sxml.add_verse_to_lyrics(verse_type, verse_number, text)
|
sxml.add_verse_to_lyrics(verse_type, verse_number, text)
|
||||||
search_text = search_text + text
|
|
||||||
song.search_lyrics = search_text.lower()
|
|
||||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
# Process verse order
|
# Process verse order
|
||||||
verse_order = []
|
verse_order = []
|
||||||
@ -534,13 +533,9 @@ class FoilPresenter(object):
|
|||||||
for titelstring in foilpresenterfolie.titel.titelstring:
|
for titelstring in foilpresenterfolie.titel.titelstring:
|
||||||
if not song.title:
|
if not song.title:
|
||||||
song.title = self._child(titelstring)
|
song.title = self._child(titelstring)
|
||||||
song.search_title = unicode(song.title)
|
|
||||||
song.alternate_title = u''
|
song.alternate_title = u''
|
||||||
else:
|
else:
|
||||||
song.alternate_title = self._child(titelstring)
|
song.alternate_title = self._child(titelstring)
|
||||||
song.search_title += u'@' + song.alternate_title
|
|
||||||
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
|
|
||||||
unicode(song.search_title)).lower().strip()
|
|
||||||
|
|
||||||
def _process_topics(self, foilpresenterfolie, song):
|
def _process_topics(self, foilpresenterfolie, song):
|
||||||
"""
|
"""
|
||||||
@ -565,10 +560,3 @@ class FoilPresenter(object):
|
|||||||
song.topics.append(topic)
|
song.topics.append(topic)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _dump_xml(self, xml):
|
|
||||||
"""
|
|
||||||
Debugging aid to dump XML so that we can see what we have.
|
|
||||||
"""
|
|
||||||
return etree.tostring(xml, encoding=u'UTF-8',
|
|
||||||
xml_declaration=True, pretty_print=True)
|
|
||||||
|
@ -266,9 +266,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
Receiver.send_message(u'songs_load_list')
|
Receiver.send_message(u'songs_load_list')
|
||||||
|
|
||||||
def onExportClick(self):
|
def onExportClick(self):
|
||||||
if not hasattr(self, u'export_wizard'):
|
export_wizard = SongExportForm(self, self.parent)
|
||||||
self.export_wizard = SongExportForm(self, self.parent)
|
export_wizard.exec_()
|
||||||
self.export_wizard.exec_()
|
|
||||||
|
|
||||||
def onNewClick(self):
|
def onNewClick(self):
|
||||||
log.debug(u'onNewClick')
|
log.debug(u'onNewClick')
|
||||||
@ -412,29 +411,32 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def serviceLoad(self, item):
|
def serviceLoad(self, item):
|
||||||
"""
|
"""
|
||||||
Triggered by a song being loaded by the service item
|
Triggered by a song being loaded by the service manager.
|
||||||
"""
|
"""
|
||||||
log.debug(u'serviceLoad')
|
log.debug(u'serviceLoad')
|
||||||
if self.plugin.status != PluginStatus.Active or not item.data_string:
|
if self.plugin.status != PluginStatus.Active or not item.data_string:
|
||||||
return
|
return
|
||||||
|
if item.data_string[u'title'].find(u'@') == -1:
|
||||||
|
# This file seems to be an old one (prior to 1.9.5), which means,
|
||||||
|
# that the search title (data_string[u'title']) is probably wrong.
|
||||||
|
# We add "@" to search title and hope that we do not add any
|
||||||
|
# duplicate. This should work for songs without alternate title.
|
||||||
search_results = self.parent.manager.get_all_objects(Song,
|
search_results = self.parent.manager.get_all_objects(Song,
|
||||||
Song.search_title == re.compile(r'\W+', re.UNICODE).sub(u' ',
|
Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(u' ',
|
||||||
item.data_string[u'title'].split(u'@')[0].lower()).strip(),
|
item.data_string[u'title'].strip()) + u'@').strip().lower(),
|
||||||
|
Song.search_title.asc())
|
||||||
|
else:
|
||||||
|
search_results = self.parent.manager.get_all_objects(Song,
|
||||||
|
Song.search_title == item.data_string[u'title'],
|
||||||
Song.search_title.asc())
|
Song.search_title.asc())
|
||||||
author_list = item.data_string[u'authors'].split(u', ')
|
author_list = item.data_string[u'authors'].split(u', ')
|
||||||
# The service item always has an author (at least it has u'' as
|
|
||||||
# author). However, songs saved in the database do not have to have
|
|
||||||
# an author.
|
|
||||||
if u'' in author_list:
|
|
||||||
author_list.remove(u'')
|
|
||||||
editId = 0
|
editId = 0
|
||||||
add_song = True
|
add_song = True
|
||||||
if search_results:
|
if search_results:
|
||||||
for song in search_results:
|
for song in search_results:
|
||||||
same_authors = True
|
same_authors = True
|
||||||
# If the author counts are different, we do not have to do any
|
# If the author counts are different, we do not have to do any
|
||||||
# further checking. This is also important when a song does not
|
# further checking.
|
||||||
# have any author (because we can not loop over an empty list).
|
|
||||||
if len(song.authors) == len(author_list):
|
if len(song.authors) == len(author_list):
|
||||||
for author in song.authors:
|
for author in song.authors:
|
||||||
if author.display_name not in author_list:
|
if author.display_name not in author_list:
|
||||||
|
@ -36,7 +36,7 @@ from sqlalchemy.orm.exc import UnmappedClassError
|
|||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.core.lib.db import BaseModel
|
from openlp.core.lib.db import BaseModel
|
||||||
from openlp.plugins.songs.lib import add_author_unknown
|
from openlp.plugins.songs.lib import clean_song
|
||||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile
|
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile
|
||||||
from songimport import SongImport
|
from songimport import SongImport
|
||||||
|
|
||||||
@ -167,12 +167,11 @@ class OpenLPSongImport(SongImport):
|
|||||||
old_titles = song.search_title.split(u'@')
|
old_titles = song.search_title.split(u'@')
|
||||||
if len(old_titles) > 1:
|
if len(old_titles) > 1:
|
||||||
new_song.alternate_title = old_titles[1]
|
new_song.alternate_title = old_titles[1]
|
||||||
else:
|
# Values will be set when cleaning the song.
|
||||||
new_song.alternate_title = u''
|
new_song.search_title = u''
|
||||||
new_song.search_title = song.search_title.strip()
|
new_song.search_lyrics = u''
|
||||||
new_song.song_number = song.song_number
|
new_song.song_number = song.song_number
|
||||||
new_song.lyrics = song.lyrics
|
new_song.lyrics = song.lyrics
|
||||||
new_song.search_lyrics = song.search_lyrics
|
|
||||||
new_song.verse_order = song.verse_order
|
new_song.verse_order = song.verse_order
|
||||||
new_song.copyright = song.copyright
|
new_song.copyright = song.copyright
|
||||||
new_song.comments = song.comments
|
new_song.comments = song.comments
|
||||||
@ -181,31 +180,26 @@ class OpenLPSongImport(SongImport):
|
|||||||
for author in song.authors:
|
for author in song.authors:
|
||||||
existing_author = self.manager.get_object_filtered(
|
existing_author = self.manager.get_object_filtered(
|
||||||
Author, Author.display_name == author.display_name)
|
Author, Author.display_name == author.display_name)
|
||||||
if existing_author:
|
if existing_author is None:
|
||||||
new_song.authors.append(existing_author)
|
existing_author = Author.populate(
|
||||||
else:
|
|
||||||
new_song.authors.append(Author.populate(
|
|
||||||
first_name=author.first_name,
|
first_name=author.first_name,
|
||||||
last_name=author.last_name,
|
last_name=author.last_name,
|
||||||
display_name=author.display_name))
|
display_name=author.display_name)
|
||||||
if not new_song.authors:
|
new_song.authors.append(existing_author)
|
||||||
add_author_unknown(self.manager, new_song)
|
|
||||||
if song.book:
|
if song.book:
|
||||||
existing_song_book = self.manager.get_object_filtered(
|
existing_song_book = self.manager.get_object_filtered(
|
||||||
Book, Book.name == song.book.name)
|
Book, Book.name == song.book.name)
|
||||||
if existing_song_book:
|
if existing_song_book is None:
|
||||||
new_song.book = existing_song_book
|
existing_song_book = Book.populate(name=song.book.name,
|
||||||
else:
|
|
||||||
new_song.book = Book.populate(name=song.book.name,
|
|
||||||
publisher=song.book.publisher)
|
publisher=song.book.publisher)
|
||||||
|
new_song.book = existing_song_book
|
||||||
if song.topics:
|
if song.topics:
|
||||||
for topic in song.topics:
|
for topic in song.topics:
|
||||||
existing_topic = self.manager.get_object_filtered(
|
existing_topic = self.manager.get_object_filtered(
|
||||||
Topic, Topic.name == topic.name)
|
Topic, Topic.name == topic.name)
|
||||||
if existing_topic:
|
if existing_topic is None:
|
||||||
|
existing_topic = Topic.populate(name=topic.name)
|
||||||
new_song.topics.append(existing_topic)
|
new_song.topics.append(existing_topic)
|
||||||
else:
|
|
||||||
new_song.topics.append(Topic.populate(name=topic.name))
|
|
||||||
# if has_media_files:
|
# if has_media_files:
|
||||||
# if song.media_files:
|
# if song.media_files:
|
||||||
# for media_file in song.media_files:
|
# for media_file in song.media_files:
|
||||||
@ -217,6 +211,7 @@ class OpenLPSongImport(SongImport):
|
|||||||
# else:
|
# else:
|
||||||
# new_song.media_files.append(MediaFile.populate(
|
# new_song.media_files.append(MediaFile.populate(
|
||||||
# file_name=media_file.file_name))
|
# file_name=media_file.file_name))
|
||||||
|
clean_song(self.manager, new_song)
|
||||||
self.manager.save_object(new_song)
|
self.manager.save_object(new_song)
|
||||||
song_count += 1
|
song_count += 1
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
|
@ -29,7 +29,7 @@ import re
|
|||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, translate
|
from openlp.core.lib import Receiver, translate
|
||||||
from openlp.plugins.songs.lib import add_author_unknown, VerseType
|
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
||||||
from openlp.plugins.songs.lib.ui import SongStrings
|
from openlp.plugins.songs.lib.ui import SongStrings
|
||||||
from openlp.plugins.songs.lib.xml import SongXML
|
from openlp.plugins.songs.lib.xml import SongXML
|
||||||
@ -245,12 +245,6 @@ class SongImport(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def remove_punctuation(self, text):
|
|
||||||
"""
|
|
||||||
Extracts alphanumeric words for searchable fields
|
|
||||||
"""
|
|
||||||
return re.sub(r'\W+', u' ', text, re.UNICODE)
|
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
"""
|
"""
|
||||||
All fields have been set to this song. Write the song to disk.
|
All fields have been set to this song. Write the song to disk.
|
||||||
@ -259,11 +253,11 @@ class SongImport(QtCore.QObject):
|
|||||||
song = Song()
|
song = Song()
|
||||||
song.title = self.title
|
song.title = self.title
|
||||||
song.alternate_title = self.alternate_title
|
song.alternate_title = self.alternate_title
|
||||||
song.search_title = self.remove_punctuation(self.title).lower() \
|
# Values will be set when cleaning the song.
|
||||||
+ '@' + self.remove_punctuation(self.alternate_title).lower()
|
song.search_title = u''
|
||||||
song.search_title = song.search_title.strip()
|
|
||||||
song.song_number = self.song_number
|
|
||||||
song.search_lyrics = u''
|
song.search_lyrics = u''
|
||||||
|
song.verse_order = u''
|
||||||
|
song.song_number = self.song_number
|
||||||
verses_changed_to_other = {}
|
verses_changed_to_other = {}
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
other_count = 1
|
other_count = 1
|
||||||
@ -280,8 +274,6 @@ class SongImport(QtCore.QObject):
|
|||||||
new_verse_def)
|
new_verse_def)
|
||||||
verse_def = new_verse_def
|
verse_def = new_verse_def
|
||||||
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
||||||
song.search_lyrics += u' ' + self.remove_punctuation(verse_text)
|
|
||||||
song.search_lyrics = song.search_lyrics.lower()
|
|
||||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
if not len(self.verse_order_list) and \
|
if not len(self.verse_order_list) and \
|
||||||
self.verse_order_list_generated_useful:
|
self.verse_order_list_generated_useful:
|
||||||
@ -303,9 +295,6 @@ class SongImport(QtCore.QObject):
|
|||||||
last_name=authortext.split(u' ')[-1],
|
last_name=authortext.split(u' ')[-1],
|
||||||
first_name=u' '.join(authortext.split(u' ')[:-1]))
|
first_name=u' '.join(authortext.split(u' ')[:-1]))
|
||||||
song.authors.append(author)
|
song.authors.append(author)
|
||||||
# No author, add the default author.
|
|
||||||
if not song.authors:
|
|
||||||
add_author_unknown(self.manager, song)
|
|
||||||
for filename in self.media_files:
|
for filename in self.media_files:
|
||||||
media_file = self.manager.get_object_filtered(MediaFile,
|
media_file = self.manager.get_object_filtered(MediaFile,
|
||||||
MediaFile.file_name == filename)
|
MediaFile.file_name == filename)
|
||||||
@ -326,6 +315,7 @@ class SongImport(QtCore.QObject):
|
|||||||
if topic is None:
|
if topic is None:
|
||||||
topic = Topic.populate(name=topictext)
|
topic = Topic.populate(name=topictext)
|
||||||
song.topics.append(topic)
|
song.topics.append(topic)
|
||||||
|
clean_song(self.manager, song)
|
||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
|
|
||||||
|
@ -154,7 +154,8 @@ class SongShowPlusImport(SongImport):
|
|||||||
elif blockKey == COMMENTS:
|
elif blockKey == COMMENTS:
|
||||||
self.comments = unicode(data, u'cp1252')
|
self.comments = unicode(data, u'cp1252')
|
||||||
elif blockKey == VERSE_ORDER:
|
elif blockKey == VERSE_ORDER:
|
||||||
verseTag = self.toOpenLPVerseTag(data)
|
verseTag = self.toOpenLPVerseTag(data, True)
|
||||||
|
if verseTag:
|
||||||
self.sspVerseOrderList.append(unicode(verseTag,
|
self.sspVerseOrderList.append(unicode(verseTag,
|
||||||
u'cp1252'))
|
u'cp1252'))
|
||||||
elif blockKey == SONG_BOOK:
|
elif blockKey == SONG_BOOK:
|
||||||
@ -174,7 +175,7 @@ class SongShowPlusImport(SongImport):
|
|||||||
WizardStrings.ImportingType % file_name)
|
WizardStrings.ImportingType % file_name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def toOpenLPVerseTag(self, verseName):
|
def toOpenLPVerseTag(self, verseName, ignoreUnique=False):
|
||||||
if verseName.find(" ") != -1:
|
if verseName.find(" ") != -1:
|
||||||
verseParts = verseName.split(" ")
|
verseParts = verseName.split(" ")
|
||||||
verseType = verseParts[0]
|
verseType = verseParts[0]
|
||||||
@ -195,6 +196,8 @@ class SongShowPlusImport(SongImport):
|
|||||||
verseTag = "B"
|
verseTag = "B"
|
||||||
else:
|
else:
|
||||||
if not self.otherList.has_key(verseName):
|
if not self.otherList.has_key(verseName):
|
||||||
|
if ignoreUnique:
|
||||||
|
return None
|
||||||
self.otherCount = self.otherCount + 1
|
self.otherCount = self.otherCount + 1
|
||||||
self.otherList[verseName] = str(self.otherCount)
|
self.otherList[verseName] = str(self.otherCount)
|
||||||
verseTag = "O"
|
verseTag = "O"
|
||||||
|
@ -66,7 +66,7 @@ import re
|
|||||||
|
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
|
||||||
from openlp.plugins.songs.lib import add_author_unknown, VerseType
|
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -236,10 +236,9 @@ class OpenLyrics(object):
|
|||||||
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
|
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
|
||||||
properties = etree.SubElement(song_xml, u'properties')
|
properties = etree.SubElement(song_xml, u'properties')
|
||||||
titles = etree.SubElement(properties, u'titles')
|
titles = etree.SubElement(properties, u'titles')
|
||||||
self._add_text_to_element(u'title', titles, song.title.strip())
|
self._add_text_to_element(u'title', titles, song.title)
|
||||||
if song.alternate_title:
|
if song.alternate_title:
|
||||||
self._add_text_to_element(
|
self._add_text_to_element(u'title', titles, song.alternate_title)
|
||||||
u'title', titles, song.alternate_title.strip())
|
|
||||||
if song.comments:
|
if song.comments:
|
||||||
comments = etree.SubElement(properties, u'comments')
|
comments = etree.SubElement(properties, u'comments')
|
||||||
self._add_text_to_element(u'comment', comments, song.comments)
|
self._add_text_to_element(u'comment', comments, song.comments)
|
||||||
@ -303,6 +302,10 @@ class OpenLyrics(object):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
song = Song()
|
song = Song()
|
||||||
|
# Values will be set when cleaning the song.
|
||||||
|
song.search_lyrics = u''
|
||||||
|
song.verse_order = u''
|
||||||
|
song.search_title = u''
|
||||||
self._process_copyright(properties, song)
|
self._process_copyright(properties, song)
|
||||||
self._process_cclinumber(properties, song)
|
self._process_cclinumber(properties, song)
|
||||||
self._process_titles(properties, song)
|
self._process_titles(properties, song)
|
||||||
@ -312,6 +315,7 @@ class OpenLyrics(object):
|
|||||||
self._process_authors(properties, song)
|
self._process_authors(properties, song)
|
||||||
self._process_songbooks(properties, song)
|
self._process_songbooks(properties, song)
|
||||||
self._process_topics(properties, song)
|
self._process_topics(properties, song)
|
||||||
|
clean_song(self.manager, song)
|
||||||
self.manager.save_object(song)
|
self.manager.save_object(song)
|
||||||
return song.id
|
return song.id
|
||||||
|
|
||||||
@ -382,8 +386,6 @@ class OpenLyrics(object):
|
|||||||
last_name=display_name.split(u' ')[-1],
|
last_name=display_name.split(u' ')[-1],
|
||||||
first_name=u' '.join(display_name.split(u' ')[:-1]))
|
first_name=u' '.join(display_name.split(u' ')[:-1]))
|
||||||
song.authors.append(author)
|
song.authors.append(author)
|
||||||
if not song.authors:
|
|
||||||
add_author_unknown(self.manager, song)
|
|
||||||
|
|
||||||
def _process_cclinumber(self, properties, song):
|
def _process_cclinumber(self, properties, song):
|
||||||
"""
|
"""
|
||||||
@ -443,7 +445,6 @@ class OpenLyrics(object):
|
|||||||
The song object.
|
The song object.
|
||||||
"""
|
"""
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
search_text = u''
|
|
||||||
for verse in lyrics.verse:
|
for verse in lyrics.verse:
|
||||||
text = u''
|
text = u''
|
||||||
for lines in verse.lines:
|
for lines in verse.lines:
|
||||||
@ -462,8 +463,6 @@ class OpenLyrics(object):
|
|||||||
if self._get(verse, u'lang'):
|
if self._get(verse, u'lang'):
|
||||||
lang = self._get(verse, u'lang')
|
lang = self._get(verse, u'lang')
|
||||||
sxml.add_verse_to_lyrics(verse_type, verse_number, text, lang)
|
sxml.add_verse_to_lyrics(verse_type, verse_number, text, lang)
|
||||||
search_text = search_text + text
|
|
||||||
song.search_lyrics = search_text.lower()
|
|
||||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
# Process verse order
|
# Process verse order
|
||||||
if hasattr(properties, u'verseOrder'):
|
if hasattr(properties, u'verseOrder'):
|
||||||
@ -510,13 +509,9 @@ class OpenLyrics(object):
|
|||||||
for title in properties.titles.title:
|
for title in properties.titles.title:
|
||||||
if not song.title:
|
if not song.title:
|
||||||
song.title = self._text(title)
|
song.title = self._text(title)
|
||||||
song.search_title = unicode(song.title)
|
|
||||||
song.alternate_title = u''
|
song.alternate_title = u''
|
||||||
else:
|
else:
|
||||||
song.alternate_title = self._text(title)
|
song.alternate_title = self._text(title)
|
||||||
song.search_title += u'@' + song.alternate_title
|
|
||||||
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
|
|
||||||
unicode(song.search_title)).lower().strip()
|
|
||||||
|
|
||||||
def _process_topics(self, properties, song):
|
def _process_topics(self, properties, song):
|
||||||
"""
|
"""
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
|
|
||||||
@ -35,8 +34,7 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate, \
|
|||||||
Receiver
|
Receiver
|
||||||
from openlp.core.lib.db import Manager
|
from openlp.core.lib.db import Manager
|
||||||
from openlp.core.lib.ui import UiStrings
|
from openlp.core.lib.ui import UiStrings
|
||||||
from openlp.plugins.songs.lib import add_author_unknown, SongMediaItem, \
|
from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab
|
||||||
SongsTab, SongXML
|
|
||||||
from openlp.plugins.songs.lib.db import init_schema, Song
|
from openlp.plugins.songs.lib.db import init_schema, Song
|
||||||
from openlp.plugins.songs.lib.importer import SongFormat
|
from openlp.plugins.songs.lib.importer import SongFormat
|
||||||
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
|
from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
|
||||||
@ -62,7 +60,6 @@ class SongsPlugin(Plugin):
|
|||||||
self.manager = Manager(u'songs', init_schema)
|
self.manager = Manager(u'songs', init_schema)
|
||||||
self.icon_path = u':/plugins/plugin_songs.png'
|
self.icon_path = u':/plugins/plugin_songs.png'
|
||||||
self.icon = build_icon(self.icon_path)
|
self.icon = build_icon(self.icon_path)
|
||||||
self.whitespace = re.compile(r'\W+', re.UNICODE)
|
|
||||||
|
|
||||||
def initialise(self):
|
def initialise(self):
|
||||||
log.info(u'Songs Initialising')
|
log.info(u'Songs Initialising')
|
||||||
@ -141,38 +138,18 @@ class SongsPlugin(Plugin):
|
|||||||
Rebuild each song.
|
Rebuild each song.
|
||||||
"""
|
"""
|
||||||
maxSongs = self.manager.get_object_count(Song)
|
maxSongs = self.manager.get_object_count(Song)
|
||||||
|
if maxSongs == 0:
|
||||||
|
return
|
||||||
progressDialog = QtGui.QProgressDialog(
|
progressDialog = QtGui.QProgressDialog(
|
||||||
translate('SongsPlugin', 'Reindexing songs...'), UiStrings.Cancel,
|
translate('SongsPlugin', 'Reindexing songs...'), UiStrings.Cancel,
|
||||||
0, maxSongs + 1, self.formparent)
|
0, maxSongs, self.formparent)
|
||||||
progressDialog.setWindowModality(QtCore.Qt.WindowModal)
|
progressDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||||
songs = self.manager.get_all_objects(Song)
|
songs = self.manager.get_all_objects(Song)
|
||||||
counter = 0
|
for number, song in enumerate(songs):
|
||||||
for song in songs:
|
clean_song(self.manager, song)
|
||||||
counter += 1
|
progressDialog.setValue(number + 1)
|
||||||
# The song does not have any author, add one.
|
|
||||||
if not song.authors:
|
|
||||||
add_author_unknown(self.manager, song)
|
|
||||||
if song.title is None:
|
|
||||||
song.title = u''
|
|
||||||
if song.alternate_title is None:
|
|
||||||
song.alternate_title = u''
|
|
||||||
song.search_title = self.whitespace.sub(u' ', song.title.lower() +
|
|
||||||
u' ' + song.alternate_title.lower()).strip()
|
|
||||||
# Remove the "language" attribute from lyrics tag. This is not very
|
|
||||||
# important, but this keeps the database clean. This can be removed
|
|
||||||
# when everybody has run the reindex tool once.
|
|
||||||
song.lyrics = song.lyrics.replace(
|
|
||||||
u'<lyrics language="en">', u'<lyrics>')
|
|
||||||
lyrics = u''
|
|
||||||
verses = SongXML().get_verses(song.lyrics)
|
|
||||||
for verse in verses:
|
|
||||||
lyrics = lyrics + self.whitespace.sub(u' ', verse[1]) + u' '
|
|
||||||
song.search_lyrics = lyrics.lower()
|
|
||||||
progressDialog.setValue(counter)
|
|
||||||
self.manager.save_objects(songs)
|
self.manager.save_objects(songs)
|
||||||
progressDialog.setValue(counter + 1)
|
self.mediaItem.onSearchTextButtonClick()
|
||||||
self.mediaItem.displayResultsSong(
|
|
||||||
self.manager.get_all_objects(Song, order_by_ref=Song.search_title))
|
|
||||||
|
|
||||||
def onSongImportItemClicked(self):
|
def onSongImportItemClicked(self):
|
||||||
if self.mediaItem:
|
if self.mediaItem:
|
||||||
@ -183,10 +160,9 @@ class SongsPlugin(Plugin):
|
|||||||
self.mediaItem.onExportClick()
|
self.mediaItem.onExportClick()
|
||||||
|
|
||||||
def about(self):
|
def about(self):
|
||||||
about_text = translate('SongsPlugin', '<strong>Songs Plugin</strong>'
|
return translate('SongsPlugin', '<strong>Songs Plugin</strong>'
|
||||||
'<br />The songs plugin provides the ability to display and '
|
'<br />The songs plugin provides the ability to display and '
|
||||||
'manage songs.')
|
'manage songs.')
|
||||||
return about_text
|
|
||||||
|
|
||||||
def usesTheme(self, theme):
|
def usesTheme(self, theme):
|
||||||
"""
|
"""
|
||||||
@ -258,6 +234,7 @@ class SongsPlugin(Plugin):
|
|||||||
for sfile in os.listdir(db_dir):
|
for sfile in os.listdir(db_dir):
|
||||||
if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'):
|
if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'):
|
||||||
song_dbs.append(os.path.join(db_dir, sfile))
|
song_dbs.append(os.path.join(db_dir, sfile))
|
||||||
|
self.onToolsReindexItemTriggered()
|
||||||
if len(song_dbs) == 0:
|
if len(song_dbs) == 0:
|
||||||
return
|
return
|
||||||
progress = QtGui.QProgressDialog(self.formparent)
|
progress = QtGui.QProgressDialog(self.formparent)
|
||||||
@ -273,9 +250,7 @@ class SongsPlugin(Plugin):
|
|||||||
importer = OpenLPSongImport(self.manager, filename=db)
|
importer = OpenLPSongImport(self.manager, filename=db)
|
||||||
importer.do_import()
|
importer.do_import()
|
||||||
progress.setValue(len(song_dbs))
|
progress.setValue(len(song_dbs))
|
||||||
self.mediaItem.displayResultsSong(
|
self.mediaItem.onSearchTextButtonClick()
|
||||||
self.manager.get_all_objects(Song, order_by_ref=Song.search_title))
|
|
||||||
self.onToolsReindexItemTriggered()
|
|
||||||
|
|
||||||
def finalise(self):
|
def finalise(self):
|
||||||
"""
|
"""
|
||||||
|