forked from bittorrenttorque/btapp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbtapp.min.js
8 lines (6 loc) · 34.1 KB
/
btapp.min.js
1
2
3
4
5
6
7
8
// Btapp.js 0.2.0
// (c) 2012 Patrick Williams, BitTorrent Inc.
// Btapp may be freely distributed under the MIT license.
// For all details and documentation:
// http://btappjs.com
(function(){"use strict";function e(e,t){if(!e)throw t}function t(){var e=navigator.userAgent.match(/Macintosh/);return e!==undefined&&e!==null}function r(e,t,n){var r=function(){e.call()?t.call():setTimeout(r,n||500)};_.defer(r)}function i(e){jQuery(document.createElement("link")).attr({href:e,type:"text/css",rel:"stylesheet"}).appendTo("head")}function s(){jQuery.facebox.settings.overlay=!0,jQuery.facebox.settings.closeImage="https://torque.bittorrent.com/facebox/src/closelabel.png",jQuery.facebox.settings.loadingImage="https://torque.bittorrent.com/facebox/src/loading.gif",jQuery.facebox.settings.opacity=.6}e(typeof JSON!="undefined","JSON is a hard dependency"),e(typeof _!="undefined","underscore/lodash is a hard dependency"),e(typeof jQuery!="undefined","jQuery is a hard dependency");var n=_.memoize(function(e){var t=new jQuery.Deferred,n=document.createElement("object"),r=e+"_onload";window[r]=function(){t.resolve()};var i=document.createElement("div");return jQuery(i).css({position:"absolute",left:"-999em","z-index":-1}),i.innerHTML='<object type="'+e+'" width="0" height="0">'+'<param name="onload" value="'+r+'" />'+"</object>",document.body.appendChild(i),t});this.PluginManagerView=Backbone.View.extend({initialize:function(e){this.model.on("plugin:install_plugin",this.download,this),this.model.on("plugin:plugin_updated",this.restart,this)},setup:function(e,t){if(typeof jQuery.facebox=="undefined"){i("https://torque.bittorrent.com/facebox/src/facebox.css"),jQuery.getScript("https://torque.bittorrent.com/facebox/src/facebox.js",_.bind(this.setup,this,e,t));return}s(),e.call(t)},restart:function(e){e.abort=!0,this.setup(function(){var e=jQuery("<div></div>");e.attr("id","plugin_download"),e.css("position","absolute"),e.css("height","200px"),e.css("width","400px"),e.css("left","%50"),e.css("margin-left","-200px");var t=jQuery("<p></p>");t.text("The "+this.model.get("product")+" plugin needs to complete an update. Please restart your browser."),e.append(t),e.hide(),jQuery("body").append(e),jQuery.facebox({div:"#plugin_download"})},this)},download:function(e){e.install=!0,this.setup(function(){var e=jQuery("<div></div>");e.attr("id","plugin_download"),e.css("position","absolute"),e.css("height","200px"),e.css("width","400px"),e.css("left","%50"),e.css("margin-left","-200px");var t=this.model.get("download_url"),n='<p style="text-align:center;">This site requires the '+this.model.get("product")+" plugin.<br>"+'<span style="font-size:8pt;">By installing this software, you<br>are agreeing to the <a href="http://www.bittorrent.com/legal/eula">EULA</a></span><br><br>'+'<a class="btn" id="download" href="'+t+'">Download</a>'+"</p>";e.append(n),this.model.on("plugin:plugin_installed",function(){jQuery(document).trigger("close.facebox"),jQuery("#plugin_download").remove()}),e.hide(),jQuery("body").append(e),jQuery.facebox({div:"#plugin_download"})},this)}}),this.PluginManager=Backbone.Model.extend({soshare_props:{latest_version:"4.4.1",mime_type:"application/x-gyre-soshare",activex_progid:"gyre.soshare",windows_download_url:"https://torque.bittorrent.com/SoShare.msi",osx_download_url:"https://torque.bittorrent.com/SoShare.pkg"},torque_props:{latest_version:"4.3.8",mime_type:"application/x-bittorrent-torque",activex_progid:"bittorrent.torque",windows_download_url:"https://torque.bittorrent.com/Torque.msi",osx_download_url:"https://torque.bittorrent.com/Torque.pkg"},defaults:{pid:"btapp_plugin_WARNING_HAVE_NOT_INITIALIZED",window_hash:"4823",product:"Torque"},initialize:function(){_.bindAll(this),this.set("pid","btapp_plugin_"+Math.floor(Math.random()*1024)),this.get("product")==="SoShare"?_.each(this.soshare_props,function(e,t){this.has(t)||this.set(t,e)},this):(this.get("product")==="Torque"||this.get("product")==="uTorrent"||this.get("product")==="BitTorrent")&&_.each(this.torque_props,function(e,t){this.has(t)||this.set(t,e)},this);var e=t()?this.get("osx_download_url"):this.get("windows_download_url");this.set("download_url",e),jQuery(_.bind(_.defer,this,this.mime_type_check))},disconnect:function(){},mime_type_check:function(){this.supports_mime_type()?this.mime_type_check_yes():this.mime_type_check_no()},mime_type_check_no:function(){var e={install:!1};this.trigger("plugin:install_plugin",e),r(this.supports_mime_type,this.mime_type_check_yes)},mime_type_check_yes:function(){this.trigger("plugin:plugin_installed");var e=n(this.get("mime_type"));e.then(_.bind(function(){this.trigger("plugin:plugin_running"),this.plugin_up_to_date_check()},this))},plugin_up_to_date_check:function(){this.plugin_up_to_date()?this.plugin_up_to_date_yes():this.plugin_up_to_date_no()},plugin_up_to_date_yes:function(){this.client_installed_check()},plugin_up_to_date_no:function(){var e={update:!0};this.trigger("plugin:update_plugin",e),e.update?this.get_plugin().checkForUpdate(_.bind(this.plugin_up_to_date_yes,this)):this.plugin_up_to_date_yes()},client_installed_check:function(){this.client_installed()?this.client_installed_check_yes():this.client_installed_check_no()},client_installed_check_no:function(){var e={install:!0};this.trigger("plugin:install_client",e);if(e.install)this.get_plugin().downloadProgram(this.get("product"),_.bind(function(e,t,n,r,i){jQuery.jStorage.set("pairing_key",i),this.trigger("plugin:downloaded_client")},this)),r(this.client_installed,this.client_running_check_yes);else{var t={check:!1};this.trigger("plugin:check_for_running_client",t),t.check&&this.client_running_check()}},client_installed_check_yes:function(){this.trigger("plugin:client_installed"),this.client_running_check()},client_running_check:function(){this.client_running()?this.client_running_check_yes():this.client_running_check_no()},client_running_check_no:function(){var e={run:!0};this.trigger("plugin:run_client",e),e.run&&this.get_plugin().runProgram(this.get("product"),function(){}),r(this.client_running,this.client_running_check_yes)},client_running_check_yes:function(){this.trigger("plugin:client_running")},supports_mime_type:function(){if(window.location.protocol==="chrome-extension:")return!0;var e=navigator.appVersion.indexOf("MSIE")!==-1?!0:!1;if(!e){navigator.plugins.refresh();for(var r=0;r<navigator.plugins.length;r++){var i=navigator.plugins[r][0];if(i.type===this.get("mime_type"))return!0}return!1}try{var t=new ActiveXObject(this.get("activex_progid"));return t!==undefined}catch(n){return!1}},plugin_up_to_date:function(){if(window.location.protocol==="chrome-extension:")return!0;var e=this.get_plugin().version,t=_.map(e.split("."),function(e){return parseInt(e,10)}),n=_.map(this.get("latest_version").split("."),function(e){return parseInt(e,10)});for(var r=0;r<t.length;r++){if(t[r]<n[r])return!1;if(t[r]>n[r])return!0}return!0},get_plugin:function(){var t=jQuery('[type="'+this.get("mime_type")+'"]');return e(t.length===1,"cannot call get_plugin before adding the plugin"),t[0]},plugin_loaded:function(){return e(this.supports_mime_type(),"you have not installed the plugin yet"),e(jQuery("#"+this.get("pid")).length!==0,"you have not yet added the plugin"),this.get_plugin().version},get_window_name:function(e){return e==="uTorrent"?"Torrent4823":e},client_running:function(){var e=this.get_plugin().isRunning(this.get_window_name(this.get("product")));return typeof e=="object"?e&&e.length>0:e},client_installed:function(){var t=this.get_plugin().getInstallVersion(this.get("product")),n="This application is not supported.";return e(t!==n,n),t}})}).call(this),function(){"use strict";function e(e,t){if(!e)throw t}function r(e){jQuery(document.createElement("link")).attr({href:e,type:"text/css",rel:"stylesheet"}).appendTo("head")}function i(){jQuery.facebox.settings.overlay=!0,jQuery.facebox.settings.closeImage="https://torque.bittorrent.com/facebox/src/closelabel.png",jQuery.facebox.settings.loadingImage="https://torque.bittorrent.com/facebox/src/loading.gif",jQuery.facebox.settings.opacity=.6}function s(){var e=navigator.userAgent.match(/Macintosh/);return e!==undefined&&e!==null}function o(e){return"http://127.0.0.1:"+e}function u(e){return o(e)+"/gui/pingimg"}function a(e){return o(e)+"/gui/pair?name="+encodeURIComponent(window.location.host)}function f(e){return o(e)+"/version/"}function l(e){var t,n=0;do t=c(n),n+=1;while(t<=e);return t}function c(e){return 7*Math.pow(e,3)+3*Math.pow(e,2)+5*e+1e4}var t=5,n=3e3;e(typeof JSON!="undefined","JSON is a hard dependency"),e(typeof _!="undefined","underscore/lodash is a hard dependency"),e(typeof jQuery!="undefined","jQuery is a hard dependency"),this.PairingView=Backbone.View.extend({initialize:function(){e(this.model.get("pairing_type")!=="native"),this.model.on("pairing:authorize",this.authorize_iframe,this)},authorize_iframe:function(t){if(typeof jQuery.facebox=="undefined"){r("https://torque.bittorrent.com/facebox/src/facebox.css"),jQuery.getScript("https://torque.bittorrent.com/facebox/src/facebox.js",_.bind(this.authorize_iframe,this,t));return}i();var n=jQuery("<div></div>");n.attr("id","pairing"),n.css("position","absolute"),n.css("height","200px"),n.css("width","400px"),n.css("left","%50"),n.css("margin-left","-200px");var s=jQuery("<iframe></iframe>"),o="https://torque.bittorrent.com",u=o+"/pairing/index.html"+"?product="+this.model.get("product")+"&mime="+this.model.get("plugin_manager").get("mime_type")+"&name="+encodeURIComponent(document.title)+"&permissions=download,create,remote";s.attr("src",u),s.css("padding","0px"),s.css("margin","0px"),n.append(s),jQuery(window).on("message",function(n){if(n.originalEvent.origin===o){e(n&&n.originalEvent&&n.originalEvent.data,"no data was passed in the message from the iframe");if(n.originalEvent.data.length===40)t.deferred.resolve(n.originalEvent.data);else{if(n.originalEvent.data!=="denied")throw"the message data from the iframe was neither a pairing key, nor a denied message";t.deferred.reject()}jQuery(document).trigger("close.facebox"),jQuery("#pairing").remove()}}),n.hide(),jQuery("body").append(n),jQuery.facebox({div:"#pairing"})}});var h={};this.PluginPairing={check_version:function(e){var t=new jQuery.Deferred;return this.trigger("pairing:check_version",{port:e}),this.get("plugin_manager").get_plugin().ajax(f(e),_.bind(function(e){if(!e.allowed||!e.success)t.reject();else{var n;try{t.resolve(JSON.parse(e.data))}catch(r){t.reject();return}}},this)),t},authorize_basic:function(e){var t;e in h?t=h[e]:(t=new jQuery.Deferred,h[e]=t,t.done(function(){delete h[e]}),this.get("plugin_manager").get_plugin().ajax(a(e),_.bind(function(e){!e.allowed||!e.success?t.reject():t.resolve(e.data)},this))),t.then(_.bind(this.authorize_port_success,this,e)),t.fail(_.bind(this.authorize_port_error,this,e))}};var p={};this.JQueryPairing={check_version:function(e){return this.trigger("pairing:check_version",{port:e}),jQuery.ajax({url:f(e),dataType:"jsonp",timeout:n})},authorize_basic:function(e){var t=_.bind(this.authorize_port_success,this,e),r=_.bind(this.authorize_port_error,this,e),i;e in p?i=p[e]:(i=jQuery.ajax({url:a(e),dataType:"jsonp",timeout:n}),p[e]=i,i.done(function(){delete p[e]})),i.then(t),i.fail(r)}};var d={};this.Pairing=Backbone.Model.extend({defaults:{pairing_type:"iframe"},initialize:function(){_.bindAll(this,"on_check_version_success"),e(this.get("plugin")===!1||this.get("plugin_manager"),"pairing is not intentionally avoiding the plugin, nor is it providing a plugin manager"),this.get("plugin_manager")?_.extend(this,PluginPairing):_.extend(this,JQueryPairing)},connect:function(){e(!this.session,"trying to port scan while one is already in progress");var n={abort:!1},r=[],i=_.after(t,_.bind(function(){if(n.abort===!0)return;this.disconnect();var t=_.reduce(r,function(t,n){e(n.state()!=="pending","executing pairing complete functionality while some queries are in flight");var r=n.state()==="resolved";return t+(r?1:0)},0);t===0&&this.trigger("pairing:stop")},this));_.times(t,function(e){var t=c(e),s=this.check_version(t);s.done(_.bind(function(){if(n.abort)return;this.on_check_version_success.apply(this,arguments)},this,t)),r.push(s),s.always(i)},this),this.session=n},disconnect:function(){this.session&&(this.session.abort=!0,this.session=null)},on_check_version_success:function(e,t){var n={version:typeof t=="object"?t.version:"unknown",name:typeof t=="object"?t.name:"unknown",port:e,authorize:!0};if(t==="invalid request"||t&&t.version)this.trigger("pairing:found",n),n.authorize&&this.authorize(e)},authorize:function(e){if(this.get("pairing_type")==="native"||s())this.authorize_basic(e);else{var t=this.get("plugin_manager").get_plugin().pair(this.get("product"));if(t.length===40)this.authorize_port_success(e,t);else{var n;e in d?n=d[e]:(n=new jQuery.Deferred,d[e]=n,n.done(function(){delete d[e]}),this.trigger("pairing:authorize",{port:e,deferred:n})),n.then(_.bind(this.authorize_port_success,this,e)),n.fail(_.bind(this.authorize_port_error,this,e))}}},authorize_port_success:function(e,t){this.trigger("pairing:authorized",{port:e,key:t})},authorize_port_error:function(e){this.trigger("pairing:denied",e)}})}.call(this),function(){"use strict";function e(e,t){if(!e)throw t}e(typeof JSON!="undefined","JSON is a hard dependency"),e(typeof _!="undefined","underscore/lodash is a hard dependency"),e(typeof PluginManager!="undefined","plugin.btapp.js is a hard dependency"),e(typeof Pairing!="undefined","pairing.btapp.js is a hard dependency"),e(typeof jQuery!="undefined","jQuery is a hard dependency"),e(typeof jQuery.jStorage!="undefined","jQuery.jStorage is a hard dependency");var t=this;this.TorrentClient=Backbone.Model.extend({initialize:function(e){this.btappCallbacks={}},storeCallbackFunction:function(e){e=e||function(){};var t="bt_";for(var n=0;n<20||t in this.btappCallbacks;n++)t+=Math.floor(Math.random()*10);return this.btappCallbacks[t]=e,t},isRPCFunctionSignature:function(t){return e(typeof t=="string","do not check function signature of non-strings"),t.match(/\[native function\](\([^\)]*\))+/)||t.match(/\[nf\](\([^\)]*\))+/)},isJSFunctionSignature:function(t){return e(typeof t=="string","do not check function signature of non-strings"),t.match(/\[nf\]bt_/)},getStoredFunction:function(t){e(TorrentClient.prototype.isJSFunctionSignature(t),'only store functions that match the pattern "[nf]bt_*"');var n=t.substring(4);return e(n in this.btappCallbacks,"trying to get a function with a key that is not recognized"),this.btappCallbacks[n]},validateArguments:function(t,n){e(typeof t=="string","expected functionValue to be a string"),e(typeof n=="object","expected variables to be an object");var r=t.match(/\(.*?\)/g);return _.any(r,function(e){return e=e.match(/\w+/g)||[],e.length===n.length&&_.all(e,function(e,t){if(typeof n[t]=="undefined")throw"client functions do not support undefined arguments";if(typeof n[t]=="null")return!0;switch(e){case"number":case"string":case"boolean":return typeof n[t]===e;case"unknown":return!0;case"array":return typeof n[t]=="object";case"dispatch":return typeof n[t]=="object"||typeof n[t]=="function";default:throw"there is an invalid type in the function signature exposed by the client"}})})},convertCallbackFunctionArgs:function(e){_.each(e,function(t,n){typeof t=="function"?e[n]=this.storeCallbackFunction(t):typeof t=="object"&&t&&this.convertCallbackFunctionArgs(t)},this)},createFunction:function(t,n,r){e(t,"cannot create a function without a session id");var i=_.bind(function(){var i=[],s;for(s=0;s<arguments.length;s++)i.push(arguments[s]);if(!TorrentClient.prototype.validateArguments.call(this,r,i))throw"arguments do not match any of the function signatures exposed by the client";this.convertCallbackFunctionArgs(i);var o=new jQuery.Deferred,u=_.bind(function(t){_.each(n,function(e){var n=decodeURIComponent(e);typeof t!="undefined"&&(t=t[n])});if(typeof t=="undefined")o.reject("return value parsing error "+JSON.stringify(t));else if(typeof t=="string"&&this.isJSFunctionSignature(t)){var r=this.getStoredFunction(t);e(r,"the client is returning a function name that does not exist"),o.resolve(r)}else o.resolve(t)},this),a=function(e){o.reject(e)};return this.query({type:"function",path:JSON.stringify(n),args:JSON.stringify(i),session:t}).done(u).fail(a),this.trigger("queries",n),o},this);return i.valueOf=function(){return r},i},query:function(t){var n=!1,r=new jQuery.Deferred;e(t.type==="update"||t.type==="state"||t.type==="function"||t.type==="disconnect",'the query type must be either "update", "state", or "function"'),t.hostname=window.location.hostname||window.location.pathname;var i=_.bind(function(e){if(e==="invalid request")throw setTimeout(_.bind(this.reset,this),1e3),"pairing occured with a torrent client that does not support the btapp api";typeof e!="object"||"error"in e?(r.reject(),this.trigger("client:error",e)):r.resolve(e)},this);return this.send_query(t).done(function(){n||i.apply(this,arguments)}).fail(function(){n||r.reject.apply(this,arguments)}),r.abort=function(){n=!0},r}}),this.FalconTorrentClient=TorrentClient.extend({initialize:function(n){TorrentClient.prototype.initialize.call(this,n),e("username"in n&&"password"in n||"remote_data"in n,"attempting to connect to client through falcon without providing the necessary credentials"),this.username=n.username,this.password=n.password,"remote_data"in n&&(this.remote_data=n.remote_data),"login_success"in n&&(this.login_success=n.login_success),"login_error"in n&&(this.login_error=n.login_error),"login_progress"in n&&(this.login_progress=n.login_progress);if(typeof falcon!="undefined"){_.defer(_.bind(this.reset,this));return}t.config={srp_root:"https://remote.utorrent.com"};var r="https://remote.utorrent.com/static/js/jsloadv2.js?v=0.57";jQuery.getScript(r,_.bind(function(e,t){function n(e){var t=[],n=[];for(var r=0;r<e.length-1;r++){var i=e[r];t.push({name:i}),n.push(i)}return t.push({name:e[e.length-1],requires:n}),t}var r=["falcon/deps/SHA-1.js","falcon/deps/jsbn.js","falcon/deps/jsbn2.js","falcon/deps/sjcl.js","falcon/falcon.js","falcon/falcon.encryption.js","falcon/falcon.api.js","falcon/falcon.session.js"],i=n(r);(new JSLoad(i,"https://remote.utorrent.com/static/js/")).load(["falcon/falcon.session.js"],_.bind(function(){this.remote_data?(this.session=new falcon.session({client_data:this.remote_data}),this.falcon=this.session.api,this.trigger("client:connected")):this.delayed_reset()},this))},this))},connect:function(){e(!this.falcon,"trying to connect with falcon already set");var t={success:_.bind(function(e){this.login_success&&this.login_success(e),this.falcon=this.session,this.trigger("client:connected",e)},this),error:_.bind(function(e,t,n){this.login_error&&this.login_error(e,t,n),this.trigger("client:error_connecting",n),this.trigger("client:error",n)},this)};this.session=new falcon.session,this.session.negotiate(this.username,this.password,{success:t.success,error:t.error,progress:this.login_progress})},disconnect:function(){},send_query:function(t){var n=new jQuery.Deferred;return e(this.falcon,"cannot send a query to the client without falcon properly connected"),this.falcon.request("/gui/",{btapp:"backbone.btapp.js"},t,function(t){e("build"in t,"expected build information in the falcon response"),e("result"in t,"expected result information in the falcon response"),n.resolve(t.result)},_.bind(function(){n.reject(),this.delayed_reset()},this),{}),n},delayed_reset:function(){this.reset_timeout=setTimeout(_.bind(function(){this.reset(),this.reset_timeout=null},this),1e3)},reset:function(){this.falcon=null,this.connect()}}),this.LocalTorrentClient=TorrentClient.extend({defaults:{product:"Torque"},initialize:function(e){TorrentClient.prototype.initialize.call(this,e),this.btapp=e.btapp,this.initialize_objects(e),this.reset_timeout=null},disconnect:function(){this.pairing&&this.pairing.disconnect(),this.plugin_manager&&this.plugin_manager.disconnect(),this.reset_timeout&&clearTimeout(this.reset_timeout)},initialize_objects:function(e){this.initialize_plugin(e),this.initialize_pairing(e)},initialize_plugin:function(e){if(this.get("plugin")===!1)return;this.plugin_manager=new PluginManager(e),new PluginManagerView({model:this.plugin_manager}),this.plugin_manager.on("all",this.trigger,this)},initialize_pairing:function(t){e(this.get("plugin")===!1||typeof this.plugin_manager!="undefined","you must initialize the plugin manager before the pairing object"),this.pairing=new Pairing(_.extend(t,{plugin_manager:this.plugin_manager})),this.pairing.get("pairing_type")!=="native"&&new PairingView({model:this.pairing}),this.pairing.on("all",this.trigger,this),e(this.has("product"),"client does not know what product to connect to");var n=this.get("product");this.pairing.on("pairing:found",function(e){if(e&&e.name.toLowerCase()===n.toLowerCase()){var t=jQuery.jStorage.get(n+"_pairing_key");t?(e.abort=!0,e.authorize=!1,this.connect_to_authenticated_port(e.port,t)):(e.abort=!0,e.authorize=!0)}else e.abort=!1,e.authorize=!1},this),this.pairing.on("pairing:authorized",_.bind(function(e){jQuery.jStorage.set(n+"_pairing_key",e.key),this.connect_to_authenticated_port(e.port,e.key)},this)),this.pairing.on("pairing:stop",this.delayed_reset,this),this.get("plugin")===!1?this.delayed_reset():this.plugin_manager.on("plugin:client_running",this.reset,this)},ajax:function(e,t,n){this.get("plugin")===!1?jQuery.ajax({url:e,success:t,error:n,dataType:"jsonp"}):this.plugin_manager.get_plugin().ajax(e,_.bind(function(e){var r;if(e.allowed&&e.success&&e.data!=="invalid request"){try{r=JSON.parse(e.data)}catch(i){var s="parsererror";n(s),this.trigger("client:error",s);return}t(r)}else this.trigger("client:error",e),n(e)},this))},connect_to_authenticated_port:function(e,t){var n=_.bind(function(){this.url="http://127.0.0.1:"+e+"/btapp/?pairing="+t,this.trigger("client:connected")},this),r=_.bind(function(){jQuery.jStorage.deleteKey(this.get("product")+"_pairing_key"),this.delayed_reset()},this),i="http://127.0.0.1:"+e+"/btapp/?pairing="+t;this.ajax(i,n,r)},delayed_reset:function(){this.reset_timeout=setTimeout(_.bind(function(){this.reset(),this.reset_timeout=null},this),1e3)},reset:function(){this.pairing.connect()},send_query:function(e){var t=new jQuery.Deferred;this.trigger("client:query",this.url,e);var n=this.url;return _.each(e,function(e,t){n+="&"+encodeURIComponent(t)+"="+encodeURIComponent(e)}),this.ajax(n,function(e){t.resolve(e)},function(){t.reject()}),t}})}.call(this),function(){"use strict";function e(e,t){if(!e)throw t}e(typeof JSON!="undefined","JSON is a hard dependency"),e(typeof _!="undefined","underscore/lodash is a hard dependency"),e(typeof TorrentClient!="undefined","client.btapp.js is a hard dependency"),e(typeof jQuery!="undefined","jQuery is a hard dependency"),e(typeof jQuery.jStorage!="undefined","jQuery.jStorage is a hard dependency");var t=3e3,n=0,r=100,i=function(e){return _.isObject(e)&&!_.isArray(e)&&jQuery.isEmptyObject(e)},s={initialize:function(){_.bindAll(this,"initializeValues","updateState","clearState","isEmpty","destructor"),this.initializeValues()},clearRemoteProcedureCalls:function(){var e=_.keys(this.bt||{});for(var t=0;t<e.length;t++){var n=e[t];delete this.bt[n],delete this[n]}this.bt={}},initializeValues:function(){this.path=null,this.session=null,this.clearRemoteProcedureCalls()},updateRemoveFunctionState:function(t){e(t in this.bt,"trying to remove a function that does not exist"),this.trigger("remove:bt:"+t),this.trigger("remove:bt",this.bt[t],t),delete this.bt[t];if(t==="get"||t==="set"||t==="unset"||t==="length")return;return e(t in this,'trying to remove the function "'+t+'", which does not exist in the prototype of this object'),this.trigger("remove:"+t),delete this[t],{}},updateRemoveObjectState:function(t,n,r,i,s){var o={},u=this.get(s);return e(u,"trying to remove a model that does not exist"),e("updateState"in u,"trying to remove an object that does not extend BtappBase"),u.updateState(t,n,r,i),u.isEmpty()&&(o[s]=u,u.trigger("destroy")),o},updateRemoveElementState:function(e,t,n,r,i){var s=_.clone(i||[]);s.push(r);if(r==="all")return this.updateState(this.session,t,n,s);if(_.isNull(n))return this.updateRemoveAttributeState(r,n);if(_.isObject(n)&&!_.isArray(n))return this.updateRemoveObjectState(e,t,n,s,r);if(_.isString(n)&&TorrentClient.prototype.isRPCFunctionSignature(n))return this.updateRemoveFunctionState(r);if(_.isString(n)&&TorrentClient.prototype.isJSFunctionSignature(n))return this.updateRemoveAttributeState(r,this.client.getStoredFunction(n));if(r!=="id")return this.updateRemoveAttributeState(r,n)},updateRemoveState:function(e,t,n,r){var i={};for(var s in n)t[s]===undefined&&_.extend(i,this.updateRemoveElementState(e,t[s],n[s],s,r));return i},updateAddFunctionState:function(t,n,r,i){var s=_.clone(r||[]);s.push(i);var o=this.client.createFunction(t,s,n);return e(!(i in this.bt),"trying to add a function that already exists"),this.bt[i]=o,i!=="get"&&i!=="set"&&i!=="unset"&&i!=="length"&&(e(!(i in this),'trying to add the function "'+i+'", which already exists in the prototype of this object'),this[i]=o,this.trigger("add:"+i)),this.trigger("add:bt:"+i),this.trigger("add:bt",this.bt[i],i),{}},updateAddObjectState:function(e,t,n,r,i){var s={},o=this.get(i);return o===undefined&&(BtappCollection.prototype.verifyPath(r)?o=new BtappCollection:o=new BtappModel({id:i}),o.path=r,o.client=this.client,s[i]=o),o.updateState(this.session,t,n,r),s},updateAddElementState:function(e,t,n,r,i){var s=_.clone(i||[]);return s.push(r),_.isString(n)&&TorrentClient.prototype.isJSFunctionSignature(n)&&(n=this.client.getStoredFunction(n)),r==="all"?this.updateState(this.session,t,n,s):_.isNull(t)?this.updateAddAttributeState(e,t,n,s,r):_.isObject(t)&&!_.isArray(t)?this.updateAddObjectState(e,t,n,s,r):_.isString(t)&&TorrentClient.prototype.isRPCFunctionSignature(t)?this.updateAddFunctionState(e,t,i,r):_.isString(t)&&TorrentClient.prototype.isJSFunctionSignature(t)?this.updateAddAttributeState(e,this.client.getStoredFunction(t),n,i,r):this.updateAddAttributeState(e,t,n,s,r)},updateAddState:function(e,t,n,r){var i={};for(var s in t)_.extend(i,this.updateAddElementState(e,t[s],n[s],s,r));return i},updateState:function(t,n,r,s){e(!i(n)||!i(r),'the client is outputing empty objects("'+s+'")...these should have been trimmed off'),this.session=t,this.path||(this.path=s,e(this.verifyPath(this.path),"cannot updateState with an invalid collection path")),n=n||{},r=r||{},this.applyStateChanges(this.updateAddState(t,n,r,s),this.updateRemoveState(t,n,r,s))},sync:function(){}};this.BtappCollection=Backbone.Collection.extend(s).extend({initialize:function(e,t){Backbone.Collection.prototype.initialize.apply(this,arguments),s.initialize.apply(this,arguments),this.on("add",this.customAddEvent,this),this.on("remove",this.customRemoveEvent,this),this.on("change",this.customChangeEvent,this)},customEvent:function(t,n){if(_.isFunction(n))return;e(n&&n.id,"called a custom "+t+" event without a valid attribute"),this.trigger(t+":"+n.id,n)},customAddEvent:function(e){this.customEvent("add",e)},customRemoveEvent:function(e){this.customEvent("remove",e)},customChangeEvent:function(e){this.customEvent("change",e)},destructor:function(){this.off("add",this.customAddEvent,this),this.off("remove",this.customRemoveEvent,this),this.off("change",this.customChangeEvent,this),this.trigger("destroy")},clearState:function(){this.each(function(e){e.clearState()}),this.initializeValues(),this.reset(),this.destructor()},verifyPath:function(e){var t=[["btapp","torrent"],["btapp","torrent","all","*","file"],["btapp","torrent","all","*","peer"],["btapp","label"],["btapp","label","all","*","torrent"],["btapp","label","all","*","torrent","all","*","file"],["btapp","label","all","*","torrent","all","*","peer"],["btapp","rss"],["btapp","rss","all","*","item"],["btapp","stream"],["btapp","stream","all","*","diskio"]];return _.any(t,function(t){if(t.length!==e.length)return!1;for(var n=0;n<t.length;n++){if(t[n]==="*")continue;if(t[n]!==e[n])return!1}return!0})},updateRemoveAttributeState:function(e,t){throw"trying to remove an invalid type from a BtappCollection"},updateAddAttributeState:function(e,t,n,r,i){throw"trying to add an invalid type to a BtappCollection"},isEmpty:function(){return i(this.bt)&&this.length===0},applyStateChanges:function(e,t){this.add(_.values(e)),this.remove(_.values(t))}}),this.BtappModel=Backbone.Model.extend(s).extend({initialize:function(e){Backbone.Model.prototype.initialize.apply(this,arguments),s.initialize.apply(this,arguments),this.on("change",this.customEvents,this)},destructor:function(){this.off("change",this.customEvents,this),this.trigger("destroy")},clearState:function(){this.initializeValues();var e=_.clone(this.attributes);delete e.id,_.each(e,function(e){e&&_.isObject(e)&&e.hasOwnProperty("clearState")&&e.clearState()}),Backbone.Model.prototype.set.call(this,e,{internal:!0,unset:!0}),this.destructor()},customEvents:function(){var e=this.changedAttributes();_.each(e,_.bind(function(e,t){if(e===undefined){var n=this.previous(t);this.trigger("remove",n,t),this.trigger("remove:"+t,n)}else this.previous(t)===undefined&&(this.trigger("add",e,t),this.trigger("add:"+t,e))},this))},verifyPath:function(e){return!0},updateRemoveAttributeState:function(t,n){var r={};return e(this.get(t)===n,"trying to remove an attribute, but did not provide the correct previous value"),r[t]=this.get(t),r},updateAddAttributeState:function(t,n,r,i,s){var o={};return e(this.get(s)!==n,"trying to set a variable to the existing value ["+i+" -> "+JSON.stringify(n)+"]"),r!==undefined&&e(this.get(s)===r,"trying to update an attribute, but did not provide the correct previous value"),o[s]=n,o},isEmpty:function(){var e=_.keys(this.toJSON());return i(this.bt)&&(e.length===0||e.length===1&&e[0]==="id")},applyStateChanges:function(e,t){Backbone.Model.prototype.set.call(this,e,{internal:!0}),Backbone.Model.prototype.set.call(this,t,{internal:!0,unset:!0})},set:function(e,t,n){var r=function(e,t){if(n&&"internal"in n)return;if(_.isUndefined(this.get(t)))return;throw"please use save to set attributes directly to the client"};return _.isObject(e)||e===null?_(e).each(r,this):r.call(this,t,e),Backbone.Model.prototype.set.apply(this,arguments)},save:function(e,t){var n=[];return _(e).each(function(e,t){n.push(this.bt.set(t,e))},this),jQuery.when.apply(jQuery,n)}}),this.Btapp=BtappModel.extend({initialize:function(){BtappModel.prototype.initialize.apply(this,arguments),this.path=["btapp"],this.connected_state=!1,this.client=null,this.queries=null,this.session=null,this.last_query=null,_.bindAll(this,"connect","disconnect","connected","fetch","onEvents","onFetch","onConnectionError"),this.on("add:events",this.setEvents,this)},destructor:function(){},connect:function(t){e(!this.client,"trying to connect to an undefined client"),e(!this.connected_state,"trying to connect when already connected"),this.connected_state=!0,e(!this.session,"trying to create another session while one is active"),t=t||{},this.poll_frequency=n,this.queries=t.queries||Btapp.QUERIES.ALL;var r="the queries attribute must be an array of arrays of strings";e(_.isArray(this.queries),r),e(_.all(this.queries,function(e){return _.isArray(e)&&_.all(e,function(e){return _.isString(e)})}),r),t.btapp=this,e(!_.isUndefined(TorrentClient),"client.btapp.js is a hard dependency"),this.setClient(t);var i=new jQuery.Deferred,s=function(){this.off("client:connected",s,this),i.resolve()};return this.on("client:connected",s,this),i},setClient:function(e){"username"in e&&"password"in e||"remote_data"in e?(this.client=new FalconTorrentClient(e),this.client.bind("client:error_connecting",this.disconnect,this)):this.client=new LocalTorrentClient(e),this.client.bind("all",this.trigger,this),this.client.bind("client:connected",this.fetch)},setEvents:function(e){_.each(e.toJSON(),function(t,n){if(n!=="id"){var r={};r[n]=_.bind(this.trigger,this,n),e.save(r)}},this)},disconnect:function(){this.trigger("disconnect","manual"),e(this.client,"trying to disconnect from an undefined client"),e(this.connected_state,"trying to disconnect when not connected"),this.session&&this.client.query({type:"disconnect",session:this.session}),this.connected_state=!1,this.last_query&&(this.last_query.abort(),this.last_query=null),this.session=null,this.next_timeout&&clearTimeout(this.next_timeout),this.client.disconnect(),this.client.btapp=null,this.client=null,this.queries=null,this.clearState()},connected:function(){return this.connected_state},onConnectionError:function(){this.trigger("disconnect","error"),this.poll_frequency=t,this.clearState(),this.session=null,this.last_query&&(this.last_query.abort(),this.last_query=null),this.client&&_.delay(_.bind(this.client.reset,this.client),500)},onFetch:function(t){e("session"in t,"did not recieve a session id from the client"),this.session=t.session,this.waitForEvents(t.session)},fetch:function(){this.client&&(this.last_query=this.client.query({type:"state",queries:JSON.stringify(this.queries)}).done(this.onFetch).fail(this.onConnectionError))},onEvent:function(e,t){if("add"in t||"remove"in t)t.add=t.add||{},t.remove=t.remove||{},this.updateState(e,t.add.btapp,t.remove.btapp,["btapp"]);else{if(!("callback"in t))throw"received invalid data from the client";this.client.btappCallbacks[t.callback](t.arguments)}},onEvents:function(i,s){e(this.session===i,"should not receive data for a different session after creating a new one. do not forget to abort the last call of your old session.");if(this.connected_state){this.trigger("sync",s),s.length===0?this.poll_frequency=Math.min(t,this.poll_frequency+r):this.poll_frequency=n;for(var o=0;o<s.length;o++)this.onEvent(i,s[o]);this.next_timeout=setTimeout(_.bind(this.waitForEvents,this,i),this.poll_frequency)}},waitForEvents:function(e){this.client&&(this.last_query=this.client.query({type:"update",session:e}).done(_.bind(this.onEvents,this,e)).fail(this.onConnectionError))}}),Btapp.VERSION="0.2.0",Btapp.QUERIES={ALL:[["btapp"]],DHT:[["btapp","dht"]],TORRENTS_BASIC:[["btapp","create"],["btapp","add","torrent"],["btapp","torrent","all","*","file","all","*"],["btapp","torrent","all","*","properties","all","*"]],EVENTS:[["btapp","events"]],SETTINGS:[["btapp","settings"]],REMOTE:[["btapp","connect_remote"],["btapp","settings","all","webui.uconnect_enable"]]},Btapp.STATUS={TORRENT:{DELETED:-1,DOWNLOAD_FAILED:0,ADDED:1,COMPLETE:2,METADATA_COMPLETE:3},RSS_FEED:{DELETED:-1,ADDED:1}},Btapp.REMOVE={NO_DELETE:0,DELETE_TORRENT:1,DELETE_DATA:2,DELETE_BOTH:3,DELETE_TO_TRASH:4,DELETE_CONVERTED_FILES:5},Btapp.TORRENT={PRIORITY:{LOW:0,MEDIUM:1,HIGH:2,METADATA_ONLY:3},FILE:{PRIORITY:{NO_DOWNLOAD:0,LOW:5,MEDIUM:10,HIGH:15}},REMOVE:{NO_DELETE:0,DELETE_TORRENT:1,DELETE_DATA:2,DELETE_TORRENT_AND_DATA:3}}}.call(this);