diff --git a/deluge/ui/webui/templates/ajax/TODO b/deluge/ui/webui/templates/ajax/TODO new file mode 100644 index 000000000..378a06ad4 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/TODO @@ -0,0 +1,8 @@ +[features] + Improve torrent adding to include options, file upload + Implement the Options Menu + Implement the connection manager + +[bugs] + If a torrent is removed from the core it continues to try and get details + if it was the selected torrent. diff --git a/deluge/ui/webui/templates/ajax/index.html b/deluge/ui/webui/templates/ajax/index.html new file mode 100644 index 000000000..219c094ed --- /dev/null +++ b/deluge/ui/webui/templates/ajax/index.html @@ -0,0 +1,51 @@ + + + + Deluge: AJAX UI (alpha) + + + + + + + + + + + + +
+
+ +
Deluge
+
AJAX UI (alpha)
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + diff --git a/deluge/ui/webui/templates/ajax/meta.cfg b/deluge/ui/webui/templates/ajax/meta.cfg new file mode 100644 index 000000000..3f5cc7a20 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/meta.cfg @@ -0,0 +1,9 @@ +{ + 'authors':['Damien Churchill '], + 'inherits':['classic'], + 'comment':"""Advanced AJAX enabled template. + Making use of the new json/rpc api to update + the data on the fly. + """ +} + diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_bandwidth.html b/deluge/ui/webui/templates/ajax/render/html/preferences_bandwidth.html new file mode 100644 index 000000000..a333d2d89 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_bandwidth.html @@ -0,0 +1,16 @@ +
+
+ $_('Global Bandwidth Usage') +
+
+
+
+
+
+ $_('Per Torrent Bandwidth Usage') +
+
+
+
+
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_daemon.html b/deluge/ui/webui/templates/ajax/render/html/preferences_daemon.html new file mode 100644 index 000000000..45a2b4094 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_daemon.html @@ -0,0 +1,14 @@ +
+
+ $_('Port') +
+
+
+ $_('Connections') + +
+
+ $_('Other') + +
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_download.html b/deluge/ui/webui/templates/ajax/render/html/preferences_download.html new file mode 100644 index 000000000..05a914e41 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_download.html @@ -0,0 +1,24 @@ +
+
+ $_('Download Location') +
+ +
+
+ $_('Torrent File Copy') + +
+
+ $_('Auto Add Folder') + +
+
+ $_('Allocation') + + +
+
+ $_('Options') + +
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_network.html b/deluge/ui/webui/templates/ajax/render/html/preferences_network.html new file mode 100644 index 000000000..a6b86047b --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_network.html @@ -0,0 +1,40 @@ +
+
+ $_('Ports') +
+ +
+
+ $_('DHT') + +
+
+ $_('Network Extras') + + + + +
+
+ $_('Encryption') + +
+ +
+ +
+ +
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_queue.html b/deluge/ui/webui/templates/ajax/render/html/preferences_queue.html new file mode 100644 index 000000000..be67fd78b --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_queue.html @@ -0,0 +1,21 @@ +
+
+ $_('General') + +
+
+ $_('Active Torrents') +
+
+
+ +
+
+ $_('Seeding') +
+
+
+ + +
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/preferences_webui.html b/deluge/ui/webui/templates/ajax/render/html/preferences_webui.html new file mode 100644 index 000000000..10edb27dd --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/preferences_webui.html @@ -0,0 +1,31 @@ +
+
+ $_('Template') + +
+ +
+ +
+
+
+
+ $_('Server') +
+
+
+ $_('Password') +
+
+
+
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/tab_details.html b/deluge/ui/webui/templates/ajax/render/html/tab_details.html new file mode 100644 index 000000000..b9f62b43e --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/tab_details.html @@ -0,0 +1,10 @@ +
+
$_('Name'):
+
$_('Hash'):
+
$_('Path'):
+
$_('Total Size'):
+
$_('# of files'):
+
$_('Status'):
+
$_('Tracker'):
+
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/tab_files.html b/deluge/ui/webui/templates/ajax/render/html/tab_files.html new file mode 100644 index 000000000..d412c2ba9 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/tab_files.html @@ -0,0 +1,13 @@ +
+ + + + + + + + + + +
$_('Filename')$_('Size')$_('Progress')$_('Priority')
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/tab_options.html b/deluge/ui/webui/templates/ajax/render/html/tab_options.html new file mode 100644 index 000000000..f4a14c0c4 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/tab_options.html @@ -0,0 +1,26 @@ +
+
+ $_('Bandwidth') + KiB/s
+ KiB/s
+
+
+
+ +
+ $_('Queue') +
+
+
+
+ +
+ $_('General') +
+
+
+ +
+
+
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/tab_peers.html b/deluge/ui/webui/templates/ajax/render/html/tab_peers.html new file mode 100644 index 000000000..f800793cd --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/tab_peers.html @@ -0,0 +1,14 @@ +
+ + + + + + + + + + + +
$_('Address')$_('Client')$_('Down Speed')$_('Up Speed')
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/tab_statistics.html b/deluge/ui/webui/templates/ajax/render/html/tab_statistics.html new file mode 100644 index 000000000..9a6871738 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/tab_statistics.html @@ -0,0 +1,25 @@ +
+
$_('Downloaded'):
+
$_('Uploaded'):
+
$_('Next Announce'):
+
$_('Tracker Status'):
+
+
+
$_('Speed'):
+
$_('Speed'):
+
$_('ETA'):
+
$_('Pieces'):
+
+
+
$_('Seeders'):
+
$_('Peers'):
+
$_('Availability'):
+
$_('Auto Managed'):
+
+
+
$_('Active Time'):
+
$_('Seeding Time'):
+
$_('Seed Rank'):
+
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/window_add_torrent.html b/deluge/ui/webui/templates/ajax/render/html/window_add_torrent.html new file mode 100644 index 000000000..c35d77604 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/window_add_torrent.html @@ -0,0 +1,15 @@ +
+
+ $_('From File') +
+ +
+
+ +
+
+ $_('From Url') +
+ +
+
diff --git a/deluge/ui/webui/templates/ajax/render/html/window_preferences.html b/deluge/ui/webui/templates/ajax/render/html/window_preferences.html new file mode 100644 index 000000000..986ea6d3c --- /dev/null +++ b/deluge/ui/webui/templates/ajax/render/html/window_preferences.html @@ -0,0 +1,13 @@ +
+
+

$_('Categories')

+
    +
+
+
+

+
+
+ +
+
diff --git a/deluge/ui/webui/templates/ajax/static/css/Roar.css b/deluge/ui/webui/templates/ajax/static/css/Roar.css new file mode 100644 index 000000000..efa6f539d --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/css/Roar.css @@ -0,0 +1,55 @@ +.roar-body +{ + position: absolute; + font: 12px/14px "Lucida Grande", Arial, Helvetica, Verdana, sans-serif; + color: #fff; + text-align: left; + z-index: 999; +} + +.roar +{ + position: absolute; + width: 300px; + cursor: pointer; +} +.roar-bg +{ + position: absolute; + z-index: 1000; + width: 100%; + height: 100%; + left: 0; + top: 0; + background-color: #000; + -moz-border-radius: 10px; + -webkit-border-radius: 5px; + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); +} +.roar-body-ugly .roar +{ + background-color: #333; +} +.roar-body-ugly .roar-bg +{ + display: none; +} +.roar h3 +{ + position: relative; + padding: 15px 10px 0; + margin: 0; + border: 0; + font-size: 13px; + color: #fff; + z-index: 1002; +} +.roar p +{ + position: relative; + padding: 10px 10px 15px; + margin: 0; + font-size: 12px; + color: #fff; + z-index: 1002; +} \ No newline at end of file diff --git a/deluge/ui/webui/templates/ajax/static/css/mooui.css b/deluge/ui/webui/templates/ajax/static/css/mooui.css new file mode 100644 index 000000000..4a4878c70 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/css/mooui.css @@ -0,0 +1,156 @@ +/* + * Stylesheet: Widgets.PopupMenu.css + * + * Depends: [] + */ + +ul.mooui-menu { + position: absolute; + z-index: 1000; + padding: 0; +} + +ul.mooui-menu li { + position: relative; + list-style: none; + margin: 1px; + padding-left: 2px; + padding-right: 10px; + line-height: 20px; + height: 20px; + background: no-repeat 2px; +} + +ul.mooui-menu li.mooui-menu-icon { + padding-left: 20px; +} + +ul.mooui-menu li.mooui-menu-sep { + height: 1px; +} + +ul.mooui-menu li.mooui-menu-toggle input { + cursor: pointer; +} + +ul.mooui-menu li.mooui-menu-toggle span span { + position: relative; + top: -3px; +} + +ul.mooui-menu li > ul { + display: none; + position: relative; + top: -20px; + width: 400px; +} + +ul.mooui-menu li:hover { + cursor: pointer; +} + +ul.mooui-menu li:hover > ul { + display: block; +} + +ul.mooui-menu li.mooui-menu-sep:hover { + cursor: default; +} + +/* + * Stylesheet: Widgets.ProgressBar.css + * + * Depends: [] + */ + +.mooui-progressbar span { + text-align: center; + float: left; +} + +/* + * Stylesheet: Widgets.SplitPane.css + * + * Depends: [] + */ + +.mooui-pane { + position: absolute; +} + +.mooui-splitter { + position: absolute; + background: Gray; +} + +.mooui-splitter-vertical { + cursor: e-resize; +} + +.mooui-splitter-horizontal { + cursor: n-resize; +} + +/* + * Stylesheet: Widgets.SplitPane.css + * + * Depends: [] + */ + +.mooui-tabs ul { + list-style: none; + padding: 0; + margin: 0; +} + +.mooui-tabs li { + float: left; + padding: 4px; + cursor: pointer; + height: 14px; + -moz-border-radius: 5px 5px 0 0; +} + +.mooui-tabs div { + clear: left; +} + +.mooui-tabpage { + display: none; +} + +.mooui-tabpage-active { + display: block; +} + +/* + * Stylesheet: Widgets.Window.css + * + * Depends: [] + */ + +.mooui-window { + position: fixed; + top: 0px; + left: 200px; + z-index: 100; +} + +.mooui-window-title { + margin: 10px; + cursor: pointer; + float: left; +} + +.mooui-window-close { + float: right; + width: 16px; + height: 16px; + margin: 5px; + cursor: pointer; +} + +.mooui-window-content { + clear: both; + padding: 5px; +} \ No newline at end of file diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/gtk-edit.png b/deluge/ui/webui/templates/ajax/static/icons/16/gtk-edit.png new file mode 100644 index 000000000..5200e14f5 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/gtk-edit.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/gtk-yes.png b/deluge/ui/webui/templates/ajax/static/icons/16/gtk-yes.png new file mode 100644 index 000000000..f04fc1612 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/gtk-yes.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/network-idle.png b/deluge/ui/webui/templates/ajax/static/icons/16/network-idle.png new file mode 100644 index 000000000..0efee57e5 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/network-idle.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/view-refresh.png b/deluge/ui/webui/templates/ajax/static/icons/16/view-refresh.png new file mode 100644 index 000000000..3fd71d6e5 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/view-refresh.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-ascending.png b/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-ascending.png new file mode 100644 index 000000000..beba44948 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-ascending.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-descending.png b/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-descending.png new file mode 100644 index 000000000..ea4abc4d6 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/16/view-sort-descending.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/add.png b/deluge/ui/webui/templates/ajax/static/icons/32/add.png new file mode 100644 index 000000000..2acdd8f51 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/add.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/connections.png b/deluge/ui/webui/templates/ajax/static/icons/32/connections.png new file mode 100644 index 000000000..4137b3c3b Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/connections.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/deluge.png b/deluge/ui/webui/templates/ajax/static/icons/32/deluge.png new file mode 100644 index 000000000..2d272f2ea Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/deluge.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/down.png b/deluge/ui/webui/templates/ajax/static/icons/32/down.png new file mode 100644 index 000000000..dce3f15ef Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/down.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/options.png b/deluge/ui/webui/templates/ajax/static/icons/32/options.png new file mode 100644 index 000000000..3ec71a357 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/options.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/pause.png b/deluge/ui/webui/templates/ajax/static/icons/32/pause.png new file mode 100644 index 000000000..1e9f4d535 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/pause.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/remove.png b/deluge/ui/webui/templates/ajax/static/icons/32/remove.png new file mode 100644 index 000000000..c5524f728 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/remove.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/resume.png b/deluge/ui/webui/templates/ajax/static/icons/32/resume.png new file mode 100644 index 000000000..66f32d89b Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/resume.png differ diff --git a/deluge/ui/webui/templates/ajax/static/icons/32/up.png b/deluge/ui/webui/templates/ajax/static/icons/32/up.png new file mode 100644 index 000000000..afb307b18 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/icons/32/up.png differ diff --git a/deluge/ui/webui/templates/ajax/static/images/spacer.gif b/deluge/ui/webui/templates/ajax/static/images/spacer.gif new file mode 100644 index 000000000..91ee09790 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/images/spacer.gif differ diff --git a/deluge/ui/webui/templates/ajax/static/js/Roar.js b/deluge/ui/webui/templates/ajax/static/js/Roar.js new file mode 100644 index 000000000..78580f8c0 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/Roar.js @@ -0,0 +1,166 @@ +/** + * Roar - Notifications + * + * Inspired by Growl + * + * @version 1.0.1 + * + * @license MIT-style license + * @author Harald Kirschner + * @copyright Author + */ + +var Roar = new Class({ + + Implements: [Options, Events, Chain], + + options: { + duration: 3000, + position: 'upperLeft', + container: null, + bodyFx: null, + itemFx: null, + margin: {x: 10, y: 10}, + offset: 10, + className: 'roar', + onShow: $empty, + onHide: $empty, + onRender: $empty + }, + + initialize: function(options) { + this.setOptions(options); + this.items = []; + this.container = $(this.options.container) || document; + }, + + alert: function(title, message, options) { + var params = Array.link(arguments, {title: String.type, message: String.type, options: Object.type}); + var items = [new Element('h3', {'html': $pick(params.title, '')})]; + if (params.message) items.push(new Element('p', {'html': params.message})); + return this.inject(items, params.options); + }, + + inject: function(elements, options) { + if (!this.body) this.render(); + options = options || {}; + + var offset = [-this.options.offset, 0]; + var last = this.items.getLast(); + if (last) { + offset[0] = last.retrieve('roar:offset'); + offset[1] = offset[0] + last.offsetHeight + this.options.offset; + } + var to = {'opacity': 1}; + to[this.align.y] = offset; + + var item = new Element('div', { + 'class': this.options.className, + 'opacity': 0 + }).adopt( + new Element('div', { + 'class': 'roar-bg', + 'opacity': 0.7 + }), + elements + ); + + item.setStyle(this.align.x, 0).store('roar:offset', offset[1]).set('morph', $merge({ + unit: 'px', + link: 'cancel', + onStart: Chain.prototype.clearChain, + transition: Fx.Transitions.Back.easeOut + }, this.options.itemFx)); + + var remove = this.remove.create({ + bind: this, + arguments: [item], + delay: 10 + }); + this.items.push(item.addEvent('click', remove)); + + if (this.options.duration) { + var over = false; + var trigger = (function() { + trigger = null; + if (!over) remove(); + }).delay(this.options.duration); + item.addEvents({ + mouseover: function() { + over = true; + }, + mouseout: function() { + over = false; + if (!trigger) remove(); + } + }); + } + item.inject(this.body).morph(to); + return this.fireEvent('onShow', [item, this.items.length]); + }, + + remove: function(item) { + var index = this.items.indexOf(item); + if (index == -1) return this; + this.items.splice(index, 1); + item.removeEvents(); + var to = {opacity: 0}; + to[this.align.y] = item.getStyle(this.align.y).toInt() - item.offsetHeight - this.options.offset; + item.morph(to).get('morph').chain(item.destroy.bind(item)); + return this.fireEvent('onHide', [item, this.items.length]).callChain(item); + }, + + empty: function() { + while (this.items.length) this.remove(this.items[0]); + return this; + }, + + render: function() { + this.position = this.options.position; + if ($type(this.position) == 'string') { + var position = {x: 'center', y: 'center'}; + this.align = {x: 'left', y: 'top'}; + if ((/left|west/i).test(this.position)) position.x = 'left'; + else if ((/right|east/i).test(this.position)) this.align.x = position.x = 'right'; + if ((/upper|top|north/i).test(this.position)) position.y = 'top'; + else if ((/bottom|lower|south/i).test(this.position)) this.align.y = position.y = 'bottom'; + this.position = position; + } + this.body = new Element('div', {'class': 'roar-body'}).inject(document.body); + if (Browser.Engine.trident4) this.body.addClass('roar-body-ugly'); + this.moveTo = this.body.setStyles.bind(this.body); + this.reposition(); + if (this.options.bodyFx) { + var morph = new Fx.Morph(this.body, $merge({ + unit: 'px', + chain: 'cancel', + transition: Fx.Transitions.Circ.easeOut + }, this.options.bodyFx)); + this.moveTo = morph.start.bind(morph); + } + var repos = this.reposition.bind(this); + window.addEvents({ + scroll: repos, + resize: repos + }); + this.fireEvent('onRender', this.body); + }, + + reposition: function() { + var max = document.getCoordinates(), scroll = document.getScroll(), margin = this.options.margin; + max.left += scroll.x; + max.right += scroll.x; + max.top += scroll.y; + max.bottom += scroll.y; + var rel = ($type(this.container) == 'element') ? this.container.getCoordinates() : max; + this.moveTo({ + left: (this.position.x == 'right') + ? (Math.min(rel.right, max.right) - margin.x) + : (Math.max(rel.left, max.left) + margin.x), + top: (this.position.y == 'bottom') + ? (Math.min(rel.bottom, max.bottom) - margin.y) + : (Math.max(rel.top, max.top) + margin.y) + }); + } + +}); \ No newline at end of file diff --git a/deluge/ui/webui/templates/ajax/static/js/Rpc.js b/deluge/ui/webui/templates/ajax/static/js/Rpc.js new file mode 100644 index 000000000..2d068da19 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/Rpc.js @@ -0,0 +1,83 @@ +/* + * Script: Rpc.js + * A JSON-RPC proxy + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +/* + * A class that creates a proxy for sending remote procedure calls over JSON + */ +JSON.RPC = new Class({ + Implements: Options, + + options: { + async: true, + methods: [] + }, + + initialize: function(url, options) { + this.setOptions(options) + this.url = url + if (this.options.methods.length == 0) { + this._execute('system.listMethods', {async: false}).each(function(method) { + this[method] = function() { + var options = this._parseargs(arguments) + return this._execute(method, options) + }.bind(this) + }, this) + } + }, + + _parseargs: function(args) { + var params = $A(args), options = params.getLast() + if ($type(options) == 'object') { + var option_keys = ['async', 'onRequest', 'onComplete', + 'onSuccess', 'onFailure', 'onException', 'onCancel'], keys = + new Hash(options).getKeys(), is_option = false + + option_keys.each(function(key) { + if (keys.contains(key)) { + is_option = true + } + }) + + if (is_option) { + params.erase(options) + } else { + options = {} + } + } else { options = {} } + options.params = params + return options + }, + + _execute: function(method, options) { + options = $pick(options, {}) + options.params = $pick(options.params, []) + options.async = $pick(options.async, this.options.async) + + data = JSON.encode({ + method: method, + params: options.params, + id: 1 + }) + + var request = new Request.JSON({ + url: this.url, + async: options.async, + onRequest: options.onRequest, + onComplete: options.onComplete, + onSuccess: function(response) { + if (options.onSuccess) {options.onSuccess(response.result)} + }, + onFailure: options.onFailure, + onException: options.onException, + onCancel: options.onCancel + }).send(data) + if (!options.async) { + return request.response.json.result + } + } +}) diff --git a/deluge/ui/webui/templates/ajax/static/js/deluge.js b/deluge/ui/webui/templates/ajax/static/js/deluge.js new file mode 100644 index 000000000..2af41cf90 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/deluge.js @@ -0,0 +1,302 @@ +Deluge.UI = { + initialize: function() { + this.torrents = {}; + this.torrentIds = []; + Deluge.Client = new JSON.RPC('/json/rpc'); + + var theme = Cookie.read('theme'); + if (theme) this.setTheme(theme); + else this.setTheme('classic'); + + this.bound = { + updated: this.updated.bindWithEvent(this), + resized: this.resized.bindWithEvent(this), + toolbar_click: this.toolbar_click.bindWithEvent(this), + file_priorities: this.file_priorities.bindWithEvent(this), + labelsChanged: this.labelsChanged.bindWithEvent(this) + }; + this.loadUi.delay(100, this); + }, + + loadUi: function() { + this.vbox = new Widgets.VBox('page', {expand: true}); + + this.toolbar = new Deluge.Widgets.Toolbar(); + this.addWindow = new Deluge.Widgets.AddWindow(); + this.prefsWindow = new Deluge.Widgets.PreferencesWindow(); + + this.statusbar = new Deluge.Widgets.StatusBar(); + this.labels = new Deluge.Widgets.Labels() + this.details = new Deluge.Widgets.Details() + + this.initialize_grid() + + this.split_horz = new Widgets.SplitPane('top', this.labels, this.grid, { + pane1: {min: 150}, + pane2: {min: 100, expand: true} + }); + var details = $W('details') + this.split_vert = new Widgets.SplitPane('main', this.split_horz, details, { + direction: 'vertical', + pane1: {min: 100, expand: true}, + pane2: {min: 200} + }); + + this.vbox.addBox(this.toolbar, {fixed: true}); + this.vbox.addBox(this.split_vert); + this.vbox.addBox(this.statusbar, {fixed: true}); + this.vbox.calculatePositions(); + this.details.expand() + + this.toolbar.addEvent('button_click', this.bound.toolbar_click); + this.details.addEvent('filesAction', this.bound.file_priorities) + this.labels.addEvent('stateChanged', this.bound.labelsChanged) + details.addEvent('resize', function(e) { + this.details.expand() + }.bindWithEvent(this)) + + window.addEvent('resize', this.bound.resized); + Deluge.UI.update(); + }, + + initialize_grid: function() { + this.grid = new Deluge.Widgets.TorrentGrid('torrents') + + var menu = new Widgets.PopupMenu() + menu.add([ + {type:'text',action:'pause',text:'Pause',icon:'/static/images/tango/pause.png'}, + {type:'text',action:'resume',text:'Resume',icon:'/static/images/tango/start.png'}, + {type:'seperator'}, + {type:'submenu',text:'Options',icon:'/static/images/tango/preferences-system.png',items: [ + {type:'submenu',text:'D/L Speed Limit',icon:'/pixmaps/downloading16.png',items: [ + {type:'text',action:'max_download_speed',value:5,text:'5 KiB/s'}, + {type:'text',action:'max_download_speed',value:10,text:'10 KiB/s'}, + {type:'text',action:'max_download_speed',value:30,text:'30 KiB/s'}, + {type:'text',action:'max_download_speed',value:80,text:'80 KiB/s'}, + {type:'text',action:'max_download_speed',value:300,text:'300 KiB/s'}, + {type:'text',action:'max_download_speed',value:-1,text:'Unlimited'} + ]}, + {type:'submenu',text:'U/L Speed Limit',icon:'/pixmaps/seeding16.png',items: [ + {type:'text',action:'max_upload_speed',value:5,text:'5 KiB/s'}, + {type:'text',action:'max_upload_speed',value:10,text:'10 KiB/s'}, + {type:'text',action:'max_upload_speed',value:30,text:'30 KiB/s'}, + {type:'text',action:'max_upload_speed',value:80,text:'80 KiB/s'}, + {type:'text',action:'max_upload_speed',value:300,text:'300 KiB/s'}, + {type:'text',action:'max_upload_speed',value:-1,text:'Unlimited'} + ]}, + {type:'submenu',text:'Connection Limit',icon:'/static/images/tango/connections.png',items: [ + {type:'text',action:'max_connections',value:50,text:'50'}, + {type:'text',action:'max_connections',value:100,text:'100'}, + {type:'text',action:'max_connections',value:200,text:'200'}, + {type:'text',action:'max_connections',value:300,text:'300'}, + {type:'text',action:'max_connections',value:500,text:'500'}, + {type:'text',action:'max_connections',value:-1,text:'Unlimited'} + ]}, + {type:'submenu',text:'Upload Slot Limit',icon:'/template/static/icons/16/view-sort-ascending.png',items: [ + {type:'text',action:'max_upload_slots',value:0,text:'0'}, + {type:'text',action:'max_upload_slots',value:1,text:'1'}, + {type:'text',action:'max_upload_slots',value:2,text:'2'}, + {type:'text',action:'max_upload_slots',value:3,text:'3'}, + {type:'text',action:'max_upload_slots',value:5,text:'5'}, + {type:'text',action:'max_upload_slots',value:-1,text:'Unlimited'} + ]}, + {type:'toggle',action:'auto_managed',value:false,text:'Auto Managed'} + ]}, + {type:'seperator'}, + {type:'submenu',text:'Queue',icon:'/template/static/icons/16/view-sort-descending.png',items:[ + {type:'text',action:'top',text:'Top',icon:'/static/images/tango/go-top.png'}, + {type:'text',action:'up',text:'Up',icon:'/static/images/tango/queue-up.png'}, + {type:'text',action:'down',text:'Down',icon:'/static/images/tango/queue-down.png'}, + {type:'text',action:'bottom',text:'Bottom',icon:'/static/images/tango/go-bottom.png'} + ]}, + {type: 'seperator'}, + {type:'text',action:'update_tracker',text:'Update Tracker',icon:'/template/static/icons/16/view-refresh.png'}, + {type:'text',action:'edit_trackers',text:'Edit Trackers',icon:'/template/static/icons/16/gtk-edit.png'}, + {type:'seperator'}, + {type:'submenu',action:'remove',value:0,text:'Remove Torrent',icon:'/static/images/tango/list-remove.png', items:[ + {type:'text',action:'remove',value:0,text:'From Session'}, + {type:'text',action:'remove',value:1,text:'... and delete Torrent file'}, + {type:'text',action:'remove',value:2,text:'... and delete Downloaded files'}, + {type:'text',action:'remove',value:3,text:'... and delete All files'} + ]}, + {type:'seperator'}, + {type:'text',action:'force_recheck',text:'Force Recheck',icon:'/static/images/tango/edit-redo.png'}, + {type:'text',action:'move_storage',text:'Move Storage',icon:'/static/images/tango/move.png'} + ]); + + menu.addEvent('action', function(e) { + this.torrent_action(e.action, e.value) + }.bind(this)) + + this.grid.addEvent('row_menu', function(e) { + e.stop() + var value = this.grid.selectedRow.torrent.is_auto_managed; + menu.items[3].items[4].set(value) + menu.torrent_id = e.row_id + menu.show(e) + }.bindWithEvent(this)) + + this.grid.addEvent('selectedchanged', function(e) { + if ($chk(this.grid.selectedRow)) + this.details.update(this.grid.selectedRow.id); + else + this.details.update(null); + }.bindWithEvent(this)) + }, + + setTheme: function(name, fn) { + this.theme = name; + if (this.themecss) this.themecss.destroy(); + this.themecss = new Asset.css('/template/static/themes/' + name + '/style.css'); + Cookie.write('theme', name); + if (this.vbox) this.vbox.refresh(); + }, + + run: function() { + if (!this.running) { + this.running = this.update.periodical(2000, this); + } + }, + + stop: function() { + if (this.running) { + $clear(this.running); + this.running = false; + } + }, + + update: function() { + filter = {} + if (this.labels.state != 'All') filter.state = this.labels.state + Deluge.Client.update_ui(Deluge.Keys.Grid, filter, { + onSuccess: this.bound.updated + }) + }, + + updated: function(data) { + this.torrents = new Hash(data.torrents); + this.stats = data.stats; + this.filters = data.filters + this.torrents.each(function(torrent, torrent_id) { + torrent.id = torrent_id; + }) + this.grid.update_torrents(this.torrents); + this.statusbar.update(this.stats); + + if ($chk(this.grid.selectedRow)) + this.details.update(this.grid.selectedRow.id); + else + this.details.update(null); + + this.labels.update(this.filters) + }, + + file_priorities: function(event) { + Deluge.Client.get_torrent_status(event.torrentId, ['file_priorities'], { + onSuccess: function(result) { + var priorities = result.file_priorities + priorities.each(function(priority, index) { + if (event.files.contains(index)) priorities[index] = event.action + }) + Deluge.Client.set_torrent_file_priorities(event.torrentId, priorities, { + onSuccess: function(response) { + this.details.update(event.torrentId) + }.bindWithEvent(this) + }) + }.bindWithEvent(this) + }) + }, + + resized: function(event) { + this.vbox.calculatePositions(); + }, + + toolbar_click: function(event) { + this.torrent_action(event.action); + }, + + labelsChanged: function(event) { + this.update() + }, + + torrent_action: function(action, value) { + var torrentIds = this.grid.get_selected_torrents() + switch (action) { + case 'resume': + Deluge.Client.resume_torrent(torrentIds) + break; + case 'pause': + Deluge.Client.pause_torrent(torrentIds) + break; + case 'top': + Deluge.Client.queue_top(torrentIds) + break; + case 'up': + Deluge.Client.queue_up(torrentIds) + break; + case 'down': + Deluge.Client.queue_down(torrentIds) + break; + case 'bottom': + Deluge.Client.queue_bottom(torrentIds) + break; + case 'force_recheck': + Deluge.Client.force_recheck(torrentIds) + break; + case 'update_tracker': + Deluge.Client.force_reannounce(torrentIds) + break; + case 'max_download_speed': + torrentIds.each(function(torrentId) { + Deluge.Client.set_torrent_max_download_speed(torrentId, value.toInt()) + }) + break; + case 'max_upload_speed': + torrentIds.each(function(torrentId) { + Deluge.Client.set_torrent_max_upload_speed(torrentId, value.toInt()) + }) + break; + case 'max_connections': + torrentIds.each(function(torrentId) { + Deluge.Client.set_torrent_max_connections(torrentId, value.toInt()) + }) + break; + case 'max_upload_slots': + torrentIds.each(function(torrentId) { + Deluge.Client.set_torrent_max_upload_slots(torrentId, value.toInt()) + }) + break; + case 'auto_managed': + torrentIds.each(function(torrentId) { + Deluge.Client.set_torrent_auto_managed(torrentId, value) + }) + break; + case 'add': + this.addWindow.show() + break; + case 'remove': + var removeTorrent = false, removeFiles = false; + if (value == 1) removeTorrent = true; + else if (value == 2) removeFiles = true; + else if (value > 3) { + removeTorrent = true; + removeFiles = true; + } + Deluge.Client.remove_torrent(torrentIds, removeTorrent, removeFiles); + break; + case 'preferences': + this.prefsWindow.show() + break; + default: + break; + } + this.update() + } +}; + +window.addEvent('domready', function(e) { + Deluge.UI.initialize(); + Deluge.UI.run(); +}); + + diff --git a/deluge/ui/webui/templates/ajax/static/js/deluge.widgets.js b/deluge/ui/webui/templates/ajax/static/js/deluge.widgets.js new file mode 100644 index 000000000..ba8a21233 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/deluge.widgets.js @@ -0,0 +1,909 @@ +/* + * Script: deluge.widgets.js + * Collection of widgets for use in the deluge web-ui + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +var Deluge = $empty; + +Deluge.Keys = { + Grid: ['queue', 'name', 'total_size', 'state', 'progress', + 'num_seeds', 'total_seeds', 'num_peers', 'total_peers', + 'download_payload_rate', 'upload_payload_rate', 'eta', 'ratio', + 'distributed_copies' ,'is_auto_managed'], + + Statistics: ['total_done', 'total_payload_download', 'total_uploaded', + 'total_payload_upload', 'next_announce', 'tracker_status', + 'num_pieces', 'piece_length', 'is_auto_managed', 'active_time', + 'seeding_time', 'seed_rank'], + + Files: ['files', 'file_progress', 'file_priorities'], + + Peers: ['peers', 'seeds'], + + Details: ['name', 'save_path', 'total_size', 'num_files', 'tracker_status', 'tracker'], + + Options: ['max_download_speed', 'max_upload_speed', 'max_connections', + 'max_upload_slots','is_auto_managed', 'stop_at_ratio', + 'stop_ratio', 'remove_at_ratio', 'private', 'prioritize_first_last'] +} + +Deluge.Keys.Statistics.extend(Deluge.Keys.Grid) + +Deluge.Widgets = $empty; + +Deluge.Widgets.Details = new Class({ + Extends: Widgets.Tabs, + + initialize: function() { + this.parent($$('#details .mooui-tabs')[0]) + + this.statistics = new Deluge.Widgets.StatisticsPage() + this.details = new Deluge.Widgets.DetailsPage() + this.files = new Deluge.Widgets.FilesPage() + this.peers = new Deluge.Widgets.PeersPage() + this.options = new Deluge.Widgets.OptionsPage() + + this.addPage(this.statistics) + this.addPage(this.details) + this.addPage(this.files) + this.addPage(this.peers) + this.addPage(this.options) + this.addEvent('pageChanged', function(e) { + this.update(this.torrentId); + }.bindWithEvent(this)); + this.addEvent('resize', this.resized.bindWithEvent(this)) + + this.files.addEvent('menuAction', function(e) { + files = [] + this.files.grid.get_selected().each(function(file) { + files.push(file.fileIndex) + }) + e.files = files + this.fireEvent('filesAction', e) + }.bindWithEvent(this)) + }, + + keys: { + 0: Deluge.Keys.Statistics, + 1: Deluge.Keys.Details, + 2: Deluge.Keys.Files, + 3: Deluge.Keys.Peers, + 4: Deluge.Keys.Options + }, + + update: function(torrentId) { + this.torrentId = torrentId + if (!this.torrentId) return + var keys = this.keys[this.currentPage], page = this.pages[this.currentPage]; + Deluge.Client.get_torrent_status(torrentId, keys, { + onSuccess: function(torrent) { + torrent.id = torrentId + if (page.update) page.update(torrent) + }.bindWithEvent(this) + }) + }, + + resized: function(event) { + this.pages.each(function(page) { + page.getSizeModifiers() + page.sets({ + width: event.width - page.element.modifiers.x, + height: event.height - page.element.modifiers.y - 28 + }) + }) + } +}); + +Deluge.Widgets.StatisticsPage = new Class({ + Extends: Widgets.TabPage, + + options: { + url: '/template/render/html/tab_statistics.html' + }, + + initialize: function() { + this.parent('Statistics') + }, + + update: function(torrent) { + var data = { + downloaded: torrent.total_done.toBytes()+' ('+torrent.total_payload_download.toBytes()+')', + uploaded: torrent.total_uploaded.toBytes()+' ('+torrent.total_payload_upload.toBytes()+')', + share: torrent.ratio.toFixed(3), + announce: torrent.next_announce.toTime(), + tracker_status: torrent.tracker_status, + downspeed: torrent.download_payload_rate.toSpeed(), + upspeed: torrent.upload_payload_rate.toSpeed(), + eta: torrent.eta.toTime(), + pieces: torrent.num_pieces + ' (' + torrent.piece_length.toBytes() + ')', + seeders: torrent.num_seeds + ' (' + torrent.total_seeds + ')', + peers: torrent.num_peers + ' (' + torrent.total_peers + ')', + avail: torrent.distributed_copies.toFixed(3), + active_time: torrent.active_time.toTime(), + seeding_time: torrent.seeding_time.toTime(), + seed_rank: torrent.seed_rank + } + + if (torrent.is_auto_managed) {data.auto_managed = 'True'} + else {data.auto_managed = 'False'} + + this.element.getElements('dd').each(function(item) { + item.set('text', data[item.getProperty('class')]) + }, this) + } +}) + +Deluge.Widgets.DetailsPage = new Class({ + Extends: Widgets.TabPage, + + options: { + url: '/template/render/html/tab_details.html' + }, + + initialize: function() { + this.parent('Details') + }, + + update: function(torrent) { + var data = { + torrent_name: torrent.name, + hash: torrent.id, + path: torrent.save_path, + size: torrent.total_size.toBytes(), + files: torrent.num_files, + status: torrent.tracker_status, + tracker: torrent.tracker + } + this.element.getElements('dd').each(function(item) { + item.set('text', data[item.getProperty('class')]) + }, this) + } +}) + +Deluge.Widgets.FilesGrid = new Class({ + Extends: Widgets.DataGrid, + + options: { + columns: [ + {name: 'filename',text: 'Filename',type:'text',width: 350}, + {name: 'size',text: 'Size',type:'bytes',width: 80}, + {name: 'progress',text: 'Progress',type:'progress',width: 180}, + {name: 'priority',text: 'Priority',type:'icon',width: 150} + ] + }, + + priority_texts: { + 0: 'Do Not Download', + 1: 'Normal Priority', + 2: 'High Priority', + 5: 'Highest Priority' + }, + + priority_icons: { + 0: '/static/images/tango/process-stop.png', + 1: '/template/static/icons/16/gtk-yes.png', + 2: '/static/images/tango/queue-down.png', + 5: '/static/images/tango/go-bottom.png' + }, + + initialize: function(element, options) { + this.parent(element, options) + var menu = new Widgets.PopupMenu() + $A([0,1,2,5]).each(function(index) { + menu.add({ + type:'text', + action: index, + text: this.priority_texts[index], + icon: this.priority_icons[index] + }) + }, this) + + menu.addEvent('action', function(e) { + e = { + action: e.action, + torrentId: menu.row.torrentId + } + this.fireEvent('menuAction', e) + }.bind(this)) + + this.addEvent('row_menu', function(e) { + e.stop() + menu.row = e.row + menu.show(e) + }) + }, + + clear: function() { + this.rows.empty() + this.body.empty() + this.render() + }, + + update_files: function(torrent) { + torrent.files.each(function(file) { + var p = torrent.file_priorities[file.index] + var priority = {text:this.priority_texts[p], icon:this.priority_icons[p]} + + var percent = torrent.file_progress[file.index]*100.0; + row = { + id: torrent.id + '-' + file.index, + data: { + filename: file.path, + size: file.size, + progress: {percent: percent, text: percent.toFixed(2) + '%'}, + priority: priority + }, + fileIndex: file.index, + torrentId: torrent.id + + } + if (this.has(row.id)) { + this.updateRow(row, true) + } else { + this.addRow(row, true) + } + }, this) + this.render() + } +}); + +Deluge.Widgets.FilesPage = new Class({ + Extends: Widgets.TabPage, + + options: { + url: '/template/render/html/tab_files.html' + }, + + initialize: function(el) { + this.parent('Files') + this.torrentId = -1 + this.addEvent('loaded', this.loaded.bindWithEvent(this)) + this.addEvent('resize', this.resized.bindWithEvent(this)) + }, + + loaded: function(event) { + this.grid = new Deluge.Widgets.FilesGrid('files') + this.grid.addEvent('menuAction', this.menu_action.bindWithEvent(this)) + + if (this.been_resized) { + this.resized(this.been_resized) + delete this.been_resized + } + }, + + resize_check: function(event) { + + }, + + resized: function(e) { + if (!this.grid) { + this.been_resized = e; + return + } + + this.element.getPadding() + this.grid.sets({ + width: e.width - this.element.padding.x, + height: e.height - this.element.padding.y + }) + }, + + menu_action: function(e) { + this.fireEvent('menuAction', e) + }, + + update: function(torrent) { + if (this.torrentId != torrent.id) { + this.torrentId = torrent.id + this.grid.rows.empty() + this.grid.body.empty() + } + this.grid.update_files(torrent) + } +}) + +Deluge.Widgets.PeersPage = new Class({ + Extends: Widgets.TabPage, + + options: { + url: '/template/render/html/tab_peers.html' + }, + + initialize: function(el) { + this.parent('Peers') + this.addEvent('resize', this.resized.bindWithEvent(this)) + this.addEvent('loaded', this.loaded.bindWithEvent(this)) + }, + + loaded: function(event) { + this.grid = new Widgets.DataGrid($('peers'), { + columns: [ + {name: 'country',type:'image',width: 20}, + {name: 'address',text: 'Address',type:'text',width: 80}, + {name: 'client',text: 'Client',type:'text',width: 180}, + {name: 'downspeed',text: 'Down Speed',type:'speed',width: 100}, + {name: 'upspeed',text: 'Up Speed',type:'speed',width: 100}, + ]}) + this.torrentId = -1 + if (this.been_resized) { + this.resized(this.been_resized) + delete this.been_resized + } + }, + + resized: function(e) { + if (!this.grid) { + this.been_resized = e; + return + } + + this.element.getPadding() + this.grid.sets({ + width: e.width - this.element.padding.x, + height: e.height - this.element.padding.y + }) + }, + + update: function(torrent) { + if (this.torrentId != torrent.id) { + this.torrentId = torrent.id + this.grid.rows.empty() + this.grid.body.empty() + } + var peers = [] + torrent.peers.each(function(peer) { + if (peer.country.strip() != '') { + peer.country = '/pixmaps/flags/' + peer.country.toLowerCase() + '.png' + } else { + peer.country = '/templates/static/images/spacer.gif' + } + row = { + id: peer.ip, + data: { + country: peer.country, + address: peer.ip, + client: peer.client, + downspeed: peer.down_speed, + upspeed: peer.up_speed + } + } + if (this.grid.has(row.id)) { + this.grid.updateRow(row, true) + } else { + this.grid.addRow(row, true) + } + peers.include(peer.ip) + }, this) + + this.grid.rows.each(function(row) { + if (!peers.contains(row.id)) { + row.element.destroy() + this.grid.rows.erase(row) + } + }, this) + this.grid.render() + } +}); + +Deluge.Widgets.OptionsPage = new Class({ + Extends: Widgets.TabPage, + + options: { + url: '/template/render/html/tab_options.html' + }, + + initialize: function() { + if (!this.element) + this.parent('Options'); + this.addEvent('loaded', function(event) { + this.loaded(event) + }.bindWithEvent(this)) + }, + + loaded: function(event) { + this.bound = { + apply: this.apply.bindWithEvent(this), + reset: this.reset.bindWithEvent(this) + } + this.form = this.element.getElement('form'); + this.changed = new Hash() + this.form.getElements('input').each(function(el) { + if (el.type == 'button') return; + el.focused = false + el.addEvent('change', function(e) { + if (!this.changed[this.torrentId]) + this.changed[this.torrentId] = {} + if (el.type == 'checkbox') + this.changed[this.torrentId][el.name] = el.checked; + else + this.changed[this.torrentId][el.name] = el.value; + }.bindWithEvent(this)); + el.addEvent('focus', function(e) { + el.focused = true; + }); + el.addEvent('blur', function(e) { + el.focused = false; + }); + }, this); + + this.form.apply.addEvent('click', this.bound.apply); + this.form.reset.addEvent('click', this.bound.reset); + }, + + apply: function(event) { + if (!this.torrentId) return + var changed = this.changed[this.torrentId] + if ($defined(changed['is_auto_managed'])) { + changed['auto_managed'] = changed['is_auto_managed'] + delete changed['is_auto_managed'] + } + Deluge.Client.set_torrent_options(this.torrentId, changed, { + onSuccess: function(event) { + delete this.changed[this.torrentId] + }.bindWithEvent(this) + }) + }, + + reset: function(event) { + if (this.torrentId) { + delete this.changed[this.torrentId] + } + Deluge.Client.get_torrent_status(this.torrentId, Deluge.Keys.Options, { + onSuccess: function(torrent) { + torrent.id = this.torrentId + this.update(torrent) + }.bindWithEvent(this) + }) + }, + + update: function(torrent) { + this.torrentId = torrent.id; + $each(torrent, function(value, key) { + var changed = this.changed[this.torrentId] + if (changed && $defined(changed[key])) return; + var type = $type(value); + if (type == 'boolean') { + this.form[key].checked = value; + } else { + if (!this.form[key].focused) + this.form[key].value = value; + }; + if (key == 'private' && value == 0) { + this.form[key].disabled = true + this.form[key].getParent().addClass('opt-disabled') + } + }, this); + } +}); + +Deluge.Widgets.Toolbar = new Class({ + Implements: Events, + Extends: Widgets.Base, + + initialize: function() { + this.parent($('toolbar')) + this.buttons = this.element.getFirst() + this.info = this.element.getLast() + this.buttons.getElements('li').each(function(el) { + el.addEvent('click', function(e) { + e.action = el.id + this.fireEvent('button_click', e) + }.bind(this)) + }, this) + } +}); + +Deluge.Widgets.StatusBar = new Class({ + Extends: Widgets.Base, + + initialize: function() { + this.parent($('status')); + this.element.getElements('li').each(function(el) { + this[el.id] = el; + }, this); + }, + + update: function(stats) { + this.connections.set('text', stats.num_connections); + this.downspeed.set('text', stats.download_rate.toSpeed()); + this.upspeed.set('text', stats.upload_rate.toSpeed()); + this.dht.set('text', stats.dht_nodes); + } +}); + +Deluge.Widgets.TorrentGrid = new Class({ + Extends: Widgets.DataGrid, + + options: { + columns: [ + {name: 'number',text: '#',type:'number',width: 20}, + {name: 'name',text: 'Name',type:'icon',width: 350}, + {name: 'size',text: 'Size',type:'bytes',width: 80}, + {name: 'progress',text: 'Progress',type:'progress',width: 180}, + {name: 'seeders',text: 'Seeders',type:'text',width: 80}, + {name: 'peers',text: 'Peers',type:'text',width: 80}, + {name: 'down',text: 'Down Speed',type:'speed',width: 100}, + {name: 'up',text: 'Up Speed',type:'speed',width: 100}, + {name: 'eta',text: 'ETA',type:'time',width: 80}, + {name: 'ratio',text: 'Ratio',type:'number',width: 60}, + {name: 'avail',text: 'Avail.',type:'number',width: 60} + ] + }, + + icons: { + 'Downloading': '/pixmaps/downloading16.png', + 'Seeding': '/pixmaps/seeding16.png', + 'Queued': '/pixmaps/queued16.png', + 'Paused': '/pixmaps/inactive16.png', + 'Error': '/pixmaps/alert16.png', + 'Checking': '/pixmaps/inactive16.png' + }, + + get_selected_torrents: function() { + var torrentIds = []; + this.get_selected().each(function(row) { + torrentIds.include(row.id); + }); + return torrentIds; + }, + + set_torrent_filter: function(state) { + state = state.replace(' ', ''); + this.filterer = function (r) { + if (r.torrent.state == state) { return true } else { return false }; + }; + this.render(); + }, + + update_torrents: function(torrents) { + torrents.getKeys().each(function(torrentId) { + var torrent = torrents[torrentId] + var torrentIds = torrents.getKeys() + if (torrent.queue == -1) {var queue = ''} + else {var queue = torrent.queue + 1} + var icon = this.icons[torrent.state] + row = { + id: torrentId, + data: { + number: queue, + name: {text: torrent.name, icon: icon}, + size: torrent.total_size, + progress: {percent: torrent.progress, text:torrent.state + ' ' + torrent.progress.toFixed(2) + '%'}, + seeders: torrent.num_seeds + ' (' + torrent.total_seeds + ')', + peers: torrent.num_peers + ' (' + torrent.total_peers + ')', + down: torrent.download_payload_rate, + up: torrent.upload_payload_rate, + eta: torrent.eta, + ratio: torrent.ratio.toFixed(3), + avail: torrent.distributed_copies.toFixed(3) + }, + torrent: torrent + } + if (this.has(row.id)) { + this.updateRow(row, true) + } else { + this.addRow(row, true) + } + + this.rows.each(function(row) { + if (!torrentIds.contains(row.id)) { + row.element.destroy() + this.rows.erase(row.id) + } + }, this) + }, this) + this.render() + } +}) + +Deluge.Widgets.Labels = new Class({ + + Extends: Widgets.Base, + + regex: /([\w]+)\s\((\d)\)/, + + initialize: function() { + this.parent($('labels')) + this.bound = { + resized: this.resized.bindWithEvent(this), + clickedState: this.clickedState.bindWithEvent(this) + } + + this.list = new Element('ul') + this.element.grab(this.list) + this.addStates() + this.state = 'All' + this.islabels = false; + this.addEvent('resize', this.resized) + }, + + addStates: function() { + this.list.grab(new Element('li').set('text', 'All').addClass('all').addClass('activestate')) + this.list.grab(new Element('li').set('text', 'Downloading').addClass('downloading')) + this.list.grab(new Element('li').set('text', 'Seeding').addClass('seeding')) + this.list.grab(new Element('li').set('text', 'Queued').addClass('queued')) + this.list.grab(new Element('li').set('text', 'Paused').addClass('paused')) + this.list.grab(new Element('li').set('text', 'Error').addClass('error')) + this.list.grab(new Element('li').set('text', 'Checking').addClass('checking')) + this.list.grab(new Element('hr')) + }, + + addLabel: function(name) { + + }, + + clickedState: function(e) { + if (this.islabels) { + var old = this.list.getElement('.' + this.state.toLowerCase()) + old.removeClass('activestate') + this.state = e.target.get('text').match(/^(\w+)/)[1] + e.target.addClass('activestate') + this.fireEvent('stateChanged', this.state) + } else { + + } + }, + + update: function(filters) { + if (filters.state.length == 1) + this.updateNoLabels(filters); + else + this.updateLabels(filters) + }, + + updateNoLabels: function(filters) { + this.islabels = false; + }, + + updateLabels: function(filters) { + this.islabels = true; + $each(filters.state, function(state) { + var el = this.list.getElement('.' + state[0].toLowerCase()) + if (!el) return + + el.set('text', state[0] + ' (' + state[1] + ')') + el.removeEvent('click', this.bound.clickedState) + el.addEvent('click', this.bound.clickedState) + }, this) + }, + + resized: function(event) { + var height = this.element.getInnerSize().y; + this.list.getSizeModifiers(); + height -= this.list.modifiers.y; + this.list.setStyle('height', height) + } +}); + +Deluge.Widgets.AddWindow = new Class({ + Extends: Widgets.Window, + options: { + width: 400, + height: 200, + title: 'Add Torrent', + url: '/template/render/html/window_add_torrent.html' + }, + + initialize: function() { + this.parent() + this.addEvent('loaded', this.loaded.bindWithEvent(this)) + }, + + loaded: function(event) { + this.formfile = this.content.getChildren()[0]; + this.formurl = this.content.getChildren()[1]; + this.formurl.addEvent('submit', function(e) { + e.stop(); + Deluge.Client.add_torrent_url(this.formurl.url.value, '', {}) + this.hide() + }.bindWithEvent(this)) + } +}); + +Deluge.Widgets.PreferencesCategory = new Class({ + Extends: Widgets.TabPage, +}); + +Deluge.Widgets.PluginPreferencesCategory = new Class({ + Extends: Deluge.Widgets.PreferencesCategory +}); + +Deluge.Widgets.GenericPreferences = new Class({ + Extends: Deluge.Widgets.PreferencesCategory, + + initialize: function(name, options) { + this.parent(name, options) + this.core = true; + this.addEvent('loaded', function(e) { + this.form = this.element.getElement('form'); + }.bindWithEvent(this)); + }, + + update: function(config) { + this.fireEvent('beforeUpdate'); + this.original = {}; + this.changed = {}; + this.inputs = this.form.getElements('input, select'); + this.inputs.each(function(input) { + if (!input.name) return; + if (!$defined(config[input.name])) return; + if (input.tagName.toLowerCase() == 'select') { + var value = config[input.name].toString(); + input.getElements('option').each(function(option) { + if (option.value == value) option.selected = true; + }); + } else if (input.type == 'text') { + input.value = config[input.name]; + } else if (input.type == 'checkbox') { + input.checked = config[input.name]; + } else if (input.type == 'radio') { + var value = config[input.name].toString() + if (input.value == value) input.checked = true; + } + this.original[input.name] = input.value; + input.addEvent('change', function(el) { + if (this.original[input.name] == input.value) { + if (this.changed[input.name]) + delete this.changed[input.name]; + } else { + this.changed[input.name] = input.value; + } + }.bindWithEvent(this)) + }, this); + this.fireEvent('update'); + } +}); + +Deluge.Widgets.WebUIPreferences = new Class({ + Extends: Deluge.Widgets.GenericPreferences, + + options: { + url: '/template/render/html/preferences_webui.html' + }, + + initialize: function() { + this.parent('Web UI'); + this.core = false; + this.addEvent('beforeUpdate', this.beforeUpdate.bindWithEvent(this)); + this.addEvent('update', this.updated.bindWithEvent(this)); + }, + + beforeUpdate: function(event) { + var templates = Deluge.Client.get_webui_templates({async: false}); + templates.each(function(template) { + var option = new Element('option'); + option.set('text', template); + this.form.template.grab(option); + }, this); + }, + + updated: function(event) { + if (this.form.template.value != 'ajax') + this.form.theme.disabled = true; + else + this.form.theme.disabled = false; + + this.form.template.addEvent('change', function(e) { + if (this.form.template.value != 'ajax') { + this.form.theme.disabled = true; + this.form.theme.addClass('disabled') + this.form.getElementById('lbl_theme').addClass('disabled') + } else { + this.form.theme.disabled = false; + this.form.getElementById('lbl_theme').removeClass('disabled') + this.form.theme.removeClass('disabled') + } + }.bindWithEvent(this)); + }, + + apply: function() { + Deluge.UI.setTheme(this.form.theme.value); + Deluge.Client.set_webui_config(this.changed, { + onSuccess: function(e) { + if (this.changed['template']) location.reload(true); + }.bindWithEvent(this) + }); + } +}); + +Deluge.Widgets.PreferencesWindow = new Class({ + Extends: Widgets.Window, + options: { + width: 500, + height: 430, + title: 'Preferences', + url: '/template/render/html/window_preferences.html' + }, + + initialize: function() { + this.parent(); + this.categories = []; + this.currentPage = -1; + this.addEvent('loaded', this.loaded.bindWithEvent(this)); + this.addEvent('show', this.shown.bindWithEvent(this)); + }, + + loaded: function(event) { + this.catlist = this.content.getElement('.categories ul'); + this.pages = this.content.getElement('.pref_pages'); + this.title = this.pages.getElement('h3'); + + this.reset = this.content.getElement('.buttons .reset'); + this.apply = this.content.getElement('.buttons .apply'); + this.apply.addEvent('click', this.applied.bindWithEvent(this)); + + this.webui = new Deluge.Widgets.WebUIPreferences(); + + this.download = new Deluge.Widgets.GenericPreferences('Download', { + url: '/template/render/html/preferences_download.html' + }); + this.network = new Deluge.Widgets.GenericPreferences('Network', { + url: '/template/render/html/preferences_network.html' + }); + this.bandwidth = new Deluge.Widgets.GenericPreferences('Bandwidth', { + url: '/template/render/html/preferences_bandwidth.html' + }); + this.daemon = new Deluge.Widgets.GenericPreferences('Daemon', { + url: '/template/render/html/preferences_daemon.html' + }); + this.queue = new Deluge.Widgets.GenericPreferences('Queue', { + url: '/template/render/html/preferences_queue.html' + }); + + this.addCategory(this.webui); + this.addCategory(this.download); + this.addCategory(this.network); + this.addCategory(this.bandwidth); + this.addCategory(this.daemon); + this.addCategory(this.queue); + }, + + addCategory: function(category) { + this.categories.include(category); + var categoryIndex = this.categories.indexOf(category); + + var tab = new Element('li'); + tab.set('text', category.name); + tab.addEvent('click', function(e) { + this.select(categoryIndex); + }.bindWithEvent(this)); + category.tab = tab; + + this.catlist.grab(tab); + this.pages.grab(category.addClass('deluge-prefs-page')); + + + if (this.currentPage < 0) { + this.currentPage = categoryIndex; + this.select(categoryIndex); + }; + }, + + select: function(id) { + this.categories[this.currentPage].removeClass('deluge-prefs-page-active'); + this.categories[this.currentPage].tab.removeClass('deluge-prefs-active'); + this.categories[id].addClass('deluge-prefs-page-active'); + this.categories[id].tab.addClass('deluge-prefs-active'); + this.title.set('text', this.categories[id].name); + this.currentPage = id; + this.fireEvent('pageChanged'); + }, + + applied: function(event) { + this.webui.apply(); + }, + + shown: function(event) { + // we want this to be blocking + var config = Deluge.Client.get_config({async: false}); + this.categories.each(function(category) { + if (category.update && category.core) category.update(config); + }); + + var webconfig = Deluge.Client.get_webui_config({async: false}); + this.webui.update(webconfig); + } +}); diff --git a/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-nc.js b/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-nc.js new file mode 100644 index 000000000..e88d4ac8c --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-nc.js @@ -0,0 +1,3816 @@ +/* +Script: Core.js + MooTools - My Object Oriented JavaScript Tools. + +License: + MIT-style license. + +Copyright: + Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/). + +Code & Documentation: + [The MooTools production team](http://mootools.net/developers/). + +Inspiration: + - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) + - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) +*/ + +var MooTools = { + 'version': '1.2.0', + 'build': '' +}; + +var Native = function(options){ + options = options || {}; + + var afterImplement = options.afterImplement || function(){}; + var generics = options.generics; + generics = (generics !== false); + var legacy = options.legacy; + var initialize = options.initialize; + var protect = options.protect; + var name = options.name; + + var object = initialize || legacy; + + object.constructor = Native; + object.$family = {name: 'native'}; + if (legacy && initialize) object.prototype = legacy.prototype; + object.prototype.constructor = object; + + if (name){ + var family = name.toLowerCase(); + object.prototype.$family = {name: family}; + Native.typize(object, family); + } + + var add = function(obj, name, method, force){ + if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method; + if (generics) Native.genericize(obj, name, protect); + afterImplement.call(obj, name, method); + return obj; + }; + + object.implement = function(a1, a2, a3){ + if (typeof a1 == 'string') return add(this, a1, a2, a3); + for (var p in a1) add(this, p, a1[p], a2); + return this; + }; + + object.alias = function(a1, a2, a3){ + if (typeof a1 == 'string'){ + a1 = this.prototype[a1]; + if (a1) add(this, a2, a1, a3); + } else { + for (var a in a1) this.alias(a, a1[a], a2); + } + return this; + }; + + return object; +}; + +Native.implement = function(objects, properties){ + for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties); +}; + +Native.genericize = function(object, property, check){ + if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){ + var args = Array.prototype.slice.call(arguments); + return object.prototype[property].apply(args.shift(), args); + }; +}; + +Native.typize = function(object, family){ + if (!object.type) object.type = function(item){ + return ($type(item) === family); + }; +}; + +Native.alias = function(objects, a1, a2, a3){ + for (var i = 0, j = objects.length; i < j; i++) objects[i].alias(a1, a2, a3); +}; + +(function(objects){ + for (var name in objects) Native.typize(objects[name], name); +})({'boolean': Boolean, 'native': Native, 'object': Object}); + +(function(objects){ + for (var name in objects) new Native({name: name, initialize: objects[name], protect: true}); +})({'String': String, 'Function': Function, 'Number': Number, 'Array': Array, 'RegExp': RegExp, 'Date': Date}); + +(function(object, methods){ + for (var i = methods.length; i--; i) Native.genericize(object, methods[i], true); + return arguments.callee; +}) +(Array, ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'toString', 'valueOf', 'indexOf', 'lastIndexOf']) +(String, ['charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase', 'valueOf']); + +function $chk(obj){ + return !!(obj || obj === 0); +}; + +function $clear(timer){ + clearTimeout(timer); + clearInterval(timer); + return null; +}; + +function $defined(obj){ + return (obj != undefined); +}; + +function $empty(){}; + +function $arguments(i){ + return function(){ + return arguments[i]; + }; +}; + +function $lambda(value){ + return (typeof value == 'function') ? value : function(){ + return value; + }; +}; + +function $extend(original, extended){ + for (var key in (extended || {})) original[key] = extended[key]; + return original; +}; + +function $unlink(object){ + var unlinked; + + switch ($type(object)){ + case 'object': + unlinked = {}; + for (var p in object) unlinked[p] = $unlink(object[p]); + break; + case 'hash': + unlinked = $unlink(object.getClean()); + break; + case 'array': + unlinked = []; + for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]); + break; + default: return object; + } + + return unlinked; +}; + +function $merge(){ + var mix = {}; + for (var i = 0, l = arguments.length; i < l; i++){ + var object = arguments[i]; + if ($type(object) != 'object') continue; + for (var key in object){ + var op = object[key], mp = mix[key]; + mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op); + } + } + return mix; +}; + +function $pick(){ + for (var i = 0, l = arguments.length; i < l; i++){ + if (arguments[i] != undefined) return arguments[i]; + } + return null; +}; + +function $random(min, max){ + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +function $splat(obj){ + var type = $type(obj); + return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : []; +}; + +var $time = Date.now || function(){ + return new Date().getTime(); +}; + +function $try(){ + for (var i = 0, l = arguments.length; i < l; i++){ + try { + return arguments[i](); + } catch(e){} + } + return null; +}; + +function $type(obj){ + if (obj == undefined) return false; + if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name; + if (obj.nodeName){ + switch (obj.nodeType){ + case 1: return 'element'; + case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace'; + } + } else if (typeof obj.length == 'number'){ + if (obj.callee) return 'arguments'; + else if (obj.item) return 'collection'; + } + return typeof obj; +}; + +var Hash = new Native({ + + name: 'Hash', + + initialize: function(object){ + if ($type(object) == 'hash') object = $unlink(object.getClean()); + for (var key in object) this[key] = object[key]; + return this; + } + +}); + +Hash.implement({ + + getLength: function(){ + var length = 0; + for (var key in this){ + if (this.hasOwnProperty(key)) length++; + } + return length; + }, + + forEach: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this); + } + }, + + getClean: function(){ + var clean = {}; + for (var key in this){ + if (this.hasOwnProperty(key)) clean[key] = this[key]; + } + return clean; + } + +}); + +Hash.alias('forEach', 'each'); + +function $H(object){ + return new Hash(object); +}; + +Array.implement({ + + forEach: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this); + } + +}); + +Array.alias('forEach', 'each'); + +function $A(iterable){ + if (iterable.item){ + var array = []; + for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i]; + return array; + } + return Array.prototype.slice.call(iterable); +}; + +function $each(iterable, fn, bind){ + var type = $type(iterable); + ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind); +}; + + +/* +Script: Browser.js + The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash. + +License: + MIT-style license. +*/ + +var Browser = new Hash({ + Engine: {name: 'unknown', version: ''}, + Platform: {name: (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()}, + Features: {xpath: !!(document.evaluate), air: !!(window.runtime)}, + Plugins: {} +}); + +if (window.opera) Browser.Engine = {name: 'presto', version: (document.getElementsByClassName) ? 950 : 925}; +else if (window.ActiveXObject) Browser.Engine = {name: 'trident', version: (window.XMLHttpRequest) ? 5 : 4}; +else if (!navigator.taintEnabled) Browser.Engine = {name: 'webkit', version: (Browser.Features.xpath) ? 420 : 419}; +else if (document.getBoxObjectFor != null) Browser.Engine = {name: 'gecko', version: (document.getElementsByClassName) ? 19 : 18}; +Browser.Engine[Browser.Engine.name] = Browser.Engine[Browser.Engine.name + Browser.Engine.version] = true; + +if (window.orientation != undefined) Browser.Platform.name = 'ipod'; + +Browser.Platform[Browser.Platform.name] = true; + +Browser.Request = function(){ + return $try(function(){ + return new XMLHttpRequest(); + }, function(){ + return new ActiveXObject('MSXML2.XMLHTTP'); + }); +}; + +Browser.Features.xhr = !!(Browser.Request()); + +Browser.Plugins.Flash = (function(){ + var version = ($try(function(){ + return navigator.plugins['Shockwave Flash'].description; + }, function(){ + return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + }) || '0 r0').match(/\d+/g); + return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)}; +})(); + +function $exec(text){ + if (!text) return text; + if (window.execScript){ + window.execScript(text); + } else { + var script = document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script.text = text; + document.head.appendChild(script); + document.head.removeChild(script); + } + return text; +}; + +Native.UID = 1; + +var $uid = (Browser.Engine.trident) ? function(item){ + return (item.uid || (item.uid = [Native.UID++]))[0]; +} : function(item){ + return item.uid || (item.uid = Native.UID++); +}; + +var Window = new Native({ + + name: 'Window', + + legacy: (Browser.Engine.trident) ? null: window.Window, + + initialize: function(win){ + $uid(win); + if (!win.Element){ + win.Element = $empty; + if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2 + win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {}; + } + return $extend(win, Window.Prototype); + }, + + afterImplement: function(property, value){ + window[property] = Window.Prototype[property] = value; + } + +}); + +Window.Prototype = {$family: {name: 'window'}}; + +new Window(window); + +var Document = new Native({ + + name: 'Document', + + legacy: (Browser.Engine.trident) ? null: window.Document, + + initialize: function(doc){ + $uid(doc); + doc.head = doc.getElementsByTagName('head')[0]; + doc.html = doc.getElementsByTagName('html')[0]; + doc.window = doc.defaultView || doc.parentWindow; + if (Browser.Engine.trident4) $try(function(){ + doc.execCommand("BackgroundImageCache", false, true); + }); + return $extend(doc, Document.Prototype); + }, + + afterImplement: function(property, value){ + document[property] = Document.Prototype[property] = value; + } + +}); + +Document.Prototype = {$family: {name: 'document'}}; + +new Document(document); + +/* +Script: Array.js + Contains Array Prototypes like copy, each, contains, and remove. + +License: + MIT-style license. +*/ + +Array.implement({ + + every: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (!fn.call(bind, this[i], i, this)) return false; + } + return true; + }, + + filter: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) results.push(this[i]); + } + return results; + }, + + clean: function() { + return this.filter($defined); + }, + + indexOf: function(item, from){ + var len = this.length; + for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ + if (this[i] === item) return i; + } + return -1; + }, + + map: function(fn, bind){ + var results = []; + for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this); + return results; + }, + + some: function(fn, bind){ + for (var i = 0, l = this.length; i < l; i++){ + if (fn.call(bind, this[i], i, this)) return true; + } + return false; + }, + + associate: function(keys){ + var obj = {}, length = Math.min(this.length, keys.length); + for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; + return obj; + }, + + link: function(object){ + var result = {}; + for (var i = 0, l = this.length; i < l; i++){ + for (var key in object){ + if (object[key](this[i])){ + result[key] = this[i]; + delete object[key]; + break; + } + } + } + return result; + }, + + contains: function(item, from){ + return this.indexOf(item, from) != -1; + }, + + extend: function(array){ + for (var i = 0, j = array.length; i < j; i++) this.push(array[i]); + return this; + }, + + getLast: function(){ + return (this.length) ? this[this.length - 1] : null; + }, + + getRandom: function(){ + return (this.length) ? this[$random(0, this.length - 1)] : null; + }, + + include: function(item){ + if (!this.contains(item)) this.push(item); + return this; + }, + + combine: function(array){ + for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); + return this; + }, + + erase: function(item){ + for (var i = this.length; i--; i){ + if (this[i] === item) this.splice(i, 1); + } + return this; + }, + + empty: function(){ + this.length = 0; + return this; + }, + + flatten: function(){ + var array = []; + for (var i = 0, l = this.length; i < l; i++){ + var type = $type(this[i]); + if (!type) continue; + array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]); + } + return array; + }, + + hexToRgb: function(array){ + if (this.length != 3) return null; + var rgb = this.map(function(value){ + if (value.length == 1) value += value; + return value.toInt(16); + }); + return (array) ? rgb : 'rgb(' + rgb + ')'; + }, + + rgbToHex: function(array){ + if (this.length < 3) return null; + if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; + var hex = []; + for (var i = 0; i < 3; i++){ + var bit = (this[i] - 0).toString(16); + hex.push((bit.length == 1) ? '0' + bit : bit); + } + return (array) ? hex : '#' + hex.join(''); + } + +}); + +/* +Script: Function.js + Contains Function Prototypes like create, bind, pass, and delay. + +License: + MIT-style license. +*/ + +Function.implement({ + + extend: function(properties){ + for (var property in properties) this[property] = properties[property]; + return this; + }, + + create: function(options){ + var self = this; + options = options || {}; + return function(event){ + var args = options.arguments; + args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0); + if (options.event) args = [event || window.event].extend(args); + var returns = function(){ + return self.apply(options.bind || null, args); + }; + if (options.delay) return setTimeout(returns, options.delay); + if (options.periodical) return setInterval(returns, options.periodical); + if (options.attempt) return $try(returns); + return returns(); + }; + }, + + pass: function(args, bind){ + return this.create({arguments: args, bind: bind}); + }, + + attempt: function(args, bind){ + return this.create({arguments: args, bind: bind, attempt: true})(); + }, + + bind: function(bind, args){ + return this.create({bind: bind, arguments: args}); + }, + + bindWithEvent: function(bind, args){ + return this.create({bind: bind, event: true, arguments: args}); + }, + + delay: function(delay, bind, args){ + return this.create({delay: delay, bind: bind, arguments: args})(); + }, + + periodical: function(interval, bind, args){ + return this.create({periodical: interval, bind: bind, arguments: args})(); + }, + + run: function(args, bind){ + return this.apply(bind, $splat(args)); + } + +}); + +/* +Script: Number.js + Contains Number Prototypes like limit, round, times, and ceil. + +License: + MIT-style license. +*/ + +Number.implement({ + + limit: function(min, max){ + return Math.min(max, Math.max(min, this)); + }, + + round: function(precision){ + precision = Math.pow(10, precision || 0); + return Math.round(this * precision) / precision; + }, + + times: function(fn, bind){ + for (var i = 0; i < this; i++) fn.call(bind, i, this); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + } + +}); + +Number.alias('times', 'each'); + +(function(math){ + var methods = {}; + math.each(function(name){ + if (!Number[name]) methods[name] = function(){ + return Math[name].apply(null, [this].concat($A(arguments))); + }; + }); + Number.implement(methods); +})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); + +/* +Script: String.js + Contains String Prototypes like camelCase, capitalize, test, and toInt. + +License: + MIT-style license. +*/ + +String.implement({ + + test: function(regex, params){ + return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this); + }, + + contains: function(string, separator){ + return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; + }, + + trim: function(){ + return this.replace(/^\s+|\s+$/g, ''); + }, + + clean: function(){ + return this.replace(/\s+/g, ' ').trim(); + }, + + camelCase: function(){ + return this.replace(/-\D/g, function(match){ + return match.charAt(1).toUpperCase(); + }); + }, + + hyphenate: function(){ + return this.replace(/[A-Z]/g, function(match){ + return ('-' + match.charAt(0).toLowerCase()); + }); + }, + + capitalize: function(){ + return this.replace(/\b[a-z]/g, function(match){ + return match.toUpperCase(); + }); + }, + + escapeRegExp: function(){ + return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + + toInt: function(base){ + return parseInt(this, base || 10); + }, + + toFloat: function(){ + return parseFloat(this); + }, + + hexToRgb: function(array){ + var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); + return (hex) ? hex.slice(1).hexToRgb(array) : null; + }, + + rgbToHex: function(array){ + var rgb = this.match(/\d{1,3}/g); + return (rgb) ? rgb.rgbToHex(array) : null; + }, + + stripScripts: function(option){ + var scripts = ''; + var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(){ + scripts += arguments[1] + '\n'; + return ''; + }); + if (option === true) $exec(scripts); + else if ($type(option) == 'function') option(scripts, text); + return text; + }, + + substitute: function(object, regexp){ + return this.replace(regexp || (/\\?\{([^}]+)\}/g), function(match, name){ + if (match.charAt(0) == '\\') return match.slice(1); + return (object[name] != undefined) ? object[name] : ''; + }); + } + +}); + +/* +Script: Hash.js + Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. + +License: + MIT-style license. +*/ + +Hash.implement({ + + has: Object.prototype.hasOwnProperty, + + keyOf: function(value){ + for (var key in this){ + if (this.hasOwnProperty(key) && this[key] === value) return key; + } + return null; + }, + + hasValue: function(value){ + return (Hash.keyOf(this, value) !== null); + }, + + extend: function(properties){ + Hash.each(properties, function(value, key){ + Hash.set(this, key, value); + }, this); + return this; + }, + + combine: function(properties){ + Hash.each(properties, function(value, key){ + Hash.include(this, key, value); + }, this); + return this; + }, + + erase: function(key){ + if (this.hasOwnProperty(key)) delete this[key]; + return this; + }, + + get: function(key){ + return (this.hasOwnProperty(key)) ? this[key] : null; + }, + + set: function(key, value){ + if (!this[key] || this.hasOwnProperty(key)) this[key] = value; + return this; + }, + + empty: function(){ + Hash.each(this, function(value, key){ + delete this[key]; + }, this); + return this; + }, + + include: function(key, value){ + var k = this[key]; + if (k == undefined) this[key] = value; + return this; + }, + + map: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + results.set(key, fn.call(bind, value, key, this)); + }, this); + return results; + }, + + filter: function(fn, bind){ + var results = new Hash; + Hash.each(this, function(value, key){ + if (fn.call(bind, value, key, this)) results.set(key, value); + }, this); + return results; + }, + + every: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false; + } + return true; + }, + + some: function(fn, bind){ + for (var key in this){ + if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true; + } + return false; + }, + + getKeys: function(){ + var keys = []; + Hash.each(this, function(value, key){ + keys.push(key); + }); + return keys; + }, + + getValues: function(){ + var values = []; + Hash.each(this, function(value){ + values.push(value); + }); + return values; + }, + + toQueryString: function(base){ + var queryString = []; + Hash.each(this, function(value, key){ + if (base) key = base + '[' + key + ']'; + var result; + switch ($type(value)){ + case 'object': result = Hash.toQueryString(value, key); break; + case 'array': + var qs = {}; + value.each(function(val, i){ + qs[i] = val; + }); + result = Hash.toQueryString(qs, key); + break; + default: result = key + '=' + encodeURIComponent(value); + } + if (value != undefined) queryString.push(result); + }); + + return queryString.join('&'); + } + +}); + +Hash.alias({keyOf: 'indexOf', hasValue: 'contains'}); + +/* +Script: Event.js + Contains the Event Native, to make the event object completely crossbrowser. + +License: + MIT-style license. +*/ + +var Event = new Native({ + + name: 'Event', + + initialize: function(event, win){ + win = win || window; + var doc = win.document; + event = event || win.event; + if (event.$extended) return event; + this.$extended = true; + var type = event.type; + var target = event.target || event.srcElement; + while (target && target.nodeType == 3) target = target.parentNode; + + if (type.test(/key/)){ + var code = event.which || event.keyCode; + var key = Event.Keys.keyOf(code); + if (type == 'keydown'){ + var fKey = code - 111; + if (fKey > 0 && fKey < 13) key = 'f' + fKey; + } + key = key || String.fromCharCode(code).toLowerCase(); + } else if (type.match(/(click|mouse|menu)/i)){ + doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; + var page = { + x: event.pageX || event.clientX + doc.scrollLeft, + y: event.pageY || event.clientY + doc.scrollTop + }; + var client = { + x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX, + y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY + }; + if (type.match(/DOMMouseScroll|mousewheel/)){ + var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; + } + var rightClick = (event.which == 3) || (event.button == 2); + var related = null; + if (type.match(/over|out/)){ + switch (type){ + case 'mouseover': related = event.relatedTarget || event.fromElement; break; + case 'mouseout': related = event.relatedTarget || event.toElement; + } + if (!(function(){ + while (related && related.nodeType == 3) related = related.parentNode; + return true; + }).create({attempt: Browser.Engine.gecko})()) related = false; + } + } + + return $extend(this, { + event: event, + type: type, + + page: page, + client: client, + rightClick: rightClick, + + wheel: wheel, + + relatedTarget: related, + target: target, + + code: code, + key: key, + + shift: event.shiftKey, + control: event.ctrlKey, + alt: event.altKey, + meta: event.metaKey + }); + } + +}); + +Event.Keys = new Hash({ + 'enter': 13, + 'up': 38, + 'down': 40, + 'left': 37, + 'right': 39, + 'esc': 27, + 'space': 32, + 'backspace': 8, + 'tab': 9, + 'delete': 46 +}); + +Event.implement({ + + stop: function(){ + return this.stopPropagation().preventDefault(); + }, + + stopPropagation: function(){ + if (this.event.stopPropagation) this.event.stopPropagation(); + else this.event.cancelBubble = true; + return this; + }, + + preventDefault: function(){ + if (this.event.preventDefault) this.event.preventDefault(); + else this.event.returnValue = false; + return this; + } + +}); + +/* +Script: Class.js + Contains the Class Function for easily creating, extending, and implementing reusable Classes. + +License: + MIT-style license. +*/ + +var Class = new Native({ + + name: 'Class', + + initialize: function(properties){ + properties = properties || {}; + var klass = function(empty){ + for (var key in this) this[key] = $unlink(this[key]); + for (var mutator in Class.Mutators){ + if (!this[mutator]) continue; + Class.Mutators[mutator](this, this[mutator]); + delete this[mutator]; + } + + this.constructor = klass; + if (empty === $empty) return this; + + var self = (this.initialize) ? this.initialize.apply(this, arguments) : this; + if (this.options && this.options.initialize) this.options.initialize.call(this); + return self; + }; + + $extend(klass, this); + klass.constructor = Class; + klass.prototype = properties; + return klass; + } + +}); + +Class.implement({ + + implement: function(){ + Class.Mutators.Implements(this.prototype, Array.slice(arguments)); + return this; + } + +}); + +Class.Mutators = { + + Implements: function(self, klasses){ + $splat(klasses).each(function(klass){ + $extend(self, ($type(klass) == 'class') ? new klass($empty) : klass); + }); + }, + + Extends: function(self, klass){ + var instance = new klass($empty); + delete instance.parent; + delete instance.parentOf; + + for (var key in instance){ + var current = self[key], previous = instance[key]; + if (current == undefined){ + self[key] = previous; + continue; + } + + var ctype = $type(current), ptype = $type(previous); + if (ctype != ptype) continue; + + switch (ctype){ + case 'function': + // this code will be only executed if the current browser does not support function.caller (currently only opera). + // we replace the function code with brute force. Not pretty, but it will only be executed if function.caller is not supported. + + if (!arguments.callee.caller) self[key] = eval('(' + String(current).replace(/\bthis\.parent\(\s*(\))?/g, function(full, close){ + return 'arguments.callee._parent_.call(this' + (close || ', '); + }) + ')'); + + // end "opera" code + self[key]._parent_ = previous; + break; + case 'object': self[key] = $merge(previous, current); + } + + } + + self.parent = function(){ + return arguments.callee.caller._parent_.apply(this, arguments); + }; + + self.parentOf = function(descendant){ + return descendant._parent_.apply(this, Array.slice(arguments, 1)); + }; + } + +}; + + +/* +Script: Class.Extras.js + Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. + +License: + MIT-style license. +*/ + +var Chain = new Class({ + + chain: function(){ + this.$chain = (this.$chain || []).extend(arguments); + return this; + }, + + callChain: function(){ + return (this.$chain && this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; + }, + + clearChain: function(){ + if (this.$chain) this.$chain.empty(); + return this; + } + +}); + +var Events = new Class({ + + addEvent: function(type, fn, internal){ + type = Events.removeOn(type); + if (fn != $empty){ + this.$events = this.$events || {}; + this.$events[type] = this.$events[type] || []; + this.$events[type].include(fn); + if (internal) fn.internal = true; + } + return this; + }, + + addEvents: function(events){ + for (var type in events) this.addEvent(type, events[type]); + return this; + }, + + fireEvent: function(type, args, delay){ + type = Events.removeOn(type); + if (!this.$events || !this.$events[type]) return this; + this.$events[type].each(function(fn){ + fn.create({'bind': this, 'delay': delay, 'arguments': args})(); + }, this); + return this; + }, + + removeEvent: function(type, fn){ + type = Events.removeOn(type); + if (!this.$events || !this.$events[type]) return this; + if (!fn.internal) this.$events[type].erase(fn); + return this; + }, + + removeEvents: function(type){ + for (var e in this.$events){ + if (type && type != e) continue; + var fns = this.$events[e]; + for (var i = fns.length; i--; i) this.removeEvent(e, fns[i]); + } + return this; + } + +}); + +Events.removeOn = function(string){ + return string.replace(/^on([A-Z])/, function(full, first) { + return first.toLowerCase(); + }); +}; + +var Options = new Class({ + + setOptions: function(){ + this.options = $merge.run([this.options].extend(arguments)); + if (!this.addEvent) return this; + for (var option in this.options){ + if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; + this.addEvent(option, this.options[option]); + delete this.options[option]; + } + return this; + } + +}); + +/* +Script: Element.js + One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, + time-saver methods to let you easily work with HTML Elements. + +License: + MIT-style license. +*/ + +Document.implement({ + + newElement: function(tag, props){ + if (Browser.Engine.trident && props){ + ['name', 'type', 'checked'].each(function(attribute){ + if (!props[attribute]) return; + tag += ' ' + attribute + '="' + props[attribute] + '"'; + if (attribute != 'checked') delete props[attribute]; + }); + tag = '<' + tag + '>'; + } + return $.element(this.createElement(tag)).set(props); + }, + + newTextNode: function(text){ + return this.createTextNode(text); + }, + + getDocument: function(){ + return this; + }, + + getWindow: function(){ + return this.defaultView || this.parentWindow; + }, + + purge: function(){ + var elements = this.getElementsByTagName('*'); + for (var i = 0, l = elements.length; i < l; i++) Browser.freeMem(elements[i]); + } + +}); + +var Element = new Native({ + + name: 'Element', + + legacy: window.Element, + + initialize: function(tag, props){ + var konstructor = Element.Constructors.get(tag); + if (konstructor) return konstructor(props); + if (typeof tag == 'string') return document.newElement(tag, props); + return $(tag).set(props); + }, + + afterImplement: function(key, value){ + if (!Array[key]) Elements.implement(key, Elements.multi(key)); + Element.Prototype[key] = value; + } + +}); + +Element.Prototype = {$family: {name: 'element'}}; + +Element.Constructors = new Hash; + +var IFrame = new Native({ + + name: 'IFrame', + + generics: false, + + initialize: function(){ + var params = Array.link(arguments, {properties: Object.type, iframe: $defined}); + var props = params.properties || {}; + var iframe = $(params.iframe) || false; + var onload = props.onload || $empty; + delete props.onload; + props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time()); + iframe = new Element(iframe || 'iframe', props); + var onFrameLoad = function(){ + var host = $try(function(){ + return iframe.contentWindow.location.host; + }); + if (host && host == window.location.host){ + var win = new Window(iframe.contentWindow); + var doc = new Document(iframe.contentWindow.document); + $extend(win.Element.prototype, Element.Prototype); + } + onload.call(iframe.contentWindow, iframe.contentWindow.document); + }; + (!window.frames[props.id]) ? iframe.addListener('load', onFrameLoad) : onFrameLoad(); + return iframe; + } + +}); + +var Elements = new Native({ + + initialize: function(elements, options){ + options = $extend({ddup: true, cash: true}, options); + elements = elements || []; + if (options.ddup || options.cash){ + var uniques = {}, returned = []; + for (var i = 0, l = elements.length; i < l; i++){ + var el = $.element(elements[i], !options.cash); + if (options.ddup){ + if (uniques[el.uid]) continue; + uniques[el.uid] = true; + } + returned.push(el); + } + elements = returned; + } + return (options.cash) ? $extend(elements, this) : elements; + } + +}); + +Elements.implement({ + + filter: function(filter, bind){ + if (!filter) return this; + return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){ + return item.match(filter); + } : filter, bind)); + } + +}); + +Elements.multi = function(property){ + return function(){ + var items = []; + var elements = true; + for (var i = 0, j = this.length; i < j; i++){ + var returns = this[i][property].apply(this[i], arguments); + items.push(returns); + if (elements) elements = ($type(returns) == 'element'); + } + return (elements) ? new Elements(items) : items; + }; +}; + +Window.implement({ + + $: function(el, nocash){ + if (el && el.$family && el.uid) return el; + var type = $type(el); + return ($[type]) ? $[type](el, nocash, this.document) : null; + }, + + $$: function(selector){ + if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector); + var elements = []; + var args = Array.flatten(arguments); + for (var i = 0, l = args.length; i < l; i++){ + var item = args[i]; + switch ($type(item)){ + case 'element': item = [item]; break; + case 'string': item = this.document.getElements(item, true); break; + default: item = false; + } + if (item) elements.extend(item); + } + return new Elements(elements); + }, + + getDocument: function(){ + return this.document; + }, + + getWindow: function(){ + return this; + } + +}); + +$.string = function(id, nocash, doc){ + id = doc.getElementById(id); + return (id) ? $.element(id, nocash) : null; +}; + +$.element = function(el, nocash){ + $uid(el); + if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){ + var proto = Element.Prototype; + for (var p in proto) el[p] = proto[p]; + }; + return el; +}; + +$.object = function(obj, nocash, doc){ + if (obj.toElement) return $.element(obj.toElement(doc), nocash); + return null; +}; + +$.textnode = $.whitespace = $.window = $.document = $arguments(0); + +Native.implement([Element, Document], { + + getElement: function(selector, nocash){ + return $(this.getElements(selector, true)[0] || null, nocash); + }, + + getElements: function(tags, nocash){ + tags = tags.split(','); + var elements = []; + var ddup = (tags.length > 1); + tags.each(function(tag){ + var partial = this.getElementsByTagName(tag.trim()); + (ddup) ? elements.extend(partial) : elements = partial; + }, this); + return new Elements(elements, {ddup: ddup, cash: !nocash}); + } + +}); + +Element.Storage = { + + get: function(uid){ + return (this[uid] || (this[uid] = {})); + } + +}; + +Element.Inserters = new Hash({ + + before: function(context, element){ + if (element.parentNode) element.parentNode.insertBefore(context, element); + }, + + after: function(context, element){ + if (!element.parentNode) return; + var next = element.nextSibling; + (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context); + }, + + bottom: function(context, element){ + element.appendChild(context); + }, + + top: function(context, element){ + var first = element.firstChild; + (first) ? element.insertBefore(context, first) : element.appendChild(context); + } + +}); + +Element.Inserters.inside = Element.Inserters.bottom; + +Element.Inserters.each(function(value, key){ + + var Key = key.capitalize(); + + Element.implement('inject' + Key, function(el){ + value(this, $(el, true)); + return this; + }); + + Element.implement('grab' + Key, function(el){ + value($(el, true), this); + return this; + }); + +}); + +Element.implement({ + + getDocument: function(){ + return this.ownerDocument; + }, + + getWindow: function(){ + return this.ownerDocument.getWindow(); + }, + + getElementById: function(id, nocash){ + var el = this.ownerDocument.getElementById(id); + if (!el) return null; + for (var parent = el.parentNode; parent != this; parent = parent.parentNode){ + if (!parent) return null; + } + return $.element(el, nocash); + }, + + set: function(prop, value){ + switch ($type(prop)){ + case 'object': + for (var p in prop) this.set(p, prop[p]); + break; + case 'string': + var property = Element.Properties.get(prop); + (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value); + } + return this; + }, + + get: function(prop){ + var property = Element.Properties.get(prop); + return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop); + }, + + erase: function(prop){ + var property = Element.Properties.get(prop); + (property && property.erase) ? property.erase.apply(this, Array.slice(arguments, 1)) : this.removeProperty(prop); + return this; + }, + + match: function(tag){ + return (!tag || Element.get(this, 'tag') == tag); + }, + + inject: function(el, where){ + Element.Inserters.get(where || 'bottom')(this, $(el, true)); + return this; + }, + + wraps: function(el, where){ + el = $(el, true); + return this.replaces(el).grab(el, where); + }, + + grab: function(el, where){ + Element.Inserters.get(where || 'bottom')($(el, true), this); + return this; + }, + + appendText: function(text, where){ + return this.grab(this.getDocument().newTextNode(text), where); + }, + + adopt: function(){ + Array.flatten(arguments).each(function(element){ + element = $(element, true); + if (element) this.appendChild(element); + }, this); + return this; + }, + + dispose: function(){ + return (this.parentNode) ? this.parentNode.removeChild(this) : this; + }, + + clone: function(contents, keepid){ + switch ($type(this)){ + case 'element': + var attributes = {}; + for (var j = 0, l = this.attributes.length; j < l; j++){ + var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase(); + if (Browser.Engine.trident && (/input/i).test(this.tagName) && (/width|height/).test(key)) continue; + var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue; + if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue; + if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value; + } + var element = new Element(this.nodeName.toLowerCase(), attributes); + if (contents !== false){ + for (var i = 0, k = this.childNodes.length; i < k; i++){ + var child = Element.clone(this.childNodes[i], true, keepid); + if (child) element.grab(child); + } + } + return element; + case 'textnode': return document.newTextNode(this.nodeValue); + } + return null; + }, + + replaces: function(el){ + el = $(el, true); + el.parentNode.replaceChild(this, el); + return this; + }, + + hasClass: function(className){ + return this.className.contains(className, ' '); + }, + + addClass: function(className){ + if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); + return this; + }, + + removeClass: function(className){ + this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1').clean(); + return this; + }, + + toggleClass: function(className){ + return this.hasClass(className) ? this.removeClass(className) : this.addClass(className); + }, + + getComputedStyle: function(property){ + if (this.currentStyle) return this.currentStyle[property.camelCase()]; + var computed = this.getWindow().getComputedStyle(this, null); + return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null; + }, + + empty: function(){ + $A(this.childNodes).each(function(node){ + Browser.freeMem(node); + Element.empty(node); + Element.dispose(node); + }, this); + return this; + }, + + destroy: function(){ + Browser.freeMem(this.empty().dispose()); + return null; + }, + + getSelected: function(){ + return new Elements($A(this.options).filter(function(option){ + return option.selected; + })); + }, + + toQueryString: function(){ + var queryString = []; + this.getElements('input, select, textarea').each(function(el){ + if (!el.name || el.disabled) return; + var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){ + return opt.value; + }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value; + $splat(value).each(function(val){ + if (val) queryString.push(el.name + '=' + encodeURIComponent(val)); + }); + }); + return queryString.join('&'); + }, + + getProperty: function(attribute){ + var EA = Element.Attributes, key = EA.Props[attribute]; + var value = (key) ? this[key] : this.getAttribute(attribute, 2); + return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null; + }, + + getProperties: function(){ + var args = $A(arguments); + return args.map(function(attr){ + return this.getProperty(attr); + }, this).associate(args); + }, + + setProperty: function(attribute, value){ + var EA = Element.Attributes, key = EA.Props[attribute], hasValue = $defined(value); + if (key && EA.Bools[attribute]) value = (value || !hasValue) ? true : false; + else if (!hasValue) return this.removeProperty(attribute); + (key) ? this[key] = value : this.setAttribute(attribute, value); + return this; + }, + + setProperties: function(attributes){ + for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); + return this; + }, + + removeProperty: function(attribute){ + var EA = Element.Attributes, key = EA.Props[attribute], isBool = (key && EA.Bools[attribute]); + (key) ? this[key] = (isBool) ? false : '' : this.removeAttribute(attribute); + return this; + }, + + removeProperties: function(){ + Array.each(arguments, this.removeProperty, this); + return this; + } + +}); + +(function(){ + +var walk = function(element, walk, start, match, all, nocash){ + var el = element[start || walk]; + var elements = []; + while (el){ + if (el.nodeType == 1 && (!match || Element.match(el, match))){ + elements.push(el); + if (!all) break; + } + el = el[walk]; + } + return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : $(elements[0], nocash); +}; + +Element.implement({ + + getPrevious: function(match, nocash){ + return walk(this, 'previousSibling', null, match, false, nocash); + }, + + getAllPrevious: function(match, nocash){ + return walk(this, 'previousSibling', null, match, true, nocash); + }, + + getNext: function(match, nocash){ + return walk(this, 'nextSibling', null, match, false, nocash); + }, + + getAllNext: function(match, nocash){ + return walk(this, 'nextSibling', null, match, true, nocash); + }, + + getFirst: function(match, nocash){ + return walk(this, 'nextSibling', 'firstChild', match, false, nocash); + }, + + getLast: function(match, nocash){ + return walk(this, 'previousSibling', 'lastChild', match, false, nocash); + }, + + getParent: function(match, nocash){ + return walk(this, 'parentNode', null, match, false, nocash); + }, + + getParents: function(match, nocash){ + return walk(this, 'parentNode', null, match, true, nocash); + }, + + getChildren: function(match, nocash){ + return walk(this, 'nextSibling', 'firstChild', match, true, nocash); + }, + + hasChild: function(el){ + el = $(el, true); + return (!!el && $A(this.getElementsByTagName(el.tagName)).contains(el)); + } + +}); + +})(); + +Element.Properties = new Hash; + +Element.Properties.style = { + + set: function(style){ + this.style.cssText = style; + }, + + get: function(){ + return this.style.cssText; + }, + + erase: function(){ + this.style.cssText = ''; + } + +}; + +Element.Properties.tag = {get: function(){ + return this.tagName.toLowerCase(); +}}; + +Element.Properties.href = {get: function(){ + return (!this.href) ? null : this.href.replace(new RegExp('^' + document.location.protocol + '\/\/' + document.location.host), ''); +}}; + +Element.Properties.html = {set: function(){ + return this.innerHTML = Array.flatten(arguments).join(''); +}}; + +Native.implement([Element, Window, Document], { + + addListener: function(type, fn){ + if (this.addEventListener) this.addEventListener(type, fn, false); + else this.attachEvent('on' + type, fn); + return this; + }, + + removeListener: function(type, fn){ + if (this.removeEventListener) this.removeEventListener(type, fn, false); + else this.detachEvent('on' + type, fn); + return this; + }, + + retrieve: function(property, dflt){ + var storage = Element.Storage.get(this.uid); + var prop = storage[property]; + if ($defined(dflt) && !$defined(prop)) prop = storage[property] = dflt; + return $pick(prop); + }, + + store: function(property, value){ + var storage = Element.Storage.get(this.uid); + storage[property] = value; + return this; + }, + + eliminate: function(property){ + var storage = Element.Storage.get(this.uid); + delete storage[property]; + return this; + } + +}); + +Element.Attributes = new Hash({ + Props: {'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'text': (Browser.Engine.trident) ? 'innerText' : 'textContent'}, + Bools: ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'], + Camels: ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'] +}); + +Browser.freeMem = function(item){ + if (!item) return; + if (Browser.Engine.trident && (/object/i).test(item.tagName)){ + for (var p in item){ + if (typeof item[p] == 'function') item[p] = $empty; + } + Element.dispose(item); + } + if (item.uid && item.removeEvents) item.removeEvents(); +}; + +(function(EA){ + + var EAB = EA.Bools, EAC = EA.Camels; + EA.Bools = EAB = EAB.associate(EAB); + Hash.extend(Hash.combine(EA.Props, EAB), EAC.associate(EAC.map(function(v){ + return v.toLowerCase(); + }))); + EA.erase('Camels'); + +})(Element.Attributes); + +window.addListener('unload', function(){ + window.removeListener('unload', arguments.callee); + document.purge(); + if (Browser.Engine.trident) CollectGarbage(); +}); + +/* +Script: Element.Event.js + Contains Element methods for dealing with events, and custom Events. + +License: + MIT-style license. +*/ + +Element.Properties.events = {set: function(events){ + this.addEvents(events); +}}; + +Native.implement([Element, Window, Document], { + + addEvent: function(type, fn){ + var events = this.retrieve('events', {}); + events[type] = events[type] || {'keys': [], 'values': []}; + if (events[type].keys.contains(fn)) return this; + events[type].keys.push(fn); + var realType = type, custom = Element.Events.get(type), condition = fn, self = this; + if (custom){ + if (custom.onAdd) custom.onAdd.call(this, fn); + if (custom.condition){ + condition = function(event){ + if (custom.condition.call(this, event)) return fn.call(this, event); + return false; + }; + } + realType = custom.base || realType; + } + var defn = function(){ + return fn.call(self); + }; + var nativeEvent = Element.NativeEvents[realType] || 0; + if (nativeEvent){ + if (nativeEvent == 2){ + defn = function(event){ + event = new Event(event, self.getWindow()); + if (condition.call(self, event) === false) event.stop(); + }; + } + this.addListener(realType, defn); + } + events[type].values.push(defn); + return this; + }, + + removeEvent: function(type, fn){ + var events = this.retrieve('events'); + if (!events || !events[type]) return this; + var pos = events[type].keys.indexOf(fn); + if (pos == -1) return this; + var key = events[type].keys.splice(pos, 1)[0]; + var value = events[type].values.splice(pos, 1)[0]; + var custom = Element.Events.get(type); + if (custom){ + if (custom.onRemove) custom.onRemove.call(this, fn); + type = custom.base || type; + } + return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this; + }, + + addEvents: function(events){ + for (var event in events) this.addEvent(event, events[event]); + return this; + }, + + removeEvents: function(type){ + var events = this.retrieve('events'); + if (!events) return this; + if (!type){ + for (var evType in events) this.removeEvents(evType); + events = null; + } else if (events[type]){ + while (events[type].keys[0]) this.removeEvent(type, events[type].keys[0]); + events[type] = null; + } + return this; + }, + + fireEvent: function(type, args, delay){ + var events = this.retrieve('events'); + if (!events || !events[type]) return this; + events[type].keys.each(function(fn){ + fn.create({'bind': this, 'delay': delay, 'arguments': args})(); + }, this); + return this; + }, + + cloneEvents: function(from, type){ + from = $(from); + var fevents = from.retrieve('events'); + if (!fevents) return this; + if (!type){ + for (var evType in fevents) this.cloneEvents(from, evType); + } else if (fevents[type]){ + fevents[type].keys.each(function(fn){ + this.addEvent(type, fn); + }, this); + } + return this; + } + +}); + +Element.NativeEvents = { + click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons + mousewheel: 2, DOMMouseScroll: 2, //mouse wheel + mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement + keydown: 2, keypress: 2, keyup: 2, //keyboard + focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements + load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window + error: 1, abort: 1, scroll: 1 //misc +}; + +(function(){ + +var $check = function(event){ + var related = event.relatedTarget; + if (related == undefined) return true; + if (related === false) return false; + return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related)); +}; + +Element.Events = new Hash({ + + mouseenter: { + base: 'mouseover', + condition: $check + }, + + mouseleave: { + base: 'mouseout', + condition: $check + }, + + mousewheel: { + base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel' + } + +}); + +})(); + +/* +Script: Element.Style.js + Contains methods for interacting with the styles of Elements in a fashionable way. + +License: + MIT-style license. +*/ + +Element.Properties.styles = {set: function(styles){ + this.setStyles(styles); +}}; + +Element.Properties.opacity = { + + set: function(opacity, novisibility){ + if (!novisibility){ + if (opacity == 0){ + if (this.style.visibility != 'hidden') this.style.visibility = 'hidden'; + } else { + if (this.style.visibility != 'visible') this.style.visibility = 'visible'; + } + } + if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1; + if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')'; + this.style.opacity = opacity; + this.store('opacity', opacity); + }, + + get: function(){ + return this.retrieve('opacity', 1); + } + +}; + +Element.implement({ + + setOpacity: function(value){ + return this.set('opacity', value, true); + }, + + getOpacity: function(){ + return this.get('opacity'); + }, + + setStyle: function(property, value){ + switch (property){ + case 'opacity': return this.set('opacity', parseFloat(value)); + case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat'; + } + property = property.camelCase(); + if ($type(value) != 'string'){ + var map = (Element.Styles.get(property) || '@').split(' '); + value = $splat(value).map(function(val, i){ + if (!map[i]) return ''; + return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val; + }).join(' '); + } else if (value == String(Number(value))){ + value = Math.round(value); + } + this.style[property] = value; + return this; + }, + + getStyle: function(property){ + switch (property){ + case 'opacity': return this.get('opacity'); + case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat'; + } + property = property.camelCase(); + var result = this.style[property]; + if (!$chk(result)){ + result = []; + for (var style in Element.ShortStyles){ + if (property != style) continue; + for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s)); + return result.join(' '); + } + result = this.getComputedStyle(property); + } + if (result){ + result = String(result); + var color = result.match(/rgba?\([\d\s,]+\)/); + if (color) result = result.replace(color[0], color[0].rgbToHex()); + } + if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result)))){ + if (property.test(/^(height|width)$/)){ + var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; + values.each(function(value){ + size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt(); + }, this); + return this['offset' + property.capitalize()] - size + 'px'; + } + if (Browser.Engine.presto && String(result).test('px')) return result; + if (property.test(/(border(.+)Width|margin|padding)/)) return '0px'; + } + return result; + }, + + setStyles: function(styles){ + for (var style in styles) this.setStyle(style, styles[style]); + return this; + }, + + getStyles: function(){ + var result = {}; + Array.each(arguments, function(key){ + result[key] = this.getStyle(key); + }, this); + return result; + } + +}); + +Element.Styles = new Hash({ + left: '@px', top: '@px', bottom: '@px', right: '@px', + width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px', + backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', + fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)', + margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', + borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', + zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@' +}); + +Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}}; + +['Top', 'Right', 'Bottom', 'Left'].each(function(direction){ + var Short = Element.ShortStyles; + var All = Element.Styles; + ['margin', 'padding'].each(function(style){ + var sd = style + direction; + Short[style][sd] = All[sd] = '@px'; + }); + var bd = 'border' + direction; + Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)'; + var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color'; + Short[bd] = {}; + Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px'; + Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@'; + Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)'; +}); + + +/* +Script: Element.Dimensions.js + Contains methods to work with size, scroll, or positioning of Elements and the window object. + +License: + MIT-style license. + +Credits: + - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html). + - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html). +*/ + +(function(){ + +Element.implement({ + + scrollTo: function(x, y){ + if (isBody(this)){ + this.getWindow().scrollTo(x, y); + } else { + this.scrollLeft = x; + this.scrollTop = y; + } + return this; + }, + + getSize: function(){ + if (isBody(this)) return this.getWindow().getSize(); + return {x: this.offsetWidth, y: this.offsetHeight}; + }, + + getScrollSize: function(){ + if (isBody(this)) return this.getWindow().getScrollSize(); + return {x: this.scrollWidth, y: this.scrollHeight}; + }, + + getScroll: function(){ + if (isBody(this)) return this.getWindow().getScroll(); + return {x: this.scrollLeft, y: this.scrollTop}; + }, + + getScrolls: function(){ + var element = this, position = {x: 0, y: 0}; + while (element && !isBody(element)){ + position.x += element.scrollLeft; + position.y += element.scrollTop; + element = element.parentNode; + } + return position; + }, + + getOffsetParent: function(){ + var element = this; + if (isBody(element)) return null; + if (!Browser.Engine.trident) return element.offsetParent; + while ((element = element.parentNode) && !isBody(element)){ + if (styleString(element, 'position') != 'static') return element; + } + return null; + }, + + getOffsets: function(){ + var element = this, position = {x: 0, y: 0}; + if (isBody(this)) return position; + + while (element && !isBody(element)){ + position.x += element.offsetLeft; + position.y += element.offsetTop; + + if (Browser.Engine.gecko){ + if (!borderBox(element)){ + position.x += leftBorder(element); + position.y += topBorder(element); + } + var parent = element.parentNode; + if (parent && styleString(parent, 'overflow') != 'visible'){ + position.x += leftBorder(parent); + position.y += topBorder(parent); + } + } else if (element != this && (Browser.Engine.trident || Browser.Engine.webkit)){ + position.x += leftBorder(element); + position.y += topBorder(element); + } + + element = element.offsetParent; + if (Browser.Engine.trident){ + while (element && !element.currentStyle.hasLayout) element = element.offsetParent; + } + } + if (Browser.Engine.gecko && !borderBox(this)){ + position.x -= leftBorder(this); + position.y -= topBorder(this); + } + return position; + }, + + getPosition: function(relative){ + if (isBody(this)) return {x: 0, y: 0}; + var offset = this.getOffsets(), scroll = this.getScrolls(); + var position = {x: offset.x - scroll.x, y: offset.y - scroll.y}; + var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0}; + return {x: position.x - relativePosition.x, y: position.y - relativePosition.y}; + }, + + getCoordinates: function(element){ + if (isBody(this)) return this.getWindow().getCoordinates(); + var position = this.getPosition(element), size = this.getSize(); + var obj = {left: position.x, top: position.y, width: size.x, height: size.y}; + obj.right = obj.left + obj.width; + obj.bottom = obj.top + obj.height; + return obj; + }, + + computePosition: function(obj){ + return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')}; + }, + + position: function(obj){ + return this.setStyles(this.computePosition(obj)); + } + +}); + +Native.implement([Document, Window], { + + getSize: function(){ + var win = this.getWindow(); + if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight}; + var doc = getCompatElement(this); + return {x: doc.clientWidth, y: doc.clientHeight}; + }, + + getScroll: function(){ + var win = this.getWindow(); + var doc = getCompatElement(this); + return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop}; + }, + + getScrollSize: function(){ + var doc = getCompatElement(this); + var min = this.getSize(); + return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)}; + }, + + getPosition: function(){ + return {x: 0, y: 0}; + }, + + getCoordinates: function(){ + var size = this.getSize(); + return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x}; + } + +}); + +// private methods + +var styleString = Element.getComputedStyle; + +function styleNumber(element, style){ + return styleString(element, style).toInt() || 0; +}; + +function borderBox(element){ + return styleString(element, '-moz-box-sizing') == 'border-box'; +}; + +function topBorder(element){ + return styleNumber(element, 'border-top-width'); +}; + +function leftBorder(element){ + return styleNumber(element, 'border-left-width'); +}; + +function isBody(element){ + return (/^(?:body|html)$/i).test(element.tagName); +}; + +function getCompatElement(element){ + var doc = element.getDocument(); + return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; +}; + +})(); + +//aliases + +Native.implement([Window, Document, Element], { + + getHeight: function(){ + return this.getSize().y; + }, + + getWidth: function(){ + return this.getSize().x; + }, + + getScrollTop: function(){ + return this.getScroll().y; + }, + + getScrollLeft: function(){ + return this.getScroll().x; + }, + + getScrollHeight: function(){ + return this.getScrollSize().y; + }, + + getScrollWidth: function(){ + return this.getScrollSize().x; + }, + + getTop: function(){ + return this.getPosition().y; + }, + + getLeft: function(){ + return this.getPosition().x; + } + +}); + +/* +Script: Selectors.js + Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support. + +License: + MIT-style license. +*/ + +Native.implement([Document, Element], { + + getElements: function(expression, nocash){ + expression = expression.split(','); + var items, local = {}; + for (var i = 0, l = expression.length; i < l; i++){ + var selector = expression[i], elements = Selectors.Utils.search(this, selector, local); + if (i != 0 && elements.item) elements = $A(elements); + items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements); + } + return new Elements(items, {ddup: (expression.length > 1), cash: !nocash}); + } + +}); + +Element.implement({ + + match: function(selector){ + if (!selector) return true; + var tagid = Selectors.Utils.parseTagAndID(selector); + var tag = tagid[0], id = tagid[1]; + if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false; + var parsed = Selectors.Utils.parseSelector(selector); + return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true; + } + +}); + +var Selectors = {Cache: {nth: {}, parsed: {}}}; + +Selectors.RegExps = { + id: (/#([\w-]+)/), + tag: (/^(\w+|\*)/), + quick: (/^(\w+|\*)$/), + splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g), + combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g) +}; + +Selectors.Utils = { + + chk: function(item, uniques){ + if (!uniques) return true; + var uid = $uid(item); + if (!uniques[uid]) return uniques[uid] = true; + return false; + }, + + parseNthArgument: function(argument){ + if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument]; + var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/); + if (!parsed) return false; + var inta = parseInt(parsed[1]); + var a = (inta || inta === 0) ? inta : 1; + var special = parsed[2] || false; + var b = parseInt(parsed[3]) || 0; + if (a != 0){ + b--; + while (b < 1) b += a; + while (b >= a) b -= a; + } else { + a = b; + special = 'index'; + } + switch (special){ + case 'n': parsed = {a: a, b: b, special: 'n'}; break; + case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break; + case 'even': parsed = {a: 2, b: 1, special: 'n'}; break; + case 'first': parsed = {a: 0, special: 'index'}; break; + case 'last': parsed = {special: 'last-child'}; break; + case 'only': parsed = {special: 'only-child'}; break; + default: parsed = {a: (a - 1), special: 'index'}; + } + + return Selectors.Cache.nth[argument] = parsed; + }, + + parseSelector: function(selector){ + if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector]; + var m, parsed = {classes: [], pseudos: [], attributes: []}; + while ((m = Selectors.RegExps.combined.exec(selector))){ + var cn = m[1], an = m[2], ao = m[3], av = m[4], pn = m[5], pa = m[6]; + if (cn){ + parsed.classes.push(cn); + } else if (pn){ + var parser = Selectors.Pseudo.get(pn); + if (parser) parsed.pseudos.push({parser: parser, argument: pa}); + else parsed.attributes.push({name: pn, operator: '=', value: pa}); + } else if (an){ + parsed.attributes.push({name: an, operator: ao, value: av}); + } + } + if (!parsed.classes.length) delete parsed.classes; + if (!parsed.attributes.length) delete parsed.attributes; + if (!parsed.pseudos.length) delete parsed.pseudos; + if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null; + return Selectors.Cache.parsed[selector] = parsed; + }, + + parseTagAndID: function(selector){ + var tag = selector.match(Selectors.RegExps.tag); + var id = selector.match(Selectors.RegExps.id); + return [(tag) ? tag[1] : '*', (id) ? id[1] : false]; + }, + + filter: function(item, parsed, local){ + var i; + if (parsed.classes){ + for (i = parsed.classes.length; i--; i){ + var cn = parsed.classes[i]; + if (!Selectors.Filters.byClass(item, cn)) return false; + } + } + if (parsed.attributes){ + for (i = parsed.attributes.length; i--; i){ + var att = parsed.attributes[i]; + if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false; + } + } + if (parsed.pseudos){ + for (i = parsed.pseudos.length; i--; i){ + var psd = parsed.pseudos[i]; + if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false; + } + } + return true; + }, + + getByTagAndID: function(ctx, tag, id){ + if (id){ + var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true); + return (item && Selectors.Filters.byTag(item, tag)) ? [item] : []; + } else { + return ctx.getElementsByTagName(tag); + } + }, + + search: function(self, expression, local){ + var splitters = []; + + var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){ + splitters.push(m1); + return ':)' + m2; + }).split(':)'); + + var items, match, filtered, item; + + for (var i = 0, l = selectors.length; i < l; i++){ + + var selector = selectors[i]; + + if (i == 0 && Selectors.RegExps.quick.test(selector)){ + items = self.getElementsByTagName(selector); + continue; + } + + var splitter = splitters[i - 1]; + + var tagid = Selectors.Utils.parseTagAndID(selector); + var tag = tagid[0], id = tagid[1]; + + if (i == 0){ + items = Selectors.Utils.getByTagAndID(self, tag, id); + } else { + var uniques = {}, found = []; + for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques); + items = found; + } + + var parsed = Selectors.Utils.parseSelector(selector); + + if (parsed){ + filtered = []; + for (var m = 0, n = items.length; m < n; m++){ + item = items[m]; + if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item); + } + items = filtered; + } + + } + + return items; + + } + +}; + +Selectors.Getters = { + + ' ': function(found, self, tag, id, uniques){ + var items = Selectors.Utils.getByTagAndID(self, tag, id); + for (var i = 0, l = items.length; i < l; i++){ + var item = items[i]; + if (Selectors.Utils.chk(item, uniques)) found.push(item); + } + return found; + }, + + '>': function(found, self, tag, id, uniques){ + var children = Selectors.Utils.getByTagAndID(self, tag, id); + for (var i = 0, l = children.length; i < l; i++){ + var child = children[i]; + if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child); + } + return found; + }, + + '+': function(found, self, tag, id, uniques){ + while ((self = self.nextSibling)){ + if (self.nodeType == 1){ + if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self); + break; + } + } + return found; + }, + + '~': function(found, self, tag, id, uniques){ + + while ((self = self.nextSibling)){ + if (self.nodeType == 1){ + if (!Selectors.Utils.chk(self, uniques)) break; + if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self); + } + } + return found; + } + +}; + +Selectors.Filters = { + + byTag: function(self, tag){ + return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag)); + }, + + byID: function(self, id){ + return (!id || (self.id && self.id == id)); + }, + + byClass: function(self, klass){ + return (self.className && self.className.contains(klass, ' ')); + }, + + byPseudo: function(self, parser, argument, local){ + return parser.call(self, argument, local); + }, + + byAttribute: function(self, name, operator, value){ + var result = Element.prototype.getProperty.call(self, name); + if (!result) return false; + if (!operator || value == undefined) return true; + switch (operator){ + case '=': return (result == value); + case '*=': return (result.contains(value)); + case '^=': return (result.substr(0, value.length) == value); + case '$=': return (result.substr(result.length - value.length) == value); + case '!=': return (result != value); + case '~=': return result.contains(value, ' '); + case '|=': return result.contains(value, '-'); + } + return false; + } + +}; + +Selectors.Pseudo = new Hash({ + + // w3c pseudo selectors + + empty: function(){ + return !(this.innerText || this.textContent || '').length; + }, + + not: function(selector){ + return !Element.match(this, selector); + }, + + contains: function(text){ + return (this.innerText || this.textContent || '').contains(text); + }, + + 'first-child': function(){ + return Selectors.Pseudo.index.call(this, 0); + }, + + 'last-child': function(){ + var element = this; + while ((element = element.nextSibling)){ + if (element.nodeType == 1) return false; + } + return true; + }, + + 'only-child': function(){ + var prev = this; + while ((prev = prev.previousSibling)){ + if (prev.nodeType == 1) return false; + } + var next = this; + while ((next = next.nextSibling)){ + if (next.nodeType == 1) return false; + } + return true; + }, + + 'nth-child': function(argument, local){ + argument = (argument == undefined) ? 'n' : argument; + var parsed = Selectors.Utils.parseNthArgument(argument); + if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local); + var count = 0; + local.positions = local.positions || {}; + var uid = $uid(this); + if (!local.positions[uid]){ + var self = this; + while ((self = self.previousSibling)){ + if (self.nodeType != 1) continue; + count ++; + var position = local.positions[$uid(self)]; + if (position != undefined){ + count = position + count; + break; + } + } + local.positions[uid] = count; + } + return (local.positions[uid] % parsed.a == parsed.b); + }, + + // custom pseudo selectors + + index: function(index){ + var element = this, count = 0; + while ((element = element.previousSibling)){ + if (element.nodeType == 1 && ++count > index) return false; + } + return (count == index); + }, + + even: function(argument, local){ + return Selectors.Pseudo['nth-child'].call(this, '2n+1', local); + }, + + odd: function(argument, local){ + return Selectors.Pseudo['nth-child'].call(this, '2n', local); + } + +}); + +/* +Script: Domready.js + Contains the domready custom event. + +License: + MIT-style license. +*/ + +Element.Events.domready = { + + onAdd: function(fn){ + if (Browser.loaded) fn.call(this); + } + +}; + +(function(){ + + var domready = function(){ + if (Browser.loaded) return; + Browser.loaded = true; + window.fireEvent('domready'); + document.fireEvent('domready'); + }; + + switch (Browser.Engine.name){ + + case 'webkit': (function(){ + (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50); + })(); break; + + case 'trident': + var temp = document.createElement('div'); + (function(){ + ($try(function(){ + temp.doScroll('left'); + return $(temp).inject(document.body).set('html', 'temp').dispose(); + })) ? domready() : arguments.callee.delay(50); + })(); + break; + + default: + window.addEvent('load', domready); + document.addEvent('DOMContentLoaded', domready); + + } + +})(); + +/* +Script: JSON.js + JSON encoder and decoder. + +License: + MIT-style license. + +See Also: + +*/ + +var JSON = new Hash({ + + encode: function(obj){ + switch ($type(obj)){ + case 'string': + return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"'; + case 'array': + return '[' + String(obj.map(JSON.encode).filter($defined)) + ']'; + case 'object': case 'hash': + var string = []; + Hash.each(obj, function(value, key){ + var json = JSON.encode(value); + if (json) string.push(JSON.encode(key) + ':' + json); + }); + return '{' + string + '}'; + case 'number': case 'boolean': return String(obj); + case false: return 'null'; + } + return null; + }, + + $specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}, + + $replaceChars: function(chr){ + return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16); + }, + + decode: function(string, secure){ + if ($type(string) != 'string' || !string.length) return null; + if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null; + return eval('(' + string + ')'); + } + +}); + +Native.implement([Hash, Array, String, Number], { + + toJSON: function(){ + return JSON.encode(this); + } + +}); + + +/* +Script: Cookie.js + Class for creating, loading, and saving browser Cookies. + +License: + MIT-style license. + +Credits: + Based on the functions by Peter-Paul Koch (http://quirksmode.org). +*/ + +var Cookie = new Class({ + + Implements: Options, + + options: { + path: false, + domain: false, + duration: false, + secure: false, + document: document + }, + + initialize: function(key, options){ + this.key = key; + this.setOptions(options); + }, + + write: function(value){ + value = encodeURIComponent(value); + if (this.options.domain) value += '; domain=' + this.options.domain; + if (this.options.path) value += '; path=' + this.options.path; + if (this.options.duration){ + var date = new Date(); + date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000); + value += '; expires=' + date.toGMTString(); + } + if (this.options.secure) value += '; secure'; + this.options.document.cookie = this.key + '=' + value; + return this; + }, + + read: function(){ + var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)'); + return (value) ? decodeURIComponent(value[1]) : null; + }, + + dispose: function(){ + new Cookie(this.key, $merge(this.options, {duration: -1})).write(''); + return this; + } + +}); + +Cookie.write = function(key, value, options){ + return new Cookie(key, options).write(value); +}; + +Cookie.read = function(key){ + return new Cookie(key).read(); +}; + +Cookie.dispose = function(key, options){ + return new Cookie(key, options).dispose(); +}; + +/* +Script: Swiff.js + Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication. + +License: + MIT-style license. + +Credits: + Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject. +*/ + +var Swiff = new Class({ + + Implements: [Options], + + options: { + id: null, + height: 1, + width: 1, + container: null, + properties: {}, + params: { + quality: 'high', + allowScriptAccess: 'always', + wMode: 'transparent', + swLiveConnect: true + }, + callBacks: {}, + vars: {} + }, + + toElement: function(){ + return this.object; + }, + + initialize: function(path, options){ + this.instance = 'Swiff_' + $time(); + + this.setOptions(options); + options = this.options; + var id = this.id = options.id || this.instance; + var container = $(options.container); + + Swiff.CallBacks[this.instance] = {}; + + var params = options.params, vars = options.vars, callBacks = options.callBacks; + var properties = $extend({height: options.height, width: options.width}, options.properties); + + var self = this; + + for (var callBack in callBacks){ + Swiff.CallBacks[this.instance][callBack] = (function(option){ + return function(){ + return option.apply(self.object, arguments); + }; + })(callBacks[callBack]); + vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack; + } + + params.flashVars = Hash.toQueryString(vars); + if (Browser.Engine.trident){ + properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; + params.movie = path; + } else { + properties.type = 'application/x-shockwave-flash'; + properties.data = path; + } + var build = ''; + } + build += ''; + this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild; + }, + + replaces: function(element){ + element = $(element, true); + element.parentNode.replaceChild(this.toElement(), element); + return this; + }, + + inject: function(element){ + $(element, true).appendChild(this.toElement()); + return this; + }, + + remote: function(){ + return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments)); + } + +}); + +Swiff.CallBacks = {}; + +Swiff.remote = function(obj, fn){ + var rs = obj.CallFunction('' + __flash__argumentsToXML(arguments, 2) + ''); + return eval(rs); +}; + +/* +Script: Fx.js + Contains the basic animation logic to be extended by all other Fx Classes. + +License: + MIT-style license. +*/ + +var Fx = new Class({ + + Implements: [Chain, Events, Options], + + options: { + /* + onStart: $empty, + onCancel: $empty, + onComplete: $empty, + */ + fps: 50, + unit: false, + duration: 500, + link: 'ignore', + transition: function(p){ + return -(Math.cos(Math.PI * p) - 1) / 2; + } + }, + + initialize: function(options){ + this.subject = this.subject || this; + this.setOptions(options); + this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt(); + var wait = this.options.wait; + if (wait === false) this.options.link = 'cancel'; + }, + + step: function(){ + var time = $time(); + if (time < this.time + this.options.duration){ + var delta = this.options.transition((time - this.time) / this.options.duration); + this.set(this.compute(this.from, this.to, delta)); + } else { + this.set(this.compute(this.from, this.to, 1)); + this.complete(); + } + }, + + set: function(now){ + return now; + }, + + compute: function(from, to, delta){ + return Fx.compute(from, to, delta); + }, + + check: function(caller){ + if (!this.timer) return true; + switch (this.options.link){ + case 'cancel': this.cancel(); return true; + case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false; + } + return false; + }, + + start: function(from, to){ + if (!this.check(arguments.callee, from, to)) return this; + this.from = from; + this.to = to; + this.time = 0; + this.startTimer(); + this.onStart(); + return this; + }, + + complete: function(){ + if (this.stopTimer()) this.onComplete(); + return this; + }, + + cancel: function(){ + if (this.stopTimer()) this.onCancel(); + return this; + }, + + onStart: function(){ + this.fireEvent('start', this.subject); + }, + + onComplete: function(){ + this.fireEvent('complete', this.subject); + if (!this.callChain()) this.fireEvent('chainComplete', this.subject); + }, + + onCancel: function(){ + this.fireEvent('cancel', this.subject).clearChain(); + }, + + pause: function(){ + this.stopTimer(); + return this; + }, + + resume: function(){ + this.startTimer(); + return this; + }, + + stopTimer: function(){ + if (!this.timer) return false; + this.time = $time() - this.time; + this.timer = $clear(this.timer); + return true; + }, + + startTimer: function(){ + if (this.timer) return false; + this.time = $time() - this.time; + this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this); + return true; + } + +}); + +Fx.compute = function(from, to, delta){ + return (to - from) * delta + from; +}; + +Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000}; + + +/* +Script: Fx.CSS.js + Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements. + +License: + MIT-style license. +*/ + +Fx.CSS = new Class({ + + Extends: Fx, + + //prepares the base from/to object + + prepare: function(element, property, values){ + values = $splat(values); + var values1 = values[1]; + if (!$chk(values1)){ + values[1] = values[0]; + values[0] = element.getStyle(property); + } + var parsed = values.map(this.parse); + return {from: parsed[0], to: parsed[1]}; + }, + + //parses a value into an array + + parse: function(value){ + value = $lambda(value)(); + value = (typeof value == 'string') ? value.split(' ') : $splat(value); + return value.map(function(val){ + val = String(val); + var found = false; + Fx.CSS.Parsers.each(function(parser, key){ + if (found) return; + var parsed = parser.parse(val); + if ($chk(parsed)) found = {value: parsed, parser: parser}; + }); + found = found || {value: val, parser: Fx.CSS.Parsers.String}; + return found; + }); + }, + + //computes by a from and to prepared objects, using their parsers. + + compute: function(from, to, delta){ + var computed = []; + (Math.min(from.length, to.length)).times(function(i){ + computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser}); + }); + computed.$family = {name: 'fx:css:value'}; + return computed; + }, + + //serves the value as settable + + serve: function(value, unit){ + if ($type(value) != 'fx:css:value') value = this.parse(value); + var returned = []; + value.each(function(bit){ + returned = returned.concat(bit.parser.serve(bit.value, unit)); + }); + return returned; + }, + + //renders the change to an element + + render: function(element, property, value, unit){ + element.setStyle(property, this.serve(value, unit)); + }, + + //searches inside the page css to find the values for a selector + + search: function(selector){ + if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector]; + var to = {}; + Array.each(document.styleSheets, function(sheet, j){ + var href = sheet.href; + if (href && href.contains('://') && !href.contains(document.domain)) return; + var rules = sheet.rules || sheet.cssRules; + Array.each(rules, function(rule, i){ + if (!rule.style) return; + var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){ + return m.toLowerCase(); + }) : null; + if (!selectorText || !selectorText.test('^' + selector + '$')) return; + Element.Styles.each(function(value, style){ + if (!rule.style[style] || Element.ShortStyles[style]) return; + value = String(rule.style[style]); + to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value; + }); + }); + }); + return Fx.CSS.Cache[selector] = to; + } + +}); + +Fx.CSS.Cache = {}; + +Fx.CSS.Parsers = new Hash({ + + Color: { + parse: function(value){ + if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true); + return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false; + }, + compute: function(from, to, delta){ + return from.map(function(value, i){ + return Math.round(Fx.compute(from[i], to[i], delta)); + }); + }, + serve: function(value){ + return value.map(Number); + } + }, + + Number: { + parse: parseFloat, + compute: Fx.compute, + serve: function(value, unit){ + return (unit) ? value + unit : value; + } + }, + + String: { + parse: $lambda(false), + compute: $arguments(1), + serve: $arguments(0) + } + +}); + + +/* +Script: Fx.Tween.js + Formerly Fx.Style, effect to transition any CSS property for an element. + +License: + MIT-style license. +*/ + +Fx.Tween = new Class({ + + Extends: Fx.CSS, + + initialize: function(element, options){ + this.element = this.subject = $(element); + this.parent(options); + }, + + set: function(property, now){ + if (arguments.length == 1){ + now = property; + property = this.property || this.options.property; + } + this.render(this.element, property, now, this.options.unit); + return this; + }, + + start: function(property, from, to){ + if (!this.check(arguments.callee, property, from, to)) return this; + var args = Array.flatten(arguments); + this.property = this.options.property || args.shift(); + var parsed = this.prepare(this.element, this.property, args); + return this.parent(parsed.from, parsed.to); + } + +}); + +Element.Properties.tween = { + + set: function(options){ + var tween = this.retrieve('tween'); + if (tween) tween.cancel(); + return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options)); + }, + + get: function(options){ + if (options || !this.retrieve('tween')){ + if (options || !this.retrieve('tween:options')) this.set('tween', options); + this.store('tween', new Fx.Tween(this, this.retrieve('tween:options'))); + } + return this.retrieve('tween'); + } + +}; + +Element.implement({ + + tween: function(property, from, to){ + this.get('tween').start(arguments); + return this; + }, + + fade: function(how){ + var fade = this.get('tween'), o = 'opacity', toggle; + how = $pick(how, 'toggle'); + switch (how){ + case 'in': fade.start(o, 1); break; + case 'out': fade.start(o, 0); break; + case 'show': fade.set(o, 1); break; + case 'hide': fade.set(o, 0); break; + case 'toggle': + var flag = this.retrieve('fade:flag', this.get('opacity') == 1); + fade.start(o, (flag) ? 0 : 1); + this.store('fade:flag', !flag); + toggle = true; + break; + default: fade.start(o, arguments); + } + if (!toggle) this.eliminate('fade:flag'); + return this; + }, + + highlight: function(start, end){ + if (!end){ + end = this.retrieve('highlight:original', this.getStyle('background-color')); + end = (end == 'transparent') ? '#fff' : end; + } + var tween = this.get('tween'); + tween.start('background-color', start || '#ffff88', end).chain(function(){ + this.setStyle('background-color', this.retrieve('highlight:original')); + tween.callChain(); + }.bind(this)); + return this; + } + +}); + + +/* +Script: Fx.Morph.js + Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules. + +License: + MIT-style license. +*/ + +Fx.Morph = new Class({ + + Extends: Fx.CSS, + + initialize: function(element, options){ + this.element = this.subject = $(element); + this.parent(options); + }, + + set: function(now){ + if (typeof now == 'string') now = this.search(now); + for (var p in now) this.render(this.element, p, now[p], this.options.unit); + return this; + }, + + compute: function(from, to, delta){ + var now = {}; + for (var p in from) now[p] = this.parent(from[p], to[p], delta); + return now; + }, + + start: function(properties){ + if (!this.check(arguments.callee, properties)) return this; + if (typeof properties == 'string') properties = this.search(properties); + var from = {}, to = {}; + for (var p in properties){ + var parsed = this.prepare(this.element, p, properties[p]); + from[p] = parsed.from; + to[p] = parsed.to; + } + return this.parent(from, to); + } + +}); + +Element.Properties.morph = { + + set: function(options){ + var morph = this.retrieve('morph'); + if (morph) morph.cancel(); + return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options)); + }, + + get: function(options){ + if (options || !this.retrieve('morph')){ + if (options || !this.retrieve('morph:options')) this.set('morph', options); + this.store('morph', new Fx.Morph(this, this.retrieve('morph:options'))); + } + return this.retrieve('morph'); + } + +}; + +Element.implement({ + + morph: function(props){ + this.get('morph').start(props); + return this; + } + +}); + +/* +Script: Fx.Transitions.js + Contains a set of advanced transitions to be used with any of the Fx Classes. + +License: + MIT-style license. + +Credits: + Easing Equations by Robert Penner, , modified and optimized to be used with MooTools. +*/ + +(function(){ + + var old = Fx.prototype.initialize; + + Fx.prototype.initialize = function(options){ + old.call(this, options); + var trans = this.options.transition; + if (typeof trans == 'string' && (trans = trans.split(':'))){ + var base = Fx.Transitions; + base = base[trans[0]] || base[trans[0].capitalize()]; + if (trans[1]) base = base['ease' + trans[1].capitalize() + (trans[2] ? trans[2].capitalize() : '')]; + this.options.transition = base; + } + }; + +})(); + +Fx.Transition = function(transition, params){ + params = $splat(params); + return $extend(transition, { + easeIn: function(pos){ + return transition(pos, params); + }, + easeOut: function(pos){ + return 1 - transition(1 - pos, params); + }, + easeInOut: function(pos){ + return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2; + } + }); +}; + +Fx.Transitions = new Hash({ + + linear: $arguments(0) + +}); + +Fx.Transitions.extend = function(transitions){ + for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]); +}; + +Fx.Transitions.extend({ + + Pow: function(p, x){ + return Math.pow(p, x[0] || 6); + }, + + Expo: function(p){ + return Math.pow(2, 8 * (p - 1)); + }, + + Circ: function(p){ + return 1 - Math.sin(Math.acos(p)); + }, + + Sine: function(p){ + return 1 - Math.sin((1 - p) * Math.PI / 2); + }, + + Back: function(p, x){ + x = x[0] || 1.618; + return Math.pow(p, 2) * ((x + 1) * p - x); + }, + + Bounce: function(p){ + var value; + for (var a = 0, b = 1; 1; a += b, b /= 2){ + if (p >= (7 - 4 * a) / 11){ + value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b; + break; + } + } + return value; + }, + + Elastic: function(p, x){ + return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3); + } + +}); + +['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){ + Fx.Transitions[transition] = new Fx.Transition(function(p){ + return Math.pow(p, [i + 2]); + }); +}); + + +/* +Script: Request.js + Powerful all purpose Request Class. Uses XMLHTTPRequest. + +License: + MIT-style license. +*/ + +var Request = new Class({ + + Implements: [Chain, Events, Options], + + options: { + /*onRequest: $empty, + onSuccess: $empty, + onFailure: $empty, + onException: $empty,*/ + url: '', + data: '', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }, + async: true, + format: false, + method: 'post', + link: 'ignore', + isSuccess: null, + emulation: true, + urlEncoded: true, + encoding: 'utf-8', + evalScripts: false, + evalResponse: false + }, + + initialize: function(options){ + this.xhr = new Browser.Request(); + this.setOptions(options); + this.options.isSuccess = this.options.isSuccess || this.isSuccess; + this.headers = new Hash(this.options.headers); + }, + + onStateChange: function(){ + if (this.xhr.readyState != 4 || !this.running) return; + this.running = false; + this.status = 0; + $try(function(){ + this.status = this.xhr.status; + }.bind(this)); + if (this.options.isSuccess.call(this, this.status)){ + this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML}; + this.success(this.response.text, this.response.xml); + } else { + this.response = {text: null, xml: null}; + this.failure(); + } + this.xhr.onreadystatechange = $empty; + }, + + isSuccess: function(){ + return ((this.status >= 200) && (this.status < 300)); + }, + + processScripts: function(text){ + if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text); + return text.stripScripts(this.options.evalScripts); + }, + + success: function(text, xml){ + this.onSuccess(this.processScripts(text), xml); + }, + + onSuccess: function(){ + this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain(); + }, + + failure: function(){ + this.onFailure(); + }, + + onFailure: function(){ + this.fireEvent('complete').fireEvent('failure', this.xhr); + }, + + setHeader: function(name, value){ + this.headers.set(name, value); + return this; + }, + + getHeader: function(name){ + return $try(function(){ + return this.xhr.getResponseHeader(name); + }.bind(this)); + }, + + check: function(caller){ + if (!this.running) return true; + switch (this.options.link){ + case 'cancel': this.cancel(); return true; + case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false; + } + return false; + }, + + send: function(options){ + if (!this.check(arguments.callee, options)) return this; + this.running = true; + + var type = $type(options); + if (type == 'string' || type == 'element') options = {data: options}; + + var old = this.options; + options = $extend({data: old.data, url: old.url, method: old.method}, options); + var data = options.data, url = options.url, method = options.method; + + switch ($type(data)){ + case 'element': data = $(data).toQueryString(); break; + case 'object': case 'hash': data = Hash.toQueryString(data); + } + + if (this.options.format){ + var format = 'format=' + this.options.format; + data = (data) ? format + '&' + data : format; + } + + if (this.options.emulation && ['put', 'delete'].contains(method)){ + var _method = '_method=' + method; + data = (data) ? _method + '&' + data : _method; + method = 'post'; + } + + if (this.options.urlEncoded && method == 'post'){ + var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : ''; + this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding); + } + + if (data && method == 'get'){ + url = url + (url.contains('?') ? '&' : '?') + data; + data = null; + } + + this.xhr.open(method.toUpperCase(), url, this.options.async); + + this.xhr.onreadystatechange = this.onStateChange.bind(this); + + this.headers.each(function(value, key){ + if (!$try(function(){ + this.xhr.setRequestHeader(key, value); + return true; + }.bind(this))) this.fireEvent('exception', [key, value]); + }, this); + + this.fireEvent('request'); + this.xhr.send(data); + if (!this.options.async) this.onStateChange(); + return this; + }, + + cancel: function(){ + if (!this.running) return this; + this.running = false; + this.xhr.abort(); + this.xhr.onreadystatechange = $empty; + this.xhr = new Browser.Request(); + this.fireEvent('cancel'); + return this; + } + +}); + +(function(){ + +var methods = {}; +['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){ + methods[method] = function(){ + var params = Array.link(arguments, {url: String.type, data: $defined}); + return this.send($extend(params, {method: method.toLowerCase()})); + }; +}); + +Request.implement(methods); + +})(); + +Element.Properties.send = { + + set: function(options){ + var send = this.retrieve('send'); + if (send) send.cancel(); + return this.eliminate('send').store('send:options', $extend({ + data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action') + }, options)); + }, + + get: function(options){ + if (options || !this.retrieve('send')){ + if (options || !this.retrieve('send:options')) this.set('send', options); + this.store('send', new Request(this.retrieve('send:options'))); + } + return this.retrieve('send'); + } + +}; + +Element.implement({ + + send: function(url){ + var sender = this.get('send'); + sender.send({data: this, url: url || sender.options.url}); + return this; + } + +}); + + +/* +Script: Request.HTML.js + Extends the basic Request Class with additional methods for interacting with HTML responses. + +License: + MIT-style license. +*/ + +Request.HTML = new Class({ + + Extends: Request, + + options: { + update: false, + evalScripts: true, + filter: false + }, + + processHTML: function(text){ + var match = text.match(/]*>([\s\S]*?)<\/body>/i); + text = (match) ? match[1] : text; + + var container = new Element('div'); + + return $try(function(){ + var root = '' + text + '', doc; + if (Browser.Engine.trident){ + doc = new ActiveXObject('Microsoft.XMLDOM'); + doc.async = false; + doc.loadXML(root); + } else { + doc = new DOMParser().parseFromString(root, 'text/xml'); + } + root = doc.getElementsByTagName('root')[0]; + for (var i = 0, k = root.childNodes.length; i < k; i++){ + var child = Element.clone(root.childNodes[i], true, true); + if (child) container.grab(child); + } + return container; + }) || container.set('html', text); + }, + + success: function(text){ + var options = this.options, response = this.response; + + response.html = text.stripScripts(function(script){ + response.javascript = script; + }); + + var temp = this.processHTML(response.html); + + response.tree = temp.childNodes; + response.elements = temp.getElements('*'); + + if (options.filter) response.tree = response.elements.filter(options.filter); + if (options.update) $(options.update).empty().adopt(response.tree); + if (options.evalScripts) $exec(response.javascript); + + this.onSuccess(response.tree, response.elements, response.html, response.javascript); + } + +}); + +Element.Properties.load = { + + set: function(options){ + var load = this.retrieve('load'); + if (load) send.cancel(); + return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options)); + }, + + get: function(options){ + if (options || ! this.retrieve('load')){ + if (options || !this.retrieve('load:options')) this.set('load', options); + this.store('load', new Request.HTML(this.retrieve('load:options'))); + } + return this.retrieve('load'); + } + +}; + +Element.implement({ + + load: function(){ + this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type})); + return this; + } + +}); + + +/* +Script: Request.JSON.js + Extends the basic Request Class with additional methods for sending and receiving JSON data. + +License: + MIT-style license. +*/ + +Request.JSON = new Class({ + + Extends: Request, + + options: { + secure: true + }, + + initialize: function(options){ + this.parent(options); + this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'}); + }, + + success: function(text){ + this.response.json = JSON.decode(text, this.options.secure); + this.onSuccess(this.response.json, text); + } + +}); diff --git a/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-yc.js b/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-yc.js new file mode 100644 index 000000000..1133826c5 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/mootools-1.2-core-yc.js @@ -0,0 +1,341 @@ +//MooTools, , My Object Oriented (JavaScript) Tools. Copyright (c) 2006-2008 Valerio Proietti, , MIT Style License. + +var MooTools={version:"1.2.0",build:""};var Native=function(J){J=J||{};var F=J.afterImplement||function(){};var G=J.generics;G=(G!==false);var H=J.legacy; +var E=J.initialize;var B=J.protect;var A=J.name;var C=E||H;C.constructor=Native;C.$family={name:"native"};if(H&&E){C.prototype=H.prototype;}C.prototype.constructor=C; +if(A){var D=A.toLowerCase();C.prototype.$family={name:D};Native.typize(C,D);}var I=function(M,K,N,L){if(!B||L||!M.prototype[K]){M.prototype[K]=N;}if(G){Native.genericize(M,K,B); +}F.call(M,K,N);return M;};C.implement=function(L,K,N){if(typeof L=="string"){return I(this,L,K,N);}for(var M in L){I(this,M,L[M],K);}return this;};C.alias=function(M,K,N){if(typeof M=="string"){M=this.prototype[M]; +if(M){I(this,K,M,N);}}else{for(var L in M){this.alias(L,M[L],K);}}return this;};return C;};Native.implement=function(D,C){for(var B=0,A=D.length;B-1:this.indexOf(A)>-1;},trim:function(){return this.replace(/^\s+|\s+$/g,"");},clean:function(){return this.replace(/\s+/g," ").trim(); +},camelCase:function(){return this.replace(/-\D/g,function(A){return A.charAt(1).toUpperCase();});},hyphenate:function(){return this.replace(/[A-Z]/g,function(A){return("-"+A.charAt(0).toLowerCase()); +});},capitalize:function(){return this.replace(/\b[a-z]/g,function(A){return A.toUpperCase();});},escapeRegExp:function(){return this.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1"); +},toInt:function(A){return parseInt(this,A||10);},toFloat:function(){return parseFloat(this);},hexToRgb:function(B){var A=this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); +return(A)?A.slice(1).hexToRgb(B):null;},rgbToHex:function(B){var A=this.match(/\d{1,3}/g);return(A)?A.rgbToHex(B):null;},stripScripts:function(B){var A=""; +var C=this.replace(/]*>([\s\S]*?)<\/script>/gi,function(){A+=arguments[1]+"\n";return"";});if(B===true){$exec(A);}else{if($type(B)=="function"){B(A,C); +}}return C;},substitute:function(A,B){return this.replace(B||(/\\?\{([^}]+)\}/g),function(D,C){if(D.charAt(0)=="\\"){return D.slice(1);}return(A[C]!=undefined)?A[C]:""; +});}});Hash.implement({has:Object.prototype.hasOwnProperty,keyOf:function(B){for(var A in this){if(this.hasOwnProperty(A)&&this[A]===B){return A;}}return null; +},hasValue:function(A){return(Hash.keyOf(this,A)!==null);},extend:function(A){Hash.each(A,function(C,B){Hash.set(this,B,C);},this);return this;},combine:function(A){Hash.each(A,function(C,B){Hash.include(this,B,C); +},this);return this;},erase:function(A){if(this.hasOwnProperty(A)){delete this[A];}return this;},get:function(A){return(this.hasOwnProperty(A))?this[A]:null; +},set:function(A,B){if(!this[A]||this.hasOwnProperty(A)){this[A]=B;}return this;},empty:function(){Hash.each(this,function(B,A){delete this[A];},this); +return this;},include:function(B,C){var A=this[B];if(A==undefined){this[B]=C;}return this;},map:function(B,C){var A=new Hash;Hash.each(this,function(E,D){A.set(D,B.call(C,E,D,this)); +},this);return A;},filter:function(B,C){var A=new Hash;Hash.each(this,function(E,D){if(B.call(C,E,D,this)){A.set(D,E);}},this);return A;},every:function(B,C){for(var A in this){if(this.hasOwnProperty(A)&&!B.call(C,this[A],A)){return false; +}}return true;},some:function(B,C){for(var A in this){if(this.hasOwnProperty(A)&&B.call(C,this[A],A)){return true;}}return false;},getKeys:function(){var A=[]; +Hash.each(this,function(C,B){A.push(B);});return A;},getValues:function(){var A=[];Hash.each(this,function(B){A.push(B);});return A;},toQueryString:function(A){var B=[]; +Hash.each(this,function(F,E){if(A){E=A+"["+E+"]";}var D;switch($type(F)){case"object":D=Hash.toQueryString(F,E);break;case"array":var C={};F.each(function(H,G){C[G]=H; +});D=Hash.toQueryString(C,E);break;default:D=E+"="+encodeURIComponent(F);}if(F!=undefined){B.push(D);}});return B.join("&");}});Hash.alias({keyOf:"indexOf",hasValue:"contains"}); +var Event=new Native({name:"Event",initialize:function(A,F){F=F||window;var K=F.document;A=A||F.event;if(A.$extended){return A;}this.$extended=true;var J=A.type; +var G=A.target||A.srcElement;while(G&&G.nodeType==3){G=G.parentNode;}if(J.test(/key/)){var B=A.which||A.keyCode;var M=Event.Keys.keyOf(B);if(J=="keydown"){var D=B-111; +if(D>0&&D<13){M="f"+D;}}M=M||String.fromCharCode(B).toLowerCase();}else{if(J.match(/(click|mouse|menu)/i)){K=(!K.compatMode||K.compatMode=="CSS1Compat")?K.html:K.body; +var I={x:A.pageX||A.clientX+K.scrollLeft,y:A.pageY||A.clientY+K.scrollTop};var C={x:(A.pageX)?A.pageX-F.pageXOffset:A.clientX,y:(A.pageY)?A.pageY-F.pageYOffset:A.clientY}; +if(J.match(/DOMMouseScroll|mousewheel/)){var H=(A.wheelDelta)?A.wheelDelta/120:-(A.detail||0)/3;}var E=(A.which==3)||(A.button==2);var L=null;if(J.match(/over|out/)){switch(J){case"mouseover":L=A.relatedTarget||A.fromElement; +break;case"mouseout":L=A.relatedTarget||A.toElement;}if(!(function(){while(L&&L.nodeType==3){L=L.parentNode;}return true;}).create({attempt:Browser.Engine.gecko})()){L=false; +}}}}return $extend(this,{event:A,type:J,page:I,client:C,rightClick:E,wheel:H,relatedTarget:L,target:G,code:B,key:M,shift:A.shiftKey,control:A.ctrlKey,alt:A.altKey,meta:A.metaKey}); +}});Event.Keys=new Hash({enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46});Event.implement({stop:function(){return this.stopPropagation().preventDefault(); +},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault(); +}else{this.event.returnValue=false;}return this;}});var Class=new Native({name:"Class",initialize:function(B){B=B||{};var A=function(E){for(var D in this){this[D]=$unlink(this[D]); +}for(var F in Class.Mutators){if(!this[F]){continue;}Class.Mutators[F](this,this[F]);delete this[F];}this.constructor=A;if(E===$empty){return this;}var C=(this.initialize)?this.initialize.apply(this,arguments):this; +if(this.options&&this.options.initialize){this.options.initialize.call(this);}return C;};$extend(A,this);A.constructor=Class;A.prototype=B;return A;}}); +Class.implement({implement:function(){Class.Mutators.Implements(this.prototype,Array.slice(arguments));return this;}});Class.Mutators={Implements:function(A,B){$splat(B).each(function(C){$extend(A,($type(C)=="class")?new C($empty):C); +});},Extends:function(self,klass){var instance=new klass($empty);delete instance.parent;delete instance.parentOf;for(var key in instance){var current=self[key],previous=instance[key]; +if(current==undefined){self[key]=previous;continue;}var ctype=$type(current),ptype=$type(previous);if(ctype!=ptype){continue;}switch(ctype){case"function":if(!arguments.callee.caller){self[key]=eval("("+String(current).replace(/\bthis\.parent\(\s*(\))?/g,function(full,close){return"arguments.callee._parent_.call(this"+(close||", "); +})+")");}self[key]._parent_=previous;break;case"object":self[key]=$merge(previous,current);}}self.parent=function(){return arguments.callee.caller._parent_.apply(this,arguments); +};self.parentOf=function(descendant){return descendant._parent_.apply(this,Array.slice(arguments,1));};}};var Chain=new Class({chain:function(){this.$chain=(this.$chain||[]).extend(arguments); +return this;},callChain:function(){return(this.$chain&&this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){if(this.$chain){this.$chain.empty(); +}return this;}});var Events=new Class({addEvent:function(C,B,A){C=Events.removeOn(C);if(B!=$empty){this.$events=this.$events||{};this.$events[C]=this.$events[C]||[]; +this.$events[C].include(B);if(A){B.internal=true;}}return this;},addEvents:function(A){for(var B in A){this.addEvent(B,A[B]);}return this;},fireEvent:function(C,B,A){C=Events.removeOn(C); +if(!this.$events||!this.$events[C]){return this;}this.$events[C].each(function(D){D.create({bind:this,delay:A,"arguments":B})();},this);return this;},removeEvent:function(B,A){B=Events.removeOn(B); +if(!this.$events||!this.$events[B]){return this;}if(!A.internal){this.$events[B].erase(A);}return this;},removeEvents:function(C){for(var D in this.$events){if(C&&C!=D){continue; +}var B=this.$events[D];for(var A=B.length;A--;A){this.removeEvent(D,B[A]);}}return this;}});Events.removeOn=function(A){return A.replace(/^on([A-Z])/,function(B,C){return C.toLowerCase(); +});};var Options=new Class({setOptions:function(){this.options=$merge.run([this.options].extend(arguments));if(!this.addEvent){return this;}for(var A in this.options){if($type(this.options[A])!="function"||!(/^on[A-Z]/).test(A)){continue; +}this.addEvent(A,this.options[A]);delete this.options[A];}return this;}});Document.implement({newElement:function(A,B){if(Browser.Engine.trident&&B){["name","type","checked"].each(function(C){if(!B[C]){return ; +}A+=" "+C+'="'+B[C]+'"';if(C!="checked"){delete B[C];}});A="<"+A+">";}return $.element(this.createElement(A)).set(B);},newTextNode:function(A){return this.createTextNode(A); +},getDocument:function(){return this;},getWindow:function(){return this.defaultView||this.parentWindow;},purge:function(){var C=this.getElementsByTagName("*"); +for(var B=0,A=C.length;B1);A.each(function(E){var F=this.getElementsByTagName(E.trim());(B)?C.extend(F):C=F;},this);return new Elements(C,{ddup:B,cash:!D}); +}});Element.Storage={get:function(A){return(this[A]||(this[A]={}));}};Element.Inserters=new Hash({before:function(B,A){if(A.parentNode){A.parentNode.insertBefore(B,A); +}},after:function(B,A){if(!A.parentNode){return ;}var C=A.nextSibling;(C)?A.parentNode.insertBefore(B,C):A.parentNode.appendChild(B);},bottom:function(B,A){A.appendChild(B); +},top:function(B,A){var C=A.firstChild;(C)?A.insertBefore(B,C):A.appendChild(B);}});Element.Inserters.inside=Element.Inserters.bottom;Element.Inserters.each(function(C,B){var A=B.capitalize(); +Element.implement("inject"+A,function(D){C(this,$(D,true));return this;});Element.implement("grab"+A,function(D){C($(D,true),this);return this;});});Element.implement({getDocument:function(){return this.ownerDocument; +},getWindow:function(){return this.ownerDocument.getWindow();},getElementById:function(D,C){var B=this.ownerDocument.getElementById(D);if(!B){return null; +}for(var A=B.parentNode;A!=this;A=A.parentNode){if(!A){return null;}}return $.element(B,C);},set:function(D,B){switch($type(D)){case"object":for(var C in D){this.set(C,D[C]); +}break;case"string":var A=Element.Properties.get(D);(A&&A.set)?A.set.apply(this,Array.slice(arguments,1)):this.setProperty(D,B);}return this;},get:function(B){var A=Element.Properties.get(B); +return(A&&A.get)?A.get.apply(this,Array.slice(arguments,1)):this.getProperty(B);},erase:function(B){var A=Element.Properties.get(B);(A&&A.erase)?A.erase.apply(this,Array.slice(arguments,1)):this.removeProperty(B); +return this;},match:function(A){return(!A||Element.get(this,"tag")==A);},inject:function(B,A){Element.Inserters.get(A||"bottom")(this,$(B,true));return this; +},wraps:function(B,A){B=$(B,true);return this.replaces(B).grab(B,A);},grab:function(B,A){Element.Inserters.get(A||"bottom")($(B,true),this);return this; +},appendText:function(B,A){return this.grab(this.getDocument().newTextNode(B),A);},adopt:function(){Array.flatten(arguments).each(function(A){A=$(A,true); +if(A){this.appendChild(A);}},this);return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(D,C){switch($type(this)){case"element":var H={}; +for(var G=0,E=this.attributes.length;G1),cash:!G});}});Element.implement({match:function(B){if(!B){return true;}var D=Selectors.Utils.parseTagAndID(B); +var A=D[0],E=D[1];if(!Selectors.Filters.byID(this,E)||!Selectors.Filters.byTag(this,A)){return false;}var C=Selectors.Utils.parseSelector(B);return(C)?Selectors.Utils.filter(this,C,{}):true; +}});var Selectors={Cache:{nth:{},parsed:{}}};Selectors.RegExps={id:(/#([\w-]+)/),tag:(/^(\w+|\*)/),quick:(/^(\w+|\*)$/),splitter:(/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),combined:(/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)}; +Selectors.Utils={chk:function(B,C){if(!C){return true;}var A=$uid(B);if(!C[A]){return C[A]=true;}return false;},parseNthArgument:function(F){if(Selectors.Cache.nth[F]){return Selectors.Cache.nth[F]; +}var C=F.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);if(!C){return false;}var E=parseInt(C[1]);var B=(E||E===0)?E:1;var D=C[2]||false;var A=parseInt(C[3])||0; +if(B!=0){A--;while(A<1){A+=B;}while(A>=B){A-=B;}}else{B=A;D="index";}switch(D){case"n":C={a:B,b:A,special:"n"};break;case"odd":C={a:2,b:0,special:"n"}; +break;case"even":C={a:2,b:1,special:"n"};break;case"first":C={a:0,special:"index"};break;case"last":C={special:"last-child"};break;case"only":C={special:"only-child"}; +break;default:C={a:(B-1),special:"index"};}return Selectors.Cache.nth[F]=C;},parseSelector:function(E){if(Selectors.Cache.parsed[E]){return Selectors.Cache.parsed[E]; +}var D,H={classes:[],pseudos:[],attributes:[]};while((D=Selectors.RegExps.combined.exec(E))){var I=D[1],G=D[2],F=D[3],B=D[4],C=D[5],J=D[6];if(I){H.classes.push(I); +}else{if(C){var A=Selectors.Pseudo.get(C);if(A){H.pseudos.push({parser:A,argument:J});}else{H.attributes.push({name:C,operator:"=",value:J});}}else{if(G){H.attributes.push({name:G,operator:F,value:B}); +}}}}if(!H.classes.length){delete H.classes;}if(!H.attributes.length){delete H.attributes;}if(!H.pseudos.length){delete H.pseudos;}if(!H.classes&&!H.attributes&&!H.pseudos){H=null; +}return Selectors.Cache.parsed[E]=H;},parseTagAndID:function(B){var A=B.match(Selectors.RegExps.tag);var C=B.match(Selectors.RegExps.id);return[(A)?A[1]:"*",(C)?C[1]:false]; +},filter:function(F,C,E){var D;if(C.classes){for(D=C.classes.length;D--;D){var G=C.classes[D];if(!Selectors.Filters.byClass(F,G)){return false;}}}if(C.attributes){for(D=C.attributes.length; +D--;D){var B=C.attributes[D];if(!Selectors.Filters.byAttribute(F,B.name,B.operator,B.value)){return false;}}}if(C.pseudos){for(D=C.pseudos.length;D--;D){var A=C.pseudos[D]; +if(!Selectors.Filters.byPseudo(F,A.parser,A.argument,E)){return false;}}}return true;},getByTagAndID:function(B,A,D){if(D){var C=(B.getElementById)?B.getElementById(D,true):Element.getElementById(B,D,true); +return(C&&Selectors.Filters.byTag(C,A))?[C]:[];}else{return B.getElementsByTagName(A);}},search:function(J,I,O){var B=[];var C=I.trim().replace(Selectors.RegExps.splitter,function(Z,Y,X){B.push(Y); +return":)"+X;}).split(":)");var K,F,E,V;for(var U=0,Q=C.length;U":function(H,G,I,A,F){var C=Selectors.Utils.getByTagAndID(G,I,A);for(var E=0,D=C.length;EA){return false; +}}return(C==A);},even:function(B,A){return Selectors.Pseudo["nth-child"].call(this,"2n+1",A);},odd:function(B,A){return Selectors.Pseudo["nth-child"].call(this,"2n",A); +}});Element.Events.domready={onAdd:function(A){if(Browser.loaded){A.call(this);}}};(function(){var B=function(){if(Browser.loaded){return ;}Browser.loaded=true; +window.fireEvent("domready");document.fireEvent("domready");};switch(Browser.Engine.name){case"webkit":(function(){(["loaded","complete"].contains(document.readyState))?B():arguments.callee.delay(50); +})();break;case"trident":var A=document.createElement("div");(function(){($try(function(){A.doScroll("left");return $(A).inject(document.body).set("html","temp").dispose(); +}))?B():arguments.callee.delay(50);})();break;default:window.addEvent("load",B);document.addEvent("DOMContentLoaded",B);}})();var JSON=new Hash({encode:function(B){switch($type(B)){case"string":return'"'+B.replace(/[\x00-\x1f\\"]/g,JSON.$replaceChars)+'"'; +case"array":return"["+String(B.map(JSON.encode).filter($defined))+"]";case"object":case"hash":var A=[];Hash.each(B,function(E,D){var C=JSON.encode(E);if(C){A.push(JSON.encode(D)+":"+C); +}});return"{"+A+"}";case"number":case"boolean":return String(B);case false:return"null";}return null;},$specialChars:{"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},$replaceChars:function(A){return JSON.$specialChars[A]||"\\u00"+Math.floor(A.charCodeAt()/16).toString(16)+(A.charCodeAt()%16).toString(16); +},decode:function(string,secure){if($type(string)!="string"||!string.length){return null;}if(secure&&!(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g,"@").replace(/"[^"\\\n\r]*"/g,""))){return null; +}return eval("("+string+")");}});Native.implement([Hash,Array,String,Number],{toJSON:function(){return JSON.encode(this);}});var Cookie=new Class({Implements:Options,options:{path:false,domain:false,duration:false,secure:false,document:document},initialize:function(B,A){this.key=B; +this.setOptions(A);},write:function(B){B=encodeURIComponent(B);if(this.options.domain){B+="; domain="+this.options.domain;}if(this.options.path){B+="; path="+this.options.path; +}if(this.options.duration){var A=new Date();A.setTime(A.getTime()+this.options.duration*24*60*60*1000);B+="; expires="+A.toGMTString();}if(this.options.secure){B+="; secure"; +}this.options.document.cookie=this.key+"="+B;return this;},read:function(){var A=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)"); +return(A)?decodeURIComponent(A[1]):null;},dispose:function(){new Cookie(this.key,$merge(this.options,{duration:-1})).write("");return this;}});Cookie.write=function(B,C,A){return new Cookie(B,A).write(C); +};Cookie.read=function(A){return new Cookie(A).read();};Cookie.dispose=function(B,A){return new Cookie(B,A).dispose();};var Swiff=new Class({Implements:[Options],options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"transparent",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object; +},initialize:function(L,M){this.instance="Swiff_"+$time();this.setOptions(M);M=this.options;var B=this.id=M.id||this.instance;var A=$(M.container);Swiff.CallBacks[this.instance]={}; +var E=M.params,G=M.vars,F=M.callBacks;var H=$extend({height:M.height,width:M.width},M.properties);var K=this;for(var D in F){Swiff.CallBacks[this.instance][D]=(function(N){return function(){return N.apply(K.object,arguments); +};})(F[D]);G[D]="Swiff.CallBacks."+this.instance+"."+D;}E.flashVars=Hash.toQueryString(G);if(Browser.Engine.trident){H.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"; +E.movie=L;}else{H.type="application/x-shockwave-flash";H.data=L;}var J=''; +}}J+="";this.object=((A)?A.empty():new Element("div")).set("html",J).firstChild;},replaces:function(A){A=$(A,true);A.parentNode.replaceChild(this.toElement(),A); +return this;},inject:function(A){$(A,true).appendChild(this.toElement());return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].extend(arguments)); +}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction(''+__flash__argumentsToXML(arguments,2)+""); +return eval(rs);};var Fx=new Class({Implements:[Chain,Events,Options],options:{fps:50,unit:false,duration:500,link:"ignore",transition:function(A){return -(Math.cos(Math.PI*A)-1)/2; +}},initialize:function(A){this.subject=this.subject||this;this.setOptions(A);this.options.duration=Fx.Durations[this.options.duration]||this.options.duration.toInt(); +var B=this.options.wait;if(B===false){this.options.link="cancel";}},step:function(){var A=$time();if(A=(7-4*B)/11){C=-Math.pow((11-6*B-11*D)/4,2)+A*A; +break;}}return C;},Elastic:function(B,A){return Math.pow(2,10*--B)*Math.cos(20*B*Math.PI*(A[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(B,A){Fx.Transitions[B]=new Fx.Transition(function(C){return Math.pow(C,[A+2]); +});});var Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false},initialize:function(A){this.xhr=new Browser.Request(); +this.setOptions(A);this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.headers=new Hash(this.options.headers);},onStateChange:function(){if(this.xhr.readyState!=4||!this.running){return ; +}this.running=false;this.status=0;$try(function(){this.status=this.xhr.status;}.bind(this));if(this.options.isSuccess.call(this,this.status)){this.response={text:this.xhr.responseText,xml:this.xhr.responseXML}; +this.success(this.response.text,this.response.xml);}else{this.response={text:null,xml:null};this.failure();}this.xhr.onreadystatechange=$empty;},isSuccess:function(){return((this.status>=200)&&(this.status<300)); +},processScripts:function(A){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return $exec(A);}return A.stripScripts(this.options.evalScripts); +},success:function(B,A){this.onSuccess(this.processScripts(B),A);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain(); +},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},setHeader:function(A,B){this.headers.set(A,B); +return this;},getHeader:function(A){return $try(function(){return this.xhr.getResponseHeader(A);}.bind(this));},check:function(A){if(!this.running){return true; +}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(A.bind(this,Array.slice(arguments,1)));return false;}return false; +},send:function(I){if(!this.check(arguments.callee,I)){return this;}this.running=true;var G=$type(I);if(G=="string"||G=="element"){I={data:I};}var D=this.options; +I=$extend({data:D.data,url:D.url,method:D.method},I);var E=I.data,B=I.url,A=I.method;switch($type(E)){case"element":E=$(E).toQueryString();break;case"object":case"hash":E=Hash.toQueryString(E); +}if(this.options.format){var H="format="+this.options.format;E=(E)?H+"&"+E:H;}if(this.options.emulation&&["put","delete"].contains(A)){var F="_method="+A; +E=(E)?F+"&"+E:F;A="post";}if(this.options.urlEncoded&&A=="post"){var C=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers.set("Content-type","application/x-www-form-urlencoded"+C); +}if(E&&A=="get"){B=B+(B.contains("?")?"&":"?")+E;E=null;}this.xhr.open(A.toUpperCase(),B,this.options.async);this.xhr.onreadystatechange=this.onStateChange.bind(this); +this.headers.each(function(K,J){if(!$try(function(){this.xhr.setRequestHeader(J,K);return true;}.bind(this))){this.fireEvent("exception",[J,K]);}},this); +this.fireEvent("request");this.xhr.send(E);if(!this.options.async){this.onStateChange();}return this;},cancel:function(){if(!this.running){return this; +}this.running=false;this.xhr.abort();this.xhr.onreadystatechange=$empty;this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});(function(){var A={}; +["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(B){A[B]=function(){var C=Array.link(arguments,{url:String.type,data:$defined}); +return this.send($extend(C,{method:B.toLowerCase()}));};});Request.implement(A);})();Element.Properties.send={set:function(A){var B=this.retrieve("send"); +if(B){B.cancel();}return this.eliminate("send").store("send:options",$extend({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")},A)); +},get:function(A){if(A||!this.retrieve("send")){if(A||!this.retrieve("send:options")){this.set("send",A);}this.store("send",new Request(this.retrieve("send:options"))); +}return this.retrieve("send");}};Element.implement({send:function(A){var B=this.get("send");B.send({data:this,url:A||B.options.url});return this;}});Request.HTML=new Class({Extends:Request,options:{update:false,evalScripts:true,filter:false},processHTML:function(C){var B=C.match(/]*>([\s\S]*?)<\/body>/i); +C=(B)?B[1]:C;var A=new Element("div");return $try(function(){var D=""+C+"",G;if(Browser.Engine.trident){G=new ActiveXObject("Microsoft.XMLDOM"); +G.async=false;G.loadXML(D);}else{G=new DOMParser().parseFromString(D,"text/xml");}D=G.getElementsByTagName("root")[0];for(var F=0,E=D.childNodes.length; +F. Copyright (c) 2006-2008 Valerio Proietti, , MIT Style License. + +var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,modifiers:{x:"left",y:"top"}},initialize:function(){var B=Array.link(arguments,{options:Object.type,element:$defined}); +this.element=$(B.element);this.document=this.element.getDocument();this.setOptions(B.options||{});var A=$type(this.options.handle);this.handles=(A=="array"||A=="collection")?$$(this.options.handle):$(this.options.handle)||this.element; +this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection=(Browser.Engine.trident)?"selectstart":"mousedown";this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:$lambda(false)}; +this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start); +return this;},start:function(C){if(this.options.preventDefault){C.preventDefault();}this.fireEvent("beforeStart",this.element);this.mouse.start=C.page; +var A=this.options.limit;this.limit={x:[],y:[]};for(var D in this.options.modifiers){if(!this.options.modifiers[D]){continue;}if(this.options.style){this.value.now[D]=this.element.getStyle(this.options.modifiers[D]).toInt(); +}else{this.value.now[D]=this.element[this.options.modifiers[D]];}if(this.options.invert){this.value.now[D]*=-1;}this.mouse.pos[D]=C.page[D]-this.value.now[D]; +if(A&&A[D]){for(var B=2;B--;B){if($chk(A[D][B])){this.limit[D][B]=$lambda(A[D][B])();}}}}if($type(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid}; +}this.document.addEvents({mousemove:this.bound.check,mouseup:this.bound.cancel});this.document.addEvent(this.selection,this.bound.eventStop);},check:function(A){if(this.options.preventDefault){A.preventDefault(); +}var B=Math.round(Math.sqrt(Math.pow(A.page.x-this.mouse.start.x,2)+Math.pow(A.page.y-this.mouse.start.y,2)));if(B>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop}); +this.fireEvent("start",this.element).fireEvent("snap",this.element);}},drag:function(A){if(this.options.preventDefault){A.preventDefault();}this.mouse.now=A.page; +for(var B in this.options.modifiers){if(!this.options.modifiers[B]){continue;}this.value.now[B]=this.mouse.now[B]-this.mouse.pos[B];if(this.options.invert){this.value.now[B]*=-1; +}if(this.options.limit&&this.limit[B]){if($chk(this.limit[B][1])&&(this.value.now[B]>this.limit[B][1])){this.value.now[B]=this.limit[B][1];}else{if($chk(this.limit[B][0])&&(this.value.now[B]B[F]){G=D.page[F]-this.options.offsets[F]-E[F];}this.tip.setStyle(C[F],G); +}},fill:function(A,B){(typeof B=="string")?A.set("html",B):A.adopt(B);},show:function(){this.fireEvent("show",this.tip);},hide:function(){this.fireEvent("hide",this.tip); +}}); \ No newline at end of file diff --git a/deluge/ui/webui/templates/ajax/static/js/mooui.js b/deluge/ui/webui/templates/ajax/static/js/mooui.js new file mode 100644 index 000000000..08599d554 --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/js/mooui.js @@ -0,0 +1,1472 @@ +/* + * Script: Native.js + * A collection of native class extensions + * + * Depends: [] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +/* + * The following Date code has been adapted from Steven Levithan's + * Date Format 1.2 javascript code. + */ +(function() { +function pad(val, len) { + val = String(val); + len = len || 2; + while (val.length < len) val = "0" + val; + return val; +}; + +var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, +timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, +timezoneClip = /[^-+\dA-Z]/g + +Date.implement({ + i18n: { + dayNames: [ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" + ], + monthNames: [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + ] + }, + + masks: { + 'default': "ddd mmm dd yyyy HH:MM:ss", + shortDate: "m/d/yy", + mediumDate: "mmm d, yyyy", + longDate: "mmmm d, yyyy", + fullDate: "dddd, mmmm d, yyyy", + shortTime: "h:MM TT", + mediumTime: "h:MM:ss TT", + longTime: "h:MM:ss TT Z", + isoDate: "yyyy-mm-dd", + isoTime: "HH:MM:ss", + isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", + isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" + }, + + format: function (mask, utc) { + // You can't provide utc if you skip other args (use the "UTC:" mask prefix) + mask = String(this.masks[mask] || mask || this.masks["default"]); + + // Allow setting the utc argument via the mask + if (mask.slice(0, 4) == "UTC:") { + mask = mask.slice(4); + utc = true; + } + + var _ = utc ? "getUTC" : "get", + d = this[_ + "Date"](), + D = this[_ + "Day"](), + m = this[_ + "Month"](), + y = this[_ + "FullYear"](), + H = this[_ + "Hours"](), + M = this[_ + "Minutes"](), + s = this[_ + "Seconds"](), + L = this[_ + "Milliseconds"](), + o = utc ? 0 : this.getTimezoneOffset(), + flags = { + d: d, + dd: pad(d), + ddd: this.i18n.dayNames[D], + dddd: this.i18n.dayNames[D + 7], + m: m + 1, + mm: pad(m + 1), + mmm: this.i18n.monthNames[m], + mmmm: this.i18n.monthNames[m + 12], + yy: String(y).slice(2), + yyyy: y, + h: H % 12 || 12, + hh: pad(H % 12 || 12), + H: H, + HH: pad(H), + M: M, + MM: pad(M), + s: s, + ss: pad(s), + l: pad(L, 3), + L: pad(L > 99 ? Math.round(L / 10) : L), + t: H < 12 ? "a" : "p", + tt: H < 12 ? "am" : "pm", + T: H < 12 ? "A" : "P", + TT: H < 12 ? "AM" : "PM", + Z: utc ? "UTC" : (String(this).match(timezone) || [""]).pop().replace(timezoneClip, ""), + o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), + S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] + }; + + return mask.replace(token, function ($0) { + return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); + }); + } +}); + +})() + +/* + * Implement a sum function into the native array + * to allow for easy adding of a list of numbers + */ +Array.implement({ + sum: function(key) { + var total = 0 + this.each(function(item) { + var value = item + if ($defined(key)) { value = item[key] } + if ($type(value) == 'number') { total += value } + }, this) + return total + } +}) + +Element.implement({ + getInnerSize: function() { + this.getPadding() + if ((/^(?:body|html)$/i).test(this.tagName)) return this.getWindow().getSize(); + return {x: this.clientWidth - this.padding.x, y: this.clientHeight - this.padding.y}; + }, + + getInnerHeight: function() { + return this.getInnerSize().y + }, + + getInnerWidth: function() { + return this.getInnerSize().x + }, + + getSizeModifiers: function() { + if (!this.modifiers) { + var border = this.getBorder() + var margin = this.getMargin() + var padding = this.getPadding() + this.modifiers = { + left: border.left + margin.left + padding.left, + right: border.right + margin.right + padding.right, + top: border.top + margin.top + padding.top, + bottom: border.bottom + margin.bottom + padding.bottom, + x: border.x + margin.x + padding.x, + y: border.y + margin.y + padding.y + } + } + }, + + getMargin: function(update) { + if (!this.margin || update) { + var parts = this.getStyle('margin').split(' ') + this.margin = { + left: parts[1].toInt(), + right: parts[3].toInt(), + top: parts[0].toInt(), + bottom: parts[2].toInt(), + x: parts[0].toInt() + parts[2].toInt(), + y: parts[1].toInt() + parts[3].toInt() + }; + } + return this.margin + }, + + getPadding: function(update) { + if (!this.padding || update) { + var parts = this.getStyle('padding').split(' ') + this.padding = { + left: parts[1].toInt(), + right: parts[3].toInt(), + top: parts[0].toInt(), + bottom: parts[2].toInt(), + x: parts[0].toInt() + parts[2].toInt(), + y: parts[1].toInt() + parts[3].toInt() + }; + } + return this.padding + }, + + getBorder: function(update) { + if (!this.border || update) { + var parts = this.getStyle('border-width').split(' ') + this.border = { + left: parts[1].toInt(), + right: parts[3].toInt(), + top: parts[0].toInt(), + bottom: parts[2].toInt(), + x: parts[0].toInt() + parts[2].toInt(), + y: parts[1].toInt() + parts[3].toInt() + }; + } + return this.border + } +}) + +/* + * Implement some formatters into the native number + * to allow for easy displaying of data in the ui + */ +Number.implement({ + toBytes: function() { + var bytes = this + if (bytes < 1024) { return bytes.toFixed(1) + 'B'; } + else { bytes = bytes / 1024; } + + if (bytes < 1024) { return bytes.toFixed(1) + 'KiB'; } + else { bytes = bytes / 1024; } + + return bytes.toFixed(1) + 'MiB' + }, + toSpeed: function() { + var bits = this + if (bits < 1024) { return bits.toFixed(1) + 'b/s'; } + else { bits = bits / 1024; } + + if (bits < 1024) { return bits.toFixed(1) + 'KiB/s'; } + else { bits = bits / 1024; } + + return bits.toFixed(2) + 'MiB/s' + }, + toTime: function() { + var time = this + if (time == 0) { return '∞' } + if (time < 60) { return time + 's'; } + else { time = time / 60; } + + if (time < 60) { + var minutes = Math.floor(time) + var seconds = Math.round(60 * (time - minutes)) + if (seconds > 0) { + return minutes + 'm ' + seconds + 's'; + } else { + return minutes + 'm'; } + } + else { time = time / 60; } + + if (time < 24) { + var hours = Math.floor(time) + var minutes = Math.round(60 * (time - hours)) + if (minutes > 0) { + return hours + 'h ' + minutes + 'm'; + } else { + return hours + 'h'; + } + } + else { time = time / 24; } + + var days = Math.floor(time) + var hours = Math.round(24 * (time - days)) + if (hours > 0) { + return days + 'd ' + hours + 'h'; + } else { + return days + 'd'; + } + } +}) + +/* + * Adds a strip function to remove whitespace from a string + */ +String.implement({ + strip: function() { + var stripped = this.replace(/^\s*/, '') + stripped.replace(/\s*$/, '') + return stripped + } +}) +/* + * Script: Sorters.js + * A collection of sorters that can be used for the DataGrid + * + * Depends: [] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Sorters = { + Simple: new Class({ + initialize: function(column, order) { + this.column = column + this.order = order + }, + + sorter: function() { + return function(a, b) { + var av, bv + av = a.cells[this.column].value + bv = b.cells[this.column].value + return this.sort(av, bv) + }.bind(this) + }, + + sort: function(a, b) { + if (a > b) + return 1 * this.order + if (a < b) + return -1 * this.order + return 0 + } + }) +} + +Sorters.Number = new Class({ + Extends: Sorters.Simple, + sort: function(a, b) { return (a-b) * this.order; } +}) + +Sorters.Progress = new Class({ + Extends: Sorters.Simple, + sort: function(a, b) { return (a.percent-b.percent) * this.order; } +}) +/* + * Script: Widgets.js + * The base class for all UI widgets + * + * Depends: [] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets = { + version: 0.1, + authors: [ + 'Damien Churchill' + ], + license: 'GPLv3' +} + +Widgets.Base = new Class({ + + Implements: [Events,Options], + + options: { + width: 0, + height: 0, + expand: false + }, + + initialize: function(element, options) { + this.setOptions(options) + this.element = $(element) + this.ismoouiwidget = true + this.width = (this.options.width) ? this.options.width : this.element.getStyle('width').toInt() + this.height = (this.options.height) ? this.options.height : this.element.getStyle('height').toInt() + $A(['addClass', 'hasClass', 'removeClass', 'toggleClass', 'getInnerSize', + 'getTop', 'getLeft', 'getWidth', 'getHeight', 'getScrollTop', + 'getScrollLeft', 'getScrollHeight', 'getScrollWidth', 'getSize', + 'getScrollSize', 'getScroll', 'getScrolls', 'getOffsetParent', + 'getOffsets', 'getPosition', 'getCoordinates', 'getInnerWidth', + 'getInnerHeight', 'getStyle', 'getParent', 'getSizeModifiers' + ]).each(function(method) { + if (this.element[method]) + this[method] = this.element[method].bind(this.element); + }, this) + }, + + refresh: function() { + this.width = (this.options.width) ? this.options.width : this.element.getStyle('width').toInt() + this.height = (this.options.height) ? this.options.height : this.element.getStyle('height').toInt() + this.getSizeModifiers(); + if (this.calculatePositions) this.calculatePositions(); + }, + + expand: function() { + var parent = this.getParent() + var parentSize = this.getParent().getInnerSize(); + this.element.getSizeModifiers(); + parentSize.y -= this.element.modifiers.y; + parentSize.x -= this.element.modifiers.x; + this.sets({'width': parentSize.x, 'height': parentSize.y}); + }, + + set: function(property, value, nofire) { + if (property == 'height' || property == 'width') { + var eventArgs = {}; + eventArgs[property] = value; + eventArgs['old_' + property] = this[property]; + this[property] = value; + if (value > 0) this.element.setStyle(property, value); + if (!nofire) this.fireEvent('resize', eventArgs); + } else { + this[property] = value; + this.element.setStyle(property, value) + } + }, + + sets: function(properties) { + properties = new Hash(properties); + var fireResize = false; + var eventArgs = {} + properties.each(function(value, key) { + if (key == 'height' || key == 'width') { + eventArgs['old_' + key] = this[key] + eventArgs[key] = value + fireResize = true; + } + this.set(key, value, true); + }, this); + if (fireResize) this.fireEvent('resize', eventArgs); + }, + + toElement: function() { + return this.element + } +}) + +$W = function(wrap) { + if (!wrap.ismoouiwidget) + return new Widgets.Base($(wrap)) + else + return wrap +} +/* + * Script: Widgets.js + * The base class for all UI widgets + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + + +Widgets.PopupMenu = new Class({ + + Extends: Widgets.WidgetBase, + + Implements: Events, + + initialize: function() { + this.items = [] + this.noClose = false + this.build() + }, + add: function(item) { + if ($type(item) == 'array') { + for (var i=0; i < item.length; i++) { + item[i].parent = this + } + this.items.extend(item) + } else if ($type(item) == 'object') { + item.parent = this + this.items.include(item) + } + this.build() + }, + _build: function(items) { + var menu = new Element('ul') + menu.addClass('mooui-menu') + items.each(function(item) { + if (item.type == 'text') { + var el = new Element('li') + el.addClass('mooui-menu-text') + new Element('span').set('text', item.text).inject(el) + if ($defined(item.icon)) { + el.setStyle('background-image', 'url('+item.icon+')') + el.addClass('mooui-menu-icon') + } + el.inject(menu) + el.addEvent('click', function(e) { + this.fireEvent('action', item) + this.hide() + }.bind(this)) + } else if (item.type == 'toggle') { + var el = new Element('li'); + el.addClass('mooui-menu-toggle'); + var toggle = new Element('span').inject(el); + item.checkbox = new Element('input', {type: 'checkbox'}).inject(toggle); + new Element('span').set('text', item.text).inject(toggle) + if (item.value) item.checkbox.checked = item.value; + if ($defined(item.icon)) { + el.setStyle('background-image', 'url('+item.icon+')'); + el.addClass('mooui-menu-icon'); + } + el.inject(menu); + var clicked = function(e) { + if (item.value == true) item.value = false + else item.value = true + this.fireEvent('action', item); + this.hide(); + }.bindWithEvent(this) + + item.set = function(value) { + item.value = value; + item.checkbox.checked = value; + } + + el.addEvent('click', clicked) + + } else if (item.type == 'seperator') { + new Element('li').addClass('mooui-menu-sep').inject(menu) + } else if (item.type == 'submenu') { + var el = new Element('li') + el.addClass('mooui-menu-sub') + new Element('span').set('text', item.text).inject(el) + if ($defined(item.icon)) { + el.setStyle('background-image', 'url('+item.icon+')') + el.addClass('mooui-menu-icon') + } + var sub = this._build(item.items).inject(el) + el.addEvent('mouseenter', function(e) { + this.fixSize(sub) + }.bind(this)) + el.inject(menu) + } + }, this) + menu.addEvent('mouseenter', function(e) { + this.fixLeft(menu) + }.bind(this)) + return menu + }, + build: function() { + var self = this + if ($defined(this.element)) { + this.element.destroy(); + } + this.element = this._build(this.items) + }, + hide: function(force) { + if (!this.no_close) { + this.element.dispose() + this.fireEvent('closed') + } else if (force) { + this.element.dispose() + this.fireEvent('closed') + } + }, + show: function(e) { + this.show_pos(e.client.x+10, e.client.y-10) + }, + + fixSize: function(el) { + var widest = 0 + el.getElements('li').each(function(item) { + var text = item.getElement('span') + var width = text.getSize().x + if (width > widest) { widest = width } + }) + el.setStyle('width', widest + 32) + }, + + fixLeft: function(el) { + var widest = 0 + el.getElements('li').each(function(item) { + var width = item.getSize().x + if (width > widest) { widest = width } + }); + + el.setStyle('width', widest + 2) + el.getElements('li ul').each(function(item) { + item.setStyle('left', widest - 20) + }); + }, + + show_pos: function(x, y) { + this.element.setStyles({ + 'left': x, + 'top': y - 2 + }); + this.element.inject(document.body) + + this.element.addEvent('mouseleave', function(e) { + this.hide() + }.bind(this)); + } +}) +/* + * Script: Widgets.ProgressBar.js + * A progress bar widget + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.ProgressBar = new Class({ + Extends: Widgets.Base, + + initialize: function() { + this.parent(new Element('div')) + this.element.addClass('mooui-progressbar') + this.bar = new Element('div').inject(this.element) + this.textspan = new Element('span').inject(this.bar) + this.sets({width: 200, height: 20}) + this.addEvent('resize', this.on_resize.bindWithEvent(this)) + }, + + on_resize: function() { + this.textspan.setStyles({'width': this.width, 'height': this.height}) + this.update(this.text, this.percent) + }, + + update: function(text, percent) { + if (this.text != text) { + this.text = text + this.textspan.set('text', text) + } + + if (this.percent != percent) { + this.percent = percent + this.bar.setStyles({ + 'width': Math.floor(this.width / 100.0 * percent), + 'height': this.height + }) + } + } +}); +/* + * Script: Widgets.SplitPane.js + * A layout splitpane widget + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.SplitPane = new Class({ + + Extends: Widgets.Base, + + options: { + direction: 'horizontal', + name: null, + splitSize: 6, + pane1: {}, + pane2: {} + }, + + initialize: function(element, pane1, pane2, options) { + this.parent(element, options); + this.horizontal = (this.options.direction == 'horizontal') ? true : false; + this.pane1 = $W(pane1); + this.pane2 = $W(pane2); + + if (this.options.expand) this.expand(); + + this.init_pane(this.pane1, this.options.pane1) + this.init_pane(this.pane2, this.options.pane2) + this.init_splitter() + this.calculatePositions() + this.setPosition(this.pane1); + this.setPosition(this.splitter); + this.setPosition(this.pane2); + this.init_drag() + this.addEvent('resize', this.onResize.bindWithEvent(this)) + }, + + init_splitter: function() { + this.splitter = new Element('div').addClass('mooui-splitter') + this.splitter.inject(this.pane1, 'after') + this.splitter.paneinfo = {} + if (this.horizontal) { + this.splitter.addClass('mooui-splitter-vertical') + } else { + this.splitter.addClass('mooui-splitter-horizontal') + } + this.splitter.grab(new Element('div')) + this.splitter.grab(new Element('div')) + this.splitter.grab(new Element('div')) + }, + + init_drag: function() { + this.drag = new Drag(this.splitter) + this.drag.addEvent('drag', this.onDrag.bindWithEvent(this)) + if (this.horizontal) { + this.drag.options.limit = { + x: [ + this.pane1.paneinfo.left + this.pane1.paneinfo.min - this.pane1.element.modifiers.x, + this.pane1.paneinfo.left + this.getInnerWidth() - this.pane2.paneinfo.min - this.pane1.element.modifiers.x - this.pane2.element.modifiers.x + ], + y: [this.pane1.paneinfo.top, this.pane1.paneinfo.top] + } + } else { + this.drag.options.limit = { + x: [this.pane1.paneinfo.left, this.pane1.paneinfo.left], + y: [ + this.pane1.paneinfo.top + this.pane1.paneinfo.min, + this.pane1.top + this.getInnerHeight() - this.pane2.paneinfo.min + ] + } + } + }, + + init_pane: function(pane, options) { + pane.addClass('mooui-pane'); + pane.paneinfo = {} + if (options) { + pane.paneinfo.min = (options.min) ? options.min : 0; + pane.paneinfo.expand = (options.expand) ? options.expand : false; + } + }, + + calculatePositions: function(resized) { + if (resized) { + if (this.horizontal) { + this.calculateResize(resized, 'width', 'height', 'x', 'y'); + } else { + this.calculateResize(resized, 'height', 'width', 'y', 'x'); + } + } else { + if (this.horizontal) { + this.calculateInitial('width', 'height', 'x', 'y'); + } else { + this.calculateInitial('height', 'width', 'y', 'x'); + } + } + }, + + calculateInitial: function(dm, ds, pm, ps) { + var size = this.getInnerSize(), position = this.pane1.getPosition(this); + this.pane1.getSizeModifiers(); this.pane2.getSizeModifiers(); + this.splitter.getSizeModifiers(); + + position.x -= this.pane1.element.margin.left; + position.y -= this.pane1.element.margin.top; + + // Calculate Size + if (this.pane1.paneinfo.expand) { + this.pane2.paneinfo[dm] = this.pane2.paneinfo.min - this.pane2.element.modifiers[pm]; + this.pane1.paneinfo[dm] = size[pm] - this.pane2.paneinfo.min - this.options.splitSize; + this.pane1.paneinfo[dm] -= this.pane1.element.modifiers[pm]; + } else { + this.pane1.paneinfo[dm] = this.pane1.paneinfo.min - this.pane1.element.modifiers[pm]; + this.pane2.paneinfo[dm] = size[pm] - this.pane1.paneinfo.min - this.options.splitSize; + this.pane2.paneinfo[dm] -= this.pane2.element.modifiers[pm]; + } + this.pane1.paneinfo[ds] = this.pane2.paneinfo[ds] = size[ps]; + this.pane1.paneinfo[ds] -= this.pane1.element.modifiers[ps]; + this.pane2.paneinfo[ds] -= this.pane2.element.modifiers[ps]; + + this.splitter.paneinfo[ds] = size[ps]; + this.splitter.paneinfo[dm] = this.options.splitSize; + + + // Calculate Position + $A([this.pane1, this.splitter, this.pane2]).each(function(item) { + item.paneinfo.left = position.x; + item.paneinfo.top = position.y; + if (item.modifiers) + position[pm] += item.paneinfo[dm] + item.modifiers[pm]; + else + position[pm] += item.paneinfo[dm] + item.element.modifiers[pm]; + }) + }, + + calculateResize: function(resized, dm, ds, pm, ps) { + var size = this.getInnerSize(), position = this.pane1.getPosition(this); + this.pane1.getSizeModifiers(); this.pane2.getSizeModifiers(); + this.splitter.getSizeModifiers(); + + position.x -= this.pane1.element.margin.left; + position.y -= this.pane1.element.margin.top; + + if (resized[dm] && resized[dm] != resized['old_' + dm]) { + if (this.pane1.paneinfo.expand) { + this.pane1.paneinfo[dm] = size[pm] - this.pane2.paneinfo[dm] - this.options.splitSize; + this.pane1.paneinfo[dm] -= this.pane1.element.modifiers[pm] + this.pane2.element.modifiers[pm]; + } else { + this.pane2.paneinfo[dm] = size[pm] - this.pane1.paneinfo[dm] - this.options.splitSize; + this.pane2.paneinfo[dm] -= this.pane2.element.modifiers[pm] + this.pane1.element.modifiers[pm]; + } + } + + if (resized[ds] && resized[ds] != resized['old_' + ds]) { + this.pane1.paneinfo[ds] = this.pane2.paneinfo[ds] = size[ps]; + this.splitter.paneinfo[ds] = size[ps]; + this.splitter.paneinfo[ds] -= this.splitter.modifiers[ps]; + this.pane1.paneinfo[ds] -= this.pane1.element.modifiers[ps]; + this.pane2.paneinfo[ds] -= this.pane2.element.modifiers[ps]; + } + + $A([this.pane1, this.splitter, this.pane2]).each(function(item) { + item.paneinfo.left = position.x; + item.paneinfo.top = position.y; + if (item.modifiers) + position[pm] += item.paneinfo[dm] + item.modifiers[pm]; + else + position[pm] += item.paneinfo[dm] + item.element.modifiers[pm]; + }) + + if (this.horizontal) { + this.drag.options.limit = { + x: [ + this.pane1.paneinfo.left + this.pane1.paneinfo.min, + this.pane1.paneinfo.left + this.getInnerWidth() - this.pane2.paneinfo.min + ], + y: [this.pane1.paneinfo.top, this.pane1.paneinfo.top] + } + } else { + this.drag.options.limit = { + x: [this.pane1.paneinfo.left, this.pane1.paneinfo.left], + y: [ + this.pane1.paneinfo.top + this.pane1.paneinfo.min, + this.pane1.top + this.getInnerHeight() - this.pane2.paneinfo.min + ] + } + } + }, + + setPosition: function(object) { + if (object.hasClass('mooui-splitter')) { + var func = object.setStyles.bind(object); + } else { + var func = object.sets.bind(object); + } + + func({ + width: object.paneinfo.width, + height: object.paneinfo.height, + left: object.paneinfo.left, + top: object.paneinfo.top + }); + }, + + onDrag: function(splitter) { + var position = splitter.getPosition(this) + if (this.horizontal) { + var diff = position.x - this.splitter.paneinfo.left + this.pane2.paneinfo.left += diff + this.pane1.paneinfo.width += diff + this.pane2.paneinfo.width -= diff + this.splitter.paneinfo.left += diff + } else { + var diff = position.y - this.splitter.paneinfo.top + this.pane2.paneinfo.top += diff + this.pane1.paneinfo.height += diff + this.pane2.paneinfo.height -= diff + this.splitter.paneinfo.top += diff + } + + this.setPosition(this.pane1) + this.setPosition(this.pane2) + }, + + onResize: function(event) { + this.calculatePositions(event) + this.setPosition(this.pane1); + this.setPosition(this.splitter); + this.setPosition(this.pane2); + } +}) +/* + * Script: Widgets.Tabs.js + * A tab widget + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.Tabs = new Class({ + Extends: Widgets.Base, + + initialize: function(element) { + this.parent(element) + this.pages = [] + this.currentPage = -1 + this.tabs_list = new Element('ul').inject(this.element) + this.pages_div = new Element('div').inject(this.element) + + if (this.options.expand) this.expand() + }, + + addPage: function(page) { + this.pages.include(page) + + var tab = new Element('li') + tab.set('text', page.name) + tab.addEvent('click', function(e) { + this.select(pageIndex) + }.bindWithEvent(this)) + page.tab = tab + + this.tabs_list.grab(tab) + this.pages_div.grab(page.element.addClass('mooui-tabpage')) + + var pageIndex = this.pages.indexOf(page) + if (this.currentPage < 0) { + this.currentPage = pageIndex + this.select(pageIndex) + } + }, + + select: function(id) { + this.pages[this.currentPage].removeClass('mooui-tabpage-active') + this.pages[this.currentPage].tab.removeClass('mooui-tabs-active') + this.pages[id].addClass('mooui-tabpage-active') + this.pages[id].tab.addClass('mooui-tabs-active') + this.currentPage = id + this.fireEvent('pageChanged') + }, + + on_resize: function() { + this._pages.each(function(page) { + page.resize(this.width, this.height - 45) + }, this) + } +}); + +Widgets.TabPage = new Class({ + Extends: Widgets.Base, + + options: { + element: null, + url: null + }, + + initialize: function(name, options) { + this.name = name + var element = null + if (options && options.element) element = options.element + else element = new Element('div') + this.parent(element, options) + if (this.options.url) { + new Request.HTML({ + url: this.options.url, + update: element, + onSuccess: function(e) { + this.fireEvent('loaded') + }.bindWithEvent(this) + }).get() + } + } +}); +/* + * Script: Widgets.VBox.js + * A layout vertical box widget + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.VBox = new Class({ + Extends: Widgets.Base, + + initialize: function(element, options) { + this.parent(element, options); + this.boxes = []; + if (this.options.expand) this.expand(); + }, + + addBox: function(box, options) { + box = $W(box) + box.boxinfo = (options) ? options : {fixed: false}; + this.boxes.include(box); + this.element.grab(box) + box.element.setStyle('position', 'absolute') + }, + + calculatePositions: function() { + if (this.options.expand) this.expand(); + var size = this.getInnerSize(); + var position = this.boxes[0].getPosition(this); + var height = size.y, resizable = 0; + this.boxes.each(function(box) { + box.getSizeModifiers() + if (!box.boxinfo.fixed) resizable++; + else height -= box.height + box.element.modifiers.y + }, this) + + position.x -= this.boxes[0].element.margin.left + position.y -= this.boxes[0].element.margin.top + + var boxHeight = height / resizable, remainder = height % resizable + this.boxes.each(function(box) { + var boxinfo = {} + if (!box.boxinfo.fixed) { + var setHeight = boxHeight - box.element.modifiers.y + if (remainder > 0) { setHeight -= 1; remainder-- } + boxinfo.height = setHeight + } else { + boxinfo.height = box.height + } + boxinfo.width = size.x - box.element.modifiers.x + boxinfo.top = position.y + boxinfo.left = position.x + box.sets({ + height: boxinfo.height, + width: boxinfo.width, + top: boxinfo.top, + left: boxinfo.left + }) + position.y += box.height + box.element.modifiers.y + }, this) + } +}) +/* + * Script: Widgets.Window.js + * A window widget + * + * Depends: [Widgets] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.Window = new Class({ + Extends: Widgets.Base, + + initialize: function(options) { + var element = new Element('div'); + this.parent(element, options); + this.addClass('mooui-window'); + this.sets({ + width: this.options.width, + height: this.options.height + }); + this.element.setStyle('opacity', 0) + this.title = new Element('h3').addClass('mooui-window-title'); + this.element.grab(this.title); + this.title.set('text', this.options.title); + + this.drag = new Drag(this.element, { + handle: this.title + }); + + this.close = new Element('div').addClass('mooui-window-close'); + this.close.inject(this.element); + this.close.addEvent('click', function(e) { + this.hide() + }.bindWithEvent(this)) + + this.content = new Element('div').addClass('mooui-window-content'); + this.content.inject(this.element); + + if (this.options.url) { + new Request.HTML({ + url: this.options.url, + update: this.content, + onSuccess: function(e) { + this.fireEvent('loaded') + }.bindWithEvent(this) + }).get() + } + }, + + show: function() { + var size = document.body.getInnerSize() + var left = (size.x - this.options.width) / 2, top = (size.y - this.options.height) / 2 + this.sets({ + left: left, + top: top + }) + document.body.grab(this.element) + this.element.setStyle('opacity', 1) + this.fireEvent('show') + }, + + hide: function() { + var tween = this.element.get('tween'); + tween.addEvent('complete', function(e) { + this.element.dispose(); + }.bind(this)); + this.element.fade(0); + } +}) +/* + * Script: Widgets.DataGrid.js + * The base class for all UI widgets + * + * Depends: [Widgets, Sorters, Widgets.ProgressBar] + * + * Copyright: + * Damien Churchill (c) 2008 + */ + +Widgets.DataGridCell = new Class({ + Implements: [Events, Options], + + options: { + name: '', + type: 'text', + width: 0 + }, + + initialize: function(options) { + this.setOptions(options) + this.element = new Element('td') + }, + + set: function(value) { + this.value = value + var text = value, set_text = true + switch (this.options.type) { + case 'bytes': + text = value.toBytes() + break; + case 'time': + text = value.toTime() + break; + case 'date': + this.value = new Date(value) + text = this.value.format('ddd mmm dd yyyy') + break; + case 'speed': + text = value.toSpeed() + break; + case 'icon': + text = value.text + this.element.setStyles({ + 'background-repeat': 'no-repeat', + 'background-position': '2px', + 'background-image': 'url(' + value.icon + ')', + 'padding-left': 20 + }) + break; + case 'image': + if (!this.image) { + this.image = new Element('img') + this.image.inject(this.element) + } + this.image.src = value + set_text = false + break; + case 'progress': + if (!this.progress) { + this.progress = new Widgets.ProgressBar() + this.progress.sets({'width': this.options.width, 'height': 14}) + this.progress.element.inject(this.element) + } + this.progress.update(value.text, value.percent) + set_text = false + break; + } + if (set_text) { this.element.set('text', text) } + this.fireEvent('changed', {cell: this}) + } +}) + +Widgets.DataGridColumn = new Class({ + Implements: [Events,Options], + + options: { + name: '', + type: 'string', + text: '', + width: 50 + }, + + initialize: function(options) { + this.setOptions(options) + this.order = 1 + }, + + setText: function(text) { + this.options.text = text + this.fireEvent('textChanged', text) + }, + + get_sorter: function(index) { + switch (this.options.type) { + case 'number': + return new Sorters.Number(index, this.order) + case 'progress': + return new Sorters.Progress(index, this.order) + default: + return new Sorters.Simple(index, this.order) + } + } +}) + +Widgets.DataGridRow = new Class({ + Implements: [Events], + + initialize: function(id, columns) { + this.id = id + this.columns = columns + this.element = new Element('tr') + this.element.store('rowid', this.id) + this.cells = [] + this.selected = false + this.columns.each(function(col) { + var cell = new Widgets.DataGridCell({ + name: col.options.name, + type: col.options.type, + width: col.options.width + }) + cell.element.inject(this.element) + cell.addEvent('changed', this.oncellchanged.bind(this)) + this.cells.include(cell) + }, this) + this.element.addEvent('contextmenu', this.oncontextmenu.bind(this)) + this.element.addEvent('click', this.onclick.bind(this)) + }, + + update: function(row) { + row = new Hash(row) + row.getKeys().each(function(item) { + if (item != 'data' || item != 'id') { + this[item] = row[item] + } + }, this) + + this.cells.each(function(cell) { + cell.set(row.data[cell.options.name]) + }, this) + return this + }, + + oncellchanged: function(e) { + e.row = this + this.fireEvent('changed', e) + }, + + oncontextmenu: function(e) { + e.row = this + this.fireEvent('menu', e) + }, + + onclick: function(e) { + e.row = this + this.fireEvent('click', e) + } +}) + +Widgets.DataGrid = new Class({ + Implements: [Options, Events], + Extends: Widgets.Base, + + options: { + columns: [], + element: null + }, + + initialize: function(element, options) { + if (!element) element = this.createElement + this.parent(element, options) + this.columns = [] + this.options.columns.each(function(column_info) { + var column = new Widgets.DataGridColumn(column_info) + column.addEvent('textChanged', function(e) { + var index = this.columns.indexOf(column) + this.header.getElements('th')[index].set('text', e) + }.bindWithEvent(this)) + this.columns.include(column) + }, this) + this.rows = [] + this.displayRows = [] + this.selectedIndex = -1 + this.filterer = false + this.sorted_by = 0 + this.columns[0].order = -1 + if ($chk(this.element)) { this.scanElement() } else { this.createElement } + this.element.setStyle('MozUserSelect', 'none') + this.resize_columns() + this.addEvent('resize', this.resized.bindWithEvent(this)) + }, + + createElement: function() { + this.element = new Element('div') + this.table = new Element('table').inject(this.element) + this.header = new Element('thead').inject(this.table).grab(new Element('tr')) + this.body = new Element('tbody').inject(this.table) + this.options.columns.each(function(column) { + new Element('th', { + 'class': column.name, + 'text': column.text + }) + }, this); + }, + + scanElement: function() { + this.element.addClass('mooui-datagrid') + this.table = this.element.getElement('table') + if (!this.table) this.table = new Element('table').inject(this.element) + this.header = this.table.getElement('thead') + if (!this.header) this.header = new Element('thead').inject(this.table) + this.body = this.table.getElement('tbody') + if (!this.body) this.body = new Element('tbody').inject(this.table) + this.header.empty(); + var headerRow = new Element('tr'); + this.header.grab(headerRow) + + this.columns.each(function(column, index) { + new Element('th').addEvent('click', function(e) { + this.sort(column, index) + this.render() + }.bindWithEvent(this)).set('text', column.options.text).inject(headerRow) + }, this) + }, + + addRow: function(row_info, noRender) { + var row = new Widgets.DataGridRow(row_info.id, this.columns) + row.store = row_info.store + this.rows.include(row) + row.update(row_info) + row.addEvent('menu', this.onrowmenu.bind(this)); + row.addEvent('click', this.onrowclick.bind(this)) + if (!noRender) this.render() + }, + + onrowmenu: function(e) { + if (!this.selectedRow) { + this.selectRow(e.row) + } + this.fireEvent('row_menu', e) + }, + + onrowclick: function(e) { + e.row.index = this.displayRows.indexOf(this.getById(e.row.id)) + if (e.shift) { + this.deselectRows() + this.selectRows(e.row.index) + } else if (e.control) { + if (e.row.selected) { this.deselectRow(e.row) } + else { this.selectRow(e.row) } + } else { + this.deselectRows() + this.selectRow(e.row) + } + this.fireEvent('row_click', e) + }, + + deselectRow: function(row) { + if (!$chk(row)) { return } + row.selected = false + row.element.removeClass('selected') + if (row == this.selectedRow) { + this.selectedRow = null + this.fireEvent('selectedchanged') + } + }, + + deselectRows: function() { + this.selected = -1 + this.rows.each(function(row) { + row.selected = false + row.element.removeClass('selected') + }) + }, + + filter: function() { + if (!$chk(this.filterer)) { + this.filterer = $lambda(true) + } + this.displayRows.empty() + this.rows.each(function(r) { + if (this.filterer(r)) {this.displayRows.include(r)} + }.bind(this)) + }, + + getById: function(id) { + var row = null + this.rows.each(function(r) {if (r.id == id) {row = r}}) + return row + }, + + get_selected: function() { + selected = [] + this.rows.each(function(row) { + if (row.selected) { selected.include(row) } + }) + return selected + }, + + has: function(id) { + if (this.getById(id)){return true;}else{return false;} + }, + + remove: function(id) { + this.getById(id) + + }, + + render: function() { + this.filter() + this.resort() + var rows = [], rowIds = [] + this.rows.each(function(row) { + if (this.displayRows.contains(row)) { + rows.include(row.element) + rowIds.include(row.id) + } + }, this) + this.body.adopt(rows) + + this.body.getChildren().each(function(row) { + var id = row.retrieve('rowid') + if (!id) { + row.destroy() + } else if (!rowIds.contains(id)) { + row.dispose() + } + }, this) + + var selected = this.body.getElements('tr.selected') + if (selected) { this.selected = $A(this.body.rows).indexOf(selected) } + }, + + resize_columns: function() { + + var total_width = this.options.columns.sum('width') + this.table.setStyle('width', total_width) + var cols = this.header.getElements('th') + cols.each(function(col, index) { + col.setStyle('width', this.options.columns[index].width) + }, this) + }, + + resized: function() { + var width = this.width + var columnWidth = this.options.columns.sum('width') + if (columnWidth > width) this.table.setStyle('width', columnWidth) + else this.table.setStyle('width', width) + var cols = this.header.getElements('th') + cols.each(function(col, index) { + var colWidth = this.columns[index].options.width; + if (index == cols.length - 1 && width > colWidth) + col.setStyle('width', width) + else { + col.setStyle('width', colWidth) + width -= colWidth + } + }, this) + }, + + selectRows: function(index) { + var selectedIndex = this.displayRows.indexOf(this.selectedRow) + if (selectedIndex > index) { + for (var i=index; i<=selectedIndex; i++) { + this.displayRows[i].selected = true + this.displayRows[i].element.addClass('selected') + } + } else if (selectedIndex < index) { + for (var i=selectedIndex; i<=index; i++) { + this.displayRows[i].selected = true + this.displayRows[i].element.addClass('selected') + } + } + }, + + selectRow: function(row) { + row.selected = true + row.element.addClass('selected') + this.selectedRow = row + this.fireEvent('selectedchanged') + }, + + sort: function(column, index) { + var sorter = column.get_sorter(index) + this.rows.sort(sorter.sorter()) + column.order *= -1 + this.sorted_by = index + }, + + resort: function() { + var column = this.columns[this.sorted_by] + column.order *= -1 + this.sort(column, this.sorted_by) + }, + + updateRow: function(row, noRender) { + this.getById(row.id).update(row) + if (!noRender) this.render() + } +}) \ No newline at end of file diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/menu_bg.jpg b/deluge/ui/webui/templates/ajax/static/themes/classic/menu_bg.jpg new file mode 100644 index 000000000..1a6fc0971 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/classic/menu_bg.jpg differ diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/simple_line_flipped.png b/deluge/ui/webui/templates/ajax/static/themes/classic/simple_line_flipped.png new file mode 100644 index 000000000..c95266314 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/classic/simple_line_flipped.png differ diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/split_horizontal.png b/deluge/ui/webui/templates/ajax/static/themes/classic/split_horizontal.png new file mode 100644 index 000000000..70c181e98 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/classic/split_horizontal.png differ diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/split_vertical.png b/deluge/ui/webui/templates/ajax/static/themes/classic/split_vertical.png new file mode 100644 index 000000000..15897be5c Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/classic/split_vertical.png differ diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/style.css b/deluge/ui/webui/templates/ajax/static/themes/classic/style.css new file mode 100644 index 000000000..9b3c018bf --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/themes/classic/style.css @@ -0,0 +1,296 @@ +/* + +----------------------------------------------------------- + +Theme Name: Ajax Classic +Theme URI: http://deluge-torrent.org +Description: Deluge Ajax Theme +Version: 0.1 +----------------------------------------------------------- + +*/ + +body { + background: #304663 url(/static/images/simple_bg.jpg) repeat-x; + font-family: trebuchet ms; + font-size: 0.7em; + color: White; +} + +#toolbar { + background: url(/static/images/simple_line.jpg) repeat-x 0px 69px; + height: 75px; +} + +#toolbar ul, #status ul { + margin: 0; + padding: 0; + display: inline; + list-style: none; +} + +#toolbar ul li, #status ul li { + float: left; + display: block; + margin: 2px; + background-repeat: no-repeat; + background-position: 2px; +} + +#toolbar #buttons { + float: left; + background: url('/static/images/simple_logo.jpg') no-repeat; + height: 71px; + padding-left: 60px; + width: 320px; +} + +#toolbar #buttons li { + width: 36px; + height: 36px; + -moz-border-radius:5px; + -webkit-border-radius: 5px; + cursor: pointer; + margin-top: 30px; +} + +#toolbar #buttons li:hover { + background-color: #386594; +} + +.info { + text-align: right; + padding: 0 50px 0 0; + color: #8fa6c3; + font-size: 16px; + letter-spacing: 4px; + font-weight: bold; +} + +.title { + color: #dce4ee; + font-size: 32px; + padding: 10px 50px 0 0; + text-align: right; +} + +.mooui-splitter { + background: none; +} + +.mooui-splitter-vertical { + background: url(split_vertical.png) repeat-y; +} + +.mooui-splitter-horizontal { + background: url(split_horizontal.png) repeat-x; +} + +#toolbar #buttons #add {background-image:url(../../icons/32/add.png)} +#toolbar #buttons #remove {background-image:url(../../icons/32/remove.png)} +#toolbar #buttons #pause {background-image:url(../../icons/32/pause.png)} +#toolbar #buttons #resume {background-image:url(../../icons/32/resume.png)} +#toolbar #buttons #up {background-image:url(../../icons/32/up.png)} +#toolbar #buttons #down {background-image:url(../../icons/32/down.png)} +#toolbar #buttons #preferences {background-image:url(../../icons/32/options.png)} +#toolbar #buttons #connections {background-image:url(../../icons/32/connections.png)} + +#status #info #connections {background-image:url(/static/images/tango/connections.png)} +#status #info #downspeed {background-image:url(/pixmaps/downloading16.png)} +#status #info #upspeed {background-image:url(/pixmaps/seeding16.png)} +#status #info #dht {background-image:url(/pixmaps/dht16.png)} + +#labels .downloading { background-image: url('/pixmaps/downloading16.png'); } +#labels .seeding { background-image: url('/pixmaps/seeding16.png'); } +#labels .queued { background-image: url('/pixmaps/queued16.png'); } +#labels .paused { background-image: url('/pixmaps/inactive16.png'); } +#labels .error { background-image: url('/pixmaps/alert16.png'); } +#labels .checking { background-image: url('/pixmaps/inactive16.png'); } + +#labels { + width: 140px; + margin: 5px; + overflow: auto; +} + +#labels ul { + list-style: none; + margin: 10px; + padding: 0; +} + +#labels li { + background-repeat: no-repeat; + background-position: 0px 2px; + padding-left: 16px; + line-height: 20px; +} +#labels li:hover { text-decoration: underline; cursor: pointer; } + +#labels .activestate { + font-weight: bold; +} + +#labels .activestate:hover { + text-decoration: none; +} + +.mooui-datagrid { + overflow: auto; + margin: 5px; + border: 1px solid #2a425c; +} + +.mooui-datagrid table { + /*background: #304663;*/ + border-spacing: 0px; +} + +.mooui-datagrid table th { + font-weight: normal; + border: 1px outset #1c2431; + margin-bottom: 2px; + cursor: pointer; +} + +.mooui-datagrid table td { + padding: 2px; + border-bottom: 1px solid #1c2431; + cursor: pointer; +} +.mooui-datagrid table tr.selected td { background: #900; } +.mooui-datagrid table tr:hover td { background: #68a; } + +.mooui-progressbar div { + background: #4573a5; + -moz-border-radius:5px; /*ff only setting*/ + -webkit-border-radius: 5px; +} + +.mooui-progressbar span { + text-align: center; +} + +#status { + height: 40px; + background: #1c2431 url(simple_line_flipped.png) repeat-x; +} + +.mooui-menu { + background: #304663 url(menu_bg.jpg) repeat-x; + border: 1px outset #37506f; +} + +ul.mooui-menu li.mooui-menu-sep { + background-color: #aaa; +} + +ul.mooui-menu li:hover { + background-color: #426187; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +ul.mooui-menu li.mooui-menu-sep:hover { + background-color: #aaa; +} + +.mooui-window { + background: #426187; + border: 1px solid #1c2431; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +.mooui-window-close { + background-image: url(window_close.png); +} + +.mooui-window-title { + margin: 5px; +} + +/* forms */ +form fieldset { + border: none; + float: left; +} + +form legend { + font-weight: bold; +} + +form label { + width: 150px; + line-height: 20px; + float: left; +} + +form input, form select { + float: left; + margin-right: 5px; + border:1px solid #23344b; + background: #99acc3; + color: #000; + line-height: 20px; +} + +form textarea { + border:1px solid #23344b; + background: #99acc3; + width:480px; +} + +form .disabled { + color: Gray; +} + +form .deluge_button, button.deluge_button { + background-color: #37506f; + border:1px solid #68a; + cursor: pointer; + background: #99acc3; + color: #000; + vertical-align:middle; + -moz-border-radius:7px; + margin: 2px; +} + +form .deluge_button:hover, button.deluge_button:hover { + background-color:#68a; +} + +form br { + clear: left; +} + +#addFromUrl label { + width: 100px; +} + +#addFromUrl #url { + width: 250px; +} + +#addFromUrl .deluge_button { + margin: 5px; + margin-left: 100px; +} + +.mooui-window-content #preferences .categories li:hover { + text-decoration: underline; +} + +.deluge-prefs-active { + font-weight: bold; +} + +.mooui-spinner { + width: 40px; + margin-right: 20px; +} + +label.fluid { + width: auto; + margin-right: 30px; +} diff --git a/deluge/ui/webui/templates/ajax/static/themes/classic/window_close.png b/deluge/ui/webui/templates/ajax/static/themes/classic/window_close.png new file mode 100644 index 000000000..289279c83 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/classic/window_close.png differ diff --git a/deluge/ui/webui/templates/ajax/static/themes/white/style.css b/deluge/ui/webui/templates/ajax/static/themes/white/style.css new file mode 100644 index 000000000..eefb8c2bf --- /dev/null +++ b/deluge/ui/webui/templates/ajax/static/themes/white/style.css @@ -0,0 +1,341 @@ +/* + +----------------------------------------------------------- + +Theme Name: Ajax White +Theme URI: http://deluge-torrent.org +Description: Deluge Ajax Theme +Version: 0.1 +----------------------------------------------------------- + +*/ + + +body { + background: #ffffff; + font-family: trebuchet ms; + font-size: 0.7em; +} + +#toolbar #buttons li:hover { + background-color: #C3D9FF; +} + +#toolbar { + border-bottom: 1px solid Black; + height: 40px; +} + +#toolbar ul, #status ul { + margin: 0; + padding: 0; + display: inline; + list-style: none; +} + +#toolbar ul li, #status ul li { + float: left; + display: block; + margin: 2px; + background-repeat: no-repeat; + background-position: 2px; +} + +#toolbar #buttons { + float: left; + background: url('/static/images/deluge32.png') no-repeat 2px; + height: 40px; + padding-left: 140px; + width: 320px; +} + +#toolbar #buttons li { + width: 36px; + height: 36px; + -moz-border-radius:5px; + -webkit-border-radius: 5px; + cursor: pointer; +} + +#toolbar #buttons li:hover { + background-color: #E0ECFF; +} + +.info { display: none; } + +.title { + position: absolute; + left: 40px; + top: 7px; + color: #8fa6c3; + padding: 0; + margin: 0; + line-height: 26px; + font-size: 26px; +} + +.mooui-splitter { + background: none; +} + +/*.mooui-splitter-vertical { + background: url(/template/static/images/split_vertical.png) repeat-y; +} + +.mooui-splitter-horizontal { + background: url(/template/static/images/split_horizontal.png) repeat-x; +}*/ + + +#toolbar #buttons #add {background-image:url(/template/static/icons/32/add.png)} +#toolbar #buttons #remove {background-image:url(/template/static/icons/32/remove.png)} +#toolbar #buttons #pause {background-image:url(/template/static/icons/32/pause.png)} +#toolbar #buttons #resume {background-image:url(/template/static/icons/32/resume.png)} +#toolbar #buttons #up {background-image:url(/template/static/icons/32/up.png)} +#toolbar #buttons #down {background-image:url(/template/static/icons/32/down.png)} +#toolbar #buttons #preferences {background-image:url(/template/static/icons/32/options.png)} +#toolbar #buttons #connections {background-image:url(/template/static/icons/32/connections.png)} + +#status #info #connections {background-image:url(/static/images/tango/connections.png)} +#status #info #downspeed {background-image:url(/pixmaps/downloading16.png)} +#status #info #upspeed {background-image:url(/pixmaps/seeding16.png)} +#status #info #dht {background-image:url(/pixmaps/dht16.png)} + +#labels .downloading { background-image: url('/pixmaps/downloading16.png'); } +#labels .seeding { background-image: url('/pixmaps/seeding16.png'); } +#labels .queued { background-image: url('/pixmaps/queued16.png'); } +#labels .paused { background-image: url('/pixmaps/inactive16.png'); } +#labels .error { background-image: url('/pixmaps/alert16.png'); } +#labels .checking { background-image: url('/pixmaps/inactive16.png'); } + + +#labels { + width: 160px; + margin: 5px; + overflow: auto; + background: #b5edbc; + -moz-border-radius: 5px; +} + +#labels ul { + list-style: none; + margin: 5px; + padding: 0; + padding-top: 20px; + padding-bottom: 20px; + background: White; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +#labels li { + background-repeat: no-repeat; + background-position: 5px 2px; + padding-left: 21px; + line-height: 20px; +} +#labels li:hover { text-decoration: underline; cursor: pointer; } + +#labels .activestate { + font-weight: bold; +} + +#labels .activestate:hover { + text-decoration: none; +} + + +.mooui-datagrid { + overflow: auto; + margin: 5px; + border: 4px solid #C3D9FF; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +.mooui-datagrid table { + /*background: #304663;*/ + border-spacing: 0px; +} + +.mooui-datagrid table th { + font-weight: normal; + background: #C3D9FF; + margin-bottom: 2px; + cursor: pointer; +} + +.mooui-datagrid table td { + padding: 2px; + border-bottom: 1px solid #1c2431; + cursor: pointer; +} +.mooui-datagrid table tr.selected td { background: #E0ECFF; } +.mooui-datagrid table tr:hover td { background: #FFFFCC; } + +.mooui-progressbar div { + background: #C3D9FF; + -moz-border-radius:5px; /*ff only setting*/ + -webkit-border-radius: 5px; +} + +.mooui-progressbar span { + text-align: center; +} + +#details { + padding: 5px; +} + +#details .mooui-tabs li { + background: #E0ECFF; + padding: 7px; + font-size: 1.1em; +} + +#details .mooui-tabs li:hover { + background: #C3D9FF; +} + +#details li.mooui-tabs-active { + background: #C3D9FF; +} + +#details .mooui-tabpage { + background: #C3D9FF; + padding: 5px; + -moz-border-radius:0 5px 5px 5px; + -webkit-border-radius: 0 5px 5px 5px; + overflow: auto; +} + +#details dl { + float: left; + min-height: 100px; +} + +#details dl dt { + float: left; + font-weight: bold; + height: 19px; +} + +#details dl dd { + margin-left: 100px; + width: 140px; + height: 19px; +} + +#details dd.downspeed, #details dd.upspeed, #details dd.eta, #details dd.pieces { + margin-left: 75px; +} + +#details form br { + clear: left; +} + +#details dd.torrent_name { + width: 500px; +} + +#details dd.tracker { + width: 500px; +} + +#status { + height: 40px; + background: #E0ECFF; + -moz-border-radius: 7px 7px 0px 0px; +} + +.mooui-menu { + background: #E0ECFF; + border: 1px outset Gray; +} + +ul.mooui-menu li.mooui-menu-sep { + background-color: Gray; +} + +ul.mooui-menu li:hover { + background-color: #a0a0a0; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +ul.mooui-menu li.mooui-menu-sep:hover { + background-color: Gray; +} + +.mooui-window { + background: #C3D9FF; + border: 1px solid #1c2431; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +.mooui-window-close { + background-image: url(/template/static/images/window_close.png); +} + +.mooui-window-title { + margin: 5px; +} + +/* forms */ +form fieldset { + border: none; + float: left; +} + +form legend { + font-weight: bold; +} + +form label { + width: 150px; + float: left; +} + +form input { + float: left; + margin-right: 5px; + border:1px solid #23344b; + background: #E0ECFF; + color: #000; +} + +form textarea { + border:1px solid #23344b; + background: #E0ECFF; + width:480px; +} + +form label.opt-disabled { + color: Gray; +} + +form .deluge_button { + background-color: #E0ECFF; + border:1px solid #68a; + cursor: pointer; + background: #E0ECFF; + color: #000; + vertical-align:middle; + -moz-border-radius:7px; + margin: 2px; + float: left; +} + +form .deluge_button:hover { + background-color:#68a; +} + +#addFromUrl label { + width: 100px; +} + +#addFromUrl input[type=submit] { + margin: 5px; + margin-left: 100px; +} diff --git a/deluge/ui/webui/templates/ajax/static/themes/white/window_close.png b/deluge/ui/webui/templates/ajax/static/themes/white/window_close.png new file mode 100644 index 000000000..289279c83 Binary files /dev/null and b/deluge/ui/webui/templates/ajax/static/themes/white/window_close.png differ diff --git a/deluge/ui/webui/templates/ajax/template_style.css b/deluge/ui/webui/templates/ajax/template_style.css new file mode 100644 index 000000000..353bad25a --- /dev/null +++ b/deluge/ui/webui/templates/ajax/template_style.css @@ -0,0 +1,139 @@ +/* + +----------------------------------------------------------- + +Theme Name: Ajax Base +Theme URI: http://deluge-torrent.org +Description: Deluge Ajax Theme +Version: 0.1 +----------------------------------------------------------- + +*/ + +html, body { + padding: 0px; + margin: 0px; + overflow: hidden; +} + +#details { + padding: 5px; +} + +#details .mooui-tabs li { + background: #37506f; + padding: 7px; + font-size: 1.1em; +} + +#details .mooui-tabs li:hover { + background: #426187; +} + +#details li.mooui-tabs-active { + background: #426187; +} + +#details .mooui-tabpage { + background: #426187; + padding: 5px; + -moz-border-radius:0 5px 5px 5px; + -webkit-border-radius: 0 5px 5px 5px; + overflow: auto; +} + +#details dl { + float: left; + min-height: 100px; +} + +#details dl dt { + float: left; + font-weight: bold; + height: 19px; +} + +#details dl dd { + margin-left: 100px; + width: 140px; + height: 19px; +} + +#details dd.downspeed, #details dd.upspeed, #details dd.eta, #details dd.pieces { + margin-left: 75px; +} + +#details form br { + clear: left; +} + +#details dd.torrent_name, #details dd.tracker, #details dd.path { + width: 500px; +} + +#status #info { + float: left; + margin: 10px; +} + +#status #info li { + height: 20px; + line-height: 20px; + width: 30px; + padding-left: 21px; +} + +#status #info #connections { + width: 15px; +} + +#status #info #downspeed, #status #info #upspeed { + background-position: 4px 2px; + margin-right: 25px; +} + +.categories { + height: 350px; +} + +.pref_pages { + height: 350px; +} + +.deluge-prefs-page { + display: none; +} + +.deluge-prefs-page-active { + display: block; +} + +.mooui-window-content #preferences .categories { + float: left; + width: 130px; + padding-left: 10px; +} + +.mooui-window-content #preferences .pref_pages { + float: left; + width: 350px; +} + +.mooui-window-content #preferences .categories ul { + list-style: none; + padding: 0; + padding-left: 10px; +} + +.mooui-window-content #preferences .categories li { + cursor: pointer; +} + +.buttons { + margin-top: 10px; + float: right; +} + +.buttons button { + margin-right: 10px; +}