/** * Copyright © Jordan Lee, Dave Perrett, Malcolm Jarvis and Bruno Bierbaumer * * This file is licensed under the GPLv2. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ function PrefsDialog(remote) { var data = { dialog: null, remote: null, elements: { }, // all the RPC session keys that we have gui controls for keys: [ 'alt-speed-down', 'alt-speed-time-begin', 'alt-speed-time-day', 'alt-speed-time-enabled', 'alt-speed-time-end', 'alt-speed-up', 'blocklist-enabled', 'blocklist-size', 'blocklist-url', 'dht-enabled', 'download-dir', 'encryption', 'idle-seeding-limit', 'idle-seeding-limit-enabled', 'lpd-enabled', 'peer-limit-global', 'peer-limit-per-torrent', 'peer-port', 'peer-port-random-on-start', 'pex-enabled', 'port-forwarding-enabled', 'rename-partial-files', 'seedRatioLimit', 'seedRatioLimited', 'speed-limit-down', 'speed-limit-down-enabled', 'speed-limit-up', 'speed-limit-up-enabled', 'start-added-torrents', 'utp-enabled' ], // map of keys that are enabled only if a 'parent' key is enabled groups: { 'alt-speed-time-enabled': ['alt-speed-time-begin', 'alt-speed-time-day', 'alt-speed-time-end' ], 'blocklist-enabled': ['blocklist-url', 'blocklist-update-button' ], 'idle-seeding-limit-enabled': [ 'idle-seeding-limit' ], 'seedRatioLimited': [ 'seedRatioLimit' ], 'speed-limit-down-enabled': [ 'speed-limit-down' ], 'speed-limit-up-enabled': [ 'speed-limit-up' ] } }, initTimeDropDown = function(e) { var i, hour, mins, value, content; for (i=0; i<24*4; ++i) { hour = parseInt(i/4, 10); mins = ((i%4) * 15); value = i * 15; content = hour + ':' + (mins || '00'); e.options[i] = new Option(content, value); } }, onPortChecked = function(response) { var is_open = response['arguments']['port-is-open'], text = 'Port is ' + (is_open ? 'Open' : 'Closed') + '', e = data.elements.root.find('#port-label'); setInnerHTML(e[0],text); }, setGroupEnabled = function(parent_key, enabled) { var i, key, keys, root; if (parent_key in data.groups) { root = data.elements.root, keys = data.groups[parent_key]; for (i=0; key=keys[i]; ++i) root.find('#'+key).attr('disabled',!enabled); } }, onBlocklistUpdateClicked = function () { data.remote.updateBlocklist(); setBlocklistButtonEnabled(false); }, setBlocklistButtonEnabled = function(b) { var e = data.elements.blocklist_button; e.attr('disabled',!b); e.val(b ? 'Update' : 'Updating...'); }, getValue = function(e) { var str; switch (e[0].type) { case 'checkbox': case 'radio': return e.prop('checked'); case 'text': case 'select-one': str = e.val(); if( parseInt(str,10).toString() === str) return parseInt(str,10); if( parseFloat(str).toString() === str) return parseFloat(str); return str; default: return null; } }, /* this callback is for controls whose changes can be applied immediately, like checkboxs, radioboxes, and selects */ onControlChanged = function(ev) { var o = {}; o[ev.target.id] = getValue($(ev.target)); data.remote.savePrefs(o); }, /* these two callbacks are for controls whose changes can't be applied immediately -- like a text entry field -- because it takes many change events for the user to get to the desired result */ onControlFocused = function(ev) { data.oldValue = getValue($(ev.target)); }, onControlBlurred = function(ev) { var newValue = getValue($(ev.target)); if (newValue !== data.oldValue) { var o = {}; o[ev.target.id] = newValue; data.remote.savePrefs(o); delete data.oldValue; } }, getDefaultMobileOptions = function() { return { width: $(window).width(), height: $(window).height(), position: [ 'left', 'top' ] }; }, initialize = function (remote) { var i, key, e, o; data.remote = remote; e = $('#prefs-dialog'); data.elements.root = e; initTimeDropDown(e.find('#alt-speed-time-begin')[0]); initTimeDropDown(e.find('#alt-speed-time-end')[0]); o = isMobileDevice ? getDefaultMobileOptions() : { width: 350, height: 400 }; o.autoOpen = false; o.show = o.hide = 'fade'; o.close = onDialogClosed; e.tabbedDialog(o); e = e.find('#blocklist-update-button'); data.elements.blocklist_button = e; e.click(onBlocklistUpdateClicked); // listen for user input for (i=0; key=data.keys[i]; ++i) { e = data.elements.root.find('#'+key); switch (e[0].type) { case 'checkbox': case 'radio': case 'select-one': e.change(onControlChanged); break; case 'text': case 'url': case 'email': case 'number': case 'search': e.focus(onControlFocused); e.blur(onControlBlurred); default: break; } } }, getValues = function() { var i, key, val, o={}, keys = data.keys, root = data.elements.root; for (i=0; key=keys[i]; ++i) { val = getValue(root.find('#'+key)); if (val !== null) o[key] = val; } return o; }, onDialogClosed = function() { transmission.hideMobileAddressbar(); $(data.dialog).trigger('closed', getValues()); }; /**** ***** PUBLIC FUNCTIONS ****/ // update the dialog's controls this.set = function (o) { var e, i, key, val, option, keys = data.keys, root = data.elements.root; setBlocklistButtonEnabled(true); for (i=0; key=keys[i]; ++i) { val = o[key]; e = root.find('#'+key); if (key === 'blocklist-size') { // special case -- regular text area e.text('' + val.toStringWithCommas()); } else switch (e[0].type) { case 'checkbox': case 'radio': e.prop('checked', val); setGroupEnabled(key, val); break; case 'text': case 'url': case 'email': case 'number': case 'search': // don't change the text if the user's editing it. // it's very annoying when that happens! if (e[0] !== document.activeElement) e.val(val); break; case 'select-one': e.val(val); break; default: break; } } }; this.show = function () { transmission.hideMobileAddressbar(); setBlocklistButtonEnabled(true); data.remote.checkPort(onPortChecked,this); data.elements.root.dialog('open'); }; this.shouldAddedTorrentsStart = function() { return data.elements.root.find('#start-added-torrents')[0].checked; }; data.dialog = this; initialize (remote); };