1/** 2 * Copyright �� Jordan Lee, Dave Perrett, Malcolm Jarvis and Bruno Bierbaumer 3 * 4 * This file is licensed under the GPLv2. 5 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 6 */ 7 8function PrefsDialog(remote) { 9 10 var data = { 11 dialog: null, 12 remote: null, 13 elements: { }, 14 15 // all the RPC session keys that we have gui controls for 16 keys: [ 17 'alt-speed-down', 18 'alt-speed-time-begin', 19 'alt-speed-time-day', 20 'alt-speed-time-enabled', 21 'alt-speed-time-end', 22 'alt-speed-up', 23 'blocklist-enabled', 24 'blocklist-size', 25 'blocklist-url', 26 'dht-enabled', 27 'download-dir', 28 'encryption', 29 'idle-seeding-limit', 30 'idle-seeding-limit-enabled', 31 'lpd-enabled', 32 'peer-limit-global', 33 'peer-limit-per-torrent', 34 'peer-port', 35 'peer-port-random-on-start', 36 'pex-enabled', 37 'port-forwarding-enabled', 38 'rename-partial-files', 39 'seedRatioLimit', 40 'seedRatioLimited', 41 'speed-limit-down', 42 'speed-limit-down-enabled', 43 'speed-limit-up', 44 'speed-limit-up-enabled', 45 'start-added-torrents', 46 'utp-enabled' 47 ], 48 49 // map of keys that are enabled only if a 'parent' key is enabled 50 groups: { 51 'alt-speed-time-enabled': ['alt-speed-time-begin', 52 'alt-speed-time-day', 53 'alt-speed-time-end' ], 54 'blocklist-enabled': ['blocklist-url', 55 'blocklist-update-button' ], 56 'idle-seeding-limit-enabled': [ 'idle-seeding-limit' ], 57 'seedRatioLimited': [ 'seedRatioLimit' ], 58 'speed-limit-down-enabled': [ 'speed-limit-down' ], 59 'speed-limit-up-enabled': [ 'speed-limit-up' ] 60 } 61 }, 62 63 initTimeDropDown = function(e) 64 { 65 var i, hour, mins, value, content; 66 67 for (i=0; i<24*4; ++i) { 68 hour = parseInt(i/4, 10); 69 mins = ((i%4) * 15); 70 value = i * 15; 71 content = hour + ':' + (mins || '00'); 72 e.options[i] = new Option(content, value); 73 } 74 }, 75 76 onPortChecked = function(response) 77 { 78 var is_open = response['arguments']['port-is-open'], 79 text = 'Port is <b>' + (is_open ? 'Open' : 'Closed') + '</b>', 80 e = data.elements.root.find('#port-label'); 81 setInnerHTML(e[0],text); 82 }, 83 84 setGroupEnabled = function(parent_key, enabled) 85 { 86 var i, key, keys, root; 87 88 if (parent_key in data.groups) 89 { 90 root = data.elements.root, 91 keys = data.groups[parent_key]; 92 93 for (i=0; key=keys[i]; ++i) 94 root.find('#'+key).attr('disabled',!enabled); 95 } 96 }, 97 98 onBlocklistUpdateClicked = function () 99 { 100 data.remote.updateBlocklist(); 101 setBlocklistButtonEnabled(false); 102 }, 103 setBlocklistButtonEnabled = function(b) 104 { 105 var e = data.elements.blocklist_button; 106 e.attr('disabled',!b); 107 e.val(b ? 'Update' : 'Updating...'); 108 }, 109 110 getValue = function(e) 111 { 112 var str; 113 114 switch (e[0].type) 115 { 116 case 'checkbox': 117 case 'radio': 118 return e.prop('checked'); 119 120 case 'text': 121 case 'url': 122 case 'email': 123 case 'number': 124 case 'search': 125 case 'select-one': 126 str = e.val(); 127 if( parseInt(str,10).toString() === str) 128 return parseInt(str,10); 129 if( parseFloat(str).toString() === str) 130 return parseFloat(str); 131 return str; 132 133 default: 134 return null; 135 } 136 }, 137 138 /* this callback is for controls whose changes can be applied 139 immediately, like checkboxs, radioboxes, and selects */ 140 onControlChanged = function(ev) 141 { 142 var o = {}; 143 o[ev.target.id] = getValue($(ev.target)); 144 data.remote.savePrefs(o); 145 }, 146 147 /* these two callbacks are for controls whose changes can't be applied 148 immediately -- like a text entry field -- because it takes many 149 change events for the user to get to the desired result */ 150 onControlFocused = function(ev) 151 { 152 data.oldValue = getValue($(ev.target)); 153 }, 154 onControlBlurred = function(ev) 155 { 156 var newValue = getValue($(ev.target)); 157 if (newValue !== data.oldValue) 158 { 159 var o = {}; 160 o[ev.target.id] = newValue; 161 data.remote.savePrefs(o); 162 delete data.oldValue; 163 } 164 }, 165 166 getDefaultMobileOptions = function() 167 { 168 return { 169 width: $(window).width(), 170 height: $(window).height(), 171 position: [ 'left', 'top' ] 172 }; 173 }, 174 175 initialize = function (remote) 176 { 177 var i, key, e, o; 178 179 data.remote = remote; 180 181 e = $('#prefs-dialog'); 182 data.elements.root = e; 183 184 initTimeDropDown(e.find('#alt-speed-time-begin')[0]); 185 initTimeDropDown(e.find('#alt-speed-time-end')[0]); 186 187 o = isMobileDevice 188 ? getDefaultMobileOptions() 189 : { width: 350, height: 400 }; 190 o.autoOpen = false; 191 o.show = o.hide = 'fade'; 192 o.close = onDialogClosed; 193 e.tabbedDialog(o); 194 195 e = e.find('#blocklist-update-button'); 196 data.elements.blocklist_button = e; 197 e.click(onBlocklistUpdateClicked); 198 199 // listen for user input 200 for (i=0; key=data.keys[i]; ++i) 201 { 202 e = data.elements.root.find('#'+key); 203 switch (e[0].type) 204 { 205 case 'checkbox': 206 case 'radio': 207 case 'select-one': 208 e.change(onControlChanged); 209 break; 210 211 case 'text': 212 case 'url': 213 case 'email': 214 case 'number': 215 case 'search': 216 e.focus(onControlFocused); 217 e.blur(onControlBlurred); 218 219 default: 220 break; 221 } 222 } 223 }, 224 225 getValues = function() 226 { 227 var i, key, val, o={}, 228 keys = data.keys, 229 root = data.elements.root; 230 231 for (i=0; key=keys[i]; ++i) { 232 val = getValue(root.find('#'+key)); 233 if (val !== null) 234 o[key] = val; 235 } 236 237 return o; 238 }, 239 240 onDialogClosed = function() 241 { 242 transmission.hideMobileAddressbar(); 243 244 $(data.dialog).trigger('closed', getValues()); 245 }; 246 247 /**** 248 ***** PUBLIC FUNCTIONS 249 ****/ 250 251 // update the dialog's controls 252 this.set = function (o) 253 { 254 var e, i, key, val, option, 255 keys = data.keys, 256 root = data.elements.root; 257 258 setBlocklistButtonEnabled(true); 259 260 for (i=0; key=keys[i]; ++i) 261 { 262 val = o[key]; 263 e = root.find('#'+key); 264 265 if (key === 'blocklist-size') 266 { 267 // special case -- regular text area 268 e.text('' + val.toStringWithCommas()); 269 } 270 else switch (e[0].type) 271 { 272 case 'checkbox': 273 case 'radio': 274 e.prop('checked', val); 275 setGroupEnabled(key, val); 276 break; 277 case 'text': 278 case 'url': 279 case 'email': 280 case 'number': 281 case 'search': 282 // don't change the text if the user's editing it. 283 // it's very annoying when that happens! 284 if (e[0] !== document.activeElement) 285 e.val(val); 286 break; 287 case 'select-one': 288 e.val(val); 289 break; 290 default: 291 break; 292 } 293 } 294 }; 295 296 this.show = function () 297 { 298 transmission.hideMobileAddressbar(); 299 300 setBlocklistButtonEnabled(true); 301 data.remote.checkPort(onPortChecked,this); 302 data.elements.root.dialog('open'); 303 }; 304 305 this.close = function () 306 { 307 transmission.hideMobileAddressbar(); 308 data.elements.root.dialog('close'); 309 }, 310 311 this.shouldAddedTorrentsStart = function() 312 { 313 return data.elements.root.find('#start-added-torrents')[0].checked; 314 }; 315 316 data.dialog = this; 317 initialize (remote); 318}; 319