summaryrefslogtreecommitdiff
path: root/www/js
diff options
context:
space:
mode:
Diffstat (limited to 'www/js')
-rw-r--r--www/js/clipboard.js535
-rw-r--r--www/js/file-transfer.js365
-rw-r--r--www/js/modernizr-debug.js441
-rw-r--r--www/js/mootools-1.6.0.js6382
-rw-r--r--www/js/mootools-debug.js5180
-rw-r--r--www/js/popupdeck.js96
-rw-r--r--www/js/rdp-start.js262
-rw-r--r--www/js/simpletabs-debug.js120
-rw-r--r--www/js/vkb-debug.js750
-rw-r--r--www/js/vkbl-de_DE.json8
-rw-r--r--www/js/vkbl-en_US.json12
-rw-r--r--www/js/webrdp-Log.js134
-rw-r--r--www/js/webrdp-debug.js1707
-rw-r--r--www/js/webrdp-ext.js19
-rw-r--r--www/js/webrdp-log.js147
15 files changed, 16158 insertions, 0 deletions
diff --git a/www/js/clipboard.js b/www/js/clipboard.js
new file mode 100644
index 0000000..77ea154
--- /dev/null
+++ b/www/js/clipboard.js
@@ -0,0 +1,535 @@
+'use strict';
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>, smake <smake at ya dot ru>.
+ *
+ */
+
+/**
+ * Main clipboard class, that dispatch clipboard events and handle their status
+ */
+
+
+class ClipboardHandler {
+ constructor(rdp) {
+ this.rdp = rdp;
+ this.sock = this.rdp.sock;
+ this.filelistIn = [];
+ this.filelistOut = [];
+ this.textIn = null;
+ this.textOut = null;
+ this.update();
+ return this;
+ }
+
+ get istransferring() {
+ /*
+ * check for any file is currently transferring
+ * returns strings "in", "out" or bool false
+ */
+ if (this.filelistOut.length > 0) {
+ for (var i = 0, f; f = this.filelistOut[i]; i++) {
+ if (f.isTransferring){
+ return "out";
+ }
+ }
+ }
+ if (this.filelistIn.length > 0) {
+ for (var i = 0, f; f = this.filelistIn[i]; i++) {
+ if (f.isTransferring){
+ return "in";
+ }
+ }
+ }
+ return false;
+ }
+
+ ClipoboardFileGet(fid) {
+ var ft = this.filelistIn[fid];
+ if (!ft) {
+ return;
+ }
+ if (this.istransferring) {
+ console.log("Transfer in progress, please await");
+ return;
+ }
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.rdp.log.debug('requesting file transfer', 1);
+ var buf = ft.nextChunkRequest;
+ // console.log(buf.toHexdump());
+ this.sock.send(buf);
+ };
+
+ }
+
+ ClipoboardSendFilelist(files) {
+ if (!files.length) {
+ console.log("There in now escape(files)")
+ return;
+ }
+ if (this.istransferring) {
+ var msg = lables.files_in_transfer;
+ this.rdp.fireEvent('alert', msg, 2);
+ console.log("Transfer in progress, please await");
+ return;
+ }
+ this.filelistOut = [];
+ this.filelistIn = [];
+ for (var i = 0, f; f = files[i]; i++) {
+ /*
+ * Bug in microsoft RDP server
+ * https://support.microsoft.com/en-us/help/2258090/copying-files-larger-than-2-gb-over-a-remote-desktop-services-or-termi
+ */
+ if (f.size < 2147483647) {
+ var j = this.filelistOut.length
+ var ft_entry = new FileEntry(this.rdp,f,j);
+ this.filelistOut[j] = ft_entry;
+ this.checkFileReadable(ft_entry);
+ } else {
+ var msg = f.name + ": " + lables.file_lager_then_2g;
+ this.rdp.fireEvent('alert', msg, 2);
+ }
+ }
+
+ if (this.filelistOut.length == 0) {
+ // popUpMessage
+ console.log("There in now escape(files)")
+ return;
+ }
+
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.rdp.log.debug('updating remote clipbord status', 1);
+ var buf = new ArrayBuffer(6);
+ var c = new Uint32Array(buf, 0, 1);
+ var count = new Uint8Array(buf, 4, 1);
+ var fmt = new Uint8Array(buf, 5, 1);
+ c[0] = 6; // ws_in_clipbrd_changed
+ count[0] = this.filelistOut.length;
+ fmt[0] = 3;
+ // console.log(buf.toHexdump());
+ this.sock.send(buf);
+ };
+ this.update();
+
+ }
+
+ getFile() {
+ if (this.rdp.sock.readyState == this.rdp.sock.OPEN) {
+ this.rdp.log.debug('requesting file transfer', 1);
+ var buf = this.nextChunkRequest;
+ console.log(buf.toHexdump());
+ this.rdp.sock.send(buf);
+ };
+ }
+
+ resetUi() {
+ var cbdinimg = document.querySelector("div#clipboardin > img");
+ cbdinimg.style.backgroundColor = "#eee";
+ cbdinimg.src = "images/cbd_empty.png";
+
+ var new_element = cbdinimg.cloneNode(true);
+ cbdinimg.parentNode.replaceChild(new_element, cbdinimg);
+ var cbdinimg = new_element;
+
+ var cbdoutimg = document.querySelector("div#clipboardout > img");
+ cbdoutimg.style.backgroundColor = "#eee";
+ cbdoutimg.src = "images/cbd_empty_out.png";
+
+ var new_element = cbdoutimg.cloneNode(true);
+ cbdoutimg.parentNode.replaceChild(new_element, cbdoutimg);
+ var cbdoutimg = new_element;
+
+ var pos = this.rdp.canvas.getPosition();
+ var size = this.rdp.canvas.getSize();
+
+ var cbdcontent = document.getElementById('cbdcontent');
+ cbdcontent.innerHTML = "";
+ cbdcontent.setStyle('max-height', size.y);
+ cbdcontent.setStyle('overflow-y', "auto");
+ cbdcontent.style.visibility = "hidden";
+ }
+
+ update() {
+ /*
+ * Re-draw
+ */
+
+ this.resetUi();
+ var cbdinimg = document.querySelector("div#clipboardin > img");
+ var cbdoutimg = document.querySelector("div#clipboardout > img");
+ var cbdcontent = document.getElementById('cbdcontent');
+ cbdcontent.addEventListener("mouseleave", function(evt) {
+ evt.target.style.visibility = "hidden";
+ });
+
+
+ cbdoutimg.cbd = this;
+ cbdoutimg.addEventListener("click", function(evt) {
+ var cbdcontent = document.getElementById('cbdcontent');
+ var cbd = evt.target.cbd;
+ if (cbd.istransferring) {
+ return;
+ }
+ cbd.filelistIn = [];
+ cbd.filelistOut = [];
+ cbd.textIn = null;
+ cbd.textOut = null;
+ cbd.update();
+ navigator.clipboard.readText().then(text => {
+ rdp.cbd.textOut = text;
+ rdp.cbd.update();
+ }).catch(err => {
+ cbdcontent.style.visibility = "visible";
+ var input = document.createElement('input');
+ cbdcontent.appendChild(input);
+ input.cbd = cbd;
+ input.addEventListener("paste", function(evt) {
+ var cbd = evt.target.cbd;
+ cbd.textOut = "" + evt.clipboardData.getData("text");
+ cbd.update();
+ });
+ input.focus();
+ console.error('Failed to read clipboard contents: ', err);
+ });
+
+ });
+
+ if (this.filelistIn.length > 0) {
+ if (this.istransferring == "in") {
+ cbdinimg.src = "images/cbd_wait.png";
+ } else {
+ cbdinimg.src = "images/cbd_files.png";
+ }
+
+ this.iconAddListners(cbdinimg);
+
+ for (var i =0; i < this.filelistIn.length; i++) {
+ var file = this.filelistIn[i];
+ cbdcontent.innerHTML += file.uiElement;
+ }
+
+ return;
+ } else if (this.filelistOut.length > 0) {
+ if (this.istransferring == "out") {
+ cbdoutimg.src = "images/cbd_wait.png";
+ } else {
+ cbdoutimg.src = "images/cbd_files.png";
+ }
+
+ this.iconAddListners(cbdoutimg);
+
+ var html = "";
+ for (var i =0; i < this.filelistOut.length; i++) {
+ var file = this.filelistOut[i];
+ cbdcontent.innerHTML += file.uiElement
+ }
+ } else if (this.textIn) {
+ cbdinimg.src = "images/cbd_text.png";
+ cbdoutimg.src = "images/cbd_empty_out.png";
+
+ this.iconAddListners(cbdinimg);
+
+ cbdcontent.innerHTML = '<input type="text" value="' + this.textIn + '">';
+ } else if (this.textOut) {
+ cbdinimg.src = "images/cbd_empty.png";
+ cbdoutimg.src = "images/cbd_text.png";
+
+ this.iconAddListners(cbdoutimg);
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.rdp.log.debug('updating remote clipbord status', 1);
+ var buf = new ArrayBuffer(6);
+ var c = new Uint32Array(buf, 0, 1);
+ var count = new Uint8Array(buf, 4, 1);
+ var fmt = new Uint8Array(buf, 5, 1);
+ c[0] = 6; // ws_in_clipbrd_changed
+ count[0] = 1;
+ fmt[0] = 2;
+ // console.log(buf.toHexdump());
+ this.sock.send(buf);
+ };
+
+ cbdcontent.innerHTML = '<input type="text" value="' + this.textOut + '">';
+ } else {
+ cbdinimg.src = "images/cbd_empty.png";
+ cbdoutimg.src = "images/cbd_empty_out.png";
+ }
+
+ }
+
+ iconAddListners (obj) {
+ var cbdcontent = document.getElementById('cbdcontent');
+ obj.addEventListener("mouseover", function(evt) {
+ var cbdcontent = document.getElementById('cbdcontent');
+ cbdcontent.style.visibility = "visible";
+ evt.target.style.backgroundColor = "darkgreen";
+ });
+ obj.addEventListener("mouseout", function(evt) {
+ var fl = document.getElementById('cbdcontent');
+ evt.target.style.backgroundColor = "#eee";
+ });
+
+ }
+
+ ClipbrdInfoParse(fmts) {
+ var clipboardimg = document.querySelector("div#clipboardin > img");
+ if (this.istransferring) {
+ console.log("Transfer in progress, please await");
+ return;
+ }
+ var fmt = 0;
+ if (fmts.length > 1) {
+ for (var i = 0; i < fmts.length; ++i) {
+ if (fmts[i] == 3) {
+ fmt = 3;
+ }
+ }
+ }
+ clipboardimg.style.backgroundColor = "darkgreen";
+ switch (fmt) {
+ case 0: // clip_format_raw
+ case 1: // clip_format_text
+ case 2: // clip_format_utf8
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.rdp.log.debug('getting raw buffer', 1);
+ var buf = new ArrayBuffer(5);
+ var c = new Uint32Array(buf, 0, 1);
+ var t = new Uint8Array(buf, 4, 1);
+ c[0] = 8; // ws_in_clipbrd_data_request
+ t[0] = 2;
+ this.sock.send(buf);
+ clipboardimg.src = "images/cbd_text.png";
+ setTimeout(function () {
+ clipboardimg.style.backgroundColor = "#eee";
+ }, 100);
+ };
+ break;
+ case 3: // clip_format_FileGroupDescriptorW
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.rdp.log.debug('getting file list', 1);
+ var buf = new ArrayBuffer(5);
+ var c = new Uint32Array(buf, 0, 1);
+ var t = new Uint8Array(buf, 4, 1);
+ c[0] = 8; // ws_in_clipbrd_data_request
+ t[0] = 3;
+ this.sock.send(buf);
+ clipboardimg.src = "images/cbd_files.png";
+ setTimeout(function () {
+ clipboardimg.style.backgroundColor = "#eee";
+ }, 100);
+ };
+ break;
+ default:
+ this.innerHTML = lables.cbd_empty;
+ break;
+ }
+ return true;
+ }
+
+ sendClipboardInfo (type) {
+ if (this.istransferring) {
+ console.log("Transfer in progress, please await");
+ return;
+ }
+ switch(type)
+ {
+ case 0:
+ case 1:
+ case 2:
+ {
+ if (this.sock.readyState != this.sock.OPEN) {
+ break;
+ }
+ // if (!this.textOut) {
+ // return;
+ // }
+ var text = new TextEncoder().encode(this.textOut);
+
+ // var buf = new ArrayBuffer(4 + 1 + 4 + text.byteLength);
+ var buf = new ArrayBuffer(4 + 1 + text.byteLength);
+ var bufArr = new Uint8Array(buf);
+ var c = new Uint32Array(buf, 0, 1);
+ var type = new Uint8Array(buf, 4, 1);
+ // var size = new Uint8Array(buf, 5, 1);
+ var data = new Uint8Array(buf, 5);
+ var tmpSize = new Uint32Array(1);
+ c[0] = 7; // ws_in_clipbrd_data
+ type[0] = 2;
+ tmpSize[0] = text.byteLength
+ // size.set(tmpSize);
+ data.set(text);
+ console.log(buf.toHexdump());
+ this.sock.send(buf);
+ }
+ break;
+ case 3:
+ {
+
+ if (this.sock.readyState != this.sock.OPEN) {
+ break;
+ }
+ if(this.filelistOut.length == 0) {
+ console.log("no files in buffer");
+ break;
+ }
+ var ftlenght = 0;
+ for (var i =0; i < this.filelistOut.length; i++) {
+ var f = this.filelistOut[i];
+ ftlenght += f.raw_ft.length;
+ };
+
+ var ftOffset = 4 + 2 + 1;
+ var buf = new ArrayBuffer(ftOffset + ftlenght);
+ var bufArr = new Uint8Array(buf);
+ var c = new Uint32Array(buf, 0, 1);
+ var type = new Uint8Array(buf, 4, 1);
+ var count = new Uint16Array(1);
+ c[0] = 7; // ws_in_clipbrd_data
+ type[0] = 3;
+ count[0] = this.filelistOut.length;
+
+ // because of buf migth be not multiple Uint16Array, which throw exeption in js
+ bufArr.set(new Uint8Array(count.buffer), 4 + 1);
+
+ for (var i =0; i < this.filelistOut.length; i++) {
+ var f = this.filelistOut[i];
+ bufArr.set(f.raw_ft, ftOffset);
+ ftOffset += f.raw_ft.length;
+ };
+
+ // console.log(buf.toHexdump());
+ this.sock.send(buf);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ reciveClipboardInfo(data) {
+ /* type is:
+ * typedef enum
+ * {
+ * clip_format_unsupported = -1,
+ * clip_format_raw,
+ * clip_format_text,
+ * clip_format_unicode,
+ * clip_format_file_list
+ * }wrdp_enum_clip_format;
+ * NOTE: must be kept in sync with core
+ */
+ if (this.istransferring) {
+ console.log("Transfer in progress, please await");
+ return;
+ }
+ var type = new Uint8Array(data, 4, 1);
+ this.filelistIn = [];
+ this.filelistOut = [];
+ this.textIn = null;
+ this.textOut = null;
+ switch(type[0])
+ {
+ case 0:
+ case 1:
+ {
+ /* text is most probably in local rdp server
+ * endocing */
+ var buf = new Uint8Array(data, 5);
+ //var text = new TextDecoder("utf-8").decode(buf);
+ //Viva la hardcoding encoding
+ var text = new TextDecoder("windows-1251").decode(buf.filter(v => v != 0));
+ this.textIn = text;
+ navigator.clipboard.writeText(text).then(function() {
+ /* clipboard successfully set */
+ // console.log("buffer setted")
+ return;
+ }, function() {
+ /* clipboard write failed */
+ var msg = lables.files_in_transfer;
+ this.rdp.fireEvent('alert', msg, 2);
+ console.log("buffer not setted")
+ return;
+ });
+ this.update();
+ }
+ break;
+ case 2:
+ {
+ var buf = new Uint8Array(data, 5);
+ var text = new TextDecoder("utf-8").decode(buf.filter(v => v != 0));
+ this.textIn = text;
+ navigator.clipboard.writeText(text).then(function() {
+ /* clipboard successfully set */
+ // console.log("buffer setted");
+ return;
+ }, function() {
+ /* clipboard write failed */
+ console.log("buffer not setted")
+ return;
+ });
+ this.update();
+ }
+ break;
+ case 3:
+ {
+ /*
+ * typedef struct
+ * {
+ * uint16_t filename_len;
+ * uint32_t file_id;
+ * uint64_t file_size, file_offset;
+ * }wrdp_backend_ft_list_entry;
+ * NOTE: must be kept in sync with core
+ * NOTE: file_offset is omited here
+ */
+ var count = new Uint16Array(data.slice(5,7));
+ var list = data.slice(7);
+ var pos = 0;
+ for (var i = 0; i < count[0]; ++i)
+ {
+ var name_len = new Uint16Array(list.slice(pos, pos + 2));
+ var entry_size = 2 + 4 + 8 + name_len[0]
+ var id = new Uint32Array(list.slice(pos + 2, pos + 2 + 4))[0];
+ var ft_entry = new Uint8Array(list.slice(pos, pos + entry_size));
+ /* "name" in utf8 encoding */
+ this.filelistIn[id] = new FileEntry(this.rdp, ft_entry);
+ pos += entry_size;
+ this.update();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ checkFileReadable(fe) {
+ var offset = 0;
+ var req_size = 8;
+
+ var blob = fe.file.slice(offset, offset + req_size);
+ var reader = new FileReader();
+ reader.fe = fe;
+ reader.onerror = function(e) {
+ var file = e.target.fe;
+ var msg = file.name + ": " + e.target.error.message;
+ file.rdp.cbd.filelistOut.pop(file);
+ file.rdp.fireEvent('alert', msg);
+ file.rdp.cbd.update();
+ if (file.rdp.sock.readyState == file.rdp.sock.OPEN) {
+ file.rdp.log.debug('updating remote clipbord status', 1);
+ var buf = new ArrayBuffer(6);
+ var c = new Uint32Array(buf, 0, 1);
+ var count = new Uint8Array(buf, 4, 1);
+ var fmt = new Uint8Array(buf, 5, 1);
+ c[0] = 6; // ws_in_clipbrd_changed
+ count[0] = file.rdp.cbd.filelistOut.length;
+ fmt[0] = 3;
+ // console.log(buf.toHexdump());
+ file.rdp.sock.send(buf);
+ };
+ };
+ reader.readAsArrayBuffer(blob);
+ }
+
+}
diff --git a/www/js/file-transfer.js b/www/js/file-transfer.js
new file mode 100644
index 0000000..e5def90
--- /dev/null
+++ b/www/js/file-transfer.js
@@ -0,0 +1,365 @@
+'use strict';
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>, smake <smake at ya dot ru>.
+ *
+ */
+
+/**
+ * File handle files in clipboard and dispath events on transferring
+ */
+
+
+class FileEntry {
+ constructor(rdp, raw_ft, fileId) {
+ if (raw_ft instanceof File) {
+ this.file = raw_ft;
+ var file = this.file;
+ var filename = new TextEncoder().encode(file.name);
+ var raw_ft = new Uint8Array(
+ new ArrayBuffer(2 + 4 + 8 + filename.length)
+ );
+ var name_len = new Uint16Array(1);
+ var id = new Uint32Array(1);
+ var size = new BigUint64Array(1);
+ name_len[0] = filename.length;
+ id[0] = fileId;
+ size[0] = BigInt(file.size);
+ raw_ft.set(new Uint8Array(name_len.buffer));
+ raw_ft.set(new Uint8Array(id.buffer), 2);
+ raw_ft.set(new Uint8Array(size.buffer), 2 + 4);
+ raw_ft.set(filename, 2 + 4 + 8);
+
+ }
+ this.raw_ft = raw_ft;
+ this.rdp = rdp;
+ this.status = 0;
+ this.name_len = new Uint16Array(this.raw_ft)[0];
+ this.id = new Uint32Array(this.raw_ft.slice(2, 2 + 4))[0];
+ this.size = new BigUint64Array(this.raw_ft.slice(6, 14).buffer)[0];
+ this.log = true;
+ this.transfer_id = null;
+ this.chunkSize = BigInt(8192);
+ this.offset = BigInt(0);
+ this.chunks = [];
+ this.lastChunkTime = null;
+ this.lastOffset = BigInt(0);
+ this.timout = 120;
+ this.refreshTimeout = 3;
+ return this;
+ }
+
+ get fullname() {
+ var fullname = new Uint8Array(this.raw_ft.slice(14, 14 + this.name_len));
+ fullname = new TextDecoder("utf-8").decode(fullname.filter(v => v != 0));
+ return fullname;
+ }
+
+ get isTransferring() {
+ if (this.status == 2) {
+ var now = new Date();
+ var timeleft = Math.floor((now - this.lastChunkTime)/100);
+ if (!this.lastChunkTime) {
+ return true;
+ }
+ if ( timeleft > this.timout ) {
+ this.SendFtFinish();
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ get name() {
+ var name = this.fullname.split('/').pop();
+ name = name.split('\\').pop();
+ return name;
+ }
+
+ get nextChunkRequest() {
+ if (this.offset >= this.size) {
+ this.status = 0;
+ return;
+ }
+ this.status = 2; //transfer in progress
+ var buf = new ArrayBuffer(4 + 4 + 8 + 8);
+ var c = new Uint32Array(buf, 0, 1);
+ c[0] = 0x09; // ws_in_ft_request
+ var file_id = new Uint32Array(buf, 4, 1);
+ var req_size = new BigUint64Array(buf, 4 + 4, 1);
+ var file_offset = new BigUint64Array(buf, 4 + 4 + 8, 1);
+ file_id[0] = this.id;
+ req_size[0] = this.size;
+ file_offset[0] = this.offset;
+
+ return buf
+ }
+
+ get progress () {
+ if (this.status == 2) {
+ return '(' + this.chunks.length + "/" + this.size / this.chunkSize + ')';
+ } else {
+ return "";
+ }
+ }
+
+ get percents () {
+ if (this.status == 2) {
+ return Math.floor((this.chunks.length / parseInt(this.size / this.chunkSize)) * 100);
+ } else {
+ return "";
+ }
+ }
+
+ get sizeHumanReadable() {
+ var bytes = parseInt(this.size);
+ return this.sizeParse(this.size);
+ }
+
+ get speed () {
+ if (this.status == 2) {
+ var bytes = parseInt(this.offset - this.lastOffset);
+ return this.sizeParse(Math.floor(bytes / this.refreshTimeout));
+ } else {
+ return "";
+ }
+ }
+
+ get uiElement () {
+ var html = "";
+ if (!this.file) {
+ if ( this.size < BigInt(2147483647) ){
+ html += '<li class="tab-menu" onclick="rdp.cbd.ClipoboardFileGet('+this.id+')">'
+ html += '<a>'
+ } else {
+ html += '<li class="tab-menu">';
+ html += '<a style="background-color: #555">(' + lables.file_lager_then_2g +') '
+ }
+ } else {
+ html += '<li class="tab-menu">'
+ html += '<a>'
+ }
+ if (this.status == 2) {
+ html += this.id + ': ' + this.name + ': ' + this.sizeHumanReadable + " " + this.percents + "% (" + this.speed + "/sec)" +'</li>'
+ } else {
+ html += this.id + ': ' + this.name + ': ' + this.sizeHumanReadable + '</li>'
+ }
+ html += '</a></li>'
+ return html;
+
+ }
+
+ newChunk(data) {
+ var size = BigInt(new Uint32Array(data,0,3)[2]);
+ this.transfer_id = new Uint32Array(data,0,3)[1];
+ if (this.log) {
+ var f = this;
+ setTimeout(function () {
+ f.log = true;
+ }, this.refreshTimeout * 1000);
+ // console.log(f.name + ": " + f.progress);
+ f.log = false;
+ this.rdp.cbd.update();
+ f.lastOffset = f.offset;
+ }
+ if (this.status == 2 && new Uint32Array(data,0,3)[2] != 0 ) {
+ if (this.offset + size >= this.size) {
+ this.status = 0;
+ this.lastChunkTime = new Date();
+ this.chunks.push(data.slice(12));
+ this.offset += size;
+ this.pushFileDownload();
+ } else {
+ this.lastChunkTime = new Date();
+ this.chunks.push(data.slice(12));
+ this.offset += size;
+ }
+ }
+ }
+
+ sendChunk(req_size, offset) {
+ if (!this.transfer_id) {
+ this.transfer_id = (Math.random() * 4294967292).round(0);
+ }
+ this.status = 2;
+
+ var offset = parseInt(offset);
+ var req_size = parseInt(req_size);
+
+ var blob = this.file.slice(offset, offset + req_size);
+ var reader = new FileReader();
+ reader.fe = this;
+ reader.onload = function(e) {
+ var file = e.target.fe;
+ var sock = e.target.fe.rdp.sock;
+ var c = e.target.result;
+ var buf = new ArrayBuffer(4 + 4 + 4 + c.byteLength);
+ var cmd = new Uint32Array(buf, 0, 3);
+ var transfer_id = new Uint32Array(buf, 0, 3);
+ var size = new Uint32Array(buf, 0, 3);
+ var data = new Uint8Array(buf)
+ cmd[0] = 0x0a; // ws_in_ft_chunk
+ transfer_id[1] = file.transfer_id;
+ size[2] = c.byteLength;
+ data.set(new Uint8Array(c), 12);
+ // console.log("Sending chunk:" + (buf.byteLength - 12));
+ // console.log(buf.toHexdump());
+ file.lastChunkTime = new Date();
+ sock.send(buf);
+ file.offset += BigInt(c.byteLength);
+ file.lastOffset = file.offset;
+ if (file.offset >= file.size) {
+ file.SendFtFinish();
+ }
+ };
+ reader.onerror = function(e) {
+ var file = e.target.fe;
+ var msg = file.name + ": " + e.target.error.message;
+ file.rdp.fireEvent('alert', msg);
+ file.SendFtFinish();
+ };
+ reader.readAsArrayBuffer(blob);
+ }
+
+ SendFtFinish() {
+ if (this.offset >= this.size) {
+ this.status = 0;
+ } else {
+ this.status = 1;
+ }
+ var buf = new ArrayBuffer(4 + 4 + 4 + 1);
+ var cmd = new Uint32Array(buf, 0, 1);
+ var id = new Uint32Array(buf, 4, 1);
+ var transfer_id = new Uint32Array(buf, 8, 1);
+ var status = new Uint8Array(buf, 12, 1);
+ cmd[0] = 0x0b; // ws_in_ft_finished
+ id[0] = this.id;
+ transfer_id[0] = this.transfer_id;
+ status[0] = this.status;
+
+ // console.log(buf.toHexdump());
+ this.rdp.sock.send(buf);
+ return;
+ }
+
+ sizeParse(bytes) {
+ var bytes = parseInt(bytes);
+ var decimals = null;
+ if(bytes == 0) return '0 Bytes';
+ var k = 1024;
+ var dm = decimals <= 0 ? 0 : decimals || 2;
+ var sizes = lables.size_names;
+ var i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
+ }
+
+ pushFileDownload () {
+ var blob = new Blob(this.chunks)
+ var url = window.URL.createObjectURL(blob);
+ var a = document.createElement("a");
+ document.body.appendChild(a);
+ a.style = "display: none";
+ a.href = url;
+ a.download = this.name;
+ a.click();
+ window.URL.revokeObjectURL(url);
+ this.chunks = [];
+ }
+
+ getFile() {
+ if (this.rdp.sock.readyState == this.rdp.sock.OPEN) {
+ this.log.debug('requesting file transfer', 1);
+ var buf = this.nextChunkRequest;
+ // console.log(buf.toHexdump());
+ this.rdp.sock.send(buf);
+ };
+ }
+
+ // startSending(offset) {
+ // if (!this.transfer_id) {
+ // this.transfer_id = (Math.random() * 4294967292).round(0);
+ // }
+ // this.status = 3;
+
+ // var offset = parseInt(0);
+ // var chunkSize = parseInt(8);
+ // var blob = this.file.slice(offset, offset + chunkSize);
+ // var reader = new FileReader();
+ // reader.fe = this;
+ // reader.onload = function(e) {
+ // var file = e.target.fe;
+ // var sock = e.target.fe.rdp.sock;
+ // var c = e.target.result;
+ // var buf = new ArrayBuffer(4 + 4 + 4 + c.byteLength);
+ // var cmd = new Uint32Array(buf, 0, 3);
+ // var transfer_id = new Uint32Array(buf, 0, 3);
+ // var size = new Uint32Array(buf, 0, 3);
+ // var data = new Uint8Array(buf)
+ // cmd[0] = 0x0a; // ws_in_ft_chunk
+ // transfer_id[1] = file.transfer_id;
+ // size[2] = c.byteLength;
+ // data.set(new Uint8Array(c), 12);
+ // console.log("Sending chunk:" + (buf.byteLength - 12));
+ // // console.log(buf.toHexdump());
+ // sock.send(buf);
+ // file.offset += BigInt(c.byteLength);
+ // file.sendNextChunk();
+ // };
+ // reader.readAsArrayBuffer(blob);
+ // }
+
+
+ // sendNextChunk() {
+ // if (this.status != 3) {
+ // return;
+ // }
+ // if (this.offset >= this.size) {
+ // this.status = 0;
+ // var buf = new ArrayBuffer(4 + 4 + 4 + 1);
+ // var cmd = new Uint32Array(buf, 0, 1);
+ // var id = new Uint32Array(buf, 4, 1);
+ // var transfer_id = new Uint32Array(buf, 8, 1);
+ // var status = new Uint8Array(buf, 9, 1);
+ // cmd[0] = 0x0b; // ws_in_ft_finished
+ // id[0] = this.id;
+ // transfer_id[0] = this.transfer_id;
+ // status[0] = 0;
+
+ // console.log(buf.toHexdump());
+ // this.rdp.sock.send(buf);
+ // return;
+ // }
+ // var offset = parseInt(this.offset);
+ // var chunkSize = parseInt(this.chunkSize);
+ // var blob = this.file.slice(offset, offset + chunkSize);
+ // var reader = new FileReader();
+ // reader.fe = this;
+ // reader.onload = function(e) {
+ // var file = e.target.fe;
+ // var sock = e.target.fe.rdp.sock;
+ // var c = e.target.result;
+ // var buf = new ArrayBuffer(4 + 4 + 4 + c.byteLength);
+ // var cmd = new Uint32Array(buf, 0, 3);
+ // var transfer_id = new Uint32Array(buf, 0, 3);
+ // var size = new Uint32Array(buf, 0, 3);
+ // var data = new Uint8Array(buf)
+ // cmd[0] = 0x0a; // ws_in_ft_chunk
+ // transfer_id[1] = file.transfer_id;
+ // size[2] = c.byteLength;
+ // data.set(new Uint8Array(c), 12);
+ // console.log("Sending chunk:" + (buf.byteLength - 12));
+ // // console.log(buf.toHexdump());
+ // sock.send(buf);
+ // file.offset += BigInt(c.byteLength);
+ // file.sendNextChunk();
+ // };
+ // reader.readAsArrayBuffer(blob);
+ // }
+}
+
+
+
+
diff --git a/www/js/modernizr-debug.js b/www/js/modernizr-debug.js
new file mode 100644
index 0000000..f344bac
--- /dev/null
+++ b/www/js/modernizr-debug.js
@@ -0,0 +1,441 @@
+/* Modernizr 2.5.3 (Custom Build) | MIT & BSD
+ * Build: http://www.modernizr.com/download/#-canvas-canvastext-input-inputtypes-websockets-webworkers-shiv-cssclasses-addtest-prefixed-testprop-testallprops-hasevent-domprefixes-load
+ */
+;
+
+
+
+window.Modernizr = (function( window, document, undefined ) {
+
+ var version = '2.5.3',
+
+ Modernizr = {},
+
+ enableClasses = true,
+
+ docElement = document.documentElement,
+
+ mod = 'modernizr',
+ modElem = document.createElement(mod),
+ mStyle = modElem.style,
+
+ inputElem = document.createElement('input') ,
+
+ smile = ':)',
+
+ toString = {}.toString, omPrefixes = 'Webkit Moz O ms',
+
+ cssomPrefixes = omPrefixes.split(' '),
+
+ domPrefixes = omPrefixes.toLowerCase().split(' '),
+
+
+ tests = {},
+ inputs = {},
+ attrs = {},
+
+ classes = [],
+
+ slice = classes.slice,
+
+ featureName,
+
+ isEventSupported = (function() {
+
+ var TAGNAMES = {
+ 'select': 'input', 'change': 'input',
+ 'submit': 'form', 'reset': 'form',
+ 'error': 'img', 'load': 'img', 'abort': 'img'
+ };
+
+ function isEventSupported( eventName, element ) {
+
+ element = element || document.createElement(TAGNAMES[eventName] || 'div');
+ eventName = 'on' + eventName;
+
+ var isSupported = eventName in element;
+
+ if ( !isSupported ) {
+ if ( !element.setAttribute ) {
+ element = document.createElement('div');
+ }
+ if ( element.setAttribute && element.removeAttribute ) {
+ element.setAttribute(eventName, '');
+ isSupported = is(element[eventName], 'function');
+
+ if ( !is(element[eventName], 'undefined') ) {
+ element[eventName] = undefined;
+ }
+ element.removeAttribute(eventName);
+ }
+ }
+
+ element = null;
+ return isSupported;
+ }
+ return isEventSupported;
+ })(),
+
+
+ _hasOwnProperty = ({}).hasOwnProperty, hasOwnProperty;
+
+ if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
+ hasOwnProperty = function (object, property) {
+ return _hasOwnProperty.call(object, property);
+ };
+ }
+ else {
+ hasOwnProperty = function (object, property) {
+ return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
+ };
+ }
+
+
+ if (!Function.prototype.bind) {
+ Function.prototype.bind = function bind(that) {
+
+ var target = this;
+
+ if (typeof target != "function") {
+ throw new TypeError();
+ }
+
+ var args = slice.call(arguments, 1),
+ bound = function () {
+
+ if (this instanceof bound) {
+
+ var F = function(){};
+ F.prototype = target.prototype;
+ var self = new F;
+
+ var result = target.apply(
+ self,
+ args.concat(slice.call(arguments))
+ );
+ if (Object(result) === result) {
+ return result;
+ }
+ return self;
+
+ } else {
+
+ return target.apply(
+ that,
+ args.concat(slice.call(arguments))
+ );
+
+ }
+
+ };
+
+ return bound;
+ };
+ }
+
+ function setCss( str ) {
+ mStyle.cssText = str;
+ }
+
+ function setCssAll( str1, str2 ) {
+ return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
+ }
+
+ function is( obj, type ) {
+ return typeof obj === type;
+ }
+
+ function contains( str, substr ) {
+ return !!~('' + str).indexOf(substr);
+ }
+
+ function testProps( props, prefixed ) {
+ for ( var i in props ) {
+ if ( mStyle[ props[i] ] !== undefined ) {
+ return prefixed == 'pfx' ? props[i] : true;
+ }
+ }
+ return false;
+ }
+
+ function testDOMProps( props, obj, elem ) {
+ for ( var i in props ) {
+ var item = obj[props[i]];
+ if ( item !== undefined) {
+
+ if (elem === false) return props[i];
+
+ if (is(item, 'function')){
+ return item.bind(elem || obj);
+ }
+
+ return item;
+ }
+ }
+ return false;
+ }
+
+ function testPropsAll( prop, prefixed, elem ) {
+
+ var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
+ props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
+
+ if(is(prefixed, "string") || is(prefixed, "undefined")) {
+ return testProps(props, prefixed);
+
+ } else {
+ props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
+ return testDOMProps(props, prefixed, elem);
+ }
+ } tests['canvas'] = function() {
+ var elem = document.createElement('canvas');
+ return !!(elem.getContext && elem.getContext('2d'));
+ };
+
+ tests['canvastext'] = function() {
+ return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
+ };
+
+
+
+ tests['websockets'] = function() {
+ for ( var i = -1, len = cssomPrefixes.length; ++i < len; ){
+ if ( window[cssomPrefixes[i] + 'WebSocket'] ){
+ return true;
+ }
+ }
+ return 'WebSocket' in window;
+ };
+ tests['webworkers'] = function() {
+ return !!window.Worker;
+ }; function webforms() {
+ Modernizr['input'] = (function( props ) {
+ for ( var i = 0, len = props.length; i < len; i++ ) {
+ attrs[ props[i] ] = !!(props[i] in inputElem);
+ }
+ if (attrs.list){
+ attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
+ }
+ return attrs;
+ })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
+ Modernizr['inputtypes'] = (function(props) {
+
+ for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
+
+ inputElem.setAttribute('type', inputElemType = props[i]);
+ bool = inputElem.type !== 'text';
+
+ if ( bool ) {
+
+ inputElem.value = smile;
+ inputElem.style.cssText = 'position:absolute;visibility:hidden;';
+
+ if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
+
+ docElement.appendChild(inputElem);
+ defaultView = document.defaultView;
+
+ bool = defaultView.getComputedStyle &&
+ defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
+ (inputElem.offsetHeight !== 0);
+
+ docElement.removeChild(inputElem);
+
+ } else if ( /^(search|tel)$/.test(inputElemType) ){
+ } else if ( /^(url|email)$/.test(inputElemType) ) {
+ bool = inputElem.checkValidity && inputElem.checkValidity() === false;
+
+ } else if ( /^color$/.test(inputElemType) ) {
+ docElement.appendChild(inputElem);
+ docElement.offsetWidth;
+ bool = inputElem.value != smile;
+ docElement.removeChild(inputElem);
+
+ } else {
+ bool = inputElem.value != smile;
+ }
+ }
+
+ inputs[ props[i] ] = !!bool;
+ }
+ return inputs;
+ })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
+ }
+ for ( var feature in tests ) {
+ if ( hasOwnProperty(tests, feature) ) {
+ featureName = feature.toLowerCase();
+ Modernizr[featureName] = tests[feature]();
+
+ classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
+ }
+ }
+
+ Modernizr.input || webforms();
+
+
+ Modernizr.addTest = function ( feature, test ) {
+ if ( typeof feature == 'object' ) {
+ for ( var key in feature ) {
+ if ( hasOwnProperty( feature, key ) ) {
+ Modernizr.addTest( key, feature[ key ] );
+ }
+ }
+ } else {
+
+ feature = feature.toLowerCase();
+
+ if ( Modernizr[feature] !== undefined ) {
+ return Modernizr;
+ }
+
+ test = typeof test == 'function' ? test() : test;
+
+ docElement.className += ' ' + (test ? '' : 'no-') + feature;
+ Modernizr[feature] = test;
+
+ }
+
+ return Modernizr;
+ };
+
+
+ setCss('');
+ modElem = inputElem = null;
+
+ ;(function(window, document) {
+
+ var options = window.html5 || {};
+
+ var reSkip = /^<|^(?:button|form|map|select|textarea)$/i;
+
+ var supportsHtml5Styles;
+
+ var supportsUnknownElements;
+
+ (function() {
+ var a = document.createElement('a');
+
+ a.innerHTML = '<xyz></xyz>';
+
+ supportsHtml5Styles = ('hidden' in a);
+ supportsUnknownElements = a.childNodes.length == 1 || (function() {
+ try {
+ (document.createElement)('a');
+ } catch(e) {
+ return true;
+ }
+ var frag = document.createDocumentFragment();
+ return (
+ typeof frag.cloneNode == 'undefined' ||
+ typeof frag.createDocumentFragment == 'undefined' ||
+ typeof frag.createElement == 'undefined'
+ );
+ }());
+
+ }()); function addStyleSheet(ownerDocument, cssText) {
+ var p = ownerDocument.createElement('p'),
+ parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
+
+ p.innerHTML = 'x<style>' + cssText + '</style>';
+ return parent.insertBefore(p.lastChild, parent.firstChild);
+ }
+
+ function getElements() {
+ var elements = html5.elements;
+ return typeof elements == 'string' ? elements.split(' ') : elements;
+ }
+
+ function shivMethods(ownerDocument) {
+ var cache = {},
+ docCreateElement = ownerDocument.createElement,
+ docCreateFragment = ownerDocument.createDocumentFragment,
+ frag = docCreateFragment();
+
+
+ ownerDocument.createElement = function(nodeName) {
+ var node = (cache[nodeName] || (cache[nodeName] = docCreateElement(nodeName))).cloneNode();
+ return html5.shivMethods && node.canHaveChildren && !reSkip.test(nodeName) ? frag.appendChild(node) : node;
+ };
+
+ ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
+ 'var n=f.cloneNode(),c=n.createElement;' +
+ 'h.shivMethods&&(' +
+ getElements().join().replace(/\w+/g, function(nodeName) {
+ cache[nodeName] = docCreateElement(nodeName);
+ frag.createElement(nodeName);
+ return 'c("' + nodeName + '")';
+ }) +
+ ');return n}'
+ )(html5, frag);
+ } function shivDocument(ownerDocument) {
+ var shived;
+ if (ownerDocument.documentShived) {
+ return ownerDocument;
+ }
+ if (html5.shivCSS && !supportsHtml5Styles) {
+ shived = !!addStyleSheet(ownerDocument,
+ 'article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
+ 'audio{display:none}' +
+ 'canvas,video{display:inline-block;*display:inline;*zoom:1}' +
+ '[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}' +
+ 'mark{background:#FF0;color:#000}'
+ );
+ }
+ if (!supportsUnknownElements) {
+ shived = !shivMethods(ownerDocument);
+ }
+ if (shived) {
+ ownerDocument.documentShived = shived;
+ }
+ return ownerDocument;
+ } var html5 = {
+
+ 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video',
+
+ 'shivCSS': !(options.shivCSS === false),
+
+ 'shivMethods': !(options.shivMethods === false),
+
+ 'type': 'default',
+ 'shivDocument': shivDocument
+ }; window.html5 = html5;
+
+ shivDocument(document);
+
+ }(this, document));
+
+ Modernizr._version = version;
+
+ Modernizr._domPrefixes = domPrefixes;
+ Modernizr._cssomPrefixes = cssomPrefixes;
+
+
+ Modernizr.hasEvent = isEventSupported;
+
+ Modernizr.testProp = function(prop){
+ return testProps([prop]);
+ };
+
+ Modernizr.testAllProps = testPropsAll;
+
+
+ Modernizr.prefixed = function(prop, obj, elem){
+ if(!obj) {
+ return testPropsAll(prop, 'pfx');
+ } else {
+ return testPropsAll(prop, obj, elem);
+ }
+ };
+
+
+ docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
+
+ (enableClasses ? ' js ' + classes.join(' ') : '');
+
+ return Modernizr;
+
+})(this, this.document);
+/*yepnope1.5.3|WTFPL*/
+(function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,i){var j=b(a),l=j.autoCallback;j.url.split(".").pop().split("?").shift(),j.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]||h),j.instead?j.instead(a,e,f,g,i):(y[j.url]?j.noexec=!0:y[j.url]=1,f.load(j.url,j.forceCSS||!j.forceJS&&"css"==j.url.split(".").pop().split("?").shift()?"c":c,j.noexec,j.attrs,j.timeout),(d(e)||d(l))&&f.load(function(){k(),e&&e(j.origUrl,i,g),l&&l(j.origUrl,i,g),y[j.url]=2})))}function i(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var j,l,m=this.yepnope.loader;if(e(a))g(a,0,m,0);else if(w(a))for(j=0;j<a.length;j++)l=a[j],e(l)?g(l,0,m,0):w(l)?B(l):Object(l)===l&&i(l,m);else Object(a)===a&&i(a,m)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,b.readyState==null&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}})(this,document);
+Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0));};
+; \ No newline at end of file
diff --git a/www/js/mootools-1.6.0.js b/www/js/mootools-1.6.0.js
new file mode 100644
index 0000000..f35c54f
--- /dev/null
+++ b/www/js/mootools-1.6.0.js
@@ -0,0 +1,6382 @@
+/* MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2018 [Valerio Proietti](https://mootools.net/).*/
+/*!
+Web Build: https://mootools.net/core/builder/e426a9ae7167c5807b173d5deff673fc
+*/
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2015 [Valerio Proietti](https://github.com/kamicane/).
+
+authors: 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)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+/*! MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2015 [Valerio Proietti](https://github.com/kamicane/).*/
+(function(){
+
+this.MooTools = {
+ version: '1.6.0',
+ build: '529422872adfff401b901b8b6c7ca5114ee95e2b'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family != null) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if ('callee' in item) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ /*<ltIE8>*/
+ if (!item.hasOwnProperty) return false;
+ /*</ltIE8>*/
+ return item instanceof object;
+};
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+/*<ltIE8>*/
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+function forEachObjectEnumberableKey(object, fn, bind){
+ if (enumerables) for (var i = enumerables.length; i--;){
+ var k = enumerables[i];
+ // signature has key-value, so overloadSetter can directly pass the
+ // method function, without swapping arguments.
+ if (hasOwnProperty.call(object, k)) fn.call(bind, k, object[k]);
+ }
+}
+/*</ltIE8>*/
+
+// Function overloading
+
+var Function = this.Function;
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ /*<ltIE8>*/
+ forEachObjectEnumberableKey(a, self, this);
+ /*</ltIE8>*/
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ else if (usePlural) args = [a];
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Array.convert = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Function.convert = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+
+Number.convert = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.convert = function(item){
+ return item + '';
+};
+
+
+
+Function.from = Function.convert;
+Number.from = Number.convert;
+String.from = String.convert;
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+ if (isType && proto) object.implement(key, proto.protect());
+ }
+
+ if (isType){
+ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
+ object.forEachMethod = function(fn){
+ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
+ fn.call(prototype, prototype[methods[i]], methods[i]);
+ }
+ for (var key in prototype) fn.call(prototype, prototype[key], key);
+ };
+ }
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'contains', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight', 'contains'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each, keys
+
+Array.implement({
+
+ /*<!ES5>*/
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+ /*</!ES5>*/
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+Object.extend({
+
+ keys: function(object){
+ var keys = [];
+ for (var k in object){
+ if (hasOwnProperty.call(object, k)) keys.push(k);
+ }
+ /*<ltIE8>*/
+ forEachObjectEnumberableKey(object, function(k){
+ keys.push(k);
+ });
+ /*</ltIE8>*/
+ return keys;
+ },
+
+ forEach: function(object, fn, bind){
+ Object.keys(object).forEach(function(key){
+ fn.call(bind, object[key], key, object);
+ });
+ }
+
+});
+
+Object.each = Object.forEach;
+
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: [Type]
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /*<!ES5>*/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
+ value = this[i];
+ if (fn.call(bind, value, i, this)) results.push(value);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var length = this.length >>> 0;
+ for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var length = this.length >>> 0, results = Array(length);
+ for (var i = 0; i < length; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*</!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ 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;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.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--;){
+ 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 = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return parseInt(value, 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('');
+ }
+
+});
+
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.convert(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /*<!ES5-bind>*/
+ bind: function(that){
+ var self = this,
+ args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+ F = function(){};
+
+ var bound = function(){
+ var context = that, length = arguments.length;
+ if (this instanceof bound){
+ F.prototype = self.prototype;
+ context = new F;
+ }
+ var result = (!args && !length)
+ ? self.call(context)
+ : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+ return context == that ? result : context;
+ };
+ return bound;
+ },
+ /*</!ES5-bind>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.convert(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -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('each', 'times');
+
+(function(math){
+
+var methods = {};
+
+math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.convert(arguments)));
+ };
+});
+
+Number.implement(methods);
+
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: [Type, Array]
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ //<!ES6>
+ contains: function(string, index){
+ return (index ? String(this).slice(index) : String(this)).indexOf(string) > -1;
+ },
+ //</!ES6>
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ trim: function(){
+ return String(this).replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return String(this).replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return String(this).replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return String(this).replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return String(this).replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = String(this).match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var parse = function(ua, platform){
+ ua = ua.toLowerCase();
+ platform = (platform ? platform.toLowerCase() : '');
+
+ // chrome is included in the edge UA, so need to check for edge first,
+ // before checking if it's chrome.
+ var UA = ua.match(/(edge)[\s\/:]([\w\d\.]+)/);
+ if (!UA){
+ UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\s\/:]([\w\d\.]+)?.*?(safari|(?:rv[\s\/:]|version[\s\/:])([\w\d\.]+)|$)/) || [null, 'unknown', 0];
+ }
+
+ if (UA[1] == 'trident'){
+ UA[1] = 'ie';
+ if (UA[4]) UA[2] = UA[4];
+ } else if (UA[1] == 'crios'){
+ UA[1] = 'chrome';
+ }
+
+ platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || ua.match(/mac|win|linux/) || ['other'])[0];
+ if (platform == 'win') platform = 'windows';
+
+ return {
+ extend: Function.prototype.extend,
+ name: (UA[1] == 'version') ? UA[3] : UA[1],
+ version: parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+ platform: platform
+ };
+};
+
+var Browser = this.Browser = parse(navigator.userAgent, navigator.platform);
+
+if (Browser.name == 'ie' && document.documentMode){
+ Browser.version = document.documentMode;
+}
+
+Browser.extend({
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+ parseUA: parse
+});
+
+
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+
+
+// String scripts
+
+Browser.exec = function(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;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+ scripts += code + '\n';
+ return '';
+ });
+ if (exec === true) Browser.exec(scripts);
+ else if (typeOf(exec) == 'function') exec(scripts, text);
+ return text;
+});
+
+// Window, Document
+
+Browser.extend({
+ Document: this.Document,
+ Window: this.Window,
+ Element: this.Element,
+ Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.convert('window').hide();
+
+Window.mirror(function(name, method){
+ window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.convert('document').hide();
+
+Document.mirror(function(name, method){
+ document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+ document.execCommand('BackgroundImageCache', false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+ var unloadEvent = function(){
+ this.detachEvent('onunload', unloadEvent);
+ document.head = document.html = document.window = null;
+ window = this.Window = document = null;
+ };
+ this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.convert;
+try {
+ arrayFrom(document.html.childNodes);
+} catch (e){
+ Array.convert = function(item){
+ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+ var i = item.length, array = new Array(i);
+ while (i--) array[i] = item[i];
+ return array;
+ }
+ return arrayFrom(item);
+ };
+
+ var prototype = Array.prototype,
+ slice = prototype.slice;
+ ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+ var method = prototype[name];
+ Array[name] = function(item){
+ return method.apply(Array.convert(item), slice.call(arguments, 1));
+ };
+ });
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ this.$family = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.convert(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(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 = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.convert(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) if (i in fns){
+ this.removeEvent(type, fns[i]);
+ }
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (this.addEvent) for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+/*
+---
+
+name: Class.Thenable
+
+description: Contains a Utility Class that can be implemented into your own Classes to make them "thenable".
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Thenable]
+
+...
+*/
+
+(function(){
+
+var STATE_PENDING = 0,
+ STATE_FULFILLED = 1,
+ STATE_REJECTED = 2;
+
+var Thenable = Class.Thenable = new Class({
+
+ $thenableState: STATE_PENDING,
+ $thenableResult: null,
+ $thenableReactions: [],
+
+ resolve: function(value){
+ resolve(this, value);
+ return this;
+ },
+
+ reject: function(reason){
+ reject(this, reason);
+ return this;
+ },
+
+ getThenableState: function(){
+ switch (this.$thenableState){
+ case STATE_PENDING:
+ return 'pending';
+
+ case STATE_FULFILLED:
+ return 'fulfilled';
+
+ case STATE_REJECTED:
+ return 'rejected';
+ }
+ },
+
+ resetThenable: function(reason){
+ reject(this, reason);
+ reset(this);
+ return this;
+ },
+
+ then: function(onFulfilled, onRejected){
+ if (typeof onFulfilled !== 'function') onFulfilled = 'Identity';
+ if (typeof onRejected !== 'function') onRejected = 'Thrower';
+
+ var thenable = new Thenable();
+
+ this.$thenableReactions.push({
+ thenable: thenable,
+ fulfillHandler: onFulfilled,
+ rejectHandler: onRejected
+ });
+
+ if (this.$thenableState !== STATE_PENDING){
+ react(this);
+ }
+
+ return thenable;
+ },
+
+ 'catch': function(onRejected){
+ return this.then(null, onRejected);
+ }
+
+});
+
+Thenable.extend({
+ resolve: function(value){
+ var thenable;
+ if (value instanceof Thenable){
+ thenable = value;
+ } else {
+ thenable = new Thenable();
+ resolve(thenable, value);
+ }
+ return thenable;
+ },
+ reject: function(reason){
+ var thenable = new Thenable();
+ reject(thenable, reason);
+ return thenable;
+ }
+});
+
+// Private functions
+
+function resolve(thenable, value){
+ if (thenable.$thenableState === STATE_PENDING){
+ if (thenable === value){
+ reject(thenable, new TypeError('Tried to resolve a thenable with itself.'));
+ } else if (value && (typeof value === 'object' || typeof value === 'function')){
+ var then;
+ try {
+ then = value.then;
+ } catch (exception){
+ reject(thenable, exception);
+ }
+ if (typeof then === 'function'){
+ var resolved = false;
+ defer(function(){
+ try {
+ then.call(
+ value,
+ function(nextValue){
+ if (!resolved){
+ resolved = true;
+ resolve(thenable, nextValue);
+ }
+ },
+ function(reason){
+ if (!resolved){
+ resolved = true;
+ reject(thenable, reason);
+ }
+ }
+ );
+ } catch (exception){
+ if (!resolved){
+ resolved = true;
+ reject(thenable, exception);
+ }
+ }
+ });
+ } else {
+ fulfill(thenable, value);
+ }
+ } else {
+ fulfill(thenable, value);
+ }
+ }
+}
+
+function fulfill(thenable, value){
+ if (thenable.$thenableState === STATE_PENDING){
+ thenable.$thenableResult = value;
+ thenable.$thenableState = STATE_FULFILLED;
+
+ react(thenable);
+ }
+}
+
+function reject(thenable, reason){
+ if (thenable.$thenableState === STATE_PENDING){
+ thenable.$thenableResult = reason;
+ thenable.$thenableState = STATE_REJECTED;
+
+ react(thenable);
+ }
+}
+
+function reset(thenable){
+ if (thenable.$thenableState !== STATE_PENDING){
+ thenable.$thenableResult = null;
+ thenable.$thenableState = STATE_PENDING;
+ }
+}
+
+function react(thenable){
+ var state = thenable.$thenableState,
+ result = thenable.$thenableResult,
+ reactions = thenable.$thenableReactions,
+ type;
+
+ if (state === STATE_FULFILLED){
+ thenable.$thenableReactions = [];
+ type = 'fulfillHandler';
+ } else if (state == STATE_REJECTED){
+ thenable.$thenableReactions = [];
+ type = 'rejectHandler';
+ }
+
+ if (type){
+ defer(handle.pass([result, reactions, type]));
+ }
+}
+
+function handle(result, reactions, type){
+ for (var i = 0, l = reactions.length; i < l; ++i){
+ var reaction = reactions[i],
+ handler = reaction[type];
+
+ if (handler === 'Identity'){
+ resolve(reaction.thenable, result);
+ } else if (handler === 'Thrower'){
+ reject(reaction.thenable, result);
+ } else {
+ try {
+ resolve(reaction.thenable, handler(result));
+ } catch (exception){
+ reject(reaction.thenable, exception);
+ }
+ }
+ }
+}
+
+var defer;
+if (typeof process !== 'undefined' && typeof process.nextTick === 'function'){
+ defer = process.nextTick;
+} else if (typeof setImmediate !== 'undefined'){
+ defer = setImmediate;
+} else {
+ defer = function(fn){
+ setTimeout(fn, 0);
+ };
+}
+
+})();
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var key = keys[i];
+ results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var key = keys[i], value = object[key];
+ if (fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var key = keys[i];
+ if (!fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var key = keys[i];
+ if (fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ values: function(object){
+ var values = [];
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var k = keys[i];
+ values.push(object[k]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ var keys = Object.keys(object);
+ for (var i = 0; i < keys.length; i++){
+ var key = keys[i];
+ if (object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+/*
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
+*/
+
+;(function(){
+
+var parsed,
+ separatorIndex,
+ combinatorIndex,
+ reversed,
+ cache = {},
+ reverseCache = {},
+ reUnescape = /\\/g;
+
+var parse = function(expression, isReversed){
+ if (expression == null) return null;
+ if (expression.Slick === true) return expression;
+ expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+ reversed = !!isReversed;
+ var currentCache = (reversed) ? reverseCache : cache;
+ if (currentCache[expression]) return currentCache[expression];
+ parsed = {
+ Slick: true,
+ expressions: [],
+ raw: expression,
+ reverse: function(){
+ return parse(this.raw, true);
+ }
+ };
+ separatorIndex = -1;
+ while (expression != (expression = expression.replace(regexp, parser)));
+ parsed.length = parsed.expressions.length;
+ return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
+
+var reverseCombinator = function(combinator){
+ if (combinator === '!') return ' ';
+ else if (combinator === ' ') return '!';
+ else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+ else return '!' + combinator;
+};
+
+var reverse = function(expression){
+ var expressions = expression.expressions;
+ for (var i = 0; i < expressions.length; i++){
+ var exp = expressions[i];
+ var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+ for (var j = 0; j < exp.length; j++){
+ var cexp = exp[j];
+ if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+ cexp.combinator = cexp.reverseCombinator;
+ delete cexp.reverseCombinator;
+ }
+
+ exp.reverse().push(last);
+ }
+ return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+ return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+ return '\\' + match;
+ });
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+ "(?x)^(?:\
+ \\s* ( , ) \\s* # Separator \n\
+ | \\s* ( <combinator>+ ) \\s* # Combinator \n\
+ | ( \\s+ ) # CombinatorChildren \n\
+ | ( <unicode>+ | \\* ) # Tag \n\
+ | \\# ( <unicode>+ ) # ID \n\
+ | \\. ( <unicode>+ ) # ClassName \n\
+ | # Attribute \n\
+ \\[ \
+ \\s* (<unicode1>+) (?: \
+ \\s* ([*^$!~|]?=) (?: \
+ \\s* (?:\
+ ([\"']?)(.*?)\\9 \
+ )\
+ ) \
+ )? \\s* \
+ \\](?!\\]) \n\
+ | :+ ( <unicode>+ )(?:\
+ \\( (?:\
+ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+ ) \\)\
+ )?\
+ )"
+*/
+ "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+ .replace(/<combinator>/, '[' + escapeRegExp('>+~`!@$%^&={}\\;</') + ']')
+ .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+ .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+ rawMatch,
+
+ separator,
+ combinator,
+ combinatorChildren,
+
+ tagName,
+ id,
+ className,
+
+ attributeKey,
+ attributeOperator,
+ attributeQuote,
+ attributeValue,
+
+ pseudoMarker,
+ pseudoClass,
+ pseudoQuote,
+ pseudoClassQuotedValue,
+ pseudoClassValue
+){
+ if (separator || separatorIndex === -1){
+ parsed.expressions[++separatorIndex] = [];
+ combinatorIndex = -1;
+ if (separator) return '';
+ }
+
+ if (combinator || combinatorChildren || combinatorIndex === -1){
+ combinator = combinator || ' ';
+ var currentSeparator = parsed.expressions[separatorIndex];
+ if (reversed && currentSeparator[combinatorIndex])
+ currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+ currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+ }
+
+ var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+ if (tagName){
+ currentParsed.tag = tagName.replace(reUnescape, '');
+
+ } else if (id){
+ currentParsed.id = id.replace(reUnescape, '');
+
+ } else if (className){
+ className = className.replace(reUnescape, '');
+
+ if (!currentParsed.classList) currentParsed.classList = [];
+ if (!currentParsed.classes) currentParsed.classes = [];
+ currentParsed.classList.push(className);
+ currentParsed.classes.push({
+ value: className,
+ regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+ });
+
+ } else if (pseudoClass){
+ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+ pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+ if (!currentParsed.pseudos) currentParsed.pseudos = [];
+ currentParsed.pseudos.push({
+ key: pseudoClass.replace(reUnescape, ''),
+ value: pseudoClassValue,
+ type: pseudoMarker.length == 1 ? 'class' : 'element'
+ });
+
+ } else if (attributeKey){
+ attributeKey = attributeKey.replace(reUnescape, '');
+ attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+ var test, regexp;
+
+ switch (attributeOperator){
+ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
+ case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
+ case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+ case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
+ case '=' : test = function(value){
+ return attributeValue == value;
+ }; break;
+ case '*=' : test = function(value){
+ return value && value.indexOf(attributeValue) > -1;
+ }; break;
+ case '!=' : test = function(value){
+ return attributeValue != value;
+ }; break;
+ default : test = function(value){
+ return !!value;
+ };
+ }
+
+ if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+ return false;
+ };
+
+ if (!test) test = function(value){
+ return value && regexp.test(value);
+ };
+
+ if (!currentParsed.attributes) currentParsed.attributes = [];
+ currentParsed.attributes.push({
+ key: attributeKey,
+ operator: attributeOperator,
+ value: attributeValue,
+ test: test
+ });
+
+ }
+
+ return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+ return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+ featuresCache = {},
+ toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+ return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+ (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+ var nodeType = document.nodeType;
+ if (nodeType == 9); // document
+ else if (nodeType) document = document.ownerDocument; // node
+ else if (document.navigator) document = document.document; // window
+ else return;
+
+ // check if it's the old document
+
+ if (this.document === document) return;
+ this.document = document;
+
+ // check if we have done feature detection on this document before
+
+ var root = document.documentElement,
+ rootUid = this.getUIDXML(root),
+ features = featuresCache[rootUid],
+ feature;
+
+ if (features){
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+ return;
+ }
+
+ features = featuresCache[rootUid] = {};
+
+ features.root = root;
+ features.isXMLDocument = this.isXML(document);
+
+ features.brokenStarGEBTN
+ = features.starSelectsClosedQSA
+ = features.idGetsName
+ = features.brokenMixedCaseQSA
+ = features.brokenGEBCN
+ = features.brokenCheckedQSA
+ = features.brokenEmptyAttributeQSA
+ = features.isHTMLDocument
+ = features.nativeMatchesSelector
+ = false;
+
+ var starSelectsClosed, starSelectsComments,
+ brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+ brokenFormAttributeGetter;
+
+ var selected, id = 'slick_uniqueid';
+ var testNode = document.createElement('div');
+
+ var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+ testRoot.appendChild(testNode);
+
+ // on non-HTML documents innerHTML and getElementsById doesnt work properly
+ try {
+ testNode.innerHTML = '<a id="'+id+'"></a>';
+ features.isHTMLDocument = !!document.getElementById(id);
+ } catch (e){}
+
+ if (features.isHTMLDocument){
+
+ testNode.style.display = 'none';
+
+ // IE returns comment nodes for getElementsByTagName('*') for some documents
+ testNode.appendChild(document.createComment(''));
+ starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+ // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.getElementsByTagName('*');
+ starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch (e){};
+
+ features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+ // IE returns elements with the name instead of just id for getElementsById for some documents
+ try {
+ testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+ features.idGetsName = document.getElementById(id) === testNode.firstChild;
+ } catch (e){}
+
+ if (testNode.getElementsByClassName){
+
+ // Safari 3.2 getElementsByClassName caches results
+ try {
+ testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+ testNode.getElementsByClassName('b').length;
+ testNode.firstChild.className = 'b';
+ cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+ } catch (e){};
+
+ // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+ try {
+ testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+ brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+ } catch (e){}
+
+ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+ }
+
+ if (testNode.querySelectorAll){
+ // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.querySelectorAll('*');
+ features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch (e){}
+
+ // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+ try {
+ testNode.innerHTML = '<a class="MiX"></a>';
+ features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+ } catch (e){}
+
+ // Webkit and Opera dont return selected options on querySelectorAll
+ try {
+ testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+ features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+ } catch (e){};
+
+ // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+ try {
+ testNode.innerHTML = '<a class=""></a>';
+ features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+ } catch (e){}
+
+ }
+
+ // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+ try {
+ testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+ brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+ } catch (e){}
+
+ // native matchesSelector function
+
+ features.nativeMatchesSelector = root.matches || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+ if (features.nativeMatchesSelector) try {
+ // if matchesSelector trows errors on incorrect sintaxes we can use it
+ features.nativeMatchesSelector.call(root, ':slick');
+ features.nativeMatchesSelector = null;
+ } catch (e){}
+
+ }
+
+ try {
+ root.slick_expando = 1;
+ delete root.slick_expando;
+ features.getUID = this.getUIDHTML;
+ } catch (e){
+ features.getUID = this.getUIDXML;
+ }
+
+ testRoot.removeChild(testNode);
+ testNode = selected = testRoot = null;
+
+ // getAttribute
+
+ features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+ var method = this.attributeGetters[name];
+ if (method) return method.call(node);
+ var attributeNode = node.getAttributeNode(name);
+ return (attributeNode) ? attributeNode.nodeValue : null;
+ } : function(node, name){
+ var method = this.attributeGetters[name];
+ return (method) ? method.call(node) : node.getAttribute(name);
+ };
+
+ // hasAttribute
+
+ features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute){
+ return node.hasAttribute(attribute);
+ } : function(node, attribute){
+ node = node.getAttributeNode(attribute);
+ return !!(node && (node.specified || node.nodeValue));
+ };
+
+ // contains
+ // FIXME: Add specs: local.contains should be different for xml and html documents?
+ var nativeRootContains = root && this.isNativeCode(root.contains),
+ nativeDocumentContains = document && this.isNativeCode(document.contains);
+
+ features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
+ return context.contains(node);
+ } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
+ // IE8 does not have .contains on document.
+ return context === node || ((context === document) ? document.documentElement : context).contains(node);
+ } : (root && root.compareDocumentPosition) ? function(context, node){
+ return context === node || !!(context.compareDocumentPosition(node) & 16);
+ } : function(context, node){
+ if (node) do {
+ if (node === context) return true;
+ } while ((node = node.parentNode));
+ return false;
+ };
+
+ // document order sorting
+ // credits to Sizzle (http://sizzlejs.com/)
+
+ features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+ return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ } : ('sourceIndex' in root) ? function(a, b){
+ if (!a.sourceIndex || !b.sourceIndex) return 0;
+ return a.sourceIndex - b.sourceIndex;
+ } : (document.createRange) ? function(a, b){
+ if (!a.ownerDocument || !b.ownerDocument) return 0;
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ } : null;
+
+ root = null;
+
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+ reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+ qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+ var found = this.found = (first) ? null : (append || []);
+
+ if (!context) return found;
+ else if (context.navigator) context = context.document; // Convert the node from a window to a document
+ else if (!context.nodeType) return found;
+
+ // setup
+
+ var parsed, i, node, nodes,
+ uniques = this.uniques = {},
+ hasOthers = !!(append && append.length),
+ contextIsDocument = (context.nodeType == 9);
+
+ if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+ // avoid duplicating items already in the append array
+ if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+ // expression checks
+
+ if (typeof expression == 'string'){ // expression is a string
+
+ /*<simple-selectors-override>*/
+ var simpleSelector = expression.match(reSimpleSelector);
+ simpleSelectors: if (simpleSelector){
+
+ var symbol = simpleSelector[1],
+ name = simpleSelector[2];
+
+ if (!symbol){
+
+ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+ nodes = context.getElementsByTagName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ } else if (symbol == '#'){
+
+ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+ node = context.getElementById(name);
+ if (!node) return found;
+ if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+ if (first) return node || null;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+ } else if (symbol == '.'){
+
+ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+ if (context.getElementsByClassName && !this.brokenGEBCN){
+ nodes = context.getElementsByClassName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ } else {
+ var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+ nodes = context.getElementsByTagName('*');
+ for (i = 0; node = nodes[i++];){
+ className = node.className;
+ if (!(className && matchClass.test(className))) continue;
+ if (first) return node;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ }
+
+ }
+
+ if (hasOthers) this.sort(found);
+ return (first) ? null : found;
+
+ }
+ /*</simple-selectors-override>*/
+
+ /*<query-selector-override>*/
+ querySelector: if (context.querySelectorAll){
+
+ if (!this.isHTMLDocument
+ || qsaFailExpCache[expression]
+ //TODO: only skip when expression is actually mixed case
+ || this.brokenMixedCaseQSA
+ || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+ || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+ || (!contextIsDocument //Abort when !contextIsDocument and...
+ // there are multiple expressions in the selector
+ // since we currently only fix non-document rooted QSA for single expression selectors
+ && expression.indexOf(',') > -1
+ )
+ || Slick.disableQSA
+ ) break querySelector;
+
+ var _expression = expression, _context = context, currentId;
+ if (!contextIsDocument){
+ // non-document rooted QSA
+ // credits to Andrew Dupont
+ currentId = _context.getAttribute('id'), slickid = 'slickid__';
+ _context.setAttribute('id', slickid);
+ _expression = '#' + slickid + ' ' + _expression;
+ context = _context.parentNode;
+ }
+
+ try {
+ if (first) return context.querySelector(_expression) || null;
+ else nodes = context.querySelectorAll(_expression);
+ } catch (e){
+ qsaFailExpCache[expression] = 1;
+ break querySelector;
+ } finally {
+ if (!contextIsDocument){
+ if (currentId) _context.setAttribute('id', currentId);
+ else _context.removeAttribute('id');
+ context = _context;
+ }
+ }
+
+ if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ } else for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ if (hasOthers) this.sort(found);
+ return found;
+
+ }
+ /*</query-selector-override>*/
+
+ parsed = this.Slick.parse(expression);
+ if (!parsed.length) return found;
+ } else if (expression == null){ // there is no expression
+ return found;
+ } else if (expression.Slick){ // expression is a parsed Slick object
+ parsed = expression;
+ } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+ (found) ? found.push(expression) : found = expression;
+ return found;
+ } else { // other junk
+ return found;
+ }
+
+ /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+ // cache elements for the nth selectors
+
+ this.posNTH = {};
+ this.posNTHLast = {};
+ this.posNTHType = {};
+ this.posNTHTypeLast = {};
+
+ /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+ this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+ if (found == null) found = [];
+
+ // default engine
+
+ var j, m, n;
+ var combinator, tag, id, classList, classes, attributes, pseudos;
+ var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+ search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+ combinator = 'combinator:' + currentBit.combinator;
+ if (!this[combinator]) continue search;
+
+ tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+ id = currentBit.id;
+ classList = currentBit.classList;
+ classes = currentBit.classes;
+ attributes = currentBit.attributes;
+ pseudos = currentBit.pseudos;
+ lastBit = (j === (currentExpression.length - 1));
+
+ this.bitUniques = {};
+
+ if (lastBit){
+ this.uniques = uniques;
+ this.found = found;
+ } else {
+ this.uniques = {};
+ this.found = [];
+ }
+
+ if (j === 0){
+ this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+ if (first && lastBit && found.length) break search;
+ } else {
+ if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ if (found.length) break search;
+ } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ }
+
+ currentItems = this.found;
+ }
+
+ // should sort if there are nodes in append and if you pass multiple expressions.
+ if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+ return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+ var uid = node.getAttribute(this.uidk);
+ if (!uid){
+ uid = this.uidx++;
+ node.setAttribute(this.uidk, uid);
+ }
+ return uid;
+};
+
+local.getUIDHTML = function(node){
+ return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+ if (!this.documentSorter) return results;
+ results.sort(this.documentSorter);
+ return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+ var parsed = argument.match(this.matchNTH);
+ if (!parsed) return false;
+ var special = parsed[2] || false;
+ var a = parsed[1] || 1;
+ if (a == '-') a = -1;
+ var b = +parsed[3] || 0;
+ parsed =
+ (special == 'n') ? {a: a, b: b} :
+ (special == 'odd') ? {a: 2, b: 1} :
+ (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
+
+ return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+ return function(node, argument){
+ var uid = this.getUID(node);
+ if (!this[positions][uid]){
+ var parent = node.parentNode;
+ if (!parent) return false;
+ var el = parent[child], count = 1;
+ if (ofType){
+ var nodeName = node.nodeName;
+ do {
+ if (el.nodeName != nodeName) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ } else {
+ do {
+ if (el.nodeType != 1) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ }
+ }
+ argument = argument || 'n';
+ var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+ if (!parsed) return false;
+ var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+ if (a == 0) return b == pos;
+ if (a > 0){
+ if (pos < b) return false;
+ } else {
+ if (b < pos) return false;
+ }
+ return ((pos - b) % a) == 0;
+ };
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+ var uid = this.getUID(node);
+ if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+ this.uniques[uid] = true;
+ this.found.push(node);
+ }
+};
+
+local.matchNode = function(node, selector){
+ if (this.isHTMLDocument && this.nativeMatchesSelector){
+ try {
+ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+ } catch (matchError){}
+ }
+
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+
+ // simple (single) selectors
+ var expressions = parsed.expressions, simpleExpCounter = 0, i, currentExpression;
+ for (i = 0; (currentExpression = expressions[i]); i++){
+ if (currentExpression.length == 1){
+ var exp = currentExpression[0];
+ if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+ simpleExpCounter++;
+ }
+ }
+
+ if (simpleExpCounter == parsed.length) return false;
+
+ var nodes = this.search(this.document, parsed), item;
+ for (i = 0; item = nodes[i++];){
+ if (item === node) return true;
+ }
+ return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+ var pseudoName = 'pseudo:' + name;
+ if (this[pseudoName]) return this[pseudoName](node, argument);
+ var attribute = this.getAttribute(node, name);
+ return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+ if (tag){
+ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+ if (tag == '*'){
+ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+ } else {
+ if (nodeName != tag) return false;
+ }
+ }
+
+ if (id && node.getAttribute('id') != id) return false;
+
+ var i, part, cls;
+ if (classes) for (i = classes.length; i--;){
+ cls = this.getAttribute(node, 'class');
+ if (!(cls && classes[i].regexp.test(cls))) return false;
+ }
+ if (attributes) for (i = attributes.length; i--;){
+ part = attributes[i];
+ if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+ }
+ if (pseudos) for (i = pseudos.length; i--;){
+ part = pseudos[i];
+ if (!this.matchPseudo(node, part.key, part.value)) return false;
+ }
+ return true;
+};
+
+var combinators = {
+
+ ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+ var i, item, children;
+
+ if (this.isHTMLDocument){
+ getById: if (id){
+ item = this.document.getElementById(id);
+ if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+ // all[id] returns all the elements with that name or id inside node
+ // if theres just one it will return the element, else it will be a collection
+ children = node.all[id];
+ if (!children) return;
+ if (!children[0]) children = [children];
+ for (i = 0; item = children[i++];){
+ var idNode = item.getAttributeNode('id');
+ if (idNode && idNode.nodeValue == id){
+ this.push(item, tag, null, classes, attributes, pseudos);
+ break;
+ }
+ }
+ return;
+ }
+ if (!item){
+ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+ if (this.contains(this.root, node)) return;
+ else break getById;
+ } else if (this.document !== node && !this.contains(node, item)) return;
+ this.push(item, tag, null, classes, attributes, pseudos);
+ return;
+ }
+ getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+ children = node.getElementsByClassName(classList.join(' '));
+ if (!(children && children.length)) break getByClass;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+ return;
+ }
+ }
+ getByTag: {
+ children = node.getElementsByTagName(tag);
+ if (!(children && children.length)) break getByTag;
+ if (!this.brokenStarGEBTN) tag = null;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+ if ((node = node.firstChild)) do {
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ } while ((node = node.nextSibling));
+ },
+
+ '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+ while ((node = node.nextSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '^': function(node, tag, id, classes, attributes, pseudos){ // first child
+ node = node.firstChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+ while ((node = node.nextSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+ this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+ this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+ while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+ node = node.parentNode;
+ if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+ while ((node = node.previousSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+ node = node.lastChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+ while ((node = node.previousSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ }
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+ /*<pseudo-selectors>*/
+
+ 'empty': function(node){
+ var child = node.firstChild;
+ return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+ },
+
+ 'not': function(node, expression){
+ return !this.matchNode(node, expression);
+ },
+
+ 'contains': function(node, text){
+ return (node.innerText || node.textContent || '').indexOf(text) > -1;
+ },
+
+ 'first-child': function(node){
+ while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'last-child': function(node){
+ while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'only-child': function(node){
+ var prev = node;
+ while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+ return true;
+ },
+
+ /*<nth-pseudo-selectors>*/
+
+ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+ 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+ 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+ 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+ 'index': function(node, index){
+ return this['pseudo:nth-child'](node, '' + (index + 1));
+ },
+
+ 'even': function(node){
+ return this['pseudo:nth-child'](node, '2n');
+ },
+
+ 'odd': function(node){
+ return this['pseudo:nth-child'](node, '2n+1');
+ },
+
+ /*</nth-pseudo-selectors>*/
+
+ /*<of-type-pseudo-selectors>*/
+
+ 'first-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'last-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'only-of-type': function(node){
+ var prev = node, nodeName = node.nodeName;
+ while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+ return true;
+ },
+
+ /*</of-type-pseudo-selectors>*/
+
+ // custom pseudos
+
+ 'enabled': function(node){
+ return !node.disabled;
+ },
+
+ 'disabled': function(node){
+ return node.disabled;
+ },
+
+ 'checked': function(node){
+ return node.checked || node.selected;
+ },
+
+ 'focus': function(node){
+ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+ },
+
+ 'root': function(node){
+ return (node === this.root);
+ },
+
+ 'selected': function(node){
+ return node.selected;
+ }
+
+ /*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+ 'for': function(){
+ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+ },
+
+ 'href': function(){
+ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+ },
+
+ 'style': function(){
+ return (this.style) ? this.style.cssText : this.getAttribute('style');
+ },
+
+ 'tabindex': function(){
+ var attributeNode = this.getAttributeNode('tabindex');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ },
+
+ 'type': function(){
+ return this.getAttribute('type');
+ },
+
+ 'maxlength': function(){
+ var attributeNode = this.getAttributeNode('maxLength');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ }
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.7';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+ return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+ return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+ local.setDocument(container);
+ return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+ local.setDocument(node);
+ return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+ local.setDocument(node);
+ return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ local.setDocument(node);
+ return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+ local.attributeGetters[name] = fn;
+ return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+ return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+ local['pseudo:' + name] = function(node, argument){
+ return fn.call(node, argument);
+ };
+ return this;
+};
+
+Slick.lookupPseudo = function(name){
+ var pseudo = local['pseudo:' + name];
+ if (pseudo) return function(argument){
+ return pseudo.call(this, argument);
+ };
+ return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+ local.override(regexp, fn);
+ return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+ return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+/*
+---
+
+name: Element
+
+description: 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.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, IFrame, Selectors]
+
+...
+*/
+
+var Element = this.Element = function(tag, props){
+ var konstructor = Element.Constructors[tag];
+ if (konstructor) return konstructor(props);
+ if (typeof tag != 'string') return document.id(tag).set(props);
+
+ if (!props) props = {};
+
+ if (!(/^[\w-]+$/).test(tag)){
+ var parsed = Slick.parse(tag).expressions[0][0];
+ tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+ if (parsed.id && props.id == null) props.id = parsed.id;
+
+ var attributes = parsed.attributes;
+ if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+ attr = attributes[i];
+ if (props[attr.key] != null) continue;
+
+ if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+ else if (!attr.value && !attr.operator) props[attr.key] = true;
+ }
+
+ if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+ }
+
+ return document.newElement(tag, props);
+};
+
+
+if (Browser.Element){
+ Element.prototype = Browser.Element.prototype;
+ // IE8 and IE9 require the wrapping.
+ Element.prototype._fireEvent = (function(fireEvent){
+ return function(type, event){
+ return fireEvent.call(this, type, event);
+ };
+ })(Element.prototype.fireEvent);
+}
+
+new Type('Element', Element).mirror(function(name){
+ if (Array.prototype[name]) return;
+
+ var obj = {};
+ obj[name] = function(){
+ var results = [], args = arguments, elements = true;
+ for (var i = 0, l = this.length; i < l; i++){
+ var element = this[i], result = results[i] = element[name].apply(element, args);
+ elements = (elements && typeOf(result) == 'element');
+ }
+ return (elements) ? new Elements(results) : results;
+ };
+
+ Elements.implement(obj);
+});
+
+if (!Browser.Element){
+ Element.parent = Object;
+
+ Element.Prototype = {
+ '$constructor': Element,
+ '$family': Function.convert('element').hide()
+ };
+
+ Element.mirror(function(name, method){
+ Element.Prototype[name] = method;
+ });
+}
+
+Element.Constructors = {};
+
+
+
+var IFrame = new Type('IFrame', function(){
+ var params = Array.link(arguments, {
+ properties: Type.isObject,
+ iframe: function(obj){
+ return (obj != null);
+ }
+ });
+
+ var props = params.properties || {}, iframe;
+ if (params.iframe) iframe = document.id(params.iframe);
+ var onload = props.onload || function(){};
+ delete props.onload;
+ props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+ iframe = new Element(iframe || 'iframe', props);
+
+ var onLoad = function(){
+ onload.call(iframe.contentWindow);
+ };
+
+ if (window.frames[props.id]) onLoad();
+ else iframe.addListener('load', onLoad);
+ return iframe;
+});
+
+var Elements = this.Elements = function(nodes){
+ if (nodes && nodes.length){
+ var uniques = {}, node;
+ for (var i = 0; node = nodes[i++];){
+ var uid = Slick.uidOf(node);
+ if (!uniques[uid]){
+ uniques[uid] = true;
+ this.push(node);
+ }
+ }
+ }
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('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));
+ }.protect(),
+
+ push: function(){
+ var length = this.length;
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) this[length++] = item;
+ }
+ return (this.length = length);
+ }.protect(),
+
+ unshift: function(){
+ var items = [];
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) items.push(item);
+ }
+ return Array.prototype.unshift.apply(this, items);
+ }.protect(),
+
+ concat: function(){
+ var newElements = new Elements(this);
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = arguments[i];
+ if (Type.isEnumerable(item)) newElements.append(item);
+ else newElements.push(item);
+ }
+ return newElements;
+ }.protect(),
+
+ append: function(collection){
+ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+ return this;
+ }.protect(),
+
+ empty: function(){
+ while (this.length) delete this[--this.length];
+ return this;
+ }.protect()
+
+});
+
+
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+ var length = this.length;
+ var result = splice.apply(this, arguments);
+ while (length >= this.length) delete this[length--];
+ return result;
+}.protect());
+
+Array.forEachMethod(function(method, name){
+ Elements.implement(name, method);
+});
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+ createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
+} catch (e){}
+
+var escapeQuotes = function(html){
+ return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
+/*<ltIE9>*/
+// #2479 - IE8 Cannot set HTML of style element
+var canChangeStyleHTML = (function(){
+ var div = document.createElement('style'),
+ flag = false;
+ try {
+ div.innerHTML = '#justTesing{margin: 0px;}';
+ flag = !!div.innerHTML;
+ } catch (e){}
+ return flag;
+})();
+/*</ltIE9>*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (props){
+ if (props.checked != null) props.defaultChecked = props.checked;
+ if ((props.type == 'checkbox' || props.type == 'radio') && props.value == null) props.value = 'on';
+ /*<ltIE9>*/ // IE needs the type to be set before changing content of style element
+ if (!canChangeStyleHTML && tag == 'style'){
+ var styleElement = document.createElement('style');
+ styleElement.setAttribute('type', 'text/css');
+ if (props.type) delete props.type;
+ return this.id(styleElement).set(props);
+ }
+ /*</ltIE9>*/
+ /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+ if (createElementAcceptsHTML){
+ tag = '<' + tag;
+ if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+ if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+ tag += '>';
+ delete props.name;
+ delete props.type;
+ }
+ /*</ltIE8>*/
+ }
+ return this.id(this.createElement(tag)).set(props);
+ }
+
+});
+
+})();
+
+(function(){
+
+Slick.uidOf(window);
+Slick.uidOf(document);
+
+Document.implement({
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.window;
+ },
+
+ id: (function(){
+
+ var types = {
+
+ string: function(id, nocash, doc){
+ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ Slick.uidOf(el);
+ if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+ var fireEvent = el.fireEvent;
+ // wrapping needed in IE7, or else crash
+ el._fireEvent = function(type, event){
+ return fireEvent(type, event);
+ };
+ Object.append(el, Element.Prototype);
+ }
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = function(zero){
+ return zero;
+ };
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uniqueNumber) return el;
+ var type = typeOf(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
+});
+
+if (window.$ == null) Window.implement('$', function(el, nc){
+ return document.id(el, nc, this.document);
+});
+
+Window.implement({
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+[Document, Element].invoke('implement', {
+
+ getElements: function(expression){
+ return Slick.search(this, expression, new Elements);
+ },
+
+ getElement: function(expression){
+ return document.id(Slick.find(this, expression));
+ }
+
+});
+
+var contains = {contains: function(element){
+ return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+ if (!expression) return combinator;
+
+ expression = Object.clone(Slick.parse(expression));
+
+ var expressions = expression.expressions;
+ for (var i = expressions.length; i--;)
+ expressions[i][0].combinator = combinator;
+
+ return expression;
+};
+
+Object.forEach({
+ getNext: '~',
+ getPrevious: '!~',
+ getParent: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElement(injectCombinator(expression, combinator));
+ });
+});
+
+Object.forEach({
+ getAllNext: '~',
+ getAllPrevious: '!~',
+ getSiblings: '~~',
+ getChildren: '>',
+ getParents: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElements(injectCombinator(expression, combinator));
+ });
+});
+
+Element.implement({
+
+ getFirst: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+ },
+
+ getLast: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.window;
+ },
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getElementById: function(id){
+ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+ },
+
+ match: function(expression){
+ return !expression || Slick.match(this, expression);
+ }
+
+});
+
+
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+ if (arguments.length == 1){
+ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+ else if (Type.isEnumerable(selector)) return new Elements(selector);
+ }
+ return new Elements(arguments);
+});
+
+// Inserters
+
+var inserters = {
+
+ before: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element.nextSibling);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ element.insertBefore(context, element.firstChild);
+ }
+
+};
+
+inserters.inside = inserters.bottom;
+
+
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+ 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+ 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+ properties[property.toLowerCase()] = property;
+});
+
+properties.html = 'innerHTML';
+properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
+
+Object.forEach(properties, function(real, key){
+ propertySetters[key] = function(node, value){
+ node[real] = value;
+ };
+ propertyGetters[key] = function(node){
+ return node[real];
+ };
+});
+
+/*<ltIE9>*/
+propertySetters.text = (function(){
+ return function(node, value){
+ if (node.get('tag') == 'style') node.set('html', value);
+ else node[properties.text] = value;
+ };
+})(propertySetters.text);
+
+propertyGetters.text = (function(getter){
+ return function(node){
+ return (node.get('tag') == 'style') ? node.innerHTML : getter(node);
+ };
+})(propertyGetters.text);
+/*</ltIE9>*/
+
+// Booleans
+
+var bools = [
+ 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+ 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+ 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+ 'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+ var lower = bool.toLowerCase();
+ booleans[lower] = bool;
+ propertySetters[lower] = function(node, value){
+ node[bool] = !!value;
+ };
+ propertyGetters[lower] = function(node){
+ return !!node[bool];
+ };
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+ 'class': function(node, value){
+ ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
+ },
+
+ 'for': function(node, value){
+ ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+ },
+
+ 'style': function(node, value){
+ (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+ },
+
+ 'value': function(node, value){
+ node.value = (value != null) ? value : '';
+ }
+
+});
+
+propertyGetters['class'] = function(node){
+ return ('className' in node) ? node.className || null : node.getAttribute('class');
+};
+
+/* <webkit> */
+var el = document.createElement('button');
+// IE sets type as readonly and throws
+try { el.type = 'button'; } catch (e){}
+if (el.type != 'button') propertySetters.type = function(node, value){
+ node.setAttribute('type', value);
+};
+el = null;
+/* </webkit> */
+
+/*<IE>*/
+
+/*<ltIE9>*/
+// #2479 - IE8 Cannot set HTML of style element
+var canChangeStyleHTML = (function(){
+ var div = document.createElement('style'),
+ flag = false;
+ try {
+ div.innerHTML = '#justTesing{margin: 0px;}';
+ flag = !!div.innerHTML;
+ } catch (e){}
+ return flag;
+})();
+/*</ltIE9>*/
+
+var input = document.createElement('input'), volatileInputValue, html5InputSupport;
+
+// #2178
+input.value = 't';
+input.type = 'submit';
+volatileInputValue = input.value != 't';
+
+// #2443 - IE throws "Invalid Argument" when trying to use html5 input types
+try {
+ input.value = '';
+ input.type = 'email';
+ html5InputSupport = input.type == 'email';
+} catch (e){}
+
+input = null;
+
+if (volatileInputValue || !html5InputSupport) propertySetters.type = function(node, type){
+ try {
+ var value = node.value;
+ node.type = type;
+ node.value = value;
+ } catch (e){}
+};
+/*</IE>*/
+
+/* getProperty, setProperty */
+
+/* <ltIE9> */
+var pollutesGetAttribute = (function(div){
+ div.random = 'attribute';
+ return (div.getAttribute('random') == 'attribute');
+})(document.createElement('div'));
+
+var hasCloneBug = (function(test){
+ test.innerHTML = '<object><param name="should_fix" value="the unknown" /></object>';
+ return test.cloneNode(true).firstChild.childNodes.length != 1;
+})(document.createElement('div'));
+/* </ltIE9> */
+
+var hasClassList = !!document.createElement('div').classList;
+
+var classes = function(className){
+ var classNames = (className || '').clean().split(' '), uniques = {};
+ return classNames.filter(function(className){
+ if (className !== '' && !uniques[className]) return uniques[className] = className;
+ });
+};
+
+var addToClassList = function(name){
+ this.classList.add(name);
+};
+
+var removeFromClassList = function(name){
+ this.classList.remove(name);
+};
+
+Element.implement({
+
+ setProperty: function(name, value){
+ var setter = propertySetters[name.toLowerCase()];
+ if (setter){
+ setter(this, value);
+ } else {
+ /* <ltIE9> */
+ var attributeWhiteList;
+ if (pollutesGetAttribute) attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ /* </ltIE9> */
+
+ if (value == null){
+ this.removeAttribute(name);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) delete attributeWhiteList[name];
+ /* </ltIE9> */
+ } else {
+ this.setAttribute(name, '' + value);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) attributeWhiteList[name] = true;
+ /* </ltIE9> */
+ }
+ }
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ getProperty: function(name){
+ var getter = propertyGetters[name.toLowerCase()];
+ if (getter) return getter(this);
+ /* <ltIE9> */
+ if (pollutesGetAttribute){
+ var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ if (!attr) return null;
+ if (attr.expando && !attributeWhiteList[name]){
+ var outer = this.outerHTML;
+ // segment by the opening tag and find mention of attribute name
+ if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
+ attributeWhiteList[name] = true;
+ }
+ }
+ /* </ltIE9> */
+ var result = Slick.getAttribute(this, name);
+ return (!result && !Slick.hasAttribute(this, name)) ? null : result;
+ },
+
+ getProperties: function(){
+ var args = Array.convert(arguments);
+ return args.map(this.getProperty, this).associate(args);
+ },
+
+ removeProperty: function(name){
+ return this.setProperty(name, null);
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ },
+
+ set: function(prop, value){
+ var property = Element.Properties[prop];
+ (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+ }.overloadSetter(),
+
+ get: function(prop){
+ var property = Element.Properties[prop];
+ return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+ }.overloadGetter(),
+
+ erase: function(prop){
+ var property = Element.Properties[prop];
+ (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+ return this;
+ },
+
+ hasClass: hasClassList ? function(className){
+ return this.classList.contains(className);
+ } : function(className){
+ return classes(this.className).contains(className);
+ },
+
+ addClass: hasClassList ? function(className){
+ classes(className).forEach(addToClassList, this);
+ return this;
+ } : function(className){
+ this.className = classes(className + ' ' + this.className).join(' ');
+ return this;
+ },
+
+ removeClass: hasClassList ? function(className){
+ classes(className).forEach(removeFromClassList, this);
+ return this;
+ } : function(className){
+ var classNames = classes(this.className);
+ classes(className).forEach(classNames.erase, classNames);
+ this.className = classNames.join(' ');
+ return this;
+ },
+
+ toggleClass: function(className, force){
+ if (force == null) force = !this.hasClass(className);
+ return (force) ? this.addClass(className) : this.removeClass(className);
+ },
+
+ adopt: function(){
+ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+ if (length > 1) parent = fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < length; i++){
+ var element = document.id(elements[i], true);
+ if (element) parent.appendChild(element);
+ }
+
+ if (fragment) this.appendChild(fragment);
+
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ grab: function(el, where){
+ inserters[where || 'bottom'](document.id(el, true), this);
+ return this;
+ },
+
+ inject: function(el, where){
+ inserters[where || 'bottom'](this, document.id(el, true));
+ return this;
+ },
+
+ replaces: function(el){
+ el = document.id(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = document.id(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ getSelected: function(){
+ this.selectedIndex; // Safari 3.2.1
+ return new Elements(Array.convert(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ var type = el.type;
+ if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+ var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+ // IE
+ return document.id(opt).get('value');
+ }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+ Array.convert(value).each(function(val){
+ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ }
+
+});
+
+
+// appendHTML
+
+var appendInserters = {
+ before: 'beforeBegin',
+ after: 'afterEnd',
+ bottom: 'beforeEnd',
+ top: 'afterBegin',
+ inside: 'beforeEnd'
+};
+
+Element.implement('appendHTML', ('insertAdjacentHTML' in document.createElement('div')) ? function(html, where){
+ this.insertAdjacentHTML(appendInserters[where || 'bottom'], html);
+ return this;
+} : function(html, where){
+ var temp = new Element('div', {html: html}),
+ children = temp.childNodes,
+ fragment = temp.firstChild;
+
+ if (!fragment) return this;
+ if (children.length > 1){
+ fragment = document.createDocumentFragment();
+ for (var i = 0, l = children.length; i < l; i++){
+ fragment.appendChild(children[i]);
+ }
+ }
+
+ inserters[where || 'bottom'](fragment, this);
+ return this;
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+ return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+ var uid = item.uniqueNumber;
+ if (item.removeEvents) item.removeEvents();
+ if (item.clearAttributes) item.clearAttributes();
+ if (uid != null){
+ delete collected[uid];
+ delete storage[uid];
+ }
+ return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+ destroy: function(){
+ var children = clean(this).getElementsByTagName('*');
+ Array.each(children, clean);
+ Element.dispose(this);
+ return null;
+ },
+
+ empty: function(){
+ Array.convert(this.childNodes).each(Element.dispose);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ contents = contents !== false;
+ var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+ if (contents){
+ ce.append(Array.convert(clone.getElementsByTagName('*')));
+ te.append(Array.convert(this.getElementsByTagName('*')));
+ }
+
+ for (i = ce.length; i--;){
+ var node = ce[i], element = te[i];
+ if (!keepid) node.removeAttribute('id');
+ /*<ltIE9>*/
+ if (node.clearAttributes){
+ node.clearAttributes();
+ node.mergeAttributes(element);
+ node.removeAttribute('uniqueNumber');
+ if (node.options){
+ var no = node.options, eo = element.options;
+ for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+ }
+ }
+ /*</ltIE9>*/
+ var prop = formProps[element.tagName.toLowerCase()];
+ if (prop && element[prop]) node[prop] = element[prop];
+ }
+
+ /*<ltIE9>*/
+ if (hasCloneBug){
+ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+ for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+ }
+ /*</ltIE9>*/
+ return document.id(clone);
+ }
+
+});
+
+[Element, Window, Document].invoke('implement', {
+
+ addListener: function(type, fn){
+ if (window.attachEvent && !window.addEventListener){
+ collected[Slick.uidOf(this)] = this;
+ }
+ if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = get(Slick.uidOf(this)), prop = storage[property];
+ if (dflt != null && prop == null) prop = storage[property] = dflt;
+ return prop != null ? prop : null;
+ },
+
+ store: function(property, value){
+ var storage = get(Slick.uidOf(this));
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = get(Slick.uidOf(this));
+ delete storage[property];
+ return this;
+ }
+
+});
+
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener){
+ var gc = function(){
+ Object.each(collected, clean);
+ if (window.CollectGarbage) CollectGarbage();
+ window.removeListener('unload', gc);
+ };
+ window.addListener('unload', gc);
+}
+/*</ltIE9>*/
+
+Element.Properties = {};
+
+
+
+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.html = {
+
+ set: function(html){
+ if (html == null) html = '';
+ else if (typeOf(html) == 'array') html = html.join('');
+
+ /*<ltIE9>*/
+ if (this.styleSheet && !canChangeStyleHTML) this.styleSheet.cssText = html;
+ else /*</ltIE9>*/this.innerHTML = html;
+ },
+ erase: function(){
+ this.set('html', '');
+ }
+
+};
+
+var supportsHTML5Elements = true, supportsTableInnerHTML = true, supportsTRInnerHTML = true;
+
+/*<ltIE9>*/
+// technique by jdbarlett - http://jdbartlett.com/innershiv/
+var div = document.createElement('div');
+var fragment;
+div.innerHTML = '<nav></nav>';
+supportsHTML5Elements = (div.childNodes.length == 1);
+if (!supportsHTML5Elements){
+ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' ');
+ fragment = document.createDocumentFragment(), l = tags.length;
+ while (l--) fragment.createElement(tags[l]);
+}
+div = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+supportsTableInnerHTML = Function.attempt(function(){
+ var table = document.createElement('table');
+ table.innerHTML = '<tr><td></td></tr>';
+ return true;
+});
+
+/*<ltFF4>*/
+var tr = document.createElement('tr'), html = '<td></td>';
+tr.innerHTML = html;
+supportsTRInnerHTML = (tr.innerHTML == html);
+tr = null;
+/*</ltFF4>*/
+
+if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
+
+ Element.Properties.html.set = (function(set){
+
+ var translations = {
+ table: [1, '<table>', '</table>'],
+ select: [1, '<select>', '</select>'],
+ tbody: [2, '<table><tbody>', '</tbody></table>'],
+ tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+ };
+
+ translations.thead = translations.tfoot = translations.tbody;
+
+ return function(html){
+
+ /*<ltIE9>*/
+ if (this.styleSheet) return set.call(this, html);
+ /*</ltIE9>*/
+ var wrap = translations[this.get('tag')];
+ if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
+ if (!wrap) return set.call(this, html);
+
+ var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
+ if (!supportsHTML5Elements) fragment.appendChild(wrapper);
+ wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
+ while (level--) target = target.firstChild;
+ this.empty().adopt(target.childNodes);
+ if (!supportsHTML5Elements) fragment.removeChild(wrapper);
+ wrapper = null;
+ };
+
+ })(Element.Properties.html.set);
+}
+/*</IE>*/
+
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+ set: function(value){
+ var tag = this.get('tag');
+ if (tag != 'select') return this.setProperty('value', value);
+ var options = this.getElements('option');
+ value = String(value);
+ for (var i = 0; i < options.length; i++){
+ var option = options[i],
+ attr = option.getAttributeNode('value'),
+ optionValue = (attr && attr.specified) ? option.value : option.get('text');
+ if (optionValue === value) return option.selected = true;
+ }
+ },
+
+ get: function(){
+ var option = this, tag = option.get('tag');
+
+ if (tag != 'select' && tag != 'option') return this.getProperty('value');
+
+ if (tag == 'select' && !(option = option.getSelected()[0])) return '';
+
+ var attr = option.getAttributeNode('value');
+ return (attr && attr.specified) ? option.value : option.get('text');
+ }
+
+};
+testForm = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
+ set: function(id){
+ this.id = this.getAttributeNode('id').value = id;
+ },
+ get: function(){
+ return this.id || null;
+ },
+ erase: function(){
+ this.id = this.getAttributeNode('id').value = '';
+ }
+};
+/*</IE>*/
+
+})();
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function(){
+
+var _keys = {};
+var normalizeWheelSpeed = function(event){
+ var normalized;
+ if (event.wheelDelta){
+ normalized = event.wheelDelta % 120 == 0 ? event.wheelDelta / 120 : event.wheelDelta / 12;
+ } else {
+ var rawAmount = event.deltaY || event.detail || 0;
+ normalized = -(rawAmount % 3 == 0 ? rawAmount / 3 : rawAmount * 10);
+ }
+ return normalized;
+};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+ if (!win) win = window;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.event = event;
+ this.$extended = true;
+ this.shift = event.shiftKey;
+ this.control = event.ctrlKey;
+ this.alt = event.altKey;
+ this.meta = event.metaKey;
+ var type = this.type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+ this.target = document.id(target);
+
+ if (type.indexOf('key') == 0){
+ var code = this.code = (event.which || event.keyCode);
+ if (!this.shift || type != 'keypress') this.key = _keys[code];
+ if (type == 'keydown' || type == 'keyup'){
+ if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+ else if (code > 95 && code < 106) this.key = code - 96;
+ }
+ if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+ } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'wheel' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+ var doc = win.document;
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+ this.page = {
+ x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+ y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+ };
+ this.client = {
+ x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type == 'DOMMouseScroll' || type == 'wheel' || type == 'mousewheel') this.wheel = normalizeWheelSpeed(event);
+ this.rightClick = (event.which == 3 || event.button == 2);
+ if (type == 'mouseover' || type == 'mouseout' || type == 'mouseenter' || type == 'mouseleave'){
+ var overTarget = type == 'mouseover' || type == 'mouseenter';
+ var related = event.relatedTarget || event[(overTarget ? 'from' : 'to') + 'Element'];
+ while (related && related.nodeType == 3) related = related.parentNode;
+ this.relatedTarget = document.id(related);
+ }
+ } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+ this.rotation = event.rotation;
+ this.scale = event.scale;
+ this.targetTouches = event.targetTouches;
+ this.changedTouches = event.changedTouches;
+ var touches = this.touches = event.touches;
+ if (touches && touches[0]){
+ var touch = touches[0];
+ this.page = {x: touch.pageX, y: touch.pageY};
+ this.client = {x: touch.clientX, y: touch.clientY};
+ }
+ }
+
+ if (!this.client) this.client = {};
+ if (!this.page) this.page = {};
+});
+
+DOMEvent.implement({
+
+ stop: function(){
+ return this.preventDefault().stopPropagation();
+ },
+
+ 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;
+ }
+
+});
+
+DOMEvent.defineKey = function(code, key){
+ _keys[code] = key;
+ return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+ '38': 'up', '40': 'down', '37': 'left', '39': 'right',
+ '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+ '46': 'delete', '13': 'enter'
+});
+
+})();
+
+
+
+
+
+/*
+---
+
+name: Element.Event
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
+*/
+
+(function(){
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ if (!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[type],
+ condition = fn,
+ self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn, type);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event, type)) return fn.call(this, event);
+ return true;
+ };
+ }
+ if (custom.base) realType = Function.convert(custom.base).call(this, type);
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType];
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new DOMEvent(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn, arguments[2]);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var list = events[type];
+ var index = list.keys.indexOf(fn);
+ if (index == -1) return this;
+ var value = list.values[index];
+ delete list.keys[index];
+ delete list.values[index];
+ var custom = Element.Events[type];
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn, type);
+ if (custom.base) type = Function.convert(custom.base).call(this, type);
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ var attached = this.retrieve('events');
+ if (!attached) return this;
+ if (!events){
+ for (type in attached) this.removeEvents(type);
+ this.eliminate('events');
+ } else if (attached[events]){
+ attached[events].keys.each(function(fn){
+ this.removeEvent(events, fn);
+ }, this);
+ delete attached[events];
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ args = Array.convert(args);
+
+ events[type].keys.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = document.id(from);
+ var events = from.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var eventType in events) this.cloneEvents(from, eventType);
+ } else if (events[type]){
+ events[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
+ wheel: 2, 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
+ orientationchange: 2, // mobile
+ touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+ gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+ load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+ hashchange: 1, popstate: 2, pageshow: 2, pagehide: 2, // history
+ error: 1, abort: 1, scroll: 1, message: 2 //misc
+};
+
+Element.Events = {
+ mousewheel: {
+ base: 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll'
+ }
+};
+
+var check = function(event){
+ var related = event.relatedTarget;
+ if (related == null) return true;
+ if (!related) return false;
+ return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+};
+
+if ('onmouseenter' in document.documentElement){
+ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
+ Element.MouseenterCheck = check;
+} else {
+ Element.Events.mouseenter = {
+ base: 'mouseover',
+ condition: check
+ };
+
+ Element.Events.mouseleave = {
+ base: 'mouseout',
+ condition: check
+ };
+}
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+ Element.NativeEvents.propertychange = 2;
+ Element.Events.change = {
+ base: function(){
+ var type = this.type;
+ return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change';
+ },
+ condition: function(event){
+ return event.type != 'propertychange' || event.event.propertyName == 'checked';
+ }
+ };
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+ while (target && target != self){
+ if (match(target, event)) return fn.call(target, event, target);
+ target = document.id(target.parentNode);
+ }
+};
+
+var map = {
+ mouseenter: {
+ base: 'mouseover',
+ condition: Element.MouseenterCheck
+ },
+ mouseleave: {
+ base: 'mouseout',
+ condition: Element.MouseenterCheck
+ },
+ focus: {
+ base: 'focus' + (eventListenerSupport ? '' : 'in'),
+ capture: true
+ },
+ blur: {
+ base: eventListenerSupport ? 'blur' : 'focusout',
+ capture: true
+ }
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+ return {
+
+ base: 'focusin',
+
+ remove: function(self, uid){
+ var list = self.retrieve(_key + type + 'listeners', {})[uid];
+ if (list && list.forms) for (var i = list.forms.length; i--;){
+ // the form may have been destroyed, so it won't have the
+ // removeEvent method anymore. In that case the event was
+ // removed as well.
+ if (list.forms[i].removeEvent) list.forms[i].removeEvent(type, list.fns[i]);
+ }
+ },
+
+ listen: function(self, match, fn, event, target, uid){
+ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+ if (!form) return;
+
+ var listeners = self.retrieve(_key + type + 'listeners', {}),
+ listener = listeners[uid] || {forms: [], fns: []},
+ forms = listener.forms, fns = listener.fns;
+
+ if (forms.indexOf(form) != -1) return;
+ forms.push(form);
+
+ var _fn = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ form.addEvent(type, _fn);
+ fns.push(_fn);
+
+ listeners[uid] = listener;
+ self.store(_key + type + 'listeners', listeners);
+ }
+ };
+};
+
+var inputObserver = function(type){
+ return {
+ base: 'focusin',
+ listen: function(self, match, fn, event, target){
+ var events = {blur: function(){
+ this.removeEvents(events);
+ }};
+ events[type] = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ event.target.addEvents(events);
+ }
+ };
+};
+
+if (!eventListenerSupport) Object.append(map, {
+ submit: formObserver('submit'),
+ reset: formObserver('reset'),
+ change: inputObserver('change'),
+ select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+ addEvent = proto.addEvent,
+ removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+ return function(type, fn, useCapture){
+ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+ var parsed = Slick.parse(type).expressions[0][0];
+ if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+ var newType = parsed.tag;
+ parsed.pseudos.slice(1).each(function(pseudo){
+ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+ });
+ old.call(this, type, fn);
+ return method.call(this, newType, parsed.pseudos[0].value, fn);
+ };
+};
+
+var delegation = {
+
+ addEvent: function(type, match, fn){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (stored) for (var _uid in stored){
+ if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+ }
+
+ var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+ type = _map.base || _type;
+
+ match = function(target){
+ return Slick.match(target, _match);
+ };
+
+ var elementEvent = Element.Events[_type];
+ if (_map.condition || elementEvent && elementEvent.condition){
+ var __match = match, condition = _map.condition || elementEvent.condition;
+ match = function(target, event){
+ return __match(target, event) && condition.call(target, event, type);
+ };
+ }
+
+ var self = this, uid = String.uniqueID();
+ var delegator = _map.listen ? function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) _map.listen(self, match, fn, event, target, uid);
+ } : function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) bubbleUp(self, match, fn, event, target);
+ };
+
+ if (!stored) stored = {};
+ stored[uid] = {
+ match: _match,
+ fn: _fn,
+ delegator: delegator
+ };
+ storage[_type] = stored;
+ return addEvent.call(this, type, delegator, _map.capture);
+ },
+
+ removeEvent: function(type, match, fn, _uid){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (!stored) return this;
+
+ if (_uid){
+ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+ type = _map.base || _type;
+ if (_map.remove) _map.remove(this, _uid);
+ delete stored[_uid];
+ storage[_type] = stored;
+ return removeEvent.call(this, type, delegator, _map.capture);
+ }
+
+ var __uid, s;
+ if (fn) for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+ } else for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+ }
+ return this;
+ }
+
+};
+
+[Element, Window, Document].invoke('implement', {
+ addEvent: relay(addEvent, delegation.addEvent),
+ removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+/*
+---
+
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
+*/
+
+(function(){
+
+var html = document.html, el;
+
+//<ltIE9>
+// Check for oldIE, which does not remove styles when they're set to null
+el = document.createElement('div');
+el.style.color = 'red';
+el.style.color = null;
+var doesNotRemoveStyles = el.style.color == 'red';
+
+// check for oldIE, which returns border* shorthand styles in the wrong order (color-width-style instead of width-style-color)
+var border = '1px solid #123abc';
+el.style.border = border;
+var returnsBordersInWrongOrder = el.style.border != border;
+el = null;
+//</ltIE9>
+
+var hasGetComputedStyle = !!window.getComputedStyle,
+ supportBorderRadius = document.createElement('div').style.borderRadius != null;
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+var hasOpacity = (html.style.opacity != null),
+ hasFilter = (html.style.filter != null),
+ reAlpha = /alpha\(opacity=([\d.]+)\)/i;
+
+var setVisibility = function(element, opacity){
+ element.store('$opacity', opacity);
+ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
+};
+
+//<ltIE9>
+var setFilter = function(element, regexp, value){
+ var style = element.style,
+ filter = style.filter || element.getComputedStyle('filter') || '';
+ style.filter = (regexp.test(filter) ? filter.replace(regexp, value) : filter + ' ' + value).trim();
+ if (!style.filter) style.removeAttribute('filter');
+};
+//</ltIE9>
+
+var setOpacity = (hasOpacity ? function(element, opacity){
+ element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+ if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
+ if (opacity == null || opacity == 1){
+ setFilter(element, reAlpha, '');
+ if (opacity == 1 && getOpacity(element) != 1) setFilter(element, reAlpha, 'alpha(opacity=100)');
+ } else {
+ setFilter(element, reAlpha, 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')');
+ }
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+ var opacity = element.style.opacity || element.getComputedStyle('opacity');
+ return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+ var filter = (element.style.filter || element.getComputedStyle('filter')),
+ opacity;
+ if (filter) opacity = filter.match(reAlpha);
+ return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+ var opacity = element.retrieve('$opacity');
+ if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+ return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat',
+ namedPositions = {left: '0%', top: '0%', center: '50%', right: '100%', bottom: '100%'},
+ hasBackgroundPositionXY = (html.style.backgroundPositionX != null),
+ prefixPattern = /^-(ms)-/;
+
+var camelCase = function(property){
+ return property.replace(prefixPattern, '$1-').camelCase();
+};
+
+//<ltIE9>
+var removeStyle = function(style, property){
+ if (property == 'backgroundPosition'){
+ style.removeAttribute(property + 'X');
+ property += 'Y';
+ }
+ style.removeAttribute(property);
+};
+//</ltIE9>
+
+Element.implement({
+
+ getComputedStyle: function(property){
+ if (!hasGetComputedStyle && this.currentStyle) return this.currentStyle[camelCase(property)];
+ var defaultView = Element.getDocument(this).defaultView,
+ computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : '';
+ },
+
+ setStyle: function(property, value){
+ if (property == 'opacity'){
+ if (value != null) value = parseFloat(value);
+ setOpacity(this, value);
+ return this;
+ }
+ property = camelCase(property == 'float' ? floatName : property);
+ if (typeOf(value) != 'string'){
+ var map = (Element.Styles[property] || '@').split(' ');
+ value = Array.convert(value).map(function(val, i){
+ if (!map[i]) return '';
+ return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ //<ltIE9>
+ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
+ removeStyle(this.style, property);
+ }
+ //</ltIE9>
+ return this;
+ },
+
+ getStyle: function(property){
+ if (property == 'opacity') return getOpacity(this);
+ property = camelCase(property == 'float' ? floatName : property);
+ if (supportBorderRadius && property.indexOf('borderRadius') != -1){
+ return ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'].map(function(corner){
+ return this.style[corner] || '0px';
+ }, this).join(' ');
+ }
+ var result = this.style[property];
+ if (!result || property == 'zIndex'){
+ if (Element.ShortStyles.hasOwnProperty(property)){
+ result = [];
+ for (var s in Element.ShortStyles[property]) result.push(this.getStyle(s));
+ return result.join(' ');
+ }
+ result = this.getComputedStyle(property);
+ }
+ if (hasBackgroundPositionXY && /^backgroundPosition[XY]?$/.test(property)){
+ return result.replace(/(top|right|bottom|left)/g, function(position){
+ return namedPositions[position];
+ }) || '0px';
+ }
+ if (!result && property == 'backgroundPosition') return '0px 0px';
+ if (result){
+ result = String(result);
+ var color = result.match(/rgba?\([\d\s,]+\)/);
+ if (color) result = result.replace(color[0], color[0].rgbToHex());
+ }
+ if (!hasGetComputedStyle && !this.style[property]){
+ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
+ 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 ((/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
+ return '0px';
+ }
+ }
+ //<ltIE9>
+ if (returnsBordersInWrongOrder && /^border(Top|Right|Bottom|Left)?$/.test(property) && /^#/.test(result)){
+ return result.replace(/^(.+)\s(.+)\s(.+)$/, '$2 $3 $1');
+ }
+ //</ltIE9>
+
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.flatten(arguments).each(function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = {
+ left: '@px', top: '@px', bottom: '@px', right: '@px',
+ width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
+ backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', 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: '@', borderRadius: '@px @px @px @px'
+};
+
+
+
+
+
+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(@, @, @)';
+});
+
+if (hasBackgroundPositionXY) Element.ShortStyles.backgroundPosition = {backgroundPositionX: '@', backgroundPositionY: '@'};
+})();
+
+/*
+---
+
+name: Element.Dimensions
+
+description: 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).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+ child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
+ widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
+
+var svgCalculateSize = function(el){
+
+ var gCS = window.getComputedStyle(el),
+ bounds = {x: 0, y: 0};
+
+ heightComponents.each(function(css){
+ bounds.y += parseFloat(gCS[css]);
+ });
+ widthComponents.each(function(css){
+ bounds.x += parseFloat(gCS[css]);
+ });
+ return bounds;
+};
+
+var isOffset = function(el){
+ return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
+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();
+
+ //<ltIE9>
+ // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with visibility hidden.
+ if (!window.getComputedStyle) return {x: this.offsetWidth, y: this.offsetHeight};
+ //</ltIE9>
+
+ // This svg section under, calling `svgCalculateSize()`, can be removed when FF fixed the svg size bug.
+ // Bug info: https://bugzilla.mozilla.org/show_bug.cgi?id=530985
+ if (this.get('tag') == 'svg') return svgCalculateSize(this);
+
+ try {
+ var bounds = this.getBoundingClientRect();
+ return {x: bounds.width, y: bounds.height};
+ } catch (e){
+ return {x: 0, y: 0};
+ }
+ },
+
+ 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.parentNode, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: brokenOffsetParent ? function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+ while ((element = element.parentNode)){
+ if (isOffsetCheck(element)) return element;
+ }
+ return null;
+ } : function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ try {
+ return element.offsetParent;
+ } catch (e){}
+ return null;
+ },
+
+ getOffsets: function(){
+ var hasGetBoundingClientRect = this.getBoundingClientRect;
+
+ if (hasGetBoundingClientRect){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
+ return {
+ x: bound.left.toFloat() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toFloat() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+ };
+ }
+
+ 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;
+
+ element = element.offsetParent;
+ }
+
+ return position;
+ },
+
+ getPosition: function(relative){
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+
+ if (relative && (relative = document.id(relative))){
+ var relativePosition = relative.getPosition();
+ return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+ }
+ return position;
+ },
+
+ 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')
+ };
+ },
+
+ setPosition: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+
+[Document, Window].invoke('implement', {
+
+ getSize: function(){
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow(), doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this),
+ min = this.getSize(),
+ body = this.getDocument().body;
+
+ return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.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 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
+Element.alias({position: 'setPosition'}); //compatability
+
+[Window, Document, Element].invoke('implement', {
+
+ 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;
+ }
+
+});
+
+/*
+---
+
+name: Fx
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires: [Chain, Events, Options, Class.Thenable]
+
+provides: Fx
+
+...
+*/
+
+(function(){
+
+var Fx = this.Fx = new Class({
+
+ Implements: [Chain, Events, Options, Class.Thenable],
+
+ options: {
+ /*
+ onStart: nil,
+ onCancel: nil,
+ onComplete: nil,
+ */
+ fps: 60,
+ unit: false,
+ duration: 500,
+ frames: null,
+ frameSkip: true,
+ link: 'ignore'
+ },
+
+ initialize: function(options){
+ this.subject = this.subject || this;
+ this.setOptions(options);
+ },
+
+ getTransition: function(){
+ return function(p){
+ return -(Math.cos(Math.PI * p) - 1) / 2;
+ };
+ },
+
+ step: function(now){
+ if (this.options.frameSkip){
+ var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
+ this.time = now;
+ this.frame += frames;
+ } else {
+ this.frame++;
+ }
+
+ if (this.frame < this.frames){
+ var delta = this.transition(this.frame / this.frames);
+ this.set(this.compute(this.from, this.to, delta));
+ } else {
+ this.frame = this.frames;
+ this.set(this.compute(this.from, this.to, 1));
+ this.stop();
+ }
+ },
+
+ set: function(now){
+ return now;
+ },
+
+ compute: function(from, to, delta){
+ return Fx.compute(from, to, delta);
+ },
+
+ check: function(){
+ if (!this.isRunning()) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ start: function(from, to){
+ if (!this.check(from, to)) return this;
+ this.from = from;
+ this.to = to;
+ this.frame = (this.options.frameSkip) ? 0 : -1;
+ this.time = null;
+ this.transition = this.getTransition();
+ var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
+ this.duration = Fx.Durations[duration] || duration.toInt();
+ this.frameInterval = 1000 / fps;
+ this.frames = frames || Math.round(this.duration / this.frameInterval);
+ if (this.getThenableState() !== 'pending'){
+ this.resetThenable(this.subject);
+ }
+ this.fireEvent('start', this.subject);
+ pushInstance.call(this, fps);
+ return this;
+ },
+
+ stop: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ if (this.frames == this.frame){
+ this.fireEvent('complete', this.subject);
+ if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+ } else {
+ this.fireEvent('stop', this.subject);
+ }
+ this.resolve(this.subject === this ? null : this.subject);
+ }
+ return this;
+ },
+
+ cancel: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ this.frame = this.frames;
+ this.fireEvent('cancel', this.subject).clearChain();
+ this.reject(this.subject);
+ }
+ return this;
+ },
+
+ pause: function(){
+ if (this.isRunning()){
+ this.time = null;
+ pullInstance.call(this, this.options.fps);
+ }
+ return this;
+ },
+
+ resume: function(){
+ if (this.isPaused()) pushInstance.call(this, this.options.fps);
+ return this;
+ },
+
+ isRunning: function(){
+ var list = instances[this.options.fps];
+ return list && list.contains(this);
+ },
+
+ isPaused: function(){
+ return (this.frame < this.frames) && !this.isRunning();
+ }
+
+});
+
+Fx.compute = function(from, to, delta){
+ return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+
+// global timers
+
+var instances = {}, timers = {};
+
+var loop = function(){
+ var now = Date.now();
+ for (var i = this.length; i--;){
+ var instance = this[i];
+ if (instance) instance.step(now);
+ }
+};
+
+var pushInstance = function(fps){
+ var list = instances[fps] || (instances[fps] = []);
+ list.push(this);
+ if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
+};
+
+var pullInstance = function(fps){
+ var list = instances[fps];
+ if (list){
+ list.erase(this);
+ if (!list.length && timers[fps]){
+ delete instances[fps];
+ timers[fps] = clearInterval(timers[fps]);
+ }
+ }
+};
+
+})();
+
+/*
+---
+
+name: Fx.CSS
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires: [Fx, Element.Style]
+
+provides: Fx.CSS
+
+...
+*/
+
+Fx.CSS = new Class({
+
+ Extends: Fx,
+
+ //prepares the base from/to object
+
+ prepare: function(element, property, values){
+ values = Array.convert(values);
+ var from = values[0], to = values[1];
+ if (to == null){
+ to = from;
+ from = element.getStyle(property);
+ var unit = this.options.unit;
+ // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299
+ if (unit && from && typeof from == 'string' && from.slice(-unit.length) != unit && parseFloat(from) != 0){
+ element.setStyle(property, to + unit);
+ var value = element.getComputedStyle(property);
+ // IE and Opera support pixelLeft or pixelWidth
+ if (!(/px$/.test(value))){
+ value = element.style[('pixel-' + property).camelCase()];
+ if (value == null){
+ // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+ var left = element.style.left;
+ element.style.left = to + unit;
+ value = element.style.pixelLeft;
+ element.style.left = left;
+ }
+ }
+ from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0);
+ element.setStyle(property, from + unit);
+ }
+ }
+ return {from: this.parse(from), to: this.parse(to)};
+ },
+
+ //parses a value into an array
+
+ parse: function(value){
+ value = Function.convert(value)();
+ value = (typeof value == 'string') ? value.split(' ') : Array.convert(value);
+ return value.map(function(val){
+ val = String(val);
+ var found = false;
+ Object.each(Fx.CSS.Parsers, function(parser){
+ if (found) return;
+ var parsed = parser.parse(val);
+ if (parsed || parsed === 0) 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 = Function.convert('fx:css:value');
+ return computed;
+ },
+
+ //serves the value as settable
+
+ serve: function(value, unit){
+ if (typeOf(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 = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
+
+ var searchStyles = function(rules){
+ Array.each(rules, function(rule){
+ if (rule.media){
+ searchStyles(rule.rules || rule.cssRules);
+ return;
+ }
+ if (!rule.style) return;
+ var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
+ return m.toLowerCase();
+ }) : null;
+ if (!selectorText || !selectorTest.test(selectorText)) return;
+ Object.each(Element.Styles, function(value, style){
+ if (!rule.style[style] || Element.ShortStyles[style]) return;
+ value = String(rule.style[style]);
+ to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
+ });
+ });
+ };
+
+ Array.each(document.styleSheets, function(sheet){
+ var href = sheet.href;
+ if (href && href.indexOf('://') > -1 && href.indexOf(document.domain) == -1) return;
+ var rules = sheet.rules || sheet.cssRules;
+ searchStyles(rules);
+ });
+ return Fx.CSS.Cache[selector] = to;
+ }
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = {
+
+ 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: Function.convert(false),
+ compute: function(zero, one){
+ return one;
+ },
+ serve: function(zero){
+ return zero;
+ }
+ }
+
+};
+
+
+
+/*
+---
+
+name: Fx.Morph
+
+description: 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.
+
+requires: Fx.CSS
+
+provides: Fx.Morph
+
+...
+*/
+
+Fx.Morph = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(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(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){
+ this.get('morph').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var morph = this.retrieve('morph');
+ if (!morph){
+ morph = new Fx.Morph(this, {link: 'cancel'});
+ this.store('morph', morph);
+ }
+ return morph;
+ }
+
+};
+
+Element.implement({
+
+ morph: function(props){
+ this.get('morph').start(props);
+ return this;
+ }
+
+});
+
+/*
+---
+
+name: Fx.Transitions
+
+description: 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, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires: Fx
+
+provides: Fx.Transitions
+
+...
+*/
+
+Fx.implement({
+
+ getTransition: function(){
+ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
+ if (typeof trans == 'string'){
+ var data = trans.split(':');
+ trans = Fx.Transitions;
+ trans = trans[data[0]] || trans[data[0].capitalize()];
+ if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
+ }
+ return trans;
+ }
+
+});
+
+Fx.Transition = function(transition, params){
+ params = Array.convert(params);
+ var easeIn = function(pos){
+ return transition(pos, params);
+ };
+ return Object.append(easeIn, {
+ easeIn: easeIn,
+ easeOut: function(pos){
+ return 1 - transition(1 - pos, params);
+ },
+ easeInOut: function(pos){
+ return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
+ }
+ });
+};
+
+Fx.Transitions = {
+
+ linear: function(zero){
+ return zero;
+ }
+
+};
+
+
+
+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 && 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.cos(p * Math.PI / 2);
+ },
+
+ Back: function(p, x){
+ 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 = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+ break;
+ }
+ }
+ return value;
+ },
+
+ Elastic: function(p, x){
+ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && 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);
+ });
+});
+
+/*
+---
+
+name: Fx.Tween
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
+*/
+
+Fx.Tween = new Class({
+
+ Extends: Fx.CSS,
+
+ initialize: function(element, options){
+ this.element = this.subject = document.id(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(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){
+ this.get('tween').cancel().setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var tween = this.retrieve('tween');
+ if (!tween){
+ tween = new Fx.Tween(this, {link: 'cancel'});
+ this.store('tween', tween);
+ }
+ return tween;
+ }
+
+};
+
+Element.implement({
+
+ tween: function(property, from, to){
+ this.get('tween').start(property, from, to);
+ return this;
+ },
+
+ fade: function(){
+ var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle;
+ if (args[1] == null) args[1] = 'toggle';
+ switch (args[1]){
+ case 'in': method = 'start'; args[1] = 1; break;
+ case 'out': method = 'start'; args[1] = 0; break;
+ case 'show': method = 'set'; args[1] = 1; break;
+ case 'hide': method = 'set'; args[1] = 0; break;
+ case 'toggle':
+ var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1);
+ method = 'start';
+ args[1] = flag ? 0 : 1;
+ this.store('fade:flag', !flag);
+ toggle = true;
+ break;
+ default: method = 'start';
+ }
+ if (!toggle) this.eliminate('fade:flag');
+ fade[method].apply(fade, args);
+ var to = args[args.length - 1];
+
+ if (method == 'set'){
+ this.setStyle('visibility', to == 0 ? 'hidden' : 'visible');
+ } else if (to != 0){
+ if (fade.$chain.length){
+ fade.chain(function(){
+ this.element.setStyle('visibility', 'visible');
+ this.callChain();
+ });
+ } else {
+ this.setStyle('visibility', 'visible');
+ }
+ } else {
+ fade.chain(function(){
+ if (this.element.getStyle('opacity')) return;
+ this.element.setStyle('visibility', 'hidden');
+ this.callChain();
+ });
+ }
+
+ 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;
+ }
+
+});
+
+/*
+---
+
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Class.Thenable, Browser]
+
+provides: Request
+
+...
+*/
+
+(function(){
+
+var empty = function(){},
+ progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
+
+ Implements: [Chain, Events, Options, Class.Thenable],
+
+ options: {/*
+ onRequest: function(){},
+ onLoadstart: function(event, xhr){},
+ onProgress: function(event, xhr){},
+ onComplete: function(){},
+ onCancel: function(){},
+ onSuccess: function(responseText, responseXML){},
+ onFailure: function(xhr){},
+ onException: function(headerName, value){},
+ onTimeout: function(){},
+ user: '',
+ password: '',
+ withCredentials: false,*/
+ 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,
+ timeout: 0,
+ noCache: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.headers = this.options.headers;
+ },
+
+ onStateChange: function(){
+ var xhr = this.xhr;
+ if (xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ Function.attempt(function(){
+ var status = xhr.status;
+ this.status = (status == 1223) ? 204 : status;
+ }.bind(this));
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ if (this.timer){
+ clearTimeout(this.timer);
+ delete this.timer;
+ }
+
+ this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+ if (this.options.isSuccess.call(this, this.status))
+ this.success(this.response.text, this.response.xml);
+ else
+ this.failure();
+ },
+
+ isSuccess: function(){
+ var status = this.status;
+ return (status >= 200 && status < 300);
+ },
+
+ isRunning: function(){
+ return !!this.running;
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
+ return text.stripScripts(this.options.evalScripts);
+ },
+
+ success: function(text, xml){
+ this.onSuccess(this.processScripts(text), xml);
+ this.resolve({text: text, xml: xml});
+ },
+
+ onSuccess: function(){
+ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
+ },
+
+ failure: function(){
+ this.onFailure();
+ this.reject({reason: 'failure', xhr: this.xhr});
+ },
+
+ onFailure: function(){
+ this.fireEvent('complete').fireEvent('failure', this.xhr);
+ },
+
+ loadstart: function(event){
+ this.fireEvent('loadstart', [event, this.xhr]);
+ },
+
+ progress: function(event){
+ this.fireEvent('progress', [event, this.xhr]);
+ },
+
+ timeout: function(){
+ this.fireEvent('timeout', this.xhr);
+ this.reject({reason: 'timeout', xhr: this.xhr});
+ },
+
+ setHeader: function(name, value){
+ this.headers[name] = value;
+ return this;
+ },
+
+ getHeader: function(name){
+ return Function.attempt(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(options)) return this;
+
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.running = true;
+
+ var type = typeOf(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+ switch (typeOf(data)){
+ case 'element': data = document.id(data).toQueryString(); break;
+ case 'object': case 'hash': data = Object.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && !['get', 'post'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && ['post', 'put'].contains(method)){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+ this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
+ }
+
+ if (!url) url = document.location.pathname;
+
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+ if (this.options.noCache)
+ url += (url.indexOf('?') > -1 ? '&' : '?') + String.uniqueID();
+
+ if (data && (method == 'get' || method == 'delete')){
+ url += (url.indexOf('?') > -1 ? '&' : '?') + data;
+ data = null;
+ }
+
+ var xhr = this.xhr;
+ if (progressSupport){
+ xhr.onloadstart = this.loadstart.bind(this);
+ xhr.onprogress = this.progress.bind(this);
+ }
+
+ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+ if ((this.options.withCredentials) && 'withCredentials' in xhr) xhr.withCredentials = true;
+
+ xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ Object.each(this.headers, function(value, key){
+ try {
+ xhr.setRequestHeader(key, value);
+ } catch (e){
+ this.fireEvent('exception', [key, value]);
+ this.reject({reason: 'exception', xhr: xhr, exception: e});
+ }
+ }, this);
+
+ if (this.getThenableState() !== 'pending'){
+ this.resetThenable({reason: 'send'});
+ }
+ this.fireEvent('request');
+ xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ var xhr = this.xhr;
+ xhr.abort();
+ if (this.timer){
+ clearTimeout(this.timer);
+ delete this.timer;
+ }
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ this.reject({reason: 'cancel', xhr: xhr});
+ return this;
+ }
+
+});
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'patch', 'head', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'].each(function(method){
+ methods[method] = function(data){
+ var object = {
+ method: method
+ };
+ if (data != null) object.data = data;
+ return this.send(object);
+ };
+});
+
+Request.implement(methods);
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.get('send').cancel();
+ send.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var send = this.retrieve('send');
+ if (!send){
+ send = new Request({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ });
+ this.store('send', send);
+ }
+ return send;
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+})();
+
+/*
+---
+
+name: Request.HTML
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires: [Element, Request]
+
+provides: Request.HTML
+
+...
+*/
+
+Request.HTML = new Class({
+
+ Extends: Request,
+
+ options: {
+ update: false,
+ append: false,
+ evalScripts: true,
+ filter: false,
+ headers: {
+ Accept: 'text/html, application/xml, text/xml, */*'
+ }
+ },
+
+ success: function(text){
+ var options = this.options, response = this.response;
+
+ response.html = text.stripScripts(function(script){
+ response.javascript = script;
+ });
+
+ var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+ if (match) response.html = match[1];
+ var temp = new Element('div').set('html', response.html);
+
+ response.tree = temp.childNodes;
+ response.elements = temp.getElements(options.filter || '*');
+
+ if (options.filter) response.tree = response.elements;
+ if (options.update){
+ var update = document.id(options.update).empty();
+ if (options.filter) update.adopt(response.elements);
+ else update.set('html', response.html);
+ } else if (options.append){
+ var append = document.id(options.append);
+ if (options.filter) response.elements.reverse().inject(append);
+ else append.adopt(temp.getChildren());
+ }
+ if (options.evalScripts) Browser.exec(response.javascript);
+
+ this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+ this.resolve({tree: response.tree, elements: response.elements, html: response.html, javascript: response.javascript});
+ }
+
+});
+
+Element.Properties.load = {
+
+ set: function(options){
+ var load = this.get('load').cancel();
+ load.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var load = this.retrieve('load');
+ if (!load){
+ load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
+ this.store('load', load);
+ }
+ return load;
+ }
+
+};
+
+Element.implement({
+
+ load: function(){
+ this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
+ return this;
+ }
+
+});
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+ return JSON.stringify(obj);
+} : function(obj){
+ if (obj && obj.toJSON) obj = obj.toJSON();
+
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ return '[' + obj.map(JSON.encode).clean() + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.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 '' + obj;
+ case 'null': return 'null';
+ }
+
+ return null;
+};
+
+JSON.secure = true;
+
+
+JSON.decode = function(string, secure){
+ if (!string || typeOf(string) != 'string') return null;
+
+ if (secure == null) secure = JSON.secure;
+ if (secure){
+ if (JSON.parse) return JSON.parse(string);
+ if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+ }
+
+ return eval('(' + string + ')');
+};
+
+})();
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ /*onError: function(text, error){},*/
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ Object.append(this.headers, {
+ 'Accept': 'application/json',
+ 'X-Request': 'JSON'
+ });
+ },
+
+ success: function(text){
+ var json;
+ try {
+ json = this.response.json = JSON.decode(text, this.options.secure);
+ } catch (error){
+ this.fireEvent('error', [text, error]);
+ return;
+ }
+ if (json == null){
+ this.failure();
+ } else {
+ this.onSuccess(json, text);
+ this.resolve({json: json, text: text});
+ }
+ }
+
+});
+
+/*
+---
+
+name: Cookie
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+ - Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires: [Options, Browser]
+
+provides: Cookie
+
+...
+*/
+
+var Cookie = new Class({
+
+ Implements: Options,
+
+ options: {
+ path: '/',
+ domain: false,
+ duration: false,
+ secure: false,
+ document: document,
+ encode: true,
+ httpOnly: false
+ },
+
+ initialize: function(key, options){
+ this.key = key;
+ this.setOptions(options);
+ },
+
+ write: function(value){
+ if (this.options.encode) 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';
+ if (this.options.httpOnly) value += '; HttpOnly';
+ 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, Object.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();
+};
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+ loaded,
+ checks = [],
+ shouldPoll,
+ timer,
+ testElement = document.createElement('div');
+
+var domready = function(){
+ clearTimeout(timer);
+ if (!ready){
+ Browser.loaded = ready = true;
+ document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+ document.fireEvent('domready');
+ window.fireEvent('domready');
+ }
+ // cleanup scope vars
+ document = window = testElement = null;
+};
+
+var check = function(){
+ for (var i = checks.length; i--;) if (checks[i]()){
+ domready();
+ return true;
+ }
+ return false;
+};
+
+var poll = function(){
+ clearTimeout(timer);
+ if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+ try {
+ testElement.doScroll();
+ return true;
+ } catch (e){}
+ return false;
+};
+// If doScroll works already, it can't be used to determine domready
+// e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+ checks.push(doScrollWorks);
+ shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+ var state = document.readyState;
+ return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+ onAdd: function(fn){
+ if (ready) fn.call(this);
+ }
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+ base: 'load',
+ onAdd: function(fn){
+ if (loaded && this == window) fn.call(this);
+ },
+ condition: function(){
+ if (this == window){
+ domready();
+ delete Element.Events.load;
+ }
+ return true;
+ }
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+ loaded = true;
+});
+
+})(window, document);
diff --git a/www/js/mootools-debug.js b/www/js/mootools-debug.js
new file mode 100644
index 0000000..26e8850
--- /dev/null
+++ b/www/js/mootools-debug.js
@@ -0,0 +1,5180 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/1f9082c332d19034561f804233cd5b90
+
+packager build:
+ - packager build Core/Event Core/Browser Core/Class Core/Class.Extras Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/JSON Core/DOMReady
+
+...
+*/
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/).
+
+authors: 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)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.4.5',
+ build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family != null) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if (item.callee) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ /*<ltIE8>*/
+ if (!item.hasOwnProperty) return false;
+ /*</ltIE8>*/
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ else if (usePlural) args = [a];
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+ if (isType && proto) object.implement(key, proto.protect());
+ }
+
+ if (isType){
+ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]);
+ object.forEachMethod = function(fn){
+ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){
+ fn.call(prototype, prototype[methods[i]], methods[i]);
+ }
+ for (var key in prototype) fn.call(prototype, prototype[key], key)
+ };
+ }
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ /*<!ES5>*/
+ every: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){
+ value = this[i];
+ if (fn.call(bind, value, i, this)) results.push(value);
+ }
+ return results;
+ },
+
+ indexOf: function(item, from){
+ var length = this.length >>> 0;
+ for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var length = this.length >>> 0, results = Array(length);
+ for (var i = 0; i < length; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length >>> 0; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+ /*</!ES5>*/
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ 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;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.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--;){
+ 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 = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ 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('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ /*<!ES5-bind>*/
+ bind: function(that){
+ var self = this,
+ args = arguments.length > 1 ? Array.slice(arguments, 1) : null,
+ F = function(){};
+
+ var bound = function(){
+ var context = that, length = arguments.length;
+ if (this instanceof bound){
+ F.prototype = self.prototype;
+ context = new F;
+ }
+ var result = (!args && !length)
+ ? self.call(context)
+ : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments);
+ return context == that ? result : context;
+ };
+ return bound;
+ },
+ /*</!ES5-bind>*/
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass((args == null ? [] : args), bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass((args == null ? [] : args), bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -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('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ contains: function(string, separator){
+ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1;
+ },
+
+ trim: function(){
+ return String(this).replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return String(this).replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return String(this).replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return String(this).replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return String(this).replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = String(this).match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+/*
+---
+
+name: Browser
+
+description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: [Array, Function, Number, String]
+
+provides: [Browser, Window, Document]
+
+...
+*/
+
+(function(){
+
+var document = this.document;
+var window = document.window = this;
+
+var ua = navigator.userAgent.toLowerCase(),
+ platform = navigator.platform.toLowerCase(),
+ UA = ua.match(/(opera|ie|trident|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
+ mode = (UA[1] == 'ie' || UA[1] == 'trident') && document.documentMode;
+
+var Browser = this.Browser = {
+
+ extend: Function.prototype.extend,
+
+ name: (UA[1] == 'version') ? UA[3] : (UA[1] == 'trident' ? 'ie' : UA[1]),
+
+ version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
+
+ Platform: {
+ name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
+ },
+
+ Features: {
+ xpath: !!(document.evaluate),
+ air: !!(window.runtime),
+ query: !!(document.querySelector),
+ json: !!(window.JSON)
+ },
+
+ Plugins: {}
+
+};
+
+Browser[Browser.name] = true;
+Browser[Browser.name + parseInt(Browser.version, 10)] = true;
+Browser.Platform[Browser.Platform.name] = true;
+
+// Request
+
+Browser.Request = (function(){
+
+ var XMLHTTP = function(){
+ return new XMLHttpRequest();
+ };
+
+ var MSXML2 = function(){
+ return new ActiveXObject('MSXML2.XMLHTTP');
+ };
+
+ var MSXML = function(){
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ };
+
+ return Function.attempt(function(){
+ XMLHTTP();
+ return XMLHTTP;
+ }, function(){
+ MSXML2();
+ return MSXML2;
+ }, function(){
+ MSXML();
+ return MSXML;
+ });
+
+})();
+
+Browser.Features.xhr = !!(Browser.Request);
+
+// Flash detection
+
+var version = (Function.attempt(function(){
+ return navigator.plugins['Shockwave Flash'].description;
+}, function(){
+ return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+}) || '0 r0').match(/\d+/g);
+
+Browser.Plugins.Flash = {
+ version: Number(version[0] || '0.' + version[1]) || 0,
+ build: Number(version[2]) || 0
+};
+
+// String scripts
+
+Browser.exec = function(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;
+};
+
+String.implement('stripScripts', function(exec){
+ var scripts = '';
+ var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
+ scripts += code + '\n';
+ return '';
+ });
+ if (exec === true) Browser.exec(scripts);
+ else if (typeOf(exec) == 'function') exec(scripts, text);
+ return text;
+});
+
+// Window, Document
+
+Browser.extend({
+ Document: this.Document,
+ Window: this.Window,
+ Element: this.Element,
+ Event: this.Event
+});
+
+this.Window = this.$constructor = new Type('Window', function(){});
+
+this.$family = Function.from('window').hide();
+
+Window.mirror(function(name, method){
+ window[name] = method;
+});
+
+this.Document = document.$constructor = new Type('Document', function(){});
+
+document.$family = Function.from('document').hide();
+
+Document.mirror(function(name, method){
+ document[name] = method;
+});
+
+document.html = document.documentElement;
+if (!document.head) document.head = document.getElementsByTagName('head')[0];
+
+if (document.execCommand) try {
+ document.execCommand("BackgroundImageCache", false, true);
+} catch (e){}
+
+/*<ltIE9>*/
+if (this.attachEvent && !this.addEventListener){
+ var unloadEvent = function(){
+ this.detachEvent('onunload', unloadEvent);
+ document.head = document.html = document.window = null;
+ };
+ this.attachEvent('onunload', unloadEvent);
+}
+
+// IE fails on collections and <select>.options (refers to <select>)
+var arrayFrom = Array.from;
+try {
+ arrayFrom(document.html.childNodes);
+} catch(e){
+ Array.from = function(item){
+ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
+ var i = item.length, array = new Array(i);
+ while (i--) array[i] = item[i];
+ return array;
+ }
+ return arrayFrom(item);
+ };
+
+ var prototype = Array.prototype,
+ slice = prototype.slice;
+ ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
+ var method = prototype[name];
+ Array[name] = function(item){
+ return method.apply(Array.from(item), slice.call(arguments, 1));
+ };
+ });
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+(function(){
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ if (k in object) results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ var value = object[key];
+ if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
+ }
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (hasOwnProperty.call(object, key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (hasOwnProperty.call(object, key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+})();
+
+
+
+
+/*
+---
+
+name: Event
+
+description: Contains the Event Type, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires: [Window, Document, Array, Function, String, Object]
+
+provides: Event
+
+...
+*/
+
+(function() {
+
+var _keys = {};
+
+var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){
+ if (!win) win = window;
+ event = event || win.event;
+ if (event.$extended) return event;
+ this.event = event;
+ this.$extended = true;
+ this.shift = event.shiftKey;
+ this.control = event.ctrlKey;
+ this.alt = event.altKey;
+ this.meta = event.metaKey;
+ var type = this.type = event.type;
+ var target = event.target || event.srcElement;
+ while (target && target.nodeType == 3) target = target.parentNode;
+ this.target = document.id(target);
+
+ if (type.indexOf('key') == 0){
+ var code = this.code = (event.which || event.keyCode);
+ this.key = _keys[code];
+ if (type == 'keydown'){
+ if (code > 111 && code < 124) this.key = 'f' + (code - 111);
+ else if (code > 95 && code < 106) this.key = code - 96;
+ }
+ if (this.key == null) this.key = String.fromCharCode(code).toLowerCase();
+ } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){
+ var doc = win.document;
+ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+ this.page = {
+ x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
+ y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
+ };
+ this.client = {
+ x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
+ y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
+ };
+ if (type == 'DOMMouseScroll' || type == 'mousewheel')
+ this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+
+ this.rightClick = (event.which == 3 || event.button == 2);
+ if (type == 'mouseover' || type == 'mouseout'){
+ var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
+ while (related && related.nodeType == 3) related = related.parentNode;
+ this.relatedTarget = document.id(related);
+ }
+ } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){
+ this.rotation = event.rotation;
+ this.scale = event.scale;
+ this.targetTouches = event.targetTouches;
+ this.changedTouches = event.changedTouches;
+ var touches = this.touches = event.touches;
+ if (touches && touches[0]){
+ var touch = touches[0];
+ this.page = {x: touch.pageX, y: touch.pageY};
+ this.client = {x: touch.clientX, y: touch.clientY};
+ }
+ }
+
+ if (!this.client) this.client = {};
+ if (!this.page) this.page = {};
+});
+
+DOMEvent.implement({
+
+ stop: function(){
+ return this.preventDefault().stopPropagation();
+ },
+
+ 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;
+ }
+
+});
+
+DOMEvent.defineKey = function(code, key){
+ _keys[code] = key;
+ return this;
+};
+
+DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true);
+
+DOMEvent.defineKeys({
+ '38': 'up', '40': 'down', '37': 'left', '39': 'right',
+ '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab',
+ '46': 'delete', '13': 'enter'
+});
+
+})();
+
+
+
+
+
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.from(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(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 = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.from(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) if (i in fns){
+ this.removeEvent(type, fns[i]);
+ }
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (this.addEvent) for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+name: Slick.Parser
+description: Standalone CSS3 Selector parser
+provides: Slick.Parser
+...
+*/
+
+;(function(){
+
+var parsed,
+ separatorIndex,
+ combinatorIndex,
+ reversed,
+ cache = {},
+ reverseCache = {},
+ reUnescape = /\\/g;
+
+var parse = function(expression, isReversed){
+ if (expression == null) return null;
+ if (expression.Slick === true) return expression;
+ expression = ('' + expression).replace(/^\s+|\s+$/g, '');
+ reversed = !!isReversed;
+ var currentCache = (reversed) ? reverseCache : cache;
+ if (currentCache[expression]) return currentCache[expression];
+ parsed = {
+ Slick: true,
+ expressions: [],
+ raw: expression,
+ reverse: function(){
+ return parse(this.raw, true);
+ }
+ };
+ separatorIndex = -1;
+ while (expression != (expression = expression.replace(regexp, parser)));
+ parsed.length = parsed.expressions.length;
+ return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
+};
+
+var reverseCombinator = function(combinator){
+ if (combinator === '!') return ' ';
+ else if (combinator === ' ') return '!';
+ else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
+ else return '!' + combinator;
+};
+
+var reverse = function(expression){
+ var expressions = expression.expressions;
+ for (var i = 0; i < expressions.length; i++){
+ var exp = expressions[i];
+ var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
+
+ for (var j = 0; j < exp.length; j++){
+ var cexp = exp[j];
+ if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
+ cexp.combinator = cexp.reverseCombinator;
+ delete cexp.reverseCombinator;
+ }
+
+ exp.reverse().push(last);
+ }
+ return expression;
+};
+
+var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
+ return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
+ return '\\' + match;
+ });
+};
+
+var regexp = new RegExp(
+/*
+#!/usr/bin/env ruby
+puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
+__END__
+ "(?x)^(?:\
+ \\s* ( , ) \\s* # Separator \n\
+ | \\s* ( <combinator>+ ) \\s* # Combinator \n\
+ | ( \\s+ ) # CombinatorChildren \n\
+ | ( <unicode>+ | \\* ) # Tag \n\
+ | \\# ( <unicode>+ ) # ID \n\
+ | \\. ( <unicode>+ ) # ClassName \n\
+ | # Attribute \n\
+ \\[ \
+ \\s* (<unicode1>+) (?: \
+ \\s* ([*^$!~|]?=) (?: \
+ \\s* (?:\
+ ([\"']?)(.*?)\\9 \
+ )\
+ ) \
+ )? \\s* \
+ \\](?!\\]) \n\
+ | :+ ( <unicode>+ )(?:\
+ \\( (?:\
+ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
+ ) \\)\
+ )?\
+ )"
+*/
+ "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
+ .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
+ .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+ .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
+);
+
+function parser(
+ rawMatch,
+
+ separator,
+ combinator,
+ combinatorChildren,
+
+ tagName,
+ id,
+ className,
+
+ attributeKey,
+ attributeOperator,
+ attributeQuote,
+ attributeValue,
+
+ pseudoMarker,
+ pseudoClass,
+ pseudoQuote,
+ pseudoClassQuotedValue,
+ pseudoClassValue
+){
+ if (separator || separatorIndex === -1){
+ parsed.expressions[++separatorIndex] = [];
+ combinatorIndex = -1;
+ if (separator) return '';
+ }
+
+ if (combinator || combinatorChildren || combinatorIndex === -1){
+ combinator = combinator || ' ';
+ var currentSeparator = parsed.expressions[separatorIndex];
+ if (reversed && currentSeparator[combinatorIndex])
+ currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
+ currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
+ }
+
+ var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
+
+ if (tagName){
+ currentParsed.tag = tagName.replace(reUnescape, '');
+
+ } else if (id){
+ currentParsed.id = id.replace(reUnescape, '');
+
+ } else if (className){
+ className = className.replace(reUnescape, '');
+
+ if (!currentParsed.classList) currentParsed.classList = [];
+ if (!currentParsed.classes) currentParsed.classes = [];
+ currentParsed.classList.push(className);
+ currentParsed.classes.push({
+ value: className,
+ regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
+ });
+
+ } else if (pseudoClass){
+ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
+ pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
+
+ if (!currentParsed.pseudos) currentParsed.pseudos = [];
+ currentParsed.pseudos.push({
+ key: pseudoClass.replace(reUnescape, ''),
+ value: pseudoClassValue,
+ type: pseudoMarker.length == 1 ? 'class' : 'element'
+ });
+
+ } else if (attributeKey){
+ attributeKey = attributeKey.replace(reUnescape, '');
+ attributeValue = (attributeValue || '').replace(reUnescape, '');
+
+ var test, regexp;
+
+ switch (attributeOperator){
+ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break;
+ case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break;
+ case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
+ case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break;
+ case '=' : test = function(value){
+ return attributeValue == value;
+ }; break;
+ case '*=' : test = function(value){
+ return value && value.indexOf(attributeValue) > -1;
+ }; break;
+ case '!=' : test = function(value){
+ return attributeValue != value;
+ }; break;
+ default : test = function(value){
+ return !!value;
+ };
+ }
+
+ if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
+ return false;
+ };
+
+ if (!test) test = function(value){
+ return value && regexp.test(value);
+ };
+
+ if (!currentParsed.attributes) currentParsed.attributes = [];
+ currentParsed.attributes.push({
+ key: attributeKey,
+ operator: attributeOperator,
+ value: attributeValue,
+ test: test
+ });
+
+ }
+
+ return '';
+};
+
+// Slick NS
+
+var Slick = (this.Slick || {});
+
+Slick.parse = function(expression){
+ return parse(expression);
+};
+
+Slick.escapeRegExp = escapeRegExp;
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+name: Slick.Finder
+description: The new, superfast css selector engine.
+provides: Slick.Finder
+requires: Slick.Parser
+...
+*/
+
+;(function(){
+
+var local = {},
+ featuresCache = {},
+ toString = Object.prototype.toString;
+
+// Feature / Bug detection
+
+local.isNativeCode = function(fn){
+ return (/\{\s*\[native code\]\s*\}/).test('' + fn);
+};
+
+local.isXML = function(document){
+ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
+ (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
+};
+
+local.setDocument = function(document){
+
+ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
+ var nodeType = document.nodeType;
+ if (nodeType == 9); // document
+ else if (nodeType) document = document.ownerDocument; // node
+ else if (document.navigator) document = document.document; // window
+ else return;
+
+ // check if it's the old document
+
+ if (this.document === document) return;
+ this.document = document;
+
+ // check if we have done feature detection on this document before
+
+ var root = document.documentElement,
+ rootUid = this.getUIDXML(root),
+ features = featuresCache[rootUid],
+ feature;
+
+ if (features){
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+ return;
+ }
+
+ features = featuresCache[rootUid] = {};
+
+ features.root = root;
+ features.isXMLDocument = this.isXML(document);
+
+ features.brokenStarGEBTN
+ = features.starSelectsClosedQSA
+ = features.idGetsName
+ = features.brokenMixedCaseQSA
+ = features.brokenGEBCN
+ = features.brokenCheckedQSA
+ = features.brokenEmptyAttributeQSA
+ = features.isHTMLDocument
+ = features.nativeMatchesSelector
+ = false;
+
+ var starSelectsClosed, starSelectsComments,
+ brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
+ brokenFormAttributeGetter;
+
+ var selected, id = 'slick_uniqueid';
+ var testNode = document.createElement('div');
+
+ var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
+ testRoot.appendChild(testNode);
+
+ // on non-HTML documents innerHTML and getElementsById doesnt work properly
+ try {
+ testNode.innerHTML = '<a id="'+id+'"></a>';
+ features.isHTMLDocument = !!document.getElementById(id);
+ } catch(e){};
+
+ if (features.isHTMLDocument){
+
+ testNode.style.display = 'none';
+
+ // IE returns comment nodes for getElementsByTagName('*') for some documents
+ testNode.appendChild(document.createComment(''));
+ starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
+
+ // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.getElementsByTagName('*');
+ starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
+
+ // IE returns elements with the name instead of just id for getElementsById for some documents
+ try {
+ testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
+ features.idGetsName = document.getElementById(id) === testNode.firstChild;
+ } catch(e){};
+
+ if (testNode.getElementsByClassName){
+
+ // Safari 3.2 getElementsByClassName caches results
+ try {
+ testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
+ testNode.getElementsByClassName('b').length;
+ testNode.firstChild.className = 'b';
+ cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
+ } catch(e){};
+
+ // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
+ try {
+ testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
+ brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
+ } catch(e){};
+
+ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
+ }
+
+ if (testNode.querySelectorAll){
+ // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
+ try {
+ testNode.innerHTML = 'foo</foo>';
+ selected = testNode.querySelectorAll('*');
+ features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
+ } catch(e){};
+
+ // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
+ try {
+ testNode.innerHTML = '<a class="MiX"></a>';
+ features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
+ } catch(e){};
+
+ // Webkit and Opera dont return selected options on querySelectorAll
+ try {
+ testNode.innerHTML = '<select><option selected="selected">a</option></select>';
+ features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
+ } catch(e){};
+
+ // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
+ try {
+ testNode.innerHTML = '<a class=""></a>';
+ features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
+ } catch(e){};
+
+ }
+
+ // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
+ try {
+ testNode.innerHTML = '<form action="s"><input id="action"/></form>';
+ brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
+ } catch(e){};
+
+ // native matchesSelector function
+
+ features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
+ if (features.nativeMatchesSelector) try {
+ // if matchesSelector trows errors on incorrect sintaxes we can use it
+ features.nativeMatchesSelector.call(root, ':slick');
+ features.nativeMatchesSelector = null;
+ } catch(e){};
+
+ }
+
+ try {
+ root.slick_expando = 1;
+ delete root.slick_expando;
+ features.getUID = this.getUIDHTML;
+ } catch(e) {
+ features.getUID = this.getUIDXML;
+ }
+
+ testRoot.removeChild(testNode);
+ testNode = selected = testRoot = null;
+
+ // getAttribute
+
+ features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
+ var method = this.attributeGetters[name];
+ if (method) return method.call(node);
+ var attributeNode = node.getAttributeNode(name);
+ return (attributeNode) ? attributeNode.nodeValue : null;
+ } : function(node, name){
+ var method = this.attributeGetters[name];
+ return (method) ? method.call(node) : node.getAttribute(name);
+ };
+
+ // hasAttribute
+
+ features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
+ return node.hasAttribute(attribute);
+ } : function(node, attribute) {
+ node = node.getAttributeNode(attribute);
+ return !!(node && (node.specified || node.nodeValue));
+ };
+
+ // contains
+ // FIXME: Add specs: local.contains should be different for xml and html documents?
+ var nativeRootContains = root && this.isNativeCode(root.contains),
+ nativeDocumentContains = document && this.isNativeCode(document.contains);
+
+ features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){
+ return context.contains(node);
+ } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){
+ // IE8 does not have .contains on document.
+ return context === node || ((context === document) ? document.documentElement : context).contains(node);
+ } : (root && root.compareDocumentPosition) ? function(context, node){
+ return context === node || !!(context.compareDocumentPosition(node) & 16);
+ } : function(context, node){
+ if (node) do {
+ if (node === context) return true;
+ } while ((node = node.parentNode));
+ return false;
+ };
+
+ // document order sorting
+ // credits to Sizzle (http://sizzlejs.com/)
+
+ features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
+ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
+ return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ } : ('sourceIndex' in root) ? function(a, b){
+ if (!a.sourceIndex || !b.sourceIndex) return 0;
+ return a.sourceIndex - b.sourceIndex;
+ } : (document.createRange) ? function(a, b){
+ if (!a.ownerDocument || !b.ownerDocument) return 0;
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ } : null ;
+
+ root = null;
+
+ for (feature in features){
+ this[feature] = features[feature];
+ }
+};
+
+// Main Method
+
+var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
+ reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
+ qsaFailExpCache = {};
+
+local.search = function(context, expression, append, first){
+
+ var found = this.found = (first) ? null : (append || []);
+
+ if (!context) return found;
+ else if (context.navigator) context = context.document; // Convert the node from a window to a document
+ else if (!context.nodeType) return found;
+
+ // setup
+
+ var parsed, i,
+ uniques = this.uniques = {},
+ hasOthers = !!(append && append.length),
+ contextIsDocument = (context.nodeType == 9);
+
+ if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
+
+ // avoid duplicating items already in the append array
+ if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
+
+ // expression checks
+
+ if (typeof expression == 'string'){ // expression is a string
+
+ /*<simple-selectors-override>*/
+ var simpleSelector = expression.match(reSimpleSelector);
+ simpleSelectors: if (simpleSelector) {
+
+ var symbol = simpleSelector[1],
+ name = simpleSelector[2],
+ node, nodes;
+
+ if (!symbol){
+
+ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
+ nodes = context.getElementsByTagName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ } else if (symbol == '#'){
+
+ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
+ node = context.getElementById(name);
+ if (!node) return found;
+ if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
+ if (first) return node || null;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+
+ } else if (symbol == '.'){
+
+ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
+ if (context.getElementsByClassName && !this.brokenGEBCN){
+ nodes = context.getElementsByClassName(name);
+ if (first) return nodes[0] || null;
+ for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ } else {
+ var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
+ nodes = context.getElementsByTagName('*');
+ for (i = 0; node = nodes[i++];){
+ className = node.className;
+ if (!(className && matchClass.test(className))) continue;
+ if (first) return node;
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+ }
+
+ }
+
+ if (hasOthers) this.sort(found);
+ return (first) ? null : found;
+
+ }
+ /*</simple-selectors-override>*/
+
+ /*<query-selector-override>*/
+ querySelector: if (context.querySelectorAll) {
+
+ if (!this.isHTMLDocument
+ || qsaFailExpCache[expression]
+ //TODO: only skip when expression is actually mixed case
+ || this.brokenMixedCaseQSA
+ || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
+ || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
+ || (!contextIsDocument //Abort when !contextIsDocument and...
+ // there are multiple expressions in the selector
+ // since we currently only fix non-document rooted QSA for single expression selectors
+ && expression.indexOf(',') > -1
+ )
+ || Slick.disableQSA
+ ) break querySelector;
+
+ var _expression = expression, _context = context;
+ if (!contextIsDocument){
+ // non-document rooted QSA
+ // credits to Andrew Dupont
+ var currentId = _context.getAttribute('id'), slickid = 'slickid__';
+ _context.setAttribute('id', slickid);
+ _expression = '#' + slickid + ' ' + _expression;
+ context = _context.parentNode;
+ }
+
+ try {
+ if (first) return context.querySelector(_expression) || null;
+ else nodes = context.querySelectorAll(_expression);
+ } catch(e) {
+ qsaFailExpCache[expression] = 1;
+ break querySelector;
+ } finally {
+ if (!contextIsDocument){
+ if (currentId) _context.setAttribute('id', currentId);
+ else _context.removeAttribute('id');
+ context = _context;
+ }
+ }
+
+ if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
+ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ } else for (i = 0; node = nodes[i++];){
+ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
+ }
+
+ if (hasOthers) this.sort(found);
+ return found;
+
+ }
+ /*</query-selector-override>*/
+
+ parsed = this.Slick.parse(expression);
+ if (!parsed.length) return found;
+ } else if (expression == null){ // there is no expression
+ return found;
+ } else if (expression.Slick){ // expression is a parsed Slick object
+ parsed = expression;
+ } else if (this.contains(context.documentElement || context, expression)){ // expression is a node
+ (found) ? found.push(expression) : found = expression;
+ return found;
+ } else { // other junk
+ return found;
+ }
+
+ /*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+ // cache elements for the nth selectors
+
+ this.posNTH = {};
+ this.posNTHLast = {};
+ this.posNTHType = {};
+ this.posNTHTypeLast = {};
+
+ /*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
+ this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
+
+ if (found == null) found = [];
+
+ // default engine
+
+ var j, m, n;
+ var combinator, tag, id, classList, classes, attributes, pseudos;
+ var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
+
+ search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
+
+ combinator = 'combinator:' + currentBit.combinator;
+ if (!this[combinator]) continue search;
+
+ tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
+ id = currentBit.id;
+ classList = currentBit.classList;
+ classes = currentBit.classes;
+ attributes = currentBit.attributes;
+ pseudos = currentBit.pseudos;
+ lastBit = (j === (currentExpression.length - 1));
+
+ this.bitUniques = {};
+
+ if (lastBit){
+ this.uniques = uniques;
+ this.found = found;
+ } else {
+ this.uniques = {};
+ this.found = [];
+ }
+
+ if (j === 0){
+ this[combinator](context, tag, id, classes, attributes, pseudos, classList);
+ if (first && lastBit && found.length) break search;
+ } else {
+ if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
+ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ if (found.length) break search;
+ } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
+ }
+
+ currentItems = this.found;
+ }
+
+ // should sort if there are nodes in append and if you pass multiple expressions.
+ if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
+
+ return (first) ? (found[0] || null) : found;
+};
+
+// Utils
+
+local.uidx = 1;
+local.uidk = 'slick-uniqueid';
+
+local.getUIDXML = function(node){
+ var uid = node.getAttribute(this.uidk);
+ if (!uid){
+ uid = this.uidx++;
+ node.setAttribute(this.uidk, uid);
+ }
+ return uid;
+};
+
+local.getUIDHTML = function(node){
+ return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
+};
+
+// sort based on the setDocument documentSorter method.
+
+local.sort = function(results){
+ if (!this.documentSorter) return results;
+ results.sort(this.documentSorter);
+ return results;
+};
+
+/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
+
+local.cacheNTH = {};
+
+local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
+
+local.parseNTHArgument = function(argument){
+ var parsed = argument.match(this.matchNTH);
+ if (!parsed) return false;
+ var special = parsed[2] || false;
+ var a = parsed[1] || 1;
+ if (a == '-') a = -1;
+ var b = +parsed[3] || 0;
+ parsed =
+ (special == 'n') ? {a: a, b: b} :
+ (special == 'odd') ? {a: 2, b: 1} :
+ (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a};
+
+ return (this.cacheNTH[argument] = parsed);
+};
+
+local.createNTHPseudo = function(child, sibling, positions, ofType){
+ return function(node, argument){
+ var uid = this.getUID(node);
+ if (!this[positions][uid]){
+ var parent = node.parentNode;
+ if (!parent) return false;
+ var el = parent[child], count = 1;
+ if (ofType){
+ var nodeName = node.nodeName;
+ do {
+ if (el.nodeName != nodeName) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ } else {
+ do {
+ if (el.nodeType != 1) continue;
+ this[positions][this.getUID(el)] = count++;
+ } while ((el = el[sibling]));
+ }
+ }
+ argument = argument || 'n';
+ var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
+ if (!parsed) return false;
+ var a = parsed.a, b = parsed.b, pos = this[positions][uid];
+ if (a == 0) return b == pos;
+ if (a > 0){
+ if (pos < b) return false;
+ } else {
+ if (b < pos) return false;
+ }
+ return ((pos - b) % a) == 0;
+ };
+};
+
+/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
+
+local.pushArray = function(node, tag, id, classes, attributes, pseudos){
+ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
+};
+
+local.pushUID = function(node, tag, id, classes, attributes, pseudos){
+ var uid = this.getUID(node);
+ if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
+ this.uniques[uid] = true;
+ this.found.push(node);
+ }
+};
+
+local.matchNode = function(node, selector){
+ if (this.isHTMLDocument && this.nativeMatchesSelector){
+ try {
+ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
+ } catch(matchError) {}
+ }
+
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+
+ // simple (single) selectors
+ var expressions = parsed.expressions, simpleExpCounter = 0, i;
+ for (i = 0; (currentExpression = expressions[i]); i++){
+ if (currentExpression.length == 1){
+ var exp = currentExpression[0];
+ if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
+ simpleExpCounter++;
+ }
+ }
+
+ if (simpleExpCounter == parsed.length) return false;
+
+ var nodes = this.search(this.document, parsed), item;
+ for (i = 0; item = nodes[i++];){
+ if (item === node) return true;
+ }
+ return false;
+};
+
+local.matchPseudo = function(node, name, argument){
+ var pseudoName = 'pseudo:' + name;
+ if (this[pseudoName]) return this[pseudoName](node, argument);
+ var attribute = this.getAttribute(node, name);
+ return (argument) ? argument == attribute : !!attribute;
+};
+
+local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
+ if (tag){
+ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
+ if (tag == '*'){
+ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
+ } else {
+ if (nodeName != tag) return false;
+ }
+ }
+
+ if (id && node.getAttribute('id') != id) return false;
+
+ var i, part, cls;
+ if (classes) for (i = classes.length; i--;){
+ cls = this.getAttribute(node, 'class');
+ if (!(cls && classes[i].regexp.test(cls))) return false;
+ }
+ if (attributes) for (i = attributes.length; i--;){
+ part = attributes[i];
+ if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
+ }
+ if (pseudos) for (i = pseudos.length; i--;){
+ part = pseudos[i];
+ if (!this.matchPseudo(node, part.key, part.value)) return false;
+ }
+ return true;
+};
+
+var combinators = {
+
+ ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
+
+ var i, item, children;
+
+ if (this.isHTMLDocument){
+ getById: if (id){
+ item = this.document.getElementById(id);
+ if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
+ // all[id] returns all the elements with that name or id inside node
+ // if theres just one it will return the element, else it will be a collection
+ children = node.all[id];
+ if (!children) return;
+ if (!children[0]) children = [children];
+ for (i = 0; item = children[i++];){
+ var idNode = item.getAttributeNode('id');
+ if (idNode && idNode.nodeValue == id){
+ this.push(item, tag, null, classes, attributes, pseudos);
+ break;
+ }
+ }
+ return;
+ }
+ if (!item){
+ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label
+ if (this.contains(this.root, node)) return;
+ else break getById;
+ } else if (this.document !== node && !this.contains(node, item)) return;
+ this.push(item, tag, null, classes, attributes, pseudos);
+ return;
+ }
+ getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
+ children = node.getElementsByClassName(classList.join(' '));
+ if (!(children && children.length)) break getByClass;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
+ return;
+ }
+ }
+ getByTag: {
+ children = node.getElementsByTagName(tag);
+ if (!(children && children.length)) break getByTag;
+ if (!this.brokenStarGEBTN) tag = null;
+ for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '>': function(node, tag, id, classes, attributes, pseudos){ // direct children
+ if ((node = node.firstChild)) do {
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ } while ((node = node.nextSibling));
+ },
+
+ '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
+ while ((node = node.nextSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '^': function(node, tag, id, classes, attributes, pseudos){ // first child
+ node = node.firstChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
+ while ((node = node.nextSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
+ this['combinator:+'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
+ this['combinator:~'](node, tag, id, classes, attributes, pseudos);
+ this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
+ while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
+ node = node.parentNode;
+ if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
+ },
+
+ '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
+ while ((node = node.previousSibling)) if (node.nodeType == 1){
+ this.push(node, tag, id, classes, attributes, pseudos);
+ break;
+ }
+ },
+
+ '!^': function(node, tag, id, classes, attributes, pseudos){ // last child
+ node = node.lastChild;
+ if (node){
+ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
+ else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
+ }
+ },
+
+ '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
+ while ((node = node.previousSibling)){
+ if (node.nodeType != 1) continue;
+ var uid = this.getUID(node);
+ if (this.bitUniques[uid]) break;
+ this.bitUniques[uid] = true;
+ this.push(node, tag, id, classes, attributes, pseudos);
+ }
+ }
+
+};
+
+for (var c in combinators) local['combinator:' + c] = combinators[c];
+
+var pseudos = {
+
+ /*<pseudo-selectors>*/
+
+ 'empty': function(node){
+ var child = node.firstChild;
+ return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
+ },
+
+ 'not': function(node, expression){
+ return !this.matchNode(node, expression);
+ },
+
+ 'contains': function(node, text){
+ return (node.innerText || node.textContent || '').indexOf(text) > -1;
+ },
+
+ 'first-child': function(node){
+ while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'last-child': function(node){
+ while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
+ return true;
+ },
+
+ 'only-child': function(node){
+ var prev = node;
+ while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
+ return true;
+ },
+
+ /*<nth-pseudo-selectors>*/
+
+ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
+
+ 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
+
+ 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
+
+ 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
+
+ 'index': function(node, index){
+ return this['pseudo:nth-child'](node, '' + (index + 1));
+ },
+
+ 'even': function(node){
+ return this['pseudo:nth-child'](node, '2n');
+ },
+
+ 'odd': function(node){
+ return this['pseudo:nth-child'](node, '2n+1');
+ },
+
+ /*</nth-pseudo-selectors>*/
+
+ /*<of-type-pseudo-selectors>*/
+
+ 'first-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'last-of-type': function(node){
+ var nodeName = node.nodeName;
+ while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
+ return true;
+ },
+
+ 'only-of-type': function(node){
+ var prev = node, nodeName = node.nodeName;
+ while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
+ var next = node;
+ while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
+ return true;
+ },
+
+ /*</of-type-pseudo-selectors>*/
+
+ // custom pseudos
+
+ 'enabled': function(node){
+ return !node.disabled;
+ },
+
+ 'disabled': function(node){
+ return node.disabled;
+ },
+
+ 'checked': function(node){
+ return node.checked || node.selected;
+ },
+
+ 'focus': function(node){
+ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
+ },
+
+ 'root': function(node){
+ return (node === this.root);
+ },
+
+ 'selected': function(node){
+ return node.selected;
+ }
+
+ /*</pseudo-selectors>*/
+};
+
+for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
+
+// attributes methods
+
+var attributeGetters = local.attributeGetters = {
+
+ 'for': function(){
+ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
+ },
+
+ 'href': function(){
+ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
+ },
+
+ 'style': function(){
+ return (this.style) ? this.style.cssText : this.getAttribute('style');
+ },
+
+ 'tabindex': function(){
+ var attributeNode = this.getAttributeNode('tabindex');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ },
+
+ 'type': function(){
+ return this.getAttribute('type');
+ },
+
+ 'maxlength': function(){
+ var attributeNode = this.getAttributeNode('maxLength');
+ return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
+ }
+
+};
+
+attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength;
+
+// Slick
+
+var Slick = local.Slick = (this.Slick || {});
+
+Slick.version = '1.1.7';
+
+// Slick finder
+
+Slick.search = function(context, expression, append){
+ return local.search(context, expression, append);
+};
+
+Slick.find = function(context, expression){
+ return local.search(context, expression, null, true);
+};
+
+// Slick containment checker
+
+Slick.contains = function(container, node){
+ local.setDocument(container);
+ return local.contains(container, node);
+};
+
+// Slick attribute getter
+
+Slick.getAttribute = function(node, name){
+ local.setDocument(node);
+ return local.getAttribute(node, name);
+};
+
+Slick.hasAttribute = function(node, name){
+ local.setDocument(node);
+ return local.hasAttribute(node, name);
+};
+
+// Slick matcher
+
+Slick.match = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ local.setDocument(node);
+ return local.matchNode(node, selector);
+};
+
+// Slick attribute accessor
+
+Slick.defineAttributeGetter = function(name, fn){
+ local.attributeGetters[name] = fn;
+ return this;
+};
+
+Slick.lookupAttributeGetter = function(name){
+ return local.attributeGetters[name];
+};
+
+// Slick pseudo accessor
+
+Slick.definePseudo = function(name, fn){
+ local['pseudo:' + name] = function(node, argument){
+ return fn.call(node, argument);
+ };
+ return this;
+};
+
+Slick.lookupPseudo = function(name){
+ var pseudo = local['pseudo:' + name];
+ if (pseudo) return function(argument){
+ return pseudo.call(this, argument);
+ };
+ return null;
+};
+
+// Slick overrides accessor
+
+Slick.override = function(regexp, fn){
+ local.override(regexp, fn);
+ return this;
+};
+
+Slick.isXML = local.isXML;
+
+Slick.uidOf = function(node){
+ return local.getUIDHTML(node);
+};
+
+if (!this.Slick) this.Slick = Slick;
+
+}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
+
+
+/*
+---
+
+name: Element
+
+description: 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.
+
+requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder]
+
+provides: [Element, Elements, $, $$, Iframe, Selectors]
+
+...
+*/
+
+var Element = function(tag, props){
+ var konstructor = Element.Constructors[tag];
+ if (konstructor) return konstructor(props);
+ if (typeof tag != 'string') return document.id(tag).set(props);
+
+ if (!props) props = {};
+
+ if (!(/^[\w-]+$/).test(tag)){
+ var parsed = Slick.parse(tag).expressions[0][0];
+ tag = (parsed.tag == '*') ? 'div' : parsed.tag;
+ if (parsed.id && props.id == null) props.id = parsed.id;
+
+ var attributes = parsed.attributes;
+ if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
+ attr = attributes[i];
+ if (props[attr.key] != null) continue;
+
+ if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
+ else if (!attr.value && !attr.operator) props[attr.key] = true;
+ }
+
+ if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
+ }
+
+ return document.newElement(tag, props);
+};
+
+
+if (Browser.Element){
+ Element.prototype = Browser.Element.prototype;
+ // IE8 and IE9 require the wrapping.
+ Element.prototype._fireEvent = (function(fireEvent){
+ return function(type, event){
+ return fireEvent.call(this, type, event);
+ };
+ })(Element.prototype.fireEvent);
+}
+
+new Type('Element', Element).mirror(function(name){
+ if (Array.prototype[name]) return;
+
+ var obj = {};
+ obj[name] = function(){
+ var results = [], args = arguments, elements = true;
+ for (var i = 0, l = this.length; i < l; i++){
+ var element = this[i], result = results[i] = element[name].apply(element, args);
+ elements = (elements && typeOf(result) == 'element');
+ }
+ return (elements) ? new Elements(results) : results;
+ };
+
+ Elements.implement(obj);
+});
+
+if (!Browser.Element){
+ Element.parent = Object;
+
+ Element.Prototype = {
+ '$constructor': Element,
+ '$family': Function.from('element').hide()
+ };
+
+ Element.mirror(function(name, method){
+ Element.Prototype[name] = method;
+ });
+}
+
+Element.Constructors = {};
+
+
+
+var IFrame = new Type('IFrame', function(){
+ var params = Array.link(arguments, {
+ properties: Type.isObject,
+ iframe: function(obj){
+ return (obj != null);
+ }
+ });
+
+ var props = params.properties || {}, iframe;
+ if (params.iframe) iframe = document.id(params.iframe);
+ var onload = props.onload || function(){};
+ delete props.onload;
+ props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
+ iframe = new Element(iframe || 'iframe', props);
+
+ var onLoad = function(){
+ onload.call(iframe.contentWindow);
+ };
+
+ if (window.frames[props.id]) onLoad();
+ else iframe.addListener('load', onLoad);
+ return iframe;
+});
+
+var Elements = this.Elements = function(nodes){
+ if (nodes && nodes.length){
+ var uniques = {}, node;
+ for (var i = 0; node = nodes[i++];){
+ var uid = Slick.uidOf(node);
+ if (!uniques[uid]){
+ uniques[uid] = true;
+ this.push(node);
+ }
+ }
+ }
+};
+
+Elements.prototype = {length: 0};
+Elements.parent = Array;
+
+new Type('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));
+ }.protect(),
+
+ push: function(){
+ var length = this.length;
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) this[length++] = item;
+ }
+ return (this.length = length);
+ }.protect(),
+
+ unshift: function(){
+ var items = [];
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = document.id(arguments[i]);
+ if (item) items.push(item);
+ }
+ return Array.prototype.unshift.apply(this, items);
+ }.protect(),
+
+ concat: function(){
+ var newElements = new Elements(this);
+ for (var i = 0, l = arguments.length; i < l; i++){
+ var item = arguments[i];
+ if (Type.isEnumerable(item)) newElements.append(item);
+ else newElements.push(item);
+ }
+ return newElements;
+ }.protect(),
+
+ append: function(collection){
+ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
+ return this;
+ }.protect(),
+
+ empty: function(){
+ while (this.length) delete this[--this.length];
+ return this;
+ }.protect()
+
+});
+
+
+
+(function(){
+
+// FF, IE
+var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
+
+splice.call(object, 1, 1);
+if (object[1] == 1) Elements.implement('splice', function(){
+ var length = this.length;
+ var result = splice.apply(this, arguments);
+ while (length >= this.length) delete this[length--];
+ return result;
+}.protect());
+
+Array.forEachMethod(function(method, name){
+ Elements.implement(name, method);
+});
+
+Array.mirror(Elements);
+
+/*<ltIE8>*/
+var createElementAcceptsHTML;
+try {
+ createElementAcceptsHTML = (document.createElement('<input name=x>').name == 'x');
+} catch (e){}
+
+var escapeQuotes = function(html){
+ return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+};
+/*</ltIE8>*/
+
+Document.implement({
+
+ newElement: function(tag, props){
+ if (props && props.checked != null) props.defaultChecked = props.checked;
+ /*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
+ if (createElementAcceptsHTML && props){
+ tag = '<' + tag;
+ if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
+ if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
+ tag += '>';
+ delete props.name;
+ delete props.type;
+ }
+ /*</ltIE8>*/
+ return this.id(this.createElement(tag)).set(props);
+ }
+
+});
+
+})();
+
+(function(){
+
+Slick.uidOf(window);
+Slick.uidOf(document);
+
+Document.implement({
+
+ newTextNode: function(text){
+ return this.createTextNode(text);
+ },
+
+ getDocument: function(){
+ return this;
+ },
+
+ getWindow: function(){
+ return this.window;
+ },
+
+ id: (function(){
+
+ var types = {
+
+ string: function(id, nocash, doc){
+ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
+ return (id) ? types.element(id, nocash) : null;
+ },
+
+ element: function(el, nocash){
+ Slick.uidOf(el);
+ if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
+ var fireEvent = el.fireEvent;
+ // wrapping needed in IE7, or else crash
+ el._fireEvent = function(type, event){
+ return fireEvent(type, event);
+ };
+ Object.append(el, Element.Prototype);
+ }
+ return el;
+ },
+
+ object: function(obj, nocash, doc){
+ if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+ return null;
+ }
+
+ };
+
+ types.textnode = types.whitespace = types.window = types.document = function(zero){
+ return zero;
+ };
+
+ return function(el, nocash, doc){
+ if (el && el.$family && el.uniqueNumber) return el;
+ var type = typeOf(el);
+ return (types[type]) ? types[type](el, nocash, doc || document) : null;
+ };
+
+ })()
+
+});
+
+if (window.$ == null) Window.implement('$', function(el, nc){
+ return document.id(el, nc, this.document);
+});
+
+Window.implement({
+
+ getDocument: function(){
+ return this.document;
+ },
+
+ getWindow: function(){
+ return this;
+ }
+
+});
+
+[Document, Element].invoke('implement', {
+
+ getElements: function(expression){
+ return Slick.search(this, expression, new Elements);
+ },
+
+ getElement: function(expression){
+ return document.id(Slick.find(this, expression));
+ }
+
+});
+
+var contains = {contains: function(element){
+ return Slick.contains(this, element);
+}};
+
+if (!document.contains) Document.implement(contains);
+if (!document.createElement('div').contains) Element.implement(contains);
+
+
+
+// tree walking
+
+var injectCombinator = function(expression, combinator){
+ if (!expression) return combinator;
+
+ expression = Object.clone(Slick.parse(expression));
+
+ var expressions = expression.expressions;
+ for (var i = expressions.length; i--;)
+ expressions[i][0].combinator = combinator;
+
+ return expression;
+};
+
+Object.forEach({
+ getNext: '~',
+ getPrevious: '!~',
+ getParent: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElement(injectCombinator(expression, combinator));
+ });
+});
+
+Object.forEach({
+ getAllNext: '~',
+ getAllPrevious: '!~',
+ getSiblings: '~~',
+ getChildren: '>',
+ getParents: '!'
+}, function(combinator, method){
+ Element.implement(method, function(expression){
+ return this.getElements(injectCombinator(expression, combinator));
+ });
+});
+
+Element.implement({
+
+ getFirst: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
+ },
+
+ getLast: function(expression){
+ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
+ },
+
+ getWindow: function(){
+ return this.ownerDocument.window;
+ },
+
+ getDocument: function(){
+ return this.ownerDocument;
+ },
+
+ getElementById: function(id){
+ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
+ },
+
+ match: function(expression){
+ return !expression || Slick.match(this, expression);
+ }
+
+});
+
+
+
+if (window.$$ == null) Window.implement('$$', function(selector){
+ if (arguments.length == 1){
+ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
+ else if (Type.isEnumerable(selector)) return new Elements(selector);
+ }
+ return new Elements(arguments);
+});
+
+// Inserters
+
+var inserters = {
+
+ before: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element);
+ },
+
+ after: function(context, element){
+ var parent = element.parentNode;
+ if (parent) parent.insertBefore(context, element.nextSibling);
+ },
+
+ bottom: function(context, element){
+ element.appendChild(context);
+ },
+
+ top: function(context, element){
+ element.insertBefore(context, element.firstChild);
+ }
+
+};
+
+inserters.inside = inserters.bottom;
+
+
+
+// getProperty / setProperty
+
+var propertyGetters = {}, propertySetters = {};
+
+// properties
+
+var properties = {};
+Array.forEach([
+ 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan',
+ 'frameBorder', 'rowSpan', 'tabIndex', 'useMap'
+], function(property){
+ properties[property.toLowerCase()] = property;
+});
+
+properties.html = 'innerHTML';
+properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent';
+
+Object.forEach(properties, function(real, key){
+ propertySetters[key] = function(node, value){
+ node[real] = value;
+ };
+ propertyGetters[key] = function(node){
+ return node[real];
+ };
+});
+
+// Booleans
+
+var bools = [
+ 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked',
+ 'disabled', 'readOnly', 'multiple', 'selected', 'noresize',
+ 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay',
+ 'loop'
+];
+
+var booleans = {};
+Array.forEach(bools, function(bool){
+ var lower = bool.toLowerCase();
+ booleans[lower] = bool;
+ propertySetters[lower] = function(node, value){
+ node[bool] = !!value;
+ };
+ propertyGetters[lower] = function(node){
+ return !!node[bool];
+ };
+});
+
+// Special cases
+
+Object.append(propertySetters, {
+
+ 'class': function(node, value){
+ ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value);
+ },
+
+ 'for': function(node, value){
+ ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value);
+ },
+
+ 'style': function(node, value){
+ (node.style) ? node.style.cssText = value : node.setAttribute('style', value);
+ },
+
+ 'value': function(node, value){
+ node.value = (value != null) ? value : '';
+ }
+
+});
+
+propertyGetters['class'] = function(node){
+ return ('className' in node) ? node.className || null : node.getAttribute('class');
+};
+
+/* <webkit> */
+var el = document.createElement('button');
+// IE sets type as readonly and throws
+try { el.type = 'button'; } catch(e){}
+if (el.type != 'button') propertySetters.type = function(node, value){
+ node.setAttribute('type', value);
+};
+el = null;
+/* </webkit> */
+
+/*<IE>*/
+var input = document.createElement('input');
+input.value = 't';
+input.type = 'submit';
+if (input.value != 't') propertySetters.type = function(node, type){
+ var value = node.value;
+ node.type = type;
+ node.value = value;
+};
+input = null;
+/*</IE>*/
+
+/* getProperty, setProperty */
+
+/* <ltIE9> */
+var pollutesGetAttribute = (function(div){
+ div.random = 'attribute';
+ return (div.getAttribute('random') == 'attribute');
+})(document.createElement('div'));
+
+/* <ltIE9> */
+
+Element.implement({
+
+ setProperty: function(name, value){
+ var setter = propertySetters[name.toLowerCase()];
+ if (setter){
+ setter(this, value);
+ } else {
+ /* <ltIE9> */
+ if (pollutesGetAttribute) var attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ /* </ltIE9> */
+
+ if (value == null){
+ this.removeAttribute(name);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) delete attributeWhiteList[name];
+ /* </ltIE9> */
+ } else {
+ this.setAttribute(name, '' + value);
+ /* <ltIE9> */
+ if (pollutesGetAttribute) attributeWhiteList[name] = true;
+ /* </ltIE9> */
+ }
+ }
+ return this;
+ },
+
+ setProperties: function(attributes){
+ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+ return this;
+ },
+
+ getProperty: function(name){
+ var getter = propertyGetters[name.toLowerCase()];
+ if (getter) return getter(this);
+ /* <ltIE9> */
+ if (pollutesGetAttribute){
+ var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {});
+ if (!attr) return null;
+ if (attr.expando && !attributeWhiteList[name]){
+ var outer = this.outerHTML;
+ // segment by the opening tag and find mention of attribute name
+ if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null;
+ attributeWhiteList[name] = true;
+ }
+ }
+ /* </ltIE9> */
+ var result = Slick.getAttribute(this, name);
+ return (!result && !Slick.hasAttribute(this, name)) ? null : result;
+ },
+
+ getProperties: function(){
+ var args = Array.from(arguments);
+ return args.map(this.getProperty, this).associate(args);
+ },
+
+ removeProperty: function(name){
+ return this.setProperty(name, null);
+ },
+
+ removeProperties: function(){
+ Array.each(arguments, this.removeProperty, this);
+ return this;
+ },
+
+ set: function(prop, value){
+ var property = Element.Properties[prop];
+ (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
+ }.overloadSetter(),
+
+ get: function(prop){
+ var property = Element.Properties[prop];
+ return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
+ }.overloadGetter(),
+
+ erase: function(prop){
+ var property = Element.Properties[prop];
+ (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+ return this;
+ },
+
+ hasClass: function(className){
+ return this.className.clean().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');
+ return this;
+ },
+
+ toggleClass: function(className, force){
+ if (force == null) force = !this.hasClass(className);
+ return (force) ? this.addClass(className) : this.removeClass(className);
+ },
+
+ adopt: function(){
+ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
+ if (length > 1) parent = fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < length; i++){
+ var element = document.id(elements[i], true);
+ if (element) parent.appendChild(element);
+ }
+
+ if (fragment) this.appendChild(fragment);
+
+ return this;
+ },
+
+ appendText: function(text, where){
+ return this.grab(this.getDocument().newTextNode(text), where);
+ },
+
+ grab: function(el, where){
+ inserters[where || 'bottom'](document.id(el, true), this);
+ return this;
+ },
+
+ inject: function(el, where){
+ inserters[where || 'bottom'](this, document.id(el, true));
+ return this;
+ },
+
+ replaces: function(el){
+ el = document.id(el, true);
+ el.parentNode.replaceChild(this, el);
+ return this;
+ },
+
+ wraps: function(el, where){
+ el = document.id(el, true);
+ return this.replaces(el).grab(el, where);
+ },
+
+ getSelected: function(){
+ this.selectedIndex; // Safari 3.2.1
+ return new Elements(Array.from(this.options).filter(function(option){
+ return option.selected;
+ }));
+ },
+
+ toQueryString: function(){
+ var queryString = [];
+ this.getElements('input, select, textarea').each(function(el){
+ var type = el.type;
+ if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
+
+ var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
+ // IE
+ return document.id(opt).get('value');
+ }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
+
+ Array.from(value).each(function(val){
+ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
+ });
+ });
+ return queryString.join('&');
+ }
+
+});
+
+var collected = {}, storage = {};
+
+var get = function(uid){
+ return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item){
+ var uid = item.uniqueNumber;
+ if (item.removeEvents) item.removeEvents();
+ if (item.clearAttributes) item.clearAttributes();
+ if (uid != null){
+ delete collected[uid];
+ delete storage[uid];
+ }
+ return item;
+};
+
+var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
+
+Element.implement({
+
+ destroy: function(){
+ var children = clean(this).getElementsByTagName('*');
+ Array.each(children, clean);
+ Element.dispose(this);
+ return null;
+ },
+
+ empty: function(){
+ Array.from(this.childNodes).each(Element.dispose);
+ return this;
+ },
+
+ dispose: function(){
+ return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+ },
+
+ clone: function(contents, keepid){
+ contents = contents !== false;
+ var clone = this.cloneNode(contents), ce = [clone], te = [this], i;
+
+ if (contents){
+ ce.append(Array.from(clone.getElementsByTagName('*')));
+ te.append(Array.from(this.getElementsByTagName('*')));
+ }
+
+ for (i = ce.length; i--;){
+ var node = ce[i], element = te[i];
+ if (!keepid) node.removeAttribute('id');
+ /*<ltIE9>*/
+ if (node.clearAttributes){
+ node.clearAttributes();
+ node.mergeAttributes(element);
+ node.removeAttribute('uniqueNumber');
+ if (node.options){
+ var no = node.options, eo = element.options;
+ for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+ }
+ }
+ /*</ltIE9>*/
+ var prop = formProps[element.tagName.toLowerCase()];
+ if (prop && element[prop]) node[prop] = element[prop];
+ }
+
+ /*<ltIE9>*/
+ if (Browser.ie){
+ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
+ for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
+ }
+ /*</ltIE9>*/
+ return document.id(clone);
+ }
+
+});
+
+[Element, Window, Document].invoke('implement', {
+
+ addListener: function(type, fn){
+ if (type == 'unload'){
+ var old = fn, self = this;
+ fn = function(){
+ self.removeListener('unload', fn);
+ old();
+ };
+ } else {
+ collected[Slick.uidOf(this)] = this;
+ }
+ if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
+ else this.attachEvent('on' + type, fn);
+ return this;
+ },
+
+ removeListener: function(type, fn){
+ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
+ else this.detachEvent('on' + type, fn);
+ return this;
+ },
+
+ retrieve: function(property, dflt){
+ var storage = get(Slick.uidOf(this)), prop = storage[property];
+ if (dflt != null && prop == null) prop = storage[property] = dflt;
+ return prop != null ? prop : null;
+ },
+
+ store: function(property, value){
+ var storage = get(Slick.uidOf(this));
+ storage[property] = value;
+ return this;
+ },
+
+ eliminate: function(property){
+ var storage = get(Slick.uidOf(this));
+ delete storage[property];
+ return this;
+ }
+
+});
+
+/*<ltIE9>*/
+if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){
+ Object.each(collected, clean);
+ if (window.CollectGarbage) CollectGarbage();
+});
+/*</ltIE9>*/
+
+Element.Properties = {};
+
+
+
+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.html = {
+
+ set: function(html){
+ if (html == null) html = '';
+ else if (typeOf(html) == 'array') html = html.join('');
+ this.innerHTML = html;
+ },
+
+ erase: function(){
+ this.innerHTML = '';
+ }
+
+};
+
+/*<ltIE9>*/
+// technique by jdbarlett - http://jdbartlett.com/innershiv/
+var div = document.createElement('div');
+div.innerHTML = '<nav></nav>';
+var supportsHTML5Elements = (div.childNodes.length == 1);
+if (!supportsHTML5Elements){
+ var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '),
+ fragment = document.createDocumentFragment(), l = tags.length;
+ while (l--) fragment.createElement(tags[l]);
+}
+div = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+var supportsTableInnerHTML = Function.attempt(function(){
+ var table = document.createElement('table');
+ table.innerHTML = '<tr><td></td></tr>';
+ return true;
+});
+
+/*<ltFF4>*/
+var tr = document.createElement('tr'), html = '<td></td>';
+tr.innerHTML = html;
+var supportsTRInnerHTML = (tr.innerHTML == html);
+tr = null;
+/*</ltFF4>*/
+
+if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){
+
+ Element.Properties.html.set = (function(set){
+
+ var translations = {
+ table: [1, '<table>', '</table>'],
+ select: [1, '<select>', '</select>'],
+ tbody: [2, '<table><tbody>', '</tbody></table>'],
+ tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+ };
+
+ translations.thead = translations.tfoot = translations.tbody;
+
+ return function(html){
+ var wrap = translations[this.get('tag')];
+ if (!wrap && !supportsHTML5Elements) wrap = [0, '', ''];
+ if (!wrap) return set.call(this, html);
+
+ var level = wrap[0], wrapper = document.createElement('div'), target = wrapper;
+ if (!supportsHTML5Elements) fragment.appendChild(wrapper);
+ wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join('');
+ while (level--) target = target.firstChild;
+ this.empty().adopt(target.childNodes);
+ if (!supportsHTML5Elements) fragment.removeChild(wrapper);
+ wrapper = null;
+ };
+
+ })(Element.Properties.html.set);
+}
+/*</IE>*/
+
+/*<ltIE9>*/
+var testForm = document.createElement('form');
+testForm.innerHTML = '<select><option>s</option></select>';
+
+if (testForm.firstChild.value != 's') Element.Properties.value = {
+
+ set: function(value){
+ var tag = this.get('tag');
+ if (tag != 'select') return this.setProperty('value', value);
+ var options = this.getElements('option');
+ for (var i = 0; i < options.length; i++){
+ var option = options[i],
+ attr = option.getAttributeNode('value'),
+ optionValue = (attr && attr.specified) ? option.value : option.get('text');
+ if (optionValue == value) return option.selected = true;
+ }
+ },
+
+ get: function(){
+ var option = this, tag = option.get('tag');
+
+ if (tag != 'select' && tag != 'option') return this.getProperty('value');
+
+ if (tag == 'select' && !(option = option.getSelected()[0])) return '';
+
+ var attr = option.getAttributeNode('value');
+ return (attr && attr.specified) ? option.value : option.get('text');
+ }
+
+};
+testForm = null;
+/*</ltIE9>*/
+
+/*<IE>*/
+if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = {
+ set: function(id){
+ this.id = this.getAttributeNode('id').value = id;
+ },
+ get: function(){
+ return this.id || null;
+ },
+ erase: function(){
+ this.id = this.getAttributeNode('id').value = '';
+ }
+};
+/*</IE>*/
+
+})();
+
+
+/*
+---
+
+name: Element.Style
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires: Element
+
+provides: Element.Style
+
+...
+*/
+
+(function(){
+
+var html = document.html;
+
+//<ltIE9>
+// Check for oldIE, which does not remove styles when they're set to null
+var el = document.createElement('div');
+el.style.color = 'red';
+el.style.color = null;
+var doesNotRemoveStyles = el.style.color == 'red';
+el = null;
+//</ltIE9>
+
+Element.Properties.styles = {set: function(styles){
+ this.setStyles(styles);
+}};
+
+var hasOpacity = (html.style.opacity != null),
+ hasFilter = (html.style.filter != null),
+ reAlpha = /alpha\(opacity=([\d.]+)\)/i;
+
+var setVisibility = function(element, opacity){
+ element.store('$opacity', opacity);
+ element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden';
+};
+
+var setOpacity = (hasOpacity ? function(element, opacity){
+ element.style.opacity = opacity;
+} : (hasFilter ? function(element, opacity){
+ var style = element.style;
+ if (!element.currentStyle || !element.currentStyle.hasLayout) style.zoom = 1;
+ if (opacity == null || opacity == 1) opacity = '';
+ else opacity = 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')';
+ var filter = style.filter || element.getComputedStyle('filter') || '';
+ style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity;
+ if (!style.filter) style.removeAttribute('filter');
+} : setVisibility));
+
+var getOpacity = (hasOpacity ? function(element){
+ var opacity = element.style.opacity || element.getComputedStyle('opacity');
+ return (opacity == '') ? 1 : opacity.toFloat();
+} : (hasFilter ? function(element){
+ var filter = (element.style.filter || element.getComputedStyle('filter')),
+ opacity;
+ if (filter) opacity = filter.match(reAlpha);
+ return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
+} : function(element){
+ var opacity = element.retrieve('$opacity');
+ if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1);
+ return opacity;
+}));
+
+var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
+
+Element.implement({
+
+ getComputedStyle: function(property){
+ if (this.currentStyle) return this.currentStyle[property.camelCase()];
+ var defaultView = Element.getDocument(this).defaultView,
+ computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
+ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
+ },
+
+ setStyle: function(property, value){
+ if (property == 'opacity'){
+ if (value != null) value = parseFloat(value);
+ setOpacity(this, value);
+ return this;
+ }
+ property = (property == 'float' ? floatName : property).camelCase();
+ if (typeOf(value) != 'string'){
+ var map = (Element.Styles[property] || '@').split(' ');
+ value = Array.from(value).map(function(val, i){
+ if (!map[i]) return '';
+ return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+ }).join(' ');
+ } else if (value == String(Number(value))){
+ value = Math.round(value);
+ }
+ this.style[property] = value;
+ //<ltIE9>
+ if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){
+ this.style.removeAttribute(property);
+ }
+ //</ltIE9>
+ return this;
+ },
+
+ getStyle: function(property){
+ if (property == 'opacity') return getOpacity(this);
+ property = (property == 'float' ? floatName : property).camelCase();
+ var result = this.style[property];
+ if (!result || property == 'zIndex'){
+ 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.opera || Browser.ie){
+ if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){
+ 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.ie && (/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){
+ return '0px';
+ }
+ }
+ return result;
+ },
+
+ setStyles: function(styles){
+ for (var style in styles) this.setStyle(style, styles[style]);
+ return this;
+ },
+
+ getStyles: function(){
+ var result = {};
+ Array.flatten(arguments).each(function(key){
+ result[key] = this.getStyle(key);
+ }, this);
+ return result;
+ }
+
+});
+
+Element.Styles = {
+ 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(@, @, @)';
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Event
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary.
+
+license: MIT-style license.
+
+requires: [Element, Event]
+
+provides: Element.Event
+
+...
+*/
+
+(function(){
+
+Element.Properties.events = {set: function(events){
+ this.addEvents(events);
+}};
+
+[Element, Window, Document].invoke('implement', {
+
+ addEvent: function(type, fn){
+ var events = this.retrieve('events', {});
+ if (!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[type],
+ condition = fn,
+ self = this;
+ if (custom){
+ if (custom.onAdd) custom.onAdd.call(this, fn, type);
+ if (custom.condition){
+ condition = function(event){
+ if (custom.condition.call(this, event, type)) return fn.call(this, event);
+ return true;
+ };
+ }
+ if (custom.base) realType = Function.from(custom.base).call(this, type);
+ }
+ var defn = function(){
+ return fn.call(self);
+ };
+ var nativeEvent = Element.NativeEvents[realType];
+ if (nativeEvent){
+ if (nativeEvent == 2){
+ defn = function(event){
+ event = new DOMEvent(event, self.getWindow());
+ if (condition.call(self, event) === false) event.stop();
+ };
+ }
+ this.addListener(realType, defn, arguments[2]);
+ }
+ events[type].values.push(defn);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ var list = events[type];
+ var index = list.keys.indexOf(fn);
+ if (index == -1) return this;
+ var value = list.values[index];
+ delete list.keys[index];
+ delete list.values[index];
+ var custom = Element.Events[type];
+ if (custom){
+ if (custom.onRemove) custom.onRemove.call(this, fn, type);
+ if (custom.base) type = Function.from(custom.base).call(this, type);
+ }
+ return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
+ },
+
+ addEvents: function(events){
+ for (var event in events) this.addEvent(event, events[event]);
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ var attached = this.retrieve('events');
+ if (!attached) return this;
+ if (!events){
+ for (type in attached) this.removeEvents(type);
+ this.eliminate('events');
+ } else if (attached[events]){
+ attached[events].keys.each(function(fn){
+ this.removeEvent(events, fn);
+ }, this);
+ delete attached[events];
+ }
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ var events = this.retrieve('events');
+ if (!events || !events[type]) return this;
+ args = Array.from(args);
+
+ events[type].keys.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ cloneEvents: function(from, type){
+ from = document.id(from);
+ var events = from.retrieve('events');
+ if (!events) return this;
+ if (!type){
+ for (var eventType in events) this.cloneEvents(from, eventType);
+ } else if (events[type]){
+ events[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
+ orientationchange: 2, // mobile
+ touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
+ gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
+ focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements
+ load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+ error: 1, abort: 1, scroll: 1 //misc
+};
+
+Element.Events = {mousewheel: {
+ base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel'
+}};
+
+if ('onmouseenter' in document.documentElement){
+ Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2;
+} else {
+ var check = function(event){
+ var related = event.relatedTarget;
+ if (related == null) return true;
+ if (!related) return false;
+ return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
+ };
+
+ Element.Events.mouseenter = {
+ base: 'mouseover',
+ condition: check
+ };
+
+ Element.Events.mouseleave = {
+ base: 'mouseout',
+ condition: check
+ };
+}
+
+/*<ltIE9>*/
+if (!window.addEventListener){
+ Element.NativeEvents.propertychange = 2;
+ Element.Events.change = {
+ base: function(){
+ var type = this.type;
+ return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change'
+ },
+ condition: function(event){
+ return this.type != 'radio' || (event.event.propertyName == 'checked' && this.checked);
+ }
+ }
+}
+/*</ltIE9>*/
+
+
+
+})();
+
+
+/*
+---
+
+name: Element.Delegation
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+license: MIT-style license.
+
+requires: [Element.Event]
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(){
+
+var eventListenerSupport = !!window.addEventListener;
+
+Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2;
+
+var bubbleUp = function(self, match, fn, event, target){
+ while (target && target != self){
+ if (match(target, event)) return fn.call(target, event, target);
+ target = document.id(target.parentNode);
+ }
+};
+
+var map = {
+ mouseenter: {
+ base: 'mouseover'
+ },
+ mouseleave: {
+ base: 'mouseout'
+ },
+ focus: {
+ base: 'focus' + (eventListenerSupport ? '' : 'in'),
+ capture: true
+ },
+ blur: {
+ base: eventListenerSupport ? 'blur' : 'focusout',
+ capture: true
+ }
+};
+
+/*<ltIE9>*/
+var _key = '$delegation:';
+var formObserver = function(type){
+
+ return {
+
+ base: 'focusin',
+
+ remove: function(self, uid){
+ var list = self.retrieve(_key + type + 'listeners', {})[uid];
+ if (list && list.forms) for (var i = list.forms.length; i--;){
+ list.forms[i].removeEvent(type, list.fns[i]);
+ }
+ },
+
+ listen: function(self, match, fn, event, target, uid){
+ var form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
+ if (!form) return;
+
+ var listeners = self.retrieve(_key + type + 'listeners', {}),
+ listener = listeners[uid] || {forms: [], fns: []},
+ forms = listener.forms, fns = listener.fns;
+
+ if (forms.indexOf(form) != -1) return;
+ forms.push(form);
+
+ var _fn = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ form.addEvent(type, _fn);
+ fns.push(_fn);
+
+ listeners[uid] = listener;
+ self.store(_key + type + 'listeners', listeners);
+ }
+ };
+};
+
+var inputObserver = function(type){
+ return {
+ base: 'focusin',
+ listen: function(self, match, fn, event, target){
+ var events = {blur: function(){
+ this.removeEvents(events);
+ }};
+ events[type] = function(event){
+ bubbleUp(self, match, fn, event, target);
+ };
+ event.target.addEvents(events);
+ }
+ };
+};
+
+if (!eventListenerSupport) Object.append(map, {
+ submit: formObserver('submit'),
+ reset: formObserver('reset'),
+ change: inputObserver('change'),
+ select: inputObserver('select')
+});
+/*</ltIE9>*/
+
+var proto = Element.prototype,
+ addEvent = proto.addEvent,
+ removeEvent = proto.removeEvent;
+
+var relay = function(old, method){
+ return function(type, fn, useCapture){
+ if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture);
+ var parsed = Slick.parse(type).expressions[0][0];
+ if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture);
+ var newType = parsed.tag;
+ parsed.pseudos.slice(1).each(function(pseudo){
+ newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : '');
+ });
+ old.call(this, type, fn);
+ return method.call(this, newType, parsed.pseudos[0].value, fn);
+ };
+};
+
+var delegation = {
+
+ addEvent: function(type, match, fn){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (stored) for (var _uid in stored){
+ if (stored[_uid].fn == fn && stored[_uid].match == match) return this;
+ }
+
+ var _type = type, _match = match, _fn = fn, _map = map[type] || {};
+ type = _map.base || _type;
+
+ match = function(target){
+ return Slick.match(target, _match);
+ };
+
+ var elementEvent = Element.Events[_type];
+ if (elementEvent && elementEvent.condition){
+ var __match = match, condition = elementEvent.condition;
+ match = function(target, event){
+ return __match(target, event) && condition.call(target, event, type);
+ };
+ }
+
+ var self = this, uid = String.uniqueID();
+ var delegator = _map.listen ? function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) _map.listen(self, match, fn, event, target, uid);
+ } : function(event, target){
+ if (!target && event && event.target) target = event.target;
+ if (target) bubbleUp(self, match, fn, event, target);
+ };
+
+ if (!stored) stored = {};
+ stored[uid] = {
+ match: _match,
+ fn: _fn,
+ delegator: delegator
+ };
+ storage[_type] = stored;
+ return addEvent.call(this, type, delegator, _map.capture);
+ },
+
+ removeEvent: function(type, match, fn, _uid){
+ var storage = this.retrieve('$delegates', {}), stored = storage[type];
+ if (!stored) return this;
+
+ if (_uid){
+ var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {};
+ type = _map.base || _type;
+ if (_map.remove) _map.remove(this, _uid);
+ delete stored[_uid];
+ storage[_type] = stored;
+ return removeEvent.call(this, type, delegator);
+ }
+
+ var __uid, s;
+ if (fn) for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid);
+ } else for (__uid in stored){
+ s = stored[__uid];
+ if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid);
+ }
+ return this;
+ }
+
+};
+
+[Element, Window, Document].invoke('implement', {
+ addEvent: relay(addEvent, delegation.addEvent),
+ removeEvent: relay(removeEvent, delegation.removeEvent)
+});
+
+})();
+
+
+/*
+---
+
+name: Element.Dimensions
+
+description: 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).
+
+requires: [Element, Element.Style]
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+var element = document.createElement('div'),
+ child = document.createElement('div');
+element.style.height = '0';
+element.appendChild(child);
+var brokenOffsetParent = (child.offsetParent === element);
+element = child = null;
+
+var isOffset = function(el){
+ return styleString(el, 'position') != 'static' || isBody(el);
+};
+
+var isOffsetStatic = function(el){
+ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
+};
+
+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.parentNode, position = {x: 0, y: 0};
+ while (element && !isBody(element)){
+ position.x += element.scrollLeft;
+ position.y += element.scrollTop;
+ element = element.parentNode;
+ }
+ return position;
+ },
+
+ getOffsetParent: brokenOffsetParent ? function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
+ while ((element = element.parentNode)){
+ if (isOffsetCheck(element)) return element;
+ }
+ return null;
+ } : function(){
+ var element = this;
+ if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
+
+ try {
+ return element.offsetParent;
+ } catch(e) {}
+ return null;
+ },
+
+ getOffsets: function(){
+ if (this.getBoundingClientRect && !Browser.Platform.ios){
+ var bound = this.getBoundingClientRect(),
+ html = document.id(this.getDocument().documentElement),
+ htmlScroll = html.getScroll(),
+ elemScrolls = this.getScrolls(),
+ isFixed = (styleString(this, 'position') == 'fixed');
+
+ return {
+ x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+ y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+ };
+ }
+
+ 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.firefox){
+ 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.safari){
+ position.x += leftBorder(element);
+ position.y += topBorder(element);
+ }
+
+ element = element.offsetParent;
+ }
+ if (Browser.firefox && !borderBox(this)){
+ position.x -= leftBorder(this);
+ position.y -= topBorder(this);
+ }
+ return position;
+ },
+
+ getPosition: function(relative){
+ var offset = this.getOffsets(),
+ scroll = this.getScrolls();
+ var position = {
+ x: offset.x - scroll.x,
+ y: offset.y - scroll.y
+ };
+
+ if (relative && (relative = document.id(relative))){
+ var relativePosition = relative.getPosition();
+ return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
+ }
+ return position;
+ },
+
+ 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')
+ };
+ },
+
+ setPosition: function(obj){
+ return this.setStyles(this.computePosition(obj));
+ }
+
+});
+
+
+[Document, Window].invoke('implement', {
+
+ getSize: function(){
+ var doc = getCompatElement(this);
+ return {x: doc.clientWidth, y: doc.clientHeight};
+ },
+
+ getScroll: function(){
+ var win = this.getWindow(), doc = getCompatElement(this);
+ return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+ },
+
+ getScrollSize: function(){
+ var doc = getCompatElement(this),
+ min = this.getSize(),
+ body = this.getDocument().body;
+
+ return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.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
+Element.alias({position: 'setPosition'}); //compatability
+
+[Window, Document, Element].invoke('implement', {
+
+ 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;
+ }
+
+});
+
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+SeeAlso: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (typeof JSON == 'undefined') this.JSON = {};
+
+
+
+(function(){
+
+var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
+
+var escape = function(chr){
+ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
+};
+
+JSON.validate = function(string){
+ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+
+ return (/^[\],:{}\s]*$/).test(string);
+};
+
+JSON.encode = JSON.stringify ? function(obj){
+ return JSON.stringify(obj);
+} : function(obj){
+ if (obj && obj.toJSON) obj = obj.toJSON();
+
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ return '[' + obj.map(JSON.encode).clean() + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.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 '' + obj;
+ case 'null': return 'null';
+ }
+
+ return null;
+};
+
+JSON.decode = function(string, secure){
+ if (!string || typeOf(string) != 'string') return null;
+
+ if (secure || JSON.secure){
+ if (JSON.parse) return JSON.parse(string);
+ if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
+ }
+
+ return eval('(' + string + ')');
+};
+
+})();
+
+/*
+---
+
+name: DOMReady
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires: [Browser, Element, Element.Event]
+
+provides: [DOMReady, DomReady]
+
+...
+*/
+
+(function(window, document){
+
+var ready,
+ loaded,
+ checks = [],
+ shouldPoll,
+ timer,
+ testElement = document.createElement('div');
+
+var domready = function(){
+ clearTimeout(timer);
+ if (ready) return;
+ Browser.loaded = ready = true;
+ document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
+
+ document.fireEvent('domready');
+ window.fireEvent('domready');
+};
+
+var check = function(){
+ for (var i = checks.length; i--;) if (checks[i]()){
+ domready();
+ return true;
+ }
+ return false;
+};
+
+var poll = function(){
+ clearTimeout(timer);
+ if (!check()) timer = setTimeout(poll, 10);
+};
+
+document.addListener('DOMContentLoaded', domready);
+
+/*<ltIE8>*/
+// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
+// testElement.doScroll() throws when the DOM is not ready, only in the top window
+var doScrollWorks = function(){
+ try {
+ testElement.doScroll();
+ return true;
+ } catch (e){}
+ return false;
+};
+// If doScroll works already, it can't be used to determine domready
+// e.g. in an iframe
+if (testElement.doScroll && !doScrollWorks()){
+ checks.push(doScrollWorks);
+ shouldPoll = true;
+}
+/*</ltIE8>*/
+
+if (document.readyState) checks.push(function(){
+ var state = document.readyState;
+ return (state == 'loaded' || state == 'complete');
+});
+
+if ('onreadystatechange' in document) document.addListener('readystatechange', check);
+else shouldPoll = true;
+
+if (shouldPoll) poll();
+
+Element.Events.domready = {
+ onAdd: function(fn){
+ if (ready) fn.call(this);
+ }
+};
+
+// Make sure that domready fires before load
+Element.Events.load = {
+ base: 'load',
+ onAdd: function(fn){
+ if (loaded && this == window) fn.call(this);
+ },
+ condition: function(){
+ if (this == window){
+ domready();
+ delete Element.Events.load;
+ }
+ return true;
+ }
+};
+
+// This is based on the custom load event
+window.addEvent('load', function(){
+ loaded = true;
+});
+
+})(window, document);
+
+/*
+---
+
+script: Base64.js
+
+description: String methods for encoding and decoding Base64 data
+
+license: MIT-style license.
+
+authors: Ryan Florence (http://ryanflorence.com), webtoolkit.info
+
+requires:
+ - core:1.2.4: [String]
+
+provides: [String.toBase64, String.decodeBase64]
+
+...
+*/
+
+
+(function(){
+
+ // Base64 string methods taken from http://www.webtoolkit.info/
+ var Base64 = {
+
+ _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+
+ encode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+ input = Base64._utf8_encode(input);
+ while (i < input.length) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ };
+ output = output +
+ this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
+ this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+ };
+ return output;
+ },
+
+ decode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0;
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+ while (i < input.length) {
+ enc1 = this._keyStr.indexOf(input.charAt(i++));
+ enc2 = this._keyStr.indexOf(input.charAt(i++));
+ enc3 = this._keyStr.indexOf(input.charAt(i++));
+ enc4 = this._keyStr.indexOf(input.charAt(i++));
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+ output = output + String.fromCharCode(chr1);
+ if (enc3 != 64) {
+ output = output + String.fromCharCode(chr2);
+ };
+ if (enc4 != 64) {
+ output = output + String.fromCharCode(chr3);
+ };
+ };
+ output = Base64._utf8_decode(output);
+ return output;
+ },
+
+ // private method for UTF-8 encoding
+ _utf8_encode : function (string) {
+ string = string.replace(/\r\n/g,"\n");
+ var utftext = "";
+ for (var n = 0; n < string.length; n++) {
+ var c = string.charCodeAt(n);
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ } else if((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ } else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ };
+
+ };
+ return utftext;
+ },
+
+
+ _utf8_decode : function (utftext) {
+ var string = "";
+ var i = 0;
+ var c = c1 = c2 = 0;
+ while ( i < utftext.length ) {
+ c = utftext.charCodeAt(i);
+ if (c < 128) {
+ string += String.fromCharCode(c);
+ i++;
+ } else if((c > 191) && (c < 224)) {
+ c2 = utftext.charCodeAt(i+1);
+ string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ } else {
+ c2 = utftext.charCodeAt(i+1);
+ c3 = utftext.charCodeAt(i+2);
+ string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ };
+ };
+ return string;
+ }
+
+ };
+
+ String.implement({
+ toBase64: function(){
+ return Base64.encode(this);
+ },
+
+ decodeBase64: function(){
+ return Base64.decode(this);
+ }
+ });
+
+})();
+
+/*
+---
+
+name: Request
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires: [Object, Element, Chain, Events, Options, Browser]
+
+provides: Request
+
+...
+*/
+
+(function(){
+
+var empty = function(){},
+ progressSupport = ('onprogress' in new Browser.Request);
+
+var Request = this.Request = new Class({
+
+ Implements: [Chain, Events, Options],
+
+ options: {/*
+ onRequest: function(){},
+ onLoadstart: function(event, xhr){},
+ onProgress: function(event, xhr){},
+ onComplete: function(){},
+ onCancel: function(){},
+ onSuccess: function(responseText, responseXML){},
+ onFailure: function(xhr){},
+ onException: function(headerName, value){},
+ onTimeout: function(){},
+ user: '',
+ password: '',*/
+ 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,
+ timeout: 0,
+ noCache: false
+ },
+
+ initialize: function(options){
+ this.xhr = new Browser.Request();
+ this.setOptions(options);
+ this.headers = this.options.headers;
+ },
+
+ onStateChange: function(){
+ var xhr = this.xhr;
+ if (xhr.readyState != 4 || !this.running) return;
+ this.running = false;
+ this.status = 0;
+ Function.attempt(function(){
+ var status = xhr.status;
+ this.status = (status == 1223) ? 204 : status;
+ }.bind(this));
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ clearTimeout(this.timer);
+
+ this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
+ if (this.options.isSuccess.call(this, this.status))
+ this.success(this.response.text, this.response.xml);
+ else
+ this.failure();
+ },
+
+ isSuccess: function(){
+ var status = this.status;
+ return (status >= 200 && status < 300);
+ },
+
+ isRunning: function(){
+ return !!this.running;
+ },
+
+ processScripts: function(text){
+ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.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);
+ },
+
+ loadstart: function(event){
+ this.fireEvent('loadstart', [event, this.xhr]);
+ },
+
+ progress: function(event){
+ this.fireEvent('progress', [event, this.xhr]);
+ },
+
+ timeout: function(){
+ this.fireEvent('timeout', this.xhr);
+ },
+
+ setHeader: function(name, value){
+ this.headers[name] = value;
+ return this;
+ },
+
+ getHeader: function(name){
+ return Function.attempt(function(){
+ return this.xhr.getResponseHeader(name);
+ }.bind(this));
+ },
+
+ check: function(){
+ if (!this.running) return true;
+ switch (this.options.link){
+ case 'cancel': this.cancel(); return true;
+ case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
+ }
+ return false;
+ },
+
+ send: function(options){
+ if (!this.check(options)) return this;
+
+ this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+ this.running = true;
+
+ var type = typeOf(options);
+ if (type == 'string' || type == 'element') options = {data: options};
+
+ var old = this.options;
+ options = Object.append({data: old.data, url: old.url, method: old.method}, options);
+ var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+ switch (typeOf(data)){
+ case 'element': data = document.id(data).toQueryString(); break;
+ case 'object': case 'hash': data = Object.toQueryString(data);
+ }
+
+ if (this.options.format){
+ var format = 'format=' + this.options.format;
+ data = (data) ? format + '&' + data : format;
+ }
+
+ if (this.options.emulation && !['get', 'post'].contains(method)){
+ var _method = '_method=' + method;
+ data = (data) ? _method + '&' + data : _method;
+ method = 'post';
+ }
+
+ if (this.options.urlEncoded && ['post', 'put'].contains(method)){
+ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+ this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
+ }
+
+ if (!url) url = document.location.pathname;
+
+ var trimPosition = url.lastIndexOf('/');
+ if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+ if (this.options.noCache)
+ url += (url.contains('?') ? '&' : '?') + String.uniqueID();
+
+ if (data && method == 'get'){
+ url += (url.contains('?') ? '&' : '?') + data;
+ data = null;
+ }
+
+ var xhr = this.xhr;
+ if (progressSupport){
+ xhr.onloadstart = this.loadstart.bind(this);
+ xhr.onprogress = this.progress.bind(this);
+ }
+
+ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
+ if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
+
+ xhr.onreadystatechange = this.onStateChange.bind(this);
+
+ Object.each(this.headers, function(value, key){
+ try {
+ xhr.setRequestHeader(key, value);
+ } catch (e){
+ this.fireEvent('exception', [key, value]);
+ }
+ }, this);
+
+ this.fireEvent('request');
+ xhr.send(data);
+ if (!this.options.async) this.onStateChange();
+ else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
+ return this;
+ },
+
+ cancel: function(){
+ if (!this.running) return this;
+ this.running = false;
+ var xhr = this.xhr;
+ xhr.abort();
+ clearTimeout(this.timer);
+ xhr.onreadystatechange = empty;
+ if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
+ this.xhr = new Browser.Request();
+ this.fireEvent('cancel');
+ return this;
+ }
+
+});
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
+ methods[method] = function(data){
+ var object = {
+ method: method
+ };
+ if (data != null) object.data = data;
+ return this.send(object);
+ };
+});
+
+Request.implement(methods);
+
+Element.Properties.send = {
+
+ set: function(options){
+ var send = this.get('send').cancel();
+ send.setOptions(options);
+ return this;
+ },
+
+ get: function(){
+ var send = this.retrieve('send');
+ if (!send){
+ send = new Request({
+ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+ });
+ this.store('send', send);
+ }
+ return send;
+ }
+
+};
+
+Element.implement({
+
+ send: function(url){
+ var sender = this.get('send');
+ sender.send({data: this, url: url || sender.options.url});
+ return this;
+ }
+
+});
+
+})();
+
+/*
+---
+
+name: Request.JSON
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires: [Request, JSON]
+
+provides: Request.JSON
+
+...
+*/
+
+Request.JSON = new Class({
+
+ Extends: Request,
+
+ options: {
+ /*onError: function(text, error){},*/
+ secure: true
+ },
+
+ initialize: function(options){
+ this.parent(options);
+ Object.append(this.headers, {
+ 'Accept': 'application/json',
+ 'X-Request': 'JSON'
+ });
+ },
+
+ success: function(text){
+ var json;
+ try {
+ json = this.response.json = JSON.decode(text, this.options.secure);
+ } catch (error){
+ this.fireEvent('error', [text, error]);
+ return;
+ }
+ if (json == null) this.onFailure();
+ else this.onSuccess(json, text);
+ }
+
+});
+
diff --git a/www/js/popupdeck.js b/www/js/popupdeck.js
new file mode 100644
index 0000000..70ab5c0
--- /dev/null
+++ b/www/js/popupdeck.js
@@ -0,0 +1,96 @@
+'use strict';
+
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>, smake <smake at ya dot ru>.
+ *
+ */
+
+//pop up message procedure
+var popUpDeck = null;
+var popUpElements = [];
+function initPopUpDeck(){
+ popUpDeck = document.createElement('div');
+ document.body.appendChild(popUpDeck);
+
+ popUpDeck.set('class', 'popupwrapper');
+ document.querySelector('.popupwrapper').style.display = 'none';
+}
+
+function cleanPopUpDeck(){
+ for(var i=0; i<popUpElements.length; i++){
+ popUpElements[i].removeEvents();
+ popUpElements[i].destroy();
+ document.querySelector('.popupwrapper').style.display = 'none';
+ }
+}
+
+function popUpMessage(type, msg, timeout, callback, center){
+ var newMessage = document.createElement('div');
+ popUpDeck.appendChild(newMessage);
+ document.querySelector('.popupwrapper').style.display = 'block';
+
+ newMessage.set('class', 'popupmessage');
+ newMessage.set('text', msg);
+ popUpDeck.addEvent('mouseleave',
+ function(){
+ if(callback)
+ callback();
+ newMessage.destroy();
+ newMessage = null;
+ document.querySelector('.popupwrapper').style.display = 'none';
+ });
+
+ var color = {
+ r: 255,
+ g: 255,
+ b: 255
+ };
+
+ if(type=='error'){
+ color.r = 247;
+ color.g = 203;
+ color.b = 30;
+ }else
+ if(type=='message'){
+ color.r = 107;
+ color.g = 180;
+ color.b = 229;
+ }else
+ if(type=='critical'){
+ color.r = 255;
+ color.g = 0;
+ color.b = 0;
+ }
+
+ if(center){
+ newMessage.setStyle('position','absolute');
+ newMessage.setStyle('top', document.body.offsetHeight/2);
+ newMessage.setStyle('z-index', '1235');
+ }
+ newMessage.setStyle('background-color','rgba(' + color.r
+ + ',' + color.g
+ + ',' + color.b
+ + ', 0.8)' );
+
+ if(timeout){
+ window.setTimeout(
+ function(){
+ if(newMessage){
+ if(callback)
+ callback();
+ newMessage.destroy();
+ document.querySelector('.popupwrapper').style.display = 'none';
+ }
+ },
+ timeout*5000);
+ }
+
+ popUpElements.push(newMessage);
+
+ return newMessage;
+}
+
+function noInstancePopUp(){
+ popUpMessage('critical', "This instance seems to be not working. Try to enter the console again.", 0, noInstancePopUp, true);
+}
diff --git a/www/js/rdp-start.js b/www/js/rdp-start.js
new file mode 100644
index 0000000..ee509e1
--- /dev/null
+++ b/www/js/rdp-start.js
@@ -0,0 +1,262 @@
+'use strict';
+
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>, smake <smake at ya dot ru>.
+ *
+ */
+
+function RDPStart(uri, title){
+ uri = wsBase
+ document.querySelector('.rdp_connect_dialog').style.display = 'none';
+ if (title === undefined) {
+ // title = "Portal rdp session " + $('rdphost').value.trim();
+ title = "Portal remote session";
+ }
+ if(!embedded){
+ $('dvLoading').setStyles({'visibility':'visible'});
+ }
+ var controls = document.querySelector('#extracommands');
+ rdp = new webrdp.RDP(uri, $('screen'), controls, !RIMtablet, RIMtablet, vkbd);
+
+ rdp.addEvent('alert', function(msg) {
+ popUpMessage('error', msg, 5);
+ });
+ rdp.addEvent('connected', function() {
+ cleanPopUpDeck();
+ document.title = title;
+ var button = $("rdpconnect");
+ button.removeEvents();
+ window.removeEvent('resize', OnDesktopSize);
+ button.value = 'Disconnect';
+ button.addEvent('click', rdp.Disconnect.bind(rdp));
+ window.addEvent("beforeunload", rdp.Disconnect.bind(rdp));
+ });
+ rdp.addEvent('disconnected', function() {
+ showDialog(true);
+ if(embedded){
+ $('maindialog').addClass('invisible');
+ noInstancePopUp()
+ }
+ var button = $("rdpconnect");
+ button.removeEvents();
+ button.value = 'Connect';
+ button.addEvent('click', function(){RDPStart();});
+ OnDesktopSize();
+ window.addEvent('resize', OnDesktopSize);
+ if(!embedded)
+ {
+ document.querySelector('.rdp_connect_dialog').style.display = 'block';
+ }
+ });
+ rdp.addEvent('mouserelease', ResetRdpMouseFlags);
+ rdp.addEvent('touch2', function() {
+ ShowMouseHelper($('mousehelper').hasClass('invisible'));
+ });
+ rdp.addEvent('touch3', function() {
+ vkbd.toggle();
+ });
+ rdp.addEvent('touch4', function() {
+ if (confirm('Are you sure you want to disconnect?')) {
+ rdp.Disconnect();
+ }
+ });
+ showDialog(false);
+ // rdp.log.loglevel = "debug";
+ rdp.Run();
+}
+
+function SetRdpMouseFlags() {
+ var mf = {
+ 'r': $('rclick').checked,
+ 'm': $('mclick').checked,
+ 'a': $('aclick').checked,
+ 's': $('sclick').checked,
+ 'c': $('cclick').checked,
+ };
+ rdp.SetArtificialMouseFlags(mf);
+}
+function ResetRdpMouseFlags() {
+ $('rclick').checked = false;
+ $('mclick').checked = false;
+ $('aclick').checked = false;
+ $('sclick').checked = false;
+ $('cclick').checked = false;
+ rdp.SetArtificialMouseFlags(null);
+}
+function ShowMouseHelper(show) {
+ var mh = $('mousehelper');
+ inDrag = false;
+ if (show) {
+ mh.setStyles({'position':'absolute','top':mhy,'left':mhx,'z-index':999});
+ mh.addEvent('mousedown',DragStart);
+ $('rclick').addEvent('change', SetRdpMouseFlags);
+ $('mclick').addEvent('change', SetRdpMouseFlags);
+ $('aclick').addEvent('change', SetRdpMouseFlags);
+ $('sclick').addEvent('change', SetRdpMouseFlags);
+ $('cclick').addEvent('change', SetRdpMouseFlags);
+ mh.removeClass('invisible');
+ } else {
+ mh.removeEvents();
+ mh.addClass('invisible');
+ $('rclick').removeEvents();
+ $('mclick').removeEvents();
+ $('aclick').removeEvents();
+ $('sclick').removeEvents();
+ $('cclick').removeEvents();
+ }
+}
+
+function OnDesktopSize() {
+ ResizeCanvas($('dtsize').value);
+ DrawLogo();
+}
+
+function DragStart(evt) {
+ var mh = $('mousehelper');
+ if (!mh.hasClass('invisible')) {
+ inDrag = true;
+ dragX = evt.page.x;
+ dragY = evt.page.y;
+ window.addEvent('mouseup',DragEnd);
+ window.addEvent('touchmove',DragMove);
+ }
+}
+function DragEnd(evt) {
+ inDrag = false;
+ var mh = $('mousehelper');
+ window.removeEvent('touchmove',DragMove);
+ window.removeEvent('mouseup',DragEnd);
+}
+function DragMove(evt) {
+ if (inDrag) {
+ var dx = evt.page.x - dragX;
+ var dy = evt.page.y - dragY;
+ dragX = evt.page.x;
+ dragY = evt.page.y;
+ var mh = $('mousehelper');
+ if (!mh.hasClass('invisible')) {
+ mhx += dx;
+ mhy += dy;
+ mh.setStyles({'top':mhy,'left':mhx});
+ }
+ }
+}
+
+function DrawLogo() {
+ var logo = new Element('img', {'src': 'c_none.png'});
+ logo.addEvent('load', function() {
+ var screen = document.getElementById('screen');
+ var scaleWCoeficient = 0.5;
+ var scaleHCoeficient = 0.5;
+ var iw = this.width * scaleWCoeficient;
+ var ih = this.height * scaleHCoeficient;
+ var scale = (screen.height - 20) / ih;
+ screen.getContext('2d').drawImage(
+ this, 10, 10, Math.round(iw * scale), Math.round(ih * scale)
+ );
+ }.bind(logo));
+}
+
+function ResizeCanvas(sz) {
+ var w, h;
+ if (sz == 'auto') {
+ w = window.getCoordinates().width;
+ h = window.getCoordinates().height;
+ if (RIMtablet) {
+ // Toplevel bar not removable
+ h -= 31;
+ }
+ if (w % 2) {
+ w -= 1;
+ }
+ } else {
+ var sza = sz.split('x');
+ var w = sza[0];
+ var h = sza[1];
+ }
+ $('screen').width = w - 10;
+ $('screen').height = h - 50;
+ $('screen').style["margin"] = "0 auto";
+}
+
+var sendDisconnect = function() {
+if (confirm('Закончить сессию ?')) {
+ $('extracommands').setStyles({'visibility':'hidden'});
+ rdp.Disconnect();
+ window.close();
+}
+}
+
+var altTabOn = false;
+function altTabEvent(){
+ if(altTabOn){
+ altTabOn = false;
+ rdp.SendKey(2);//alt+tab release
+ $('alttab').removeClass('extracommandshold');
+ }
+ else{
+ altTabOn = true;
+ rdp.SendKey(1);//alt+tab
+ $('alttab').addClass('extracommandshold');
+ }
+}
+
+function showDialog(show) {
+ if (show) {
+ ShowMouseHelper(false);
+ var dlg = $('maindialog');
+ var x = Math.round((window.getCoordinates().width - dlg.getCoordinates().width) / 2) + 'px';
+ var y = Math.round((window.getCoordinates().height - dlg.getCoordinates().height) / 2) + 'px';
+ $('extracommands').setStyles(
+ {
+ 'visibility':'hidden'
+ });
+ $('dvLoading').setStyles(
+ {
+ 'visibility':'hidden'
+ });
+ DrawLogo();
+ dlg.setStyles({
+ 'position': 'absolute',
+ 'top': y,
+ 'left': x,
+ 'z-index': 999
+ }).removeClass('invisible');
+ } else {
+ $('maindialog').addClass('invisible');
+ $('extracommands').setStyles(
+ {
+ 'visibility':'visible'
+ });
+ $('ctrlaltdelete').addEvent('click', function(){ rdp.SendKey(0); });
+ $('alttab').addEvent('click', altTabEvent);
+ $('disconnect').addEvent('click', sendDisconnect);
+ }
+}
+
+function settingsGetJSON(){
+ var screen = document.getElementById('screen');
+ return {
+ "backend_settings": {
+ "host" : $('rdphost').value.trim()
+ ,"port" : parseInt($('rdpport').value.trim())
+ ,"pcb" : $('rdppcb').value.trim()
+ ,"user" : $('rdpuser').value.trim()
+ ,"password" : $('rdppass').value
+ ,"perf" : parseInt($('perf').value.trim())
+ ,"fntlm" : parseInt($('fntlm').value.trim())
+ ,"nowallp": parseInt($('nowallp').checked ? '1' : '0')
+ ,"nowdrag": parseInt($('nowdrag').checked ? '1' : '0')
+ ,"nomani" : parseInt($('nomani').checked ? '1' : '0')
+ ,"notheme": parseInt($('notheme').checked ? '1' : '0')
+ ,"nonla" : parseInt($('nonla').checked ? '1' : '0')
+ ,"notls" : parseInt($('notls').checked ? '1' : '0')
+ ,"dtsize" : screen.width + 'x' + screen.height
+ }
+ ,"session_settings": {
+ "token" : location.search.substring(1)
+ ,"proto" : "rdp"
+ }
+ };
+}
diff --git a/www/js/simpletabs-debug.js b/www/js/simpletabs-debug.js
new file mode 100644
index 0000000..33a5e4a
--- /dev/null
+++ b/www/js/simpletabs-debug.js
@@ -0,0 +1,120 @@
+/**
+ * SimpleTabs - Unobtrusive Tabs with Ajax
+ *
+ * @example
+ *
+ * var tabs = new SimpleTabs($('tab-element'), {
+ * selector: 'h2.tab-tab'
+ * });
+ *
+ * @version 1.0
+ *
+ * @license MIT License
+ * @author Harald Kirschner <mail [at] digitarald.de>
+ * @copyright 2007 Author
+ */
+var SimpleTabs = new Class({
+
+ Implements: [Events, Options],
+
+ /**
+ * Options
+ */
+ options: {
+ show: 0,
+ selector: '.tab-tab',
+ classWrapper: 'tab-wrapper',
+ classMenu: 'tab-menu',
+ classContainer: 'tab-container',
+ onSelect: function(toggle, container, index) {
+ toggle.addClass('tab-selected');
+ container.setStyle('display', '');
+ },
+ onDeselect: function(toggle, container, index) {
+ toggle.removeClass('tab-selected');
+ container.setStyle('display', 'none');
+ },
+ onAdded: Class.empty,
+ getContent: null
+ },
+
+ /**
+ * Constructor
+ *
+ * @param {Element} The parent Element that holds the tab elements
+ * @param {Object} Options
+ */
+ initialize: function(element, options) {
+ this.element = $(element);
+ this.setOptions(options);
+ this.selected = null;
+ this.build();
+ },
+
+ build: function() {
+ this.tabs = [];
+ this.menu = new Element('ul', {'class': this.options.classMenu});
+ this.wrapper = new Element('div', {'class': this.options.classWrapper});
+
+ this.element.getElements(this.options.selector).each(function(el) {
+ var content = el.get('href') || (this.options.getContent ? this.options.getContent.call(this, el) : el.getNext());
+ this.addTab(el.innerHTML, el.title || el.innerHTML, content);
+ }, this);
+ this.element.empty().adopt(this.menu, this.wrapper);
+
+ if (this.tabs.length) this.select(this.options.show);
+ },
+
+ /**
+ * Add a new tab at the end of the tab menu
+ *
+ * @param {String} inner Text
+ * @param {String} Title
+ * @param {Element|String} Content Element or URL for Ajax
+ */
+ addTab: function(text, title, content) {
+ var grab = $(content);
+ var container = (grab || new Element('div'))
+ .setStyle('display', 'none')
+ .addClass(this.options.classContainer)
+ .inject(this.wrapper);
+ var pos = this.tabs.length;
+ var evt = (this.options.hover) ? 'mouseenter' : 'click';
+ var tab = {
+ container: container,
+ toggle: new Element('li').grab(new Element('a', {
+ href: '#',
+ title: title
+ }).grab(
+ new Element('span', {html: text})
+ )).addEvent(evt,function(e){this.onClick(e,pos);}.bind(this)).inject(this.menu)
+ };
+ this.tabs.push(tab);
+ return this.fireEvent('onAdded', [tab.toggle, tab.container, pos]);
+ },
+
+ onClick: function(evt, index) {
+ this.select(index);
+ return false;
+ },
+
+ /**
+ * Select the tab via tab-index
+ *
+ * @param {Number} Tab-index
+ */
+ select: function(index) {
+ if (this.selected === index || !this.tabs[index]) return this;
+ var tab = this.tabs[index];
+ var params = [tab.toggle, tab.container, index];
+ if (this.selected !== null) {
+ var current = this.tabs[this.selected];
+ params.append([current.toggle, current.container, this.selected]);
+ this.fireEvent('onDeselect', [current.toggle, current.container, this.selected]);
+ }
+ this.fireEvent('onSelect', params);
+ this.selected = index;
+ return this;
+ }
+
+});
diff --git a/www/js/vkb-debug.js b/www/js/vkb-debug.js
new file mode 100644
index 0000000..97b3ef3
--- /dev/null
+++ b/www/js/vkb-debug.js
@@ -0,0 +1,750 @@
+/*
+ * This code is based on:
+ * HTML Virtual Keyboard Interface Script - v1.49
+ * Copyright (c) 2011 - GreyWyvern
+ * - Licensed for free distribution under the BSDL
+ * http://www.opensource.org/licenses/bsd-license.php
+ * Found at: http://www.greywyvern.com/code/javascript/keyboard
+ *
+ * Extensive modfications and mootoolization:
+ * Copyright (c) 2012 Fritz Elfert
+ */
+
+var webrdp = webrdp || {}
+
+webrdp.vkbd = new Class({
+ Implements: [Options, Events],
+ options: {
+ target: null,
+ version: true, // Show the version
+ deadcheck: true, // Show dead keys checkbox
+ deadkeys: false, // Turn dead keys on by default
+ numpadtoggle: true, // Show numpad toggle
+ numpad: true, // Show number pad by default
+ layouts: ['en_US','de_DE'], // Keyboard layouts to load
+ deflayout: 'de_DE', // Default keyboard layout
+ size: 5, // Initial size
+ sizeswitch: true, // Show size-adjust controls
+ hoverclick: 0, // Click a key after n ms (0 = off)
+ clicksound: '/vkbclick.ogg'
+ },
+ initialize: function(options) {
+ this.setOptions(options);
+ this.VERSION = '1.49';
+ this.posX = 100;
+ this.posY = 100;
+ this.dragging = false;
+ this.VKI_shift = this.VKI_shiftlock = false;
+ this.VKI_altgr = this.VKI_altgrlock = false;
+ this.VKI_alt = this.VKI_altlock = false;
+ this.VKI_ctrl = this.VKI_ctrllock = false;
+ this.VKI_dead = false; // Flag: dead key active
+ this.VKI_kt = 'US International';
+ this.VKI_size = this.options.size;
+ this.VKI_keyCenter = 3;
+ /* ***** i18n text strings ************************************* */
+ this.VKI_i18n = {
+ '00': 'Display Number Pad',
+ '01': 'Display virtual keyboard interface',
+ '02': 'Select keyboard layout',
+ '03': 'Dead keys',
+ '04': 'On',
+ '05': 'Off',
+ '06': 'Close the keyboard',
+ '07': 'Clear',
+ '08': 'Clear this input',
+ '09': 'Version',
+ '10': 'Decrease keyboard size',
+ '11': 'Increase keyboard size'
+ };
+
+ // Load layouts
+ this.VKI_layout = {};
+ this.options.layouts.each(function(n) {
+ new Request.JSON({
+ 'url': 'js/vkbl-' + n + '.json',
+ 'method': 'get',
+ 'async': false,
+ 'onSuccess': function(obj) {
+ this.VKI_layout[obj.displayname] = obj;
+ if (n == this.options.deflayout) {
+ this.VKI_kt = obj.displayname;
+ }
+ }.bind(this)
+ }).addEvent('error', function(txt, err) {
+ console.debug('err:', err);
+ console.debug('txt:', txt);
+ }).send();
+ }, this);
+ /*
+ if (this.options.clicksound) {
+ this.audio = new Audio(this.options.clicksound);
+ }
+ */
+ this.VKI_deadkey = {};
+ this.VKI_deadkey['"'] = this.VKI_deadkey['\u00a8'] = this.VKI_deadkey['\u309B'] = { // Umlaut / Diaeresis / Greek Dialytika / Hiragana/Katakana Voiced Sound Mark
+ 'a': '\u00e4', 'e': '\u00eb', 'i': '\u00ef', 'o': '\u00f6', 'u': '\u00fc', 'y': '\u00ff', '\u03b9': '\u03ca', '\u03c5': '\u03cb',
+ '\u016B': '\u01D6', '\u00FA': '\u01D8', '\u01D4': '\u01DA', '\u00F9': '\u01DC', 'A': '\u00c4', 'E': '\u00cb', 'I': '\u00cf',
+ 'O': '\u00d6', 'U': '\u00dc', 'Y': '\u0178', '\u0399': '\u03aa', '\u03a5': '\u03ab', '\u016A': '\u01D5', '\u00DA': '\u01D7',
+ '\u01D3': '\u01D9', '\u00D9': '\u01DB', '\u304b': '\u304c', '\u304d': '\u304e', '\u304f': '\u3050', '\u3051': '\u3052',
+ '\u3053': '\u3054', '\u305f': '\u3060', '\u3061': '\u3062', '\u3064': '\u3065', '\u3066': '\u3067', '\u3068': '\u3069',
+ '\u3055': '\u3056', '\u3057': '\u3058', '\u3059': '\u305a', '\u305b': '\u305c', '\u305d': '\u305e', '\u306f': '\u3070',
+ '\u3072': '\u3073', '\u3075': '\u3076', '\u3078': '\u3079', '\u307b': '\u307c', '\u30ab': '\u30ac', '\u30ad': '\u30ae',
+ '\u30af': '\u30b0', '\u30b1': '\u30b2', '\u30b3': '\u30b4', '\u30bf': '\u30c0', '\u30c1': '\u30c2', '\u30c4': '\u30c5',
+ '\u30c6': '\u30c7', '\u30c8': '\u30c9', '\u30b5': '\u30b6', '\u30b7': '\u30b8', '\u30b9': '\u30ba', '\u30bb': '\u30bc',
+ '\u30bd': '\u30be', '\u30cf': '\u30d0', '\u30d2': '\u30d3', '\u30d5': '\u30d6', '\u30d8': '\u30d9', '\u30db': '\u30dc'
+ };
+ this.VKI_deadkey['~'] = { // Tilde / Stroke
+ 'a': '\u00e3', 'l': '\u0142', 'n': '\u00f1', 'o': '\u00f5',
+ 'A': '\u00c3', 'L': '\u0141', 'N': '\u00d1', 'O': '\u00d5'
+ };
+ this.VKI_deadkey['^'] = { // Circumflex
+ 'a': '\u00e2', 'e': '\u00ea', 'i': '\u00ee', 'o': '\u00f4', 'u': '\u00fb', 'w': '\u0175', 'y': '\u0177',
+ 'A': '\u00c2', 'E': '\u00ca', 'I': '\u00ce', 'O': '\u00d4', 'U': '\u00db', 'W': '\u0174', 'Y': '\u0176'
+ };
+ this.VKI_deadkey['\u02c7'] = { // Baltic caron
+ 'c': '\u010D', 'd': '\u010f', 'e': '\u011b', 's': '\u0161', 'l': '\u013e', 'n': '\u0148', 'r': '\u0159', 't': '\u0165',
+ 'u': '\u01d4', 'z': '\u017E', '\u00fc': '\u01da', 'C': '\u010C', 'D': '\u010e', 'E': '\u011a', 'S': '\u0160',
+ 'L': '\u013d', 'N': '\u0147', 'R': '\u0158', 'T': '\u0164', 'U': '\u01d3', 'Z': '\u017D', '\u00dc': '\u01d9'
+ };
+ this.VKI_deadkey['\u02d8'] = { // Romanian and Turkish breve
+ 'a': '\u0103', 'g': '\u011f',
+ 'A': '\u0102', 'G': '\u011e'
+ };
+ this.VKI_deadkey['-'] = this.VKI_deadkey['\u00af'] = { // Macron
+ 'a': '\u0101', 'e': '\u0113', 'i': '\u012b', 'o': '\u014d', 'u': '\u016B', 'y': '\u0233', '\u00fc': '\u01d6',
+ 'A': '\u0100', 'E': '\u0112', 'I': '\u012a', 'O': '\u014c', 'U': '\u016A', 'Y': '\u0232', '\u00dc': '\u01d5'
+ };
+ this.VKI_deadkey['`'] = { // Grave
+ 'a': '\u00e0', 'e': '\u00e8', 'i': '\u00ec', 'o': '\u00f2', 'u': '\u00f9', '\u00fc': '\u01dc',
+ 'A': '\u00c0', 'E': '\u00c8', 'I': '\u00cc', 'O': '\u00d2', 'U': '\u00d9', '\u00dc': '\u01db'
+ };
+ this.VKI_deadkey["'"] = this.VKI_deadkey['\u00b4'] = this.VKI_deadkey['\u0384'] = { // Acute / Greek Tonos
+ 'a': '\u00e1', 'e': '\u00e9', 'i': '\u00ed', 'o': '\u00f3', 'u': '\u00fa', 'y': '\u00fd', '\u03b1': '\u03ac',
+ '\u03b5': '\u03ad', '\u03b7': '\u03ae', '\u03b9': '\u03af', '\u03bf': '\u03cc', '\u03c5': '\u03cd', '\u03c9': '\u03ce',
+ '\u00fc': '\u01d8', 'A': '\u00c1', 'E': '\u00c9', 'I': '\u00cd', 'O': '\u00d3', 'U': '\u00da', 'Y': '\u00dd',
+ '\u0391': '\u0386', '\u0395': '\u0388', '\u0397': '\u0389', '\u0399': '\u038a', '\u039f': '\u038c', '\u03a5': '\u038e',
+ '\u03a9': '\u038f', '\u00dc': '\u01d7'
+ };
+ this.VKI_deadkey['\u02dd'] = { // Hungarian Double Acute Accent
+ 'o': '\u0151', 'u': '\u0171',
+ 'O': '\u0150', 'U': '\u0170'
+ };
+ this.VKI_deadkey['\u0385'] = { // Greek Dialytika + Tonos
+ '\u03b9': '\u0390', '\u03c5': '\u03b0'
+ };
+ this.VKI_deadkey['\u00b0'] = this.VKI_deadkey['\u00ba'] = { // Ring
+ 'a': '\u00e5', 'u': '\u016f',
+ 'A': '\u00c5', 'U': '\u016e'
+ };
+ this.VKI_deadkey['\u02DB'] = { // Ogonek
+ 'a': '\u0106', 'e': '\u0119', 'i': '\u012f', 'o': '\u01eb', 'u': '\u0173', 'y': '\u0177',
+ 'A': '\u0105', 'E': '\u0118', 'I': '\u012e', 'O': '\u01ea', 'U': '\u0172', 'Y': '\u0176'
+ };
+ this.VKI_deadkey['\u02D9'] = { // Dot-above
+ 'c': '\u010B', 'e': '\u0117', 'g': '\u0121', 'z': '\u017C',
+ 'C': '\u010A', 'E': '\u0116', 'G': '\u0120', 'Z': '\u017B'
+ };
+ this.VKI_deadkey['\u00B8'] = this.VKI_deadkey['\u201a'] = { // Cedilla
+ 'c': '\u00e7', 's': '\u015F',
+ 'C': '\u00c7', 'S': '\u015E'
+ };
+ this.VKI_deadkey[','] = { // Comma
+ 's': '\u0219', 't': '\u021B',
+ 'S': '\u0218', 'T': '\u021A'
+ };
+ this.VKI_deadkey['\u3002'] = { // Hiragana/Katakana Point
+ '\u306f': '\u3071', '\u3072': '\u3074', '\u3075': '\u3077', '\u3078': '\u307a', '\u307b': '\u307d',
+ '\u30cf': '\u30d1', '\u30d2': '\u30d4', '\u30d5': '\u30d7', '\u30d8': '\u30da', '\u30db': '\u30dd'
+ };
+ this.VKI_symbol = {
+ '\u00a0': 'NB\nSP', '\u200b': 'ZW\nSP', '\u200c': 'ZW\nNJ', '\u200d': 'ZW\nJ'
+ };
+ this.VKI_numpad = [
+ [['$'], ['\u00a3'], ['\u20ac'], ['\u00a5']],
+ [['7'], ['8'], ['9'], ['/']],
+ [['4'], ['5'], ['6'], ['*']],
+ [['1'], ['2'], ['3'], ['-']],
+ [['0'], ['.'], ['='], ['+']]
+ ];
+ this.cblock = [
+ [['Prt', 0x2c],['Slk', 0],['\u231b', 0x13]],
+ [['Ins', 0x2d],['\u21f1', 0x24],['\u21d1', 0x21]],
+ [['Del', 0x2e],['\u21f2', 0x23],['\u21d3', 0x23]],
+ [[''],['\u2191', 0x26],['']],
+ [['\u2190', 0x25],['\u2193', 0x28],['\u2192', 0x27]],
+ ];
+ this.fnblock = [
+ ['Esc', 0x1b], ['F1', 0x70], ['F2', 0x71], ['F3', 0x72], ['F4', 0x73], ['F5', 0x74],
+ ['F6', 0x75], ['F7', 0x76], ['F8', 0x77], ['F9', 0x78], ['F10', 0x79], ['F11', 0x7a],
+ ['F12', 0x7b]
+ ];
+ if (!this.VKI_layout[this.VKI_kt]) {
+ throw 'No layout named "' + this.VKI_kt + '"';
+ }
+ this.VKI_keyboard = new Element('table', {
+ 'id': 'keyboardInputMaster',
+ 'dir': 'ltr',
+ 'cellspacing': 0,
+ 'class': 'hidden',
+ 'styles': {
+ 'position': 'absolute',
+ 'left': this.posX,
+ 'top': this.posY,
+ 'z-index': 999
+ },
+ 'events': {
+ 'click': function(e) { e.stopPropagation(); },
+ 'selectstart': function(e) { e.stopPropagation(); }
+ }
+ }).inject(document.body);
+ this.VKI_langCode = {};
+ var thead = new Element('thead', {
+ 'events': {
+ 'mousedown': this.dragStart.bind(this),
+ }
+ }).inject(this.VKI_keyboard);
+ var tr = new Element('tr').inject(thead);
+ var th = new Element('th', {'colspan':3}).inject(tr);
+
+ var nlayouts = 0;
+ Object.each(this.VKI_layout, function(item) { if ('object' == typeof(item)) { nlayouts++; } });
+ // Build layout selector if more than one layouts
+ if (nlayouts > 1) {
+ this.kbSelect = new Element('div', {
+ 'html': this.VKI_kt,
+ 'title': this.VKI_i18n['02']
+ }).addEvent('click', function(e) {
+ var ol = e.target.getElement('ol');
+ if (!ol.style.display) {
+ ol.setStyle('display','block');
+ var scr = 0;
+ ol.getElements('li').each(function(li) {
+ if (this.VKI_kt == li.firstChild.nodeValue) {
+ li.addClass('selected');
+ scr = li.offsetTop - li.offsetHeight * 2;
+ } else {
+ li.removeClass('selected');
+ }
+ });
+ setTimeout(function() { ol.scrollTop = scr; }, 0);
+ } else {
+ ol.setStyle('display','');
+ }
+ }.bind(this)).appendText(' \u25be').inject(th);
+ var ol = new Element('ol').inject(this.kbSelect);
+ for (ktype in this.VKI_layout) {
+ if (typeof this.VKI_layout[ktype] == 'object') {
+ if (!this.VKI_layout[ktype].lang) {
+ this.VKI_layout[ktype].lang = [];
+ }
+ for (var x = 0; x < this.VKI_layout[ktype].lang.length; x++) {
+ this.VKI_langCode[this.VKI_layout[ktype].lang[x].toLowerCase().replace(/-/g, '_')] = ktype;
+ }
+ var li = new Element('li', {
+ 'title': this.VKI_layout[ktype].name,
+ 'html': ktype
+ }).inject(ol).addEvent('click', function(e) {
+ e.stopPropagation();
+ var el = e.target;
+ el.parentNode.setStyle('display','');
+ this.VKI_kt = this.kbSelect.firstChild.nodeValue = el.get('text');
+ this.VKI_buildKeys();
+ }.bind(this));
+ }
+ }
+ }
+ // Sort the layout selector alphabetically
+ this.VKI_langCode.index = [];
+ for (prop in this.VKI_langCode) {
+ if (prop != 'index' && typeof this.VKI_langCode[prop] == 'string') {
+ this.VKI_langCode.index.push(prop);
+ }
+ }
+ this.VKI_langCode.index.sort();
+ this.VKI_langCode.index.reverse();
+
+ // Build Number-pad toggle-button
+ if (this.options.numpadtoggle) {
+ new Element('span', {
+ 'html': '#',
+ 'title': this.VKI_i18n['00']
+ }).addEvent('click', function() {
+ this.kbNumpad.toggleClass('hidden');
+ }.bind(this)).inject(th);
+ }
+
+ // Build SizeUp and SizeDown buttons
+ if (this.options.sizeswitch) {
+ new Element('small', {
+ 'html': '\u21d3',
+ 'title': this.VKI_i18n['10']
+ }).addEvent('click', function() {
+ this.VKI_size--;
+ this.VKI_kbsize();
+ }.bind(this)).inject(th);
+ new Element('big', {
+ 'html': '\u21d1',
+ 'title': this.VKI_i18n['11']
+ }).addEvent('click', function() {
+ this.VKI_size++;
+ this.VKI_kbsize();
+ }.bind(this)).inject(th);
+ }
+
+ // Build Clear button
+ if (this.options.target) {
+ new Element('span', {
+ 'html': this.VKI_i18n['07'],
+ 'title': this.VKI_i18n['08']
+ }).addEvent('click', function() {
+ this.options.target.value = '';
+ this.options.target.focus();
+ }.bind(this)).inject(th);
+ }
+
+ // Build Close box
+ var closeBox = new Element('strong', {
+ 'html': 'X',
+ 'title': this.VKI_i18n['06']
+ }).addEvent('click', function() {
+ this.hide();
+ }.bind(this)).inject(th);
+
+ var tbody = new Element('tbody').inject(this.VKI_keyboard);
+ var tr = new Element('tr').inject(tbody);
+ var td = new Element('td').inject(tr);
+ var div = new Element('div').inject(td);
+
+ // Build deadKey checkbox
+ if (this.options.deadcheck) {
+ var label = new Element('label').inject(div);
+ this.deadCheckbox = new Element('input', {
+ 'type': 'checkbox',
+ 'title': this.VKI_i18n['03'] + ': ' + (this.options.deadkeys ? this.VKI_i18n['04'] : this.VKI_i18n['05']),
+ 'defaultchecked': this.options.deadkeys,
+ 'checked': this.options.deadkeys
+ }).addEvent('click', function(e) {
+ el = e.target;
+ el.set('title', this.VKI_i18n['03'] + ': ' + ((el.checked) ? this.VKI_i18n['04'] : this.VKI_i18n['05']));
+ this.modify('');
+ }.bind(this)).inject(label);
+ } else {
+ this.deadCheckbox = new Element('input', {
+ 'type': 'checkbox',
+ 'defaultchecked': this.options.deadkeys,
+ 'checked': this.options.deadkeys
+ });
+ }
+
+ // Build Version display
+ if (this.options.version) {
+ new Element('var', {
+ 'html': 'v' + this.VERSION,
+ 'title': this.VKI_i18n['09'] + ' ' + this.VERSION
+ }).inject(div);
+ }
+
+ // Build cursor block
+ this.kbCursor = new Element('td', {
+ 'id': 'keyboardInputCursor'
+ }).inject(tr);
+ var ctable = new Element('table', {
+ 'cellspacing': '0'
+ }).inject(this.kbCursor);
+ var ctbody = new Element('tbody').inject(ctable);
+ this.cblock.each(function(row) {
+ ctr = new Element('tr').inject(ctbody);
+ row.each(function(col) {
+ this.VKI_stdEvents(new Element('td', {
+ 'html': col[0],
+ 'class': (col[0].length ? '' : 'none'),
+ 'char': col[1],
+ 'events': {
+ 'click': (col[1] ? this.kclick2.bind(this) : this.dummy)
+ }
+ }).inject(ctr));
+ }.bind(this));
+ }.bind(this));
+
+ // Build NumPad
+ this.kbNumpad = new Element('td', {
+ 'id': 'keyboardInputNumpad',
+ 'class': (this.options.numpad ? '' : 'hidden')
+ }).inject(tr);
+ var ntable = new Element('table', {
+ 'cellspacing': '0'
+ }).inject(this.kbNumpad);
+ var ntbody = new Element('tbody').inject(ntable);
+ for (var x = 0; x < this.VKI_numpad.length; x++) {
+ var ntr = new Element('tr').inject(ntbody);
+ for (var y = 0; y < this.VKI_numpad[x].length; y++) {
+ this.VKI_stdEvents(new Element('td', {
+ 'html': this.VKI_numpad[x][y],
+ 'events': {
+ 'click': this.kclick.bind(this)
+ }
+ }).inject(ntr));
+ }
+ }
+
+ // Build normal keys
+ this.VKI_buildKeys();
+ this.VKI_keyboard.unselectable = 'on';
+ this.VKI_kbsize();
+ },
+ dummy: function() {
+ },
+ VKI_stdEvents: function(elem) {
+ if ('td' == elem.get('tag')) {
+ // elem.addEvent('dblclick', function(e) { e.PreventDefault(); });
+ if (this.options.hoverclick) {
+ elem.clid = 0;
+ elem.addEvents({
+ 'mouseover': function(e) {
+ var el = e.target;
+ clearTimeout(el.clid);
+ el.clid = function() {
+ this.fireEvent('click', this, 0);
+ }.delay(this.options.hoverclick);
+ }.bind(this),
+ 'mouseout': function() { clearTimeout(this.clid); }.bind(elem),
+ 'mousedown': function() { clearTimeout(this.clid); }.bind(elem),
+ 'mouseup': function() { clearTimeout(this.clid); }.bind(elem)
+ });
+ }
+ }
+ },
+ VKI_kbsize: function(e) {
+ this.VKI_size = Math.min(5, Math.max(1, this.VKI_size));
+ for (var i = 0; i < 6; i++) {
+ this.VKI_keyboard.removeClass('keyboardInputSize' + i);
+ }
+ if (this.VKI_size != 2) {
+ this.VKI_keyboard.addClass('keyboardInputSize' + this.VKI_size);
+ }
+ },
+ kdown: function(evt) {
+ },
+ kup: function(evt) {
+ },
+ kclick: function(evt) {
+ var done = false, character = '\xa0', el = evt.target;
+ if (el.firstChild.nodeName.toLowerCase() != 'small') {
+ if ((character = el.firstChild.nodeValue) == '\xa0') {
+ return;
+ }
+ } else {
+ character = el.firstChild.get('char');
+ }
+ if (this.deadCheckbox.checked && this.VKI_dead) {
+ if (this.VKI_dead != character) {
+ if (character != ' ') {
+ if (this.VKI_deadkey[this.VKI_dead][character]) {
+ this.kpress(this.VKI_deadkey[this.VKI_dead][character]);
+ done = true;
+ }
+ } else {
+ this.kpress(this.VKI_dead);
+ done = true;
+ }
+ } else {
+ done = true;
+ }
+ }
+ this.VKI_dead = false;
+
+ if (!done) {
+ if (this.deadCheckbox.checked && this.VKI_deadkey[character]) {
+ this.VKI_dead = character;
+ el.addClass('dead');
+ if (this.VKI_shift) {
+ this.modify('Shift');
+ }
+ if (this.VKI_alt) {
+ this.modify('Alt');
+ }
+ if (this.VKI_altgr) {
+ this.modify('AltGr');
+ }
+ if (this.VKI_ctrl) {
+ this.modify('Ctrl');
+ }
+ } else {
+ this.kpress(character);
+ }
+ }
+ this.modify('');
+ },
+ kclick2: function(evt) {
+ var c = evt.target.get('char');
+ if (c) {
+ this.kpress(c, true);
+ this.VKI_dead = false;
+ this.modify('');
+ }
+ },
+ VKI_buildKeys: function() {
+ this.VKI_shift = this.VKI_shiftlock = this.VKI_altgr = this.VKI_altgrlock = this.VKI_dead = false;
+ var container = this.VKI_keyboard.tBodies[0].getElement('div');
+ container.getElements('table').each(function(el) { el.destroy(); });
+ for (var x = 0, hasDeadKeys = false, lyt; lyt = this.VKI_layout[this.VKI_kt].keys[x++];) {
+ var table = new Element('table',{
+ 'cellspacing': '0'
+ }).inject(container);
+ if (lyt.length <= this.VKI_keyCenter) {
+ table.addClass('keyboardInputCenter');
+ }
+ var tbody = new Element('tbody').inject(table);
+ var tr = new Element('tr').inject(tbody);
+ for (var y = 0, lkey; lkey = lyt[y++];) {
+ var td = new Element('td').inject(tr);
+ if (this.VKI_symbol[lkey[0]]) {
+ var text = this.VKI_symbol[lkey[0]].split('\n');
+ var small = new Element('small', {
+ 'char': lkey[0]
+ }).inject(td);
+ for (var z = 0; z < text.length; z++) {
+ if (z) {
+ new Element('br').inject(small);
+ }
+ small.appendText(text[z]);
+ }
+ } else {
+ td.appendText(lkey[0] || '\xa0');
+ }
+
+ if (this.deadCheckbox.checked) {
+ for (key in this.VKI_deadkey) {
+ if (key === lkey[0]) {
+ td.addClass('deadkey');
+ break;
+ }
+ }
+ }
+ if (lyt.length > this.VKI_keyCenter && y == lyt.length) {
+ td.addClass('last');
+ }
+ if (lkey[0] == ' ' || lkey[1] == ' ') {
+ td.addClass('space');
+ }
+
+ switch (lkey[1]) {
+ case 'Caps':
+ case 'Ctrl':
+ case 'Shift':
+ case 'Alt':
+ case 'AltGr':
+ case 'AltLk':
+ td.addEvent('click', function(type) {
+ // XXX
+ this.modify(type);
+ }.bind(this, lkey[1]));
+ break;
+ case 'Tab':
+ td.addEvent('click', function(e) {
+ e.preventDefault();
+ this.kpress('\t');
+ }.bind(this));
+ break;
+ case 'Bksp':
+ td.addEvent('click', function(e) {
+ e.preventDefault();
+ this.kpress('\b');
+ }.bind(this));
+ break;
+ case 'Enter':
+ td.addEvent('click', function(e) {
+ e.preventDefault();
+ this.kpress('\r');
+ }.bind(this));
+ break;
+ default:
+ td.addEvent('click', this.kclick.bind(this));
+ break;
+ }
+ this.VKI_stdEvents(td);
+ for (var z = 0; z < 4; z++) {
+ if (this.VKI_deadkey[lkey[z] = lkey[z] || '']) {
+ hasDeadKeys = true;
+ }
+ }
+ }
+ }
+ // Hide deadkey checkbox if layout has no deadkeys
+ if (this.options.deadcheck) {
+ this.deadCheckbox.setStyle('display', hasDeadKeys ? 'inline' : 'none');
+ }
+ },
+ modify: function(type) {
+ switch (type) {
+ case 'Alt':
+ this.VKI_alt = !this.VKI_alt;
+ break;
+ case 'AltGr':
+ this.VKI_altgr = !this.VKI_altgr;
+ break;
+ case 'AltLk':
+ this.VKI_altgr = 0;
+ this.VKI_altgrlock = !this.VKI_altgrlock;
+ break;
+ case 'Caps':
+ this.VKI_shift = 0;
+ this.VKI_shiftlock = !this.VKI_shiftlock;
+ break;
+ case 'Ctrl':
+ this.VKI_ctrl = !this.VKI_ctrl;
+ break;
+ case 'Shift':
+ this.VKI_shift = !this.VKI_shift;
+ break;
+ }
+ var vchar = 0;
+ if (!this.VKI_shift != !this.VKI_shiftlock) {
+ vchar += 1;
+ }
+ if (!this.VKI_altgr != !this.VKI_altgrlock) {
+ vchar += 2;
+ }
+ var tables = this.VKI_keyboard.tBodies[0].getElement('div').getElements('table');
+ for (var x = 0; x < tables.length; x++) {
+ var tds = tables[x].getElements('td');
+ for (var y = 0; y < tds.length; y++) {
+ var classes = {}, lkey = this.VKI_layout[this.VKI_kt].keys[x][y];
+ switch (lkey[1]) {
+ case 'Alt':
+ if (this.VKI_alt) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'AltGr':
+ if (this.VKI_altgr) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'AltLk':
+ if (this.VKI_altgrlock) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'Shift':
+ if (this.VKI_shift) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'Caps':
+ if (this.VKI_shiftlock) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'Ctrl':
+ if (this.VKI_ctrl) {
+ classes.pressed = 1;
+ }
+ break;
+ case 'Tab':
+ case 'Enter':
+ case 'Bksp':
+ break;
+ default:
+ if (type) {
+ tds[y].removeChild(tds[y].firstChild);
+ if (this.VKI_symbol[lkey[vchar]]) {
+ var text = this.VKI_symbol[lkey[vchar]].split('\n');
+ var small = new Element('small', {
+ 'char': lkey[vchar]
+ }).inject(tds[y]);
+ for (var z = 0; z < text.length; z++) {
+ if (z) {
+ new Element('br').inject(small);
+ }
+ small.appendText(text[z]);
+ }
+ } else {
+ tds[y].appendText(lkey[vchar] || '\xa0');
+ }
+ }
+ if (this.deadCheckbox.checked) {
+ var character =
+ tds[y].firstChild.nodeValue || tds[y].firstChild.className;
+ if (this.VKI_dead) {
+ if (character == this.VKI_dead) {
+ classes.pressed = 1;
+ }
+ if (this.VKI_deadkey[this.VKI_dead][character]) {
+ classes.target = 1;
+ }
+ }
+ if (this.VKI_deadkey[character]) {
+ classes.deadkey = 1;
+ }
+ }
+ break;
+ }
+ if (y == tds.length - 1 && tds.length > this.VKI_keyCenter) {
+ classes.last = 1;
+ }
+ if (lkey[0] == ' ' || lkey[1] == ' ') {
+ classes.space = 1;
+ }
+ tds[y].removeClass('pressed').removeClass('target').removeClass('deadkey').removeClass('last').removeClass('space');
+ Object.each(classes, function(i,k) { tds[y].addClass(k); });
+ }
+ }
+ },
+ kpress: function(text, special) {
+ var e = {
+ code: text.charCodeAt(0),
+ shift: this.VKI_shift,
+ control: this.VKI_ctrl,
+ alt: this.VKI_alt,
+ meta: this.VKI_altgr,
+ special: special
+ };
+ this.fireEvent('vkpress', e);
+ if (this.VKI_alt) {
+ this.modify("Alt");
+ }
+ if (this.VKI_altgr) {
+ this.modify("AltGr");
+ }
+ if (this.VKI_ctrl) {
+ this.modify("Ctrl");
+ }
+ if (this.VKI_shift) {
+ this.modify("Shift");
+ }
+ },
+ show: function() {
+ this.VKI_keyboard.removeClass('hidden');
+ },
+ hide: function() {
+ this.VKI_keyboard.addClass('hidden');
+ },
+ toggle: function() {
+ this.VKI_keyboard.toggleClass('hidden');
+ },
+ dragEnd: function(evt) {
+ this.dragging = false;
+ window.removeEvent('mouseup', this.dragEnd.bind(this));
+ window.removeEvent('mousemove', this.dragMove.bind(this));
+ window.removeEvent('touchmove', this.dragMove.bind(this));
+ },
+ dragStart: function(evt) {
+ this.dragX = evt.page.x;
+ this.dragY = evt.page.y;
+ this.dragging = true;
+ window.addEvent('mouseup', this.dragEnd.bind(this));
+ window.addEvent('mousemove', this.dragMove.bind(this));
+ window.addEvent('touchmove', this.dragMove.bind(this));
+ },
+ dragMove: function(evt) {
+ if (this.dragging) {
+ this.posX += evt.page.x - this.dragX;
+ this.posY += evt.page.y - this.dragY;
+ this.dragX = evt.page.x;
+ this.dragY = evt.page.y;
+ this.VKI_keyboard.setStyles({'top':this.posY,'left':this.posX});
+ }
+ }
+});
diff --git a/www/js/vkbl-de_DE.json b/www/js/vkbl-de_DE.json
new file mode 100644
index 0000000..f65cef7
--- /dev/null
+++ b/www/js/vkbl-de_DE.json
@@ -0,0 +1,8 @@
+ { "name": "German", "displayname":"Deutsch", "keys": [
+ [["^", "\u00b0"], ["1", "!"], ["2", "%x22", "\u00b2"], ["3", "\u00a7", "\u00b3"], ["4", "$"], ["5", "%"], ["6", "&"], ["7", "/", "{"], ["8", "(", "["], ["9", ")", "]"], ["0", "=", "}"], ["\u00df", "?", "\\"], ["\u00b4", "`"], ["Bksp", "Bksp"]],
+ [["Tab", "Tab"], ["q", "Q", "@"], ["w", "W"], ["e", "E", "\u20ac"], ["r", "R"], ["t", "T"], ["z", "Z"], ["u", "U"], ["i", "I"], ["o", "O"], ["p", "P"], ["\u00fc", "\u00dc"], ["+", "*", "~"], ["#", "'"]],
+ [["Caps", "Caps"], ["a", "A"], ["s", "S"], ["d", "D"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L"], ["\u00f6", "\u00d6"], ["\u00e4", "\u00c4"], ["Enter", "Enter"]],
+ [["Shift", "Shift"], ["<", ">", "\u00a6"], ["y", "Y"], ["x", "X"], ["c", "C"], ["v", "V"], ["b", "B"], ["n", "N"], ["m", "M", "\u00b5"], [",", ";"], [".", ":"], ["-", "_"], ["Ctrl", "Ctrl"]],
+ [["Alt", "Alt"], [" ", " ", " ", " "], ["AltGr", "AltGr"]]
+ ], "lang": ["de"]
+ }
diff --git a/www/js/vkbl-en_US.json b/www/js/vkbl-en_US.json
new file mode 100644
index 0000000..7e989f9
--- /dev/null
+++ b/www/js/vkbl-en_US.json
@@ -0,0 +1,12 @@
+{
+ "name":"US International",
+ "displayname":"US International",
+ "keys": [
+ [["`", "~"], ["1", "!", "\u00a1", "\u00b9"], ["2", "@", "\u00b2"], ["3", "#", "\u00b3"], ["4", "$", "\u00a4", "\u00a3"], ["5", "%", "\u20ac"], ["6", "^", "\u00bc"], ["7", "&", "\u00bd"], ["8", "*", "\u00be"], ["9", "(", "\u2018"], ["0", ")", "\u2019"], ["-", "_", "\u00a5"], ["=", "+", "\u00d7", "\u00f7"], ["Bksp", "Bksp"]],
+ [["Tab", "Tab"], ["q", "Q", "\u00e4", "\u00c4"], ["w", "W", "\u00e5", "\u00c5"], ["e", "E", "\u00e9", "\u00c9"], ["r", "R", "\u00ae"], ["t", "T", "\u00fe", "\u00de"], ["y", "Y", "\u00fc", "\u00dc"], ["u", "U", "\u00fa", "\u00da"], ["i", "I", "\u00ed", "\u00cd"], ["o", "O", "\u00f3", "\u00d3"], ["p", "P", "\u00f6", "\u00d6"], ["[", "{", "\u00ab"], ["]", "}", "\u00bb"], ["\\", "|", "\u00ac", "\u00a6"]],
+ [["Caps", "Caps"], ["a", "A", "\u00e1", "\u00c1"], ["s", "S", "\u00df", "\u00a7"], ["d", "D", "\u00f0", "\u00d0"], ["f", "F"], ["g", "G"], ["h", "H"], ["j", "J"], ["k", "K"], ["l", "L", "\u00f8", "\u00d8"], [";", ":", "\u00b6", "\u00b0"], ["'", "%x22", "\u00b4", "\u00a8"], ["Enter", "Enter"]],
+ [["Shift", "Shift"], ["z", "Z", "\u00e6", "\u00c6"], ["x", "X"], ["c", "C", "\u00a9", "\u00a2"], ["v", "V"], ["b", "B"], ["n", "N", "\u00f1", "\u00d1"], ["m", "M", "\u00b5"], [",", "<", "\u00e7", "\u00c7"], [".", ">"], ["/", "?", "\u00bf"], ["Shift", "Shift"]],
+ [[" ", " ", " ", " "], ["Alt", "Alt"]]
+ ],
+ "lang": ["en"]
+}
diff --git a/www/js/webrdp-Log.js b/www/js/webrdp-Log.js
new file mode 100644
index 0000000..455a736
--- /dev/null
+++ b/www/js/webrdp-Log.js
@@ -0,0 +1,134 @@
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, smake <smake at ya dot ru>.
+ *
+ */
+
+webrdp.o2s = function(obj, depth) {
+ depth = depth || [];
+ if (depth.contains(obj)) {
+ return '{SELF}';
+ }
+ switch (typeof(obj)) {
+ case 'undefined':
+ return 'undefined';
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ var string = [];
+ depth.push(obj);
+ for (var i = 0; i < obj.length; ++i) {
+ string.push(webrdp.o2s(obj[i], depth));
+ }
+ depth.pop();
+ return '[' + string + ']';
+ case 'object':
+ case 'hash':
+ var string = [];
+ depth.push(obj);
+ var isE = (obj instanceof UIEvent);
+ Object.each(obj, function(v, k) {
+ if (v instanceof HTMLElement) {
+ string.push(k + '={HTMLElement}');
+ } else if (isE && (('layerX' == k) || ('layerY' == k) ('view' == k))) {
+ string.push(k + '=!0');
+ } else {
+ try {
+ var vstr = webrdp.o2s(v, depth);
+ if (vstr) {
+ string.push(k + '=' + vstr);
+ }
+ } catch (error) {
+ string.push(k + '=??E??');
+ }
+ }
+ });
+ depth.pop();
+ return '{' + string + '}';
+ case 'number':
+ case 'boolean':
+ return '' + obj;
+ case 'null':
+ return 'null';
+ }
+ return null;
+};
+
+webrdp.Log = new Class({
+ initialize: function() {
+ this.ws = null;
+ this.history = [];
+ this.loglevel = null;
+ },
+ _p: function(pfx, a) {
+ var line = '';
+ var i;
+ for (i = 0; i < a.length; ++i) {
+ switch (typeof(a[i])) {
+ case 'string':
+ case 'number':
+ case 'boolean':
+ case 'null':
+ line += a[i] + ' ';
+ break;
+ default:
+ line += webrdp.o2s(a[i]) + ' ';
+ break;
+ }
+ }
+ if (0 < line.length) {
+ this.ws.send(pfx + line);
+ }
+ },
+ drop: function() {
+ },
+ debug: function() {
+ if (this.loglevel == "debug") {
+ var entry = {
+ "date": new Date(),
+ "logtype": arguments.callee.name,
+ "direction": arguments[0],
+ "data": arguments[1]
+ }
+ this.history.push(entry);
+ }
+ },
+ info: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('I:');
+ this._p.apply(this, a);
+ }
+ },
+ warn: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('W:');
+ this._p.apply(this, a);
+ }
+ },
+ err: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('E:');
+ this._p.apply(this, a);
+ }
+ },
+ show: function(count) {
+ if (!count) {
+ var count = 30;
+ }
+ var first = this.history.length - count > 0 ? this.history.length - count : 0;
+ for (var i = first; i < this.history.length; i++) {
+ var h = this.history[i];
+ var header = '[' + h.date.toLocaleTimeString('ru') + '.' + h.date.getMilliseconds() + ']';
+ var header = '[' + h.date.toLocaleTimeString('ru') + '.' + h.date.getMilliseconds() + ']';
+ header += '[' + h.logtype + '] ' + h.direction + ':';
+ console.log(header);
+ console.hex(h.data);
+ }
+ },
+ setWS: function(_ws) {
+ this.ws = _ws;
+ }
+});
diff --git a/www/js/webrdp-debug.js b/www/js/webrdp-debug.js
new file mode 100644
index 0000000..12c7e52
--- /dev/null
+++ b/www/js/webrdp-debug.js
@@ -0,0 +1,1707 @@
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 wsgate devs, NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>.
+ *
+ */
+
+var webrdp = webrdp || {}
+
+webrdp.hasconsole = (typeof console !== 'undefined' && 'debug' in console && 'info' in console && 'warn' in console && 'error' in console);
+
+webrdp.WSrunner = new Class( {
+ Implements: Events,
+ initialize: function(url) {
+ this.url = url;
+ this.log = new webrdp.Log();
+ },
+ Run: function() {
+ try {
+ this.sock = new WebSocket(this.url);
+ } catch (err) { }
+ this.sock.binaryType = 'arraybuffer';
+ this.sock.onopen = this.onWSopen.bind(this);
+ this.sock.onclose = this.onWSclose.bind(this);
+ this.sock.onmessage = this.onWSmsg.bind(this);
+ this.sock.onerror = this.onWSerr.bind(this);
+ this.sock.sendd = this.sock.send;
+ this.sock.send = (data) => {
+ this.log.debug('wsout', data);
+ this.sock.sendd(data);
+ }
+
+ }
+});
+
+webrdp.RDP = new Class( {
+ Extends: webrdp.WSrunner,
+ initialize: function(url, canvas, controls, cssCursor, useTouch, vkbd) {
+ this.canvas = canvas;
+ this.cctx = canvas.getContext('2d');
+ this.cctx.strokeStyle = 'rgba(255,255,255,0)';
+ this.cctx.FillStyle = 'rgba(255,255,255,0)';
+ this.bstore = new Element('canvas', {
+ 'width':this.canvas.width,
+ 'height':this.canvas.height,
+ });
+ this.bctx = this.bstore.getContext('2d');
+ this.aMF = 0;
+ this.Tcool = true;
+ this.pTe = null;
+ this.ccnt = 0;
+ this.clx = 0;
+ this.cly = 0;
+ this.clw = 0;
+ this.clh = 0;
+ this.mX = 0;
+ this.mY = 0;
+ this.chx = 10;
+ this.chy = 10;
+ this.modkeys = [144, ];
+ this.cursors = new Array();
+ this.sid = null;
+ this.open = false;
+ this.cssC = cssCursor;
+ this.uT = useTouch;
+ this.controls = controls;
+ if (!cssCursor) {
+ this.cI = new Element('img', {
+ 'src': '/c_default.png',
+ 'styles': {
+ 'position': 'absolute',
+ 'z-index': 998,
+ 'left': this.mX - this.chx,
+ 'top': this.mY - this.chy
+ }
+ }).inject(document.body);
+ }
+ if (vkbd) {
+ vkbd.addEvent('vkpress', this.onKv.bind(this));
+ }
+ //browser identiying variables
+ this.msie = window.navigator.userAgent.indexOf('MSIE ');
+ this.trident = window.navigator.userAgent.indexOf('Trident/');
+ this.parent(url);
+ //add the toggle function to the keyboard language button
+ $('keyboardlanguage').addEvent('click', this.ToggleLanguageButton.bind(this));
+ },
+ Disconnect: function() {
+ this._reset();
+ },
+ SendKey: function(comb) {
+ //code 0 : ctrl+alt+delete
+ //code 1 : alt+tab
+ //code 2 : alt+tab release
+
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.log.debug('send special combination', comb);
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 3; // WSOP_CS_SPECIALCOMB
+ a[1] = comb;
+ this.sock.send(buf);
+ };
+ },
+ SendCredentials: function() {
+ var infoJSONstring = JSON.stringify(settingsGetJSON());
+ var len = infoJSONstring.length;
+ var buf = new ArrayBuffer((len + 1)*4); // 4 bytes for each char
+ var bufView = new Uint32Array(buf);
+ bufView[0] = 4; // WSOP_CS_CREDENTIAL_JSON
+ for(var i = 0; i<len; i++){
+ bufView[i+1] = infoJSONstring.charCodeAt(i);
+ }
+ this.sock.send(buf);
+ },
+ /**
+ * Multilanguage mode
+ */
+ useIME: false,
+ ToggleLanguageButton: function(){
+ if(this.useIME){
+ this.useIME = false;
+ $('keyboardlanguage').removeClass('extracommandshold');
+ }else{
+ this.useIME = true;
+ $('keyboardlanguage').addClass('extracommandshold');
+ }
+ },
+ /**
+ * Used when the special input method is on
+ */
+ IMEon: false,
+ /**
+ * The textarea element object
+ */
+ textAreaInput: null,
+ /**
+ * This function adds a textarea element on top of the canvas for the purpose of keyboard input
+ */
+ SetupCanvas: function(){
+ if(this.textAreaInput)return;
+
+ var pos = this.canvas.getPosition();
+ var size = this.canvas.getSize();
+
+ this.textAreaInput = document.createElement('textarea');
+ this.textAreaInput.rdp = this;
+
+ this.textAreaInput.set('id', 'textareainput');
+
+ this.textAreaInput.setStyle('width', size.x);
+ this.textAreaInput.setStyle('height', size.y);
+ this.textAreaInput.setStyle('position', 'absolute');
+ this.textAreaInput.setStyle('opacity', 0);
+ this.textAreaInput.setStyle('resize', 'none');
+ this.textAreaInput.setStyle('cursor', 'default');
+ this.canvas.setStyle('cursor', 'none');
+
+ this.textAreaInput.setPosition(pos);
+
+ this.textAreaInput.addEvent('keydown', this.KeyDownEvent.bind(this));
+ this.textAreaInput.addEvent('keypress', this.KeyPressEvent.bind(this));
+ this.textAreaInput.addEvent('keypress', this.KeyPressEvent.bind(this));
+ this.textAreaInput.addEvent('keyup', this.KeyUpEvent.bind(this));
+ this.textAreaInput.addEvent('copy', function(evt){
+ if (evt.preventDefault) evt.preventDefault();
+ if (evt.stopPropagation) evt.stopPropagation();
+ });
+ this.textAreaInput.addEvent('paste', function(evt){
+ if (evt.preventDefault) evt.preventDefault();
+ if (evt.stopPropagation) evt.stopPropagation();
+ // alert("pasted");
+ });
+
+
+ document.body.appendChild(this.textAreaInput);
+
+ //start the IME helper refresh
+ refreshIMEhelper();
+
+ //make sure the textarea is always on focus
+ this.textAreaInput.focus();
+ this.textAreaInput.addEvent('blur', function(){
+ setTimeout(function(){
+ if($('textareainput')){
+ var cbdcontent = document.getElementById('cbdcontent');
+ if (cbdcontent.style.visibility != "visible") {
+ $('textareainput').focus();
+ }
+ }
+ },20);
+ });
+
+ },
+ /**
+ * Creating clipboard object and setting drag`n`drop area
+ */
+ SetupDragnDrop: function(){
+ this.cbd = new ClipboardHandler(this);
+ var area = this.textAreaInput;
+ area.addEventListener("dragenter", function(evt) {
+ var drop_zone = document.createElement('div');
+ document.body.appendChild(drop_zone);
+ drop_zone.rdp = evt.target.rdp;
+
+ var html = lables.drop_here;
+ drop_zone.innerHTML = html;
+
+ var pos = event.target.getPosition();
+ // var size = event.target.getSize();
+
+ drop_zone.set('id', 'drop_zone');
+ // drop_zone.setStyle('z-index', '1000000');
+ drop_zone.setStyle('position', 'absolute');
+ drop_zone.setStyle('position', 'absolute');
+ drop_zone.setStyle('resize', 'none');
+ drop_zone.setPosition(pos);
+ drop_zone.setStyle('width', event.target.style.width);
+ drop_zone.setStyle('height', event.target.style.height);
+
+ drop_zone.addEventListener("dragover", function(evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
+ }, false);
+
+ drop_zone.addEventListener("drop", function(evt) {
+ var cbd = evt.target.rdp.cbd;
+ evt.stopPropagation();
+ evt.preventDefault();
+ cbd.ClipoboardSendFilelist(evt.dataTransfer.files);
+ var drop_zone = document.getElementById('drop_zone');
+ document.body.removeChild(drop_zone);
+ }, false);
+
+ drop_zone.addEventListener("dragleave", function(evt) {
+ var drop_zone = document.getElementById('drop_zone');
+ document.body.removeChild(drop_zone);
+ }, false);
+
+ }, false);
+ },
+ /**
+ * Returns true when a non character is pressed
+ */
+ FunctionalKey: function(key){
+ return (key != 32 && ((key <= 46) || (91 <= key && key <= 145)));
+ },
+ /**
+ * Takes the contents of the textarea and sends them to the server
+ */
+ DumpTextArea: function(key){
+ if(key==13||key==0)
+ if(this.IMEon){
+ var textinput = this.textAreaInput.get("value");
+ if(textinput!=""){
+ if(textinput[0]=='\n'){
+ textinput = textinput.substring(1);
+ }
+ this.SendUnicodeString(textinput);
+ this.textAreaInput.set("value","");
+ }
+ this.IMEon=false;
+ }
+ }, /**
+ * Position cursor image
+ */
+ cP: function() {
+ this.cI.setStyles({'left': this.mX - this.chx, 'top': this.mY - this.chy});
+ },
+ /**
+ * Check, if a given point is inside the clipping region.
+ */
+ _ckclp: function(x, y) {
+ if (this.clw || this.clh) {
+ return (
+ (x >= this.clx) &&
+ (x <= (this.clx + this.clw)) &&
+ (y >= this.cly) &&
+ (y <= (this.cly + this.clh))
+ );
+ }
+ // No clipping region
+ return true;
+ },
+ /**
+ * Main message loop.
+ */
+ _pmsg: function(data) { // process a binary RDP message from our queue
+ // 0-12 code is WSGATE protocol with a little tuning
+ // 13-XXX codes is our own protocol draft implementation
+ var op, hdr, count, rects, bmdata, rgba, compressed, i, offs, x, y, sx, sy, w, h, dw, dh, bpp, color, len;
+ op = new Uint32Array(data, 0, 1);
+ switch (op[0]) {
+ case 0x0:
+ // BeginPaint
+ // this.log.debug('BeginPaint');
+ this._ctxS();
+ break;
+ case 0x01:
+ // EndPaint
+ // this.log.debug('EndPaint');
+ this._ctxR();
+ break;
+ case 0x02:
+ // Single bitmap
+ //
+ // 0 uint32 Destination X
+ // 1 uint32 Destination Y
+ // 2 uint32 Width
+ // 3 uint32 Height
+ // 4 uint32 Destination Width
+ // 5 uint32 Destination Height
+ // 6 uint32 Bits per Pixel
+ // 7 uint32 Flag: Compressed
+ // 8 uint32 DataSize
+ //
+ hdr = new Uint32Array(data, 4, 9);
+ bmdata = new Uint8Array(data, 40);
+ x = hdr[0];
+ y = hdr[1];
+ w = hdr[2];
+ h = hdr[3];
+ dw = hdr[4];
+ dh = hdr[5];
+ bpp = hdr[6];
+ compressed = (hdr[7] != 0);
+ len = hdr[8];
+ if ((bpp == 16) || (bpp == 15)) {
+ if (this._ckclp(x, y) && this._ckclp(x + dw, y + dh)) {
+ // this.log.debug('BMi:',(compressed ? ' C ' : ' U '),' x=',x,'y=',y,' w=',w,' h=',h,' l=',len);
+ var outB = this.cctx.createImageData(w, h);
+ if (compressed) {
+ webrdp.dRLE16_RGBA(bmdata, len, w, outB.data);
+ webrdp.flipV(outB.data, w, h);
+ } else {
+ webrdp.dRGB162RGBA(bmdata, len, outB.data);
+ }
+ this.cctx.putImageData(outB, x, y, 0, 0, dw, dh);
+ } else {
+ // this.log.debug('BMc:',(compressed ? ' C ' : ' U '),' x=',x,'y=',y,' w=',w,' h=',h,' bpp=',bpp);
+ // putImageData ignores the clipping region, so we must
+ // clip ourselves: We first paint into a second canvas,
+ // then use drawImage (which honors clipping).
+
+ var outB = this.bctx.createImageData(w, h);
+ if (compressed) {
+ webrdp.dRLE16_RGBA(bmdata, len, w, outB.data);
+ webrdp.flipV(outB.data, w, h);
+ } else {
+ webrdp.dRGB162RGBA(bmdata, len, outB.data);
+ }
+ this.bctx.putImageData(outB, 0, 0, 0, 0, dw, dh);
+ this.cctx.drawImage(this.bstore, 0, 0, dw, dh, x, y, dw, dh);
+ }
+ } else {
+ this.log.warn('BPP <> 15/16 not yet implemented');
+ }
+ break;
+ case 0x03:
+ // Primary: OPAQUE_RECT_ORDER
+ // x, y , w, h, color
+ hdr = new Int32Array(data, 4, 4);
+ rgba = new Uint8Array(data, 20, 4);
+ // this.log.debug('Fill:',hdr[0], hdr[1], hdr[2], hdr[3], this._c2s(rgba));
+ this.cctx.fillStyle = this._c2s(rgba);
+ this.cctx.fillRect(hdr[0], hdr[1], hdr[2], hdr[3]);
+ break;
+ case 0x04:
+ // SetBounds
+ // left, top, right, bottom
+ hdr = new Int32Array(data, 4, 4);
+ this._cR(hdr[0], hdr[1], hdr[2] - hdr[0], hdr[3] - hdr[1], true);
+ break;
+ case 0x05:
+ // PatBlt
+ if (28 == data.byteLength) {
+ // Solid brush style
+ // x, y, width, height, fgcolor, rop3
+ hdr = new Int32Array(data, 4, 4);
+ x = hdr[0];
+ y = hdr[1];
+ w = hdr[2];
+ h = hdr[3];
+ rgba = new Uint8Array(data, 20, 4);
+ this._ctxS();
+ this._cR(x, y, w, h, false);
+ if (this._sROP(new Uint32Array(data, 24, 1)[0])) {
+ this.cctx.fillStyle = this._c2s(rgba);
+ this.cctx.fillRect(x, y, w, h);
+ }
+ this._ctxR();
+ } else {
+ this.log.warn('PatBlt: Patterned brush not yet implemented');
+ }
+ break;
+ case 0x06:
+ // Multi Opaque rect
+ // color, nrects
+ // rect1.x,rect1.y,rect1.w,rect1.h ... rectn.x,rectn.y,rectn.w,rectn.h
+ rgba = new Uint8Array(data, 4, 4);
+ count = new Uint32Array(data, 8, 1);
+ rects = new Uint32Array(data, 12, count[0] * 4);
+ // this.log.debug('MultiFill: ', count[0], " ", this._c2s(rgba));
+ this.cctx.fillStyle = this._c2s(rgba);
+ offs = 0;
+ // var c = this._c2s(rgba);
+ for (i = 0; i < count[0]; ++i) {
+ this.cctx.fillRect(rects[offs], rects[offs+1], rects[offs+2], rects[offs+3]);
+ // this._fR(rects[offs], rects[offs+1], rects[offs+2], rects[offs+3], c);
+ offs += 4;
+ }
+ break;
+ case 0x07:
+ // ScrBlt
+ // rop3, x, y, w, h, sx, sy
+ hdr = new Int32Array(data, 8, 6);
+ x = hdr[0];
+ y = hdr[1];
+ w = hdr[2];
+ h = hdr[3];
+ sx = hdr[4];
+ sy = hdr[5];
+ if ((w > 0) && (h > 0)) {
+ if (this._sROP(new Uint32Array(data, 4, 1)[0])) {
+ if (this._ckclp(x, y) && this._ckclp(x + w, y + h)) {
+ // No clipping necessary
+ this.cctx.putImageData(this.cctx.getImageData(sx, sy, w, h), x, y);
+ } else {
+ // Clipping necessary
+ this.bctx.putImageData(this.cctx.getImageData(sx, sy, w, h), 0, 0);
+ this.cctx.drawImage(this.bstore, 0, 0, w, h, x, y, w, h);
+ }
+ }
+ } else {
+ this.log.warn('ScrBlt: width and/or height is zero');
+ }
+ break;
+ case 0x08:
+ // PTR_NEW
+ // id, xhot, yhot
+ hdr = new Uint32Array(data, 4, 3);
+ var inline_cursor_base64 = btoa(String.fromCharCode.apply(null, new Uint8Array(data, 16)));
+ if (this.cssC) {
+ this.cursors[hdr[0]] = (this.msie > 0 || this.trident > 0) ? 'url(/cur/' + this.sid + '/' + hdr[0] + '), none' : //IE is not suporting given hot spots
+ 'url(data:image/vnd.microsoft.icon;base64,' + inline_cursor_base64 + ') ' + hdr[1] + ' ' + hdr[2] + ',default';
+ } else {
+ this.cursors[hdr[0]] = (this.msie > 0 || this.trident > 0) ? { u: '/cur/' + this.sid + '/' + hdr[0] } :
+ { u: inline_cursor_base64, x: hdr[1], y: hdr[2] };
+ }
+ break;
+ case 0x09:
+ // PTR_FREE
+ // id
+ this.cursors[new Uint32Array(data, 4, 1)[0]] = undefined;
+ break;
+ case 0x0a:
+ // PTR_SET
+ // id
+ // this.log.debug('PS:', this.cursors[new Uint32Array(data, 4, 1)[0]]);
+ if (this.cssC) {
+ if(this.textAreaInput)
+ this.textAreaInput.setStyle('cursor', this.cursors[new Uint32Array(data, 4, 1)[0]]);
+ } else {
+ var cobj = this.cursors[new Uint32Array(data, 4, 1)[0]];
+ this.chx = cobj.x;
+ this.chy = cobj.y;
+ this.cI.src = cobj.u;
+ }
+ break;
+ case 0x0b:
+ // PTR_SETNULL
+ if (this.cssC) {
+ if(this.textAreaInput)
+ this.textAreaInput.setStyle('cursor', 'none');
+ } else {
+ this.cI.src = '/c_none.png';
+ }
+ break;
+ case 0x0c:
+ // PTR_SETDEFAULT
+ if (this.cssC) {
+ if(this.textAreaInput)
+ this.textAreaInput.setStyle('cursor', 'default');
+ } else {
+ this.chx = 10;
+ this.chy = 10;
+ this.cI.src = '/c_default.png';
+ }
+ break;
+
+ // our draft protocol implementation follows
+ case 0x0d:
+ /* clipboard_send_formats_available
+ * this message contain list of clipboard format on server
+ * available for fetching
+ *
+ * typedef enum
+ * {
+ * clip_format_unsupported = -1,
+ * clip_format_raw,
+ * clip_format_text,
+ * clip_format_FileGroupDescriptorW
+ * }wrdp_enum_clip_format;
+ * NOTE: "wrdp_enum_clip_format" must be kept in sync with
+ * core api
+ *
+ */
+ {
+ // console.log(data.toHexdump());
+ var fmts = new Uint8Array(data, 4);
+ this.cbd.ClipbrdInfoParse(fmts);
+ }
+ break;
+ case 0x0e:
+ // clipboard_send_data
+ // this message contain actual clipboard data
+ // NOTE: not all clipboard formats will support data
+ // fetching
+ {
+ // console.log(data.toHexdump());
+ this.cbd.reciveClipboardInfo(data);
+ }
+ break;
+ case 0x0f:
+ // clipboard_request_data
+ // local clipboard data requested
+ {
+ // console.log(data.toHexdump());
+ var type = new Uint8Array(data, 4, 1);
+ this.cbd.sendClipboardInfo(type[0]);
+ }
+ break;
+ case 0x10:
+ /* ft_request
+ * this is filetransfer request
+ * must contain file id from cached list
+ */
+ {
+ // console.log(data.toHexdump());
+ var file_id = new Uint32Array(data, 4, 1)[0];
+ var req_size = new BigUint64Array(data, 4 + 4, 1)[0];
+ var file_offset = new BigUint64Array(data, 4 + 4 + 8, 1)[0];
+ file = this.cbd.filelistOut[file_id];
+ if (!file) {
+ console.log("Can`t find file for transfer")
+ return;
+ }
+
+ file.sendChunk(req_size, file_offset);
+ }
+ break;
+ case 0x11:
+ // ft_chunk
+ // this message contain transfer id and chunk of file data
+ {
+ // console.log(data.toHexdump())
+ var transfer_id = new Uint32Array(data,0,2)[1];
+ var file = null;
+ this.cbd.filelistIn.forEach(function(f) {
+ if (transfer_id == f.transfer_id) {
+ file = f
+ }
+ });
+ if (!file) {
+ this.cbd.filelistIn.forEach(function(f) {
+ if (f.status == 2) {
+ file = f
+ }
+ });
+ }
+ if (file) {
+ file.newChunk(data);
+ }
+ }
+ break;
+ case 0x12:
+ // ft_finish
+ // this message indicate what filetransfer is finished
+ {
+ // console.log(data.toHexdump());
+ this.cbd.update();
+ }
+ break;
+ default:
+ this.log.warn('Unknown BINRESP: ', data.byteLength);
+ }
+ },
+ _cR: function(x, y, w, h, save) {
+ if (save) {
+ this.clx = x;
+ this.cly = y;
+ this.clw = w;
+ this.clh = h;
+ }
+ // Replace clipping region, NO intersection.
+ this.cctx.beginPath();
+ this.cctx.rect(0, 0, this.canvas.width, this.canvas.height);
+ this.cctx.clip();
+ if (x == y == 0) {
+ // All zero means: reset to full canvas size
+ if ((w == h == 0) || ((w == this.canvas.width) && (h == this.canvas.height))) {
+ return;
+ }
+ }
+ // New clipping region
+ this.cctx.beginPath();
+ this.cctx.rect(x, y, w, h);
+ this.cctx.clip();
+ },
+ _fR: function(x, y, w, h, color) {
+ return;
+ if ((w < 2) || (h < 2)) {
+ this.cctx.strokeStyle = color;
+ this.cctx.beginPath();
+ this.cctx.moveTo(x, y);
+ if (w > h) {
+ this.cctx.lineWidth = h;
+ this.cctx.lineTo(x + w, y);
+ } else {
+ this.cctx.lineWidth = w;
+ this.cctx.lineTo(x, y + h);
+ }
+ this.cctx.stroke();
+ } else {
+ this.cctx.fillStyle = color;
+ this.cctx.fillRect(x, y, w, h);
+ }
+ },
+ _sROP: function(rop) {
+ switch (rop) {
+ case 0x005A0049:
+ // GDI_PATINVERT: D = P ^ D
+ this.cctx.globalCompositeOperation = 'xor';
+ return true;
+ break;
+ case 0x00F00021:
+ // GDI_PATCOPY: D = P
+ this.cctx.globalCompositeOperation = 'copy';
+ return true;
+ break;
+ case 0x00CC0020:
+ // GDI_SRCCOPY: D = S
+ this.cctx.globalCompositeOperation = 'source-over';
+ return true;
+ break;
+ default:
+ this.log.warn('Unsupported raster op: ', rop.toString(16));
+ break;
+ }
+ return false;
+ /*
+ case 0x00EE0086:
+ // GDI_SRCPAINT: D = S | D
+ break;
+ case 0x008800C6:
+ // GDI_SRCAND: D = S & D
+ break;
+ case 0x00660046:
+ // GDI_SRCINVERT: D = S ^ D
+ break;
+ case 0x00440328:
+ // GDI_SRCERASE: D = S & ~D
+ break;
+ case 0x00330008:
+ // GDI_NOTSRCCOPY: D = ~S
+ break;
+ case 0x001100A6:
+ // GDI_NOTSRCERASE: D = ~S & ~D
+ break;
+ case 0x00C000CA:
+ // GDI_MERGECOPY: D = S & P
+ break;
+ case 0x00BB0226:
+ // GDI_MERGEPAINT: D = ~S | D
+ break;
+ case 0x00FB0A09:
+ // GDI_PATPAINT: D = D | (P | ~S)
+ break;
+ case 0x00550009:
+ // GDI_DSTINVERT: D = ~D
+ break;
+ case 0x00000042:
+ // GDI_BLACKNESS: D = 0
+ break;
+ case 0x00FF0062:
+ // GDI_WHITENESS: D = 1
+ break;
+ case 0x00E20746:
+ // GDI_DSPDxax: D = (S & P) | (~S & D)
+ break;
+ case 0x00B8074A:
+ // GDI_PSDPxax: D = (S & D) | (~S & P)
+ break;
+ case 0x000C0324:
+ // GDI_SPna: D = S & ~P
+ break;
+ case 0x00220326:
+ // GDI_DSna D = D & ~S
+ break;
+ case 0x00220326:
+ // GDI_DSna: D = D & ~S
+ break;
+ case 0x00A000C9:
+ // GDI_DPa: D = D & P
+ break;
+ case 0x00A50065:
+ // GDI_PDxn: D = D ^ ~P
+ break;
+ */
+ },
+ /**
+ * Reset our state to disconnected
+ */
+ _reset: function() {
+ this.log.setWS(null);
+ this.fireEvent('disconnected');
+ if (this.sock.readyState == this.sock.OPEN) {
+ this.sock.close();
+ }
+ this.clx = 0;
+ this.cly = 0;
+ this.clw = 0;
+ this.clh = 0;
+ this.canvas.removeEvents();
+ document.removeEvents();
+ var drop_zone = document.getElementById('drop_zone');
+ if (drop_zone) {
+ document.body.removeChild(drop_zone);
+ }
+
+ try{
+ this.textAreaInput.remove();
+ }
+ catch(err){
+ }
+ this.textAreaInput = null;
+ while (this.ccnt > 0) {
+ this.cctx.restore();
+ this.ccnt -= 1;
+ }
+ this.cctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ document.title = document.title.replace(/:.*/, ': offline');
+ if (this.cssC) {
+ this.canvas.setStyle('cursor','default');
+ } else {
+ this.cI.src = '/c_default.png';
+ }
+ if (!this.cssC) {
+ this.cI.removeEvents();
+ this.cI.destroy();
+ }
+ },
+ fT: function() {
+ delete this.fTid;
+ if (this.pT) {
+ this.fireEvent('touch' + this.pT);
+ this.pT = 0;
+ return;
+ }
+ if (this.pTe) {
+ this.onMd(this.pTe);
+ this.pTe = null;
+ }
+ },
+ cT: function() {
+ this.log.debug('cT');
+ this.Tcool = true;
+ },
+ /**
+ * Event handler for touch start
+ */
+ onTs: function(evt) {
+ var tn = evt.targetTouches.length;
+ this.log.debug('Ts:', tn);
+ switch (tn) {
+ default:
+ break;
+ case 1:
+ this.pTe = evt;
+ evt.preventDefault();
+ if ('number' == typeof(this.fTid)) {
+ clearTimeout(this.fTid);
+ }
+ this.fTid = this.fT.delay(50, this);
+ break;
+ case 2:
+ this.pT = 2;
+ this.Tcool = false;
+ evt.preventDefault();
+ if ('number' == typeof(this.fTid)) {
+ clearTimeout(this.fTid);
+ }
+ this.cT.delay(500, this)
+ this.fTid = this.fT.delay(50, this);
+ break;
+ case 3:
+ this.pT = 3;
+ this.Tcool = false;
+ evt.preventDefault();
+ if ('number' == typeof(this.fTid)) {
+ clearTimeout(this.fTid);
+ }
+ this.cT.delay(500, this)
+ this.fTid = this.fT.delay(50, this);
+ break;
+ case 4:
+ this.pT = 4;
+ this.Tcool = false;
+ evt.preventDefault();
+ if ('number' == typeof(this.fTid)) {
+ clearTimeout(this.fTid);
+ }
+ this.cT.delay(500, this)
+ this.fTid = this.fT.delay(50, this);
+ break;
+ }
+ return true;
+ },
+ /**
+ * Event handler for touch start
+ */
+ onTe: function(evt) {
+ if ((0 == evt.targetTouches.length) && this.Tcool) {
+ evt.preventDefault();
+ this.onMu(evt, evt.changedTouches[0].pageX, evt.changedTouches[0].pageY);
+ }
+ },
+ /**
+ * Event handler for touch move
+ */
+ onTm: function(evt) {
+ // this.log.debug('Tm:', evt);
+ if (1 == evt.targetTouches.length) {
+ this.onMm(evt);
+ }
+ },
+ /**
+ * Event handler for mouse move events
+ */
+ onMm: function(evt) {
+ var buf, a, x, y;
+ evt.preventDefault();
+ x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX;
+ y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY;
+ if (!this.cssC) {
+ this.mX = x;
+ this.mY = y;
+ this.cP();
+ }
+ // this.log.debug('mM x: ', x, ' y: ', y);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(16);
+ a = new Uint32Array(buf);
+ a[0] = 0; // WSOP_CS_MOUSE
+ a[1] = 0x0800; // PTR_FLAGS_MOVE
+ a[2] = x;
+ a[3] = y;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for mouse down events
+ */
+ onMd: function(evt) {
+ var buf, a, x, y, which;
+ this.textAreaInput.focus();
+ this.cbd.update();
+ if (this.Tcool) {
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ if (evt.rightClick && evt.control && evt.alt) {
+ this.fireEvent('touch3');
+ return;
+ }
+ x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX;
+ y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY;
+ which = this._mB(evt);
+ //this.log.debug('mD b: ', which, ' x: ', x, ' y: ', y);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(16);
+ a = new Uint32Array(buf);
+ a[0] = 0; // WSOP_CS_MOUSE
+ a[1] = 0x8000 | which;
+ a[2] = x;
+ a[3] = y;
+ this.sock.send(buf);
+ this.mouseDownStatus[which] = true;
+ }
+ }
+ },
+ /**
+ * Event handler for mouse up events
+ */
+ onMu: function(evt, x, y) {
+ var buf, a, x, y, which;
+ if (this.Tcool) {
+ evt.preventDefault();
+ x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX;
+ y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY;
+ which = this._mB(evt);
+ this.log.debug('mU b: ', which, ' x: ', x, ' y: ', y);
+ if (this.aMF) {
+ this.fireEvent('mouserelease');
+ }
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(16);
+ a = new Uint32Array(buf);
+ a[0] = 0; // WSOP_CS_MOUSE
+ a[1] = which;
+ a[2] = x;
+ a[3] = y;
+ this.sock.send(buf);
+ this.mouseDownStatus[which] = false;
+ }
+ }
+ },
+ /**
+ * Event handler for mouse wheel events
+ */
+ onMw: function(evt) {
+ var buf, a, x, y;
+ evt.preventDefault();
+ x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX;
+ y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY;
+ // this.log.debug('mW d: ', evt.wheel, ' x: ', x, ' y: ', y);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(16);
+ a = new Uint32Array(buf);
+ a[0] = 0; // WSOP_CS_MOUSE
+ a[1] = 0x200 | ((evt.wheel > 0) ? 0x087 : 0x188);
+ a[2] = 0;
+ a[3] = 0;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Field used to keep the states of the sent mouse events
+ */
+ mouseDownStatus: {},
+ /**
+ * Event handler for mouse leaving the canvas area
+ * used to send mouse release for any unsent mouse releases
+ */
+ onMouseLeave: function(evt){
+ for(var button in this.mouseDownStatus){
+ if(this.mouseDownStatus[button]){
+ var x = (this.msie > 0 || this.trident > 0) ? evt.event.layerX - evt.event.currentTarget.offsetLeft : evt.event.layerX;
+ var y = (this.msie > 0 || this.trident > 0) ? evt.event.layerY - evt.event.currentTarget.offsetTop : evt.event.layerY;
+ var maxX = $('textareainput').getStyle('width').toInt();
+ var maxY = $('textareainput').getStyle('height').toInt();
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ if (x > maxX) x = maxX;
+ if (y > maxY) y = maxY;
+ var which = button;
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(16);
+ a = new Uint32Array(buf);
+ a[0] = 0; // WSOP_CS_MOUSE
+ a[1] = which;
+ a[2] = x;
+ a[3] = y;
+ this.sock.send(buf);
+ this.mouseDownStatus[which] = false;
+ }
+ }
+ }
+ },
+ /**
+ * Event handler for sending array of keys to be pressed
+ */
+ sendKeys: function(codes) {
+ // var myStringArray = ["Hello","World"];
+ for (var i = 0; i < codes.length; i++) {
+ alert(codes[i]);
+ if(this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+
+ }
+ }
+ },
+ /**
+ * Sends a unicode char or string
+ */
+ SendUnicodeString: function(str){
+ var len = str.length;
+ buf = new ArrayBuffer(4 * len + 4);
+ a = new Uint32Array(buf);
+ a[0] = 5; // WSOP_CS_UNICODE
+ for(var i = 0; i<len; i++){
+ a[i+1] = str.charCodeAt(i);
+ }
+ this.sock.send(buf);
+ },
+ /**
+ * Sends a scancode key event
+ * down = 1
+ * up = 0
+ */
+ SendKeyUpDown: function(key, upDown){
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = upDown;
+ a[2] = key;
+ this.sock.send(buf);
+ }
+ },
+ KeyDownEvent: function(evt){
+ if(!this.useIME){
+ this.SendKeyUpDown(evt.code, 1);
+ }else{
+ if(evt.code==229||evt.code==0)this.IMEon=true;
+ //send key presses only when IME is off
+ if(!this.IMEon){
+ if(this.FunctionalKey(evt.code)){
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ this.SendKeyUpDown(evt.code, 1);
+ }
+ }
+ }
+ },
+ KeyUpEvent: function(evt){
+ if(!this.useIME){
+ this.SendKeyUpDown(evt.code, 0);
+ this.textAreaInput.set("value","");
+ }else{
+ if(!this.IMEon)
+ if(this.FunctionalKey(evt.code)){
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ this.SendKeyUpDown(evt.code, 0);
+ }
+ this.DumpTextArea(evt.code);
+ }
+ },
+ /**
+ * Sends unicode to the server
+ */
+ KeyPressEvent: function(evt){
+ if(this.useIME)
+ if(!this.IMEon){
+ this.SendUnicodeString(String.fromCharCode(evt.code));
+ $('textareainput').set("value","");
+ if(evt.preventDefault) evt.preventDefault();
+ if(evt.stopPropagation) evt.stopPropagation();
+ }
+ this.DumpTextArea(evt.code);
+ },
+ /**
+ * Event handler for key down events
+ */
+ onKd: function(evt) {
+ var a, buf;
+ this.log.debug('kD code: ', evt.code, ' ', evt);
+ evt.preventDefault();
+ // this.log.debug('kD code: ', evt.code, ' ', evt);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 1; // down
+ a[2] = evt.code;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for key up events
+ */
+ onKu: function(evt) {
+ var a, buf;
+ evt.preventDefault();
+ this.log.debug('ku code: ', evt.code, ' ', evt);
+ if (this.sock.readyState == this.sock.OPEN) {
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 0; // up
+ a[2] = evt.code;
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for virtual keyboard
+ */
+ onKv: function(evt) {
+ var a, buf;
+ if (this.sock.readyState == this.sock.OPEN) {
+ // this.log.debug('kP code: ', evt.code);
+ buf = new ArrayBuffer(12);
+ a = new Uint32Array(buf);
+ if (evt.special) {
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 1; // down
+ a[2] = evt.code;
+ this.sock.send(buf);
+ a[0] = 1; // WSOP_CS_KUPDOWN
+ a[1] = 0; // up
+ a[2] = evt.code;
+ } else {
+ a[0] = 2; // WSOP_CS_KPRESS
+ a[1] = (evt.shift ? 1 : 0)|(evt.control ? 2 : 0)|(evt.alt ? 4 : 0)|(evt.meta ? 8 : 0);
+ a[2] = evt.code;
+ }
+ this.sock.send(buf);
+ }
+ },
+ /**
+ * Event handler for key pressed events
+ Obsv: not used anymore. Will be removed after checking it's dependants.
+ */
+ onKp: function(evt) {
+ return;
+ },
+ /**
+ * Event handler for WebSocket RX events
+ */
+ onWSmsg: function(evt) {
+ //hide the loading image when the actual streaming starts
+ if ($('dvLoading').getStyle("visibility") !== "hidden") {
+ $('dvLoading').setStyles({ 'visibility': 'hidden' });
+ }
+ switch (typeof(evt.data)) {
+ // We use text messages for alerts and debugging ...
+ case 'string':
+ // this.log.debug(evt.data);
+ switch (evt.data.substr(0,2)) {
+ case "T:":
+ this._reset();
+ break;
+ case "0:":
+ //handle control message here
+ break;
+ case "E:":
+ var msg = evt.data.substring(2);
+ if(msg.substring(0, 2)=='E:'){
+ embedded = true;
+ msg = msg.substring(2);
+ }
+ this.log.err(msg);
+ this.fireEvent('alert', msg);
+ this._reset();
+ break;
+ case 'I:':
+ this.log.info(evt.data.substring(2));
+ break;
+ case 'W:':
+ this.log.warn(evt.data.substring(2));
+ break;
+ case 'D:':
+ this.log.debug(evt.data.substring(2));
+ break;
+ case 'S:':
+ this.sid = evt.data.substring(2);
+ break;
+ case 'R:':
+ //resolution changed
+ resolution=evt.data.substr(2).split('x');
+ $('screen').width=resolution[0];
+ $('screen').height=resolution[1];
+ this.bstore.width=resolution[0];
+ this.bstore.height=resolution[1];
+ $('textareainput').setStyle('width', resolution[0]+'px');
+ $('textareainput').setStyle('height', resolution[1]+'px');
+ break;
+ case 'C:':
+ var msg = evt.data.substr(2);
+ if(msg.substr(0, 2) == 'E:'){
+ msg = msg.substr(2);
+ embedded = true;
+ }
+ if(msg == "RDP session connection started."){
+ //the connection worked so we can set the cookies
+ //settingsSet();
+ return;
+ }
+ break;
+ }
+ break;
+ // ... and binary messages for the actual RDP stuff.
+ case 'object':
+ this.log.debug('wsin', evt.data);
+ this._pmsg(evt.data);
+ break;
+ }
+
+ },
+ /**
+ * Event handler for WebSocket connect events
+ */
+ onWSopen: function(evt) {
+ this.open = true;
+ this.log.setWS(this.sock);
+ //add the textarea on top of the canvas
+ this.SetupCanvas();
+ this.SetupDragnDrop();
+ // Add listeners for the various input events
+ this.textAreaInput.addEvent('mousemove', this.onMm.bind(this));
+ this.textAreaInput.addEvent('mousedown', this.onMd.bind(this));
+ this.textAreaInput.addEvent('mouseup', this.onMu.bind(this));
+ this.textAreaInput.addEvent('mousewheel', this.onMw.bind(this));
+ this.textAreaInput.addEvent('mouseleave', this.onMouseLeave.bind(this));
+ // Disable the browser's context menu
+ this.textAreaInput.addEvent('contextmenu', function(e) {e.stop();});
+ // For touch devices
+ if (this.uT) {
+ this.textAreaInput.addEvent('touchstart', this.onTs.bind(this));
+ this.textAreaInput.addEvent('touchend', this.onTe.bind(this));
+ this.textAreaInput.addEvent('touchmove', this.onTm.bind(this));
+ }
+ if (!this.cssC) {
+ // Same events on pointer image
+ this.cI.addEvent('mousemove', this.onMm.bind(this));
+ this.cI.addEvent('mousedown', this.onMd.bind(this));
+ this.cI.addEvent('mouseup', this.onMu.bind(this));
+ this.cI.addEvent('mousewheel', this.onMw.bind(this));
+ this.cI.addEvent('contextmenu', function(e) {e.stop();});
+ if (this.uT) {
+ this.cI.addEvent('touchstart', this.onTs.bind(this));
+ this.cI.addEvent('touchend', this.onTe.bind(this));
+ this.cI.addEvent('touchmove', this.onTm.bind(this));
+ }
+ }
+ this.fireEvent('connected');
+ this.SendCredentials();
+ },
+ /**
+ * Event handler for WebSocket disconnect events
+ */
+ onWSclose: function(evt) {
+ /*if (Browser.name == 'chrome') {
+ // Current chrome is buggy in that it does not
+ // fire WebSockets error events, so we use the
+ // wasClean flag in the close event.
+ if ((!evt.wasClean) && (!this.open)) {
+ this.fireEvent('alert', 'Could not connect to WebSockets gateway');
+ }
+ }*/
+ //this.open = false;
+ this._reset();
+ },
+ /**
+ * Event handler for WebSocket error events
+ */
+ onWSerr: function (evt) {
+ this.open = false;
+ switch (this.sock.readyState) {
+ case this.sock.CONNECTING:
+ this.fireEvent('alert', 'Could not connect to WebSockets gateway');
+ break;
+ }
+ this._reset();
+ },
+ /**
+ * Convert a color value contained in an uint8 array into an rgba expression
+ * that can be used to parameterize the canvas.
+ */
+ _c2s: function(c) {
+ return 'rgba' + '(' + c[0] + ',' + c[1] + ',' + c[2] + ',' + ((0.0 + c[3]) / 255) + ')';
+ },
+ /**
+ * Save the canvas state and remember this in our object.
+ */
+ _ctxS: function() {
+ this.cctx.save();
+ this.ccnt += 1;
+ },
+ /**
+ * Restore the canvas state and remember this in our object.
+ */
+ _ctxR: function() {
+ this.cctx.restore();
+ this.ccnt -= 1;
+ },
+ /**
+ * Convert the button information of a mouse event into
+ * RDP-like flags.
+ */
+ _mB: function(evt) {
+ if (this.aMF) {
+ return this.aMF;
+ }
+ var bidx;
+ if ('event' in evt && 'button' in evt.event) {
+ bidx = evt.event.button;
+ } else {
+ bidx = evt.rightClick ? 2 : 0;
+ }
+ switch (bidx) {
+ case 0:
+ return 0x1000; // left button
+ case 1:
+ return 0x4000; // middle button
+ case 2:
+ return 0x2000; // right button
+ }
+ return 0x1000;
+ },
+ SetArtificialMouseFlags: function(mf) {
+ if (null == mf) {
+ this.aMF = 0;
+ return;
+ }
+ this.aMF = 0x1000; // left button
+ if (mf.r) {
+ this.aMF = 0x2000; // right
+ }
+ if (mf.m) {
+ this.aMF = 0x4000; // middle
+ }
+ }
+});
+webrdp.copyRGBA = function(inA, inI, outA, outI) {
+ if ('subarray' in inA) {
+ outA.set(inA.subarray(inI, inI + 4), outI);
+ } else {
+ outA[outI++] = inA[inI++];
+ outA[outI++] = inA[inI++];
+ outA[outI++] = inA[inI++];
+ outA[outI] = inA[inI];
+ }
+}
+webrdp.xorbufRGBAPel16 = function(inA, inI, outA, outI, pel) {
+ var pelR = (pel & 0xF800) >> 11;
+ var pelG = (pel & 0x7E0) >> 5;
+ var pelB = pel & 0x1F;
+ // 656 -> 888
+ pelR = (pelR << 3 & ~0x7) | (pelR >> 2);
+ pelG = (pelG << 2 & ~0x3) | (pelG >> 4);
+ pelB = (pelB << 3 & ~0x7) | (pelB >> 2);
+
+ outA[outI++] = inA[inI++] ^ pelR;
+ outA[outI++] = inA[inI++] ^ pelG;
+ outA[outI++] = inA[inI] ^ pelB;
+ outA[outI] = 255; // alpha
+}
+webrdp.buf2RGBA = function(inA, inI, outA, outI) {
+ var pel = inA[inI] | (inA[inI + 1] << 8);
+ var pelR = (pel & 0xF800) >> 11;
+ var pelG = (pel & 0x7E0) >> 5;
+ var pelB = pel & 0x1F;
+ // 656 -> 888
+ pelR = (pelR << 3 & ~0x7) | (pelR >> 2);
+ pelG = (pelG << 2 & ~0x3) | (pelG >> 4);
+ pelB = (pelB << 3 & ~0x7) | (pelB >> 2);
+
+ outA[outI++] = pelR;
+ outA[outI++] = pelG;
+ outA[outI++] = pelB;
+ outA[outI] = 255; // alpha
+}
+webrdp.pel2RGBA = function (pel, outA, outI) {
+ var pelR = (pel & 0xF800) >> 11;
+ var pelG = (pel & 0x7E0) >> 5;
+ var pelB = pel & 0x1F;
+ // 656 -> 888
+ pelR = (pelR << 3 & ~0x7) | (pelR >> 2);
+ pelG = (pelG << 2 & ~0x3) | (pelG >> 4);
+ pelB = (pelB << 3 & ~0x7) | (pelB >> 2);
+
+ outA[outI++] = pelR;
+ outA[outI++] = pelG;
+ outA[outI++] = pelB;
+ outA[outI] = 255; // alpha
+}
+
+webrdp.flipV = function(inA, width, height) {
+ var sll = width * 4;
+ var half = height / 2;
+ var lbot = sll * (height - 1);
+ var ltop = 0;
+ var tmp = new Uint8Array(sll);
+ var i, j;
+ if ('subarray' in inA) {
+ for (i = 0; i < half ; ++i) {
+ tmp.set(inA.subarray(ltop, ltop + sll));
+ inA.set(inA.subarray(lbot, lbot + sll), ltop);
+ inA.set(tmp, lbot);
+ ltop += sll;
+ lbot -= sll;
+ }
+ } else {
+ for (i = 0; i < half ; ++i) {
+ for (j = 0; j < sll; ++j) {
+ tmp[j] = inA[ltop + j];
+ inA[ltop + j] = inA[lbot + j];
+ inA[lbot + j] = tmp[j];
+ }
+ ltop += sll;
+ lbot -= sll;
+ }
+ }
+}
+
+webrdp.dRGB162RGBA = function(inA, inLength, outA) {
+ var inI = 0;
+ var outI = 0;
+ while (inI < inLength) {
+ webrdp.buf2RGBA(inA, inI, outA, outI);
+ inI += 2;
+ outI += 4;
+ }
+}
+
+webrdp.ExtractCodeId = function(bOrderHdr) {
+ var code;
+ switch (bOrderHdr) {
+ case 0xF0:
+ case 0xF1:
+ case 0xF6:
+ case 0xF8:
+ case 0xF3:
+ case 0xF2:
+ case 0xF7:
+ case 0xF4:
+ case 0xF9:
+ case 0xFA:
+ case 0xFD:
+ case 0xFE:
+ return bOrderHdr;
+ }
+ code = bOrderHdr >> 5;
+ switch (code) {
+ case 0x00:
+ case 0x01:
+ case 0x03:
+ case 0x02:
+ case 0x04:
+ return code;
+ }
+ return bOrderHdr >> 4;
+}
+webrdp.ExtractRunLength = function(code, inA, inI, advance) {
+ var runLength = 0;
+ var ladvance = 1;
+ switch (code) {
+ case 0x02:
+ runLength = inA[inI] & 0x1F;
+ if (0 == runLength) {
+ runLength = inA[inI + 1] + 1;
+ ladvance += 1;
+ } else {
+ runLength *= 8;
+ }
+ break;
+ case 0x0D:
+ runLength = inA[inI] & 0x0F;
+ if (0 == runLength) {
+ runLength = inA[inI + 1] + 1;
+ ladvance += 1;
+ } else {
+ runLength *= 8;
+ }
+ break;
+ case 0x00:
+ case 0x01:
+ case 0x03:
+ case 0x04:
+ runLength = inA[inI] & 0x1F;
+ if (0 == runLength) {
+ runLength = inA[inI + 1] + 32;
+ ladvance += 1;
+ }
+ break;
+ case 0x0C:
+ case 0x0E:
+ runLength = inA[inI] & 0x0F;
+ if (0 == runLength) {
+ runLength = inA[inI + 1] + 16;
+ ladvance += 1;
+ }
+ break;
+ case 0xF0:
+ case 0xF1:
+ case 0xF6:
+ case 0xF8:
+ case 0xF3:
+ case 0xF2:
+ case 0xF7:
+ case 0xF4:
+ runLength = inA[inI + 1] | (inA[inI + 2] << 8);
+ ladvance += 2;
+ break;
+ }
+ advance.val = ladvance;
+ return runLength;
+}
+
+webrdp.WriteFgBgImage16toRGBA = function(outA, outI, rowDelta, bitmask, fgPel, cBits) {
+ var cmpMask = 0x01;
+
+ while (cBits-- > 0) {
+ if (bitmask & cmpMask) {
+ webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel);
+ } else {
+ webrdp.copyRGBA(outA, outI - rowDelta, outA, outI);
+ }
+ outI += 4;
+ cmpMask <<= 1;
+ }
+ return outI;
+}
+
+webrdp.WriteFirstLineFgBgImage16toRGBA = function(outA, outI, bitmask, fgPel, cBits) {
+ var cmpMask = 0x01;
+
+ while (cBits-- > 0) {
+ if (bitmask & cmpMask) {
+ webrdp.pel2RGBA(fgPel, outA, outI);
+ } else {
+ webrdp.pel2RGBA(0, outA, outI);
+ }
+ outI += 4;
+ cmpMask <<= 1;
+ }
+ return outI;
+}
+
+webrdp.dRLE16_RGBA = function(inA, inLength, width, outA) {
+ var runLength;
+ var code, pixelA, pixelB, bitmask;
+ var inI = 0;
+ var outI = 0;
+ var fInsertFgPel = false;
+ var fFirstLine = true;
+ var fgPel = 0xFFFFFF;
+ var rowDelta = width * 4;
+ var advance = {val: 0};
+
+ while (inI < inLength) {
+ if (fFirstLine) {
+ if (outI >= rowDelta) {
+ fFirstLine = false;
+ fInsertFgPel = false;
+ }
+ }
+ code = webrdp.ExtractCodeId(inA[inI]);
+ if (code == 0x00 || code == 0xF0) {
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ if (fFirstLine) {
+ if (fInsertFgPel) {
+ webrdp.pel2RGBA(fgPel, outA, outI);
+ outI += 4;
+ runLength -= 1;
+ }
+ while (runLength > 0) {
+ webrdp.pel2RGBA(0, outA, outI);
+ runLength -= 1;
+ outI += 4;
+ }
+ } else {
+ if (fInsertFgPel) {
+ webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel);
+ outI += 4;
+ runLength -= 1;
+ }
+ while (runLength > 0) {
+ webrdp.copyRGBA(outA, outI - rowDelta, outA, outI);
+ runLength -= 1;
+ outI += 4;
+ }
+ }
+ fInsertFgPel = true;
+ continue;
+ }
+ fInsertFgPel = false;
+ switch (code) {
+ case 0x01:
+ case 0xF1:
+ case 0x0C:
+ case 0xF6:
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ if (code == 0x0C || code == 0xF6) {
+ fgPel = inA[inI] | (inA[inI + 1] << 8);
+ inI += 2;
+ }
+ if (fFirstLine) {
+ while (runLength > 0) {
+ webrdp.pel2RGBA(fgPel, outA, outI);
+ runLength -= 1;
+ outI += 4;
+ }
+ } else {
+ while (runLength > 0) {
+ webrdp.xorbufRGBAPel16(outA, outI - rowDelta, outA, outI, fgPel);
+ runLength -= 1;
+ outI += 4;
+ }
+ }
+ break;
+ case 0x0E:
+ case 0xF8:
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ pixelA = inA[inI] | (inA[inI + 1] << 8);
+ inI += 2;
+ pixelB = inA[inI] | (inA[inI + 1] << 8);
+ inI += 2;
+ while (runLength > 0) {
+ webrdp.pel2RGBA(pixelA, outA, outI);
+ outI += 4;
+ webrdp.pel2RGBA(pixelB, outA, outI);
+ outI += 4;
+ runLength -= 1;
+ }
+ break;
+ case 0x03:
+ case 0xF3:
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ pixelA = inA[inI] | (inA[inI + 1] << 8);
+ inI += 2;
+ while (runLength > 0) {
+ webrdp.pel2RGBA(pixelA, outA, outI);
+ outI += 4;
+ runLength -= 1;
+ }
+ break;
+ case 0x02:
+ case 0xF2:
+ case 0x0D:
+ case 0xF7:
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ if (code == 0x0D || code == 0xF7) {
+ fgPel = inA[inI] | (inA[inI + 1] << 8);
+ inI += 2;
+ }
+ if (fFirstLine) {
+ while (runLength >= 8) {
+ bitmask = inA[inI++];
+ outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, bitmask, fgPel, 8);
+ runLength -= 8;
+ }
+ } else {
+ while (runLength >= 8) {
+ bitmask = inA[inI++];
+ outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, bitmask, fgPel, 8);
+ runLength -= 8;
+ }
+ }
+ if (runLength > 0) {
+ bitmask = inA[inI++];
+ if (fFirstLine) {
+ outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, bitmask, fgPel, runLength);
+ } else {
+ outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, bitmask, fgPel, runLength);
+ }
+ }
+ break;
+ case 0x04:
+ case 0xF4:
+ runLength = webrdp.ExtractRunLength(code, inA, inI, advance);
+ inI += advance.val;
+ while (runLength > 0) {
+ webrdp.pel2RGBA(inA[inI] | (inA[inI + 1] << 8), outA, outI);
+ inI += 2;
+ outI += 4;
+ runLength -= 1;
+ }
+ break;
+ case 0xF9:
+ inI += 1;
+ if (fFirstLine) {
+ outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, 0x03, fgPel, 8);
+ } else {
+ outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, 0x03, fgPel, 8);
+ }
+ break;
+ case 0xFA:
+ inI += 1;
+ if (fFirstLine) {
+ outI = webrdp.WriteFirstLineFgBgImage16toRGBA(outA, outI, 0x05, fgPel, 8);
+ } else {
+ outI = webrdp.WriteFgBgImage16toRGBA(outA, outI, rowDelta, 0x05, fgPel, 8);
+ }
+ break;
+ case 0xFD:
+ inI += 1;
+ webrdp.pel2RGBA(0xFFFF, outA, outI);
+ outI += 4;
+ break;
+ case 0xFE:
+ inI += 1;
+ webrdp.pel2RGBA(0, outA, outI);
+ outI += 4;
+ break;
+ }
+ }
+}
+/**
+ * Continuos refresh for the IMEhelper
+ */
+function refreshIMEhelper(){
+ setTimeout(this.refreshIMEhelper,20);
+ //IME helper div
+ if(rdp.IMEon){
+ $('IMEhelper').setStyle('visibility','visible');
+ $('IMEhelper').set('html',$('textareainput').get('value'));
+ }else{
+ $('IMEhelper').setStyle('visibility','hidden');
+ }
+}
diff --git a/www/js/webrdp-ext.js b/www/js/webrdp-ext.js
new file mode 100644
index 0000000..9a96b23
--- /dev/null
+++ b/www/js/webrdp-ext.js
@@ -0,0 +1,19 @@
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, smake <smake at ya dot ru>.
+ *
+ */
+
+ArrayBuffer.prototype.toHexdump = function () {
+ return new Uint8Array(this).reduce(
+ (p, c, i, a) => p + (
+ i % 16 === 0 ? i.toString(16).padStart(6, 0) + ' ' : ' '
+ ) + c.toString(16).padStart(2, 0) + (
+ i === a.length - 1 || i % 16 === 15 ?
+ ' '.repeat((15 - i % 16) * 3) + Array.from(a).splice(i - i % 16, 16).reduce((r, v) => r + (
+ v > 31 && v < 127 || v > 159 ? String.fromCharCode(v) : '.'
+ ), ' ') + '\n' : ''
+ ), ''
+ );
+
+}
diff --git a/www/js/webrdp-log.js b/www/js/webrdp-log.js
new file mode 100644
index 0000000..62f17f6
--- /dev/null
+++ b/www/js/webrdp-log.js
@@ -0,0 +1,147 @@
+/** BSD-2-Clause license
+ *
+ * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, smake <smake at ya dot ru>.
+ *
+ */
+
+webrdp.o2s = function(obj, depth) {
+ depth = depth || [];
+ if (depth.contains(obj)) {
+ return '{SELF}';
+ }
+ switch (typeof(obj)) {
+ case 'undefined':
+ return 'undefined';
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
+ case 'array':
+ var string = [];
+ depth.push(obj);
+ for (var i = 0; i < obj.length; ++i) {
+ string.push(webrdp.o2s(obj[i], depth));
+ }
+ depth.pop();
+ return '[' + string + ']';
+ case 'object':
+ case 'hash':
+ var string = [];
+ depth.push(obj);
+ var isE = (obj instanceof UIEvent);
+ Object.each(obj, function(v, k) {
+ if (v instanceof HTMLElement) {
+ string.push(k + '={HTMLElement}');
+ } else if (isE && (('layerX' == k) || ('layerY' == k) ('view' == k))) {
+ string.push(k + '=!0');
+ } else {
+ try {
+ var vstr = webrdp.o2s(v, depth);
+ if (vstr) {
+ string.push(k + '=' + vstr);
+ }
+ } catch (error) {
+ string.push(k + '=??E??');
+ }
+ }
+ });
+ depth.pop();
+ return '{' + string + '}';
+ case 'number':
+ case 'boolean':
+ return '' + obj;
+ case 'null':
+ return 'null';
+ }
+ return null;
+};
+
+webrdp.Log = new Class({
+ initialize: function() {
+ this.ws = null;
+ this.history = [];
+ this.loglevel = null;
+ this.debuglevel = 0;
+ },
+ _p: function(pfx, a) {
+ var line = '';
+ var i;
+ for (i = 0; i < a.length; ++i) {
+ switch (typeof(a[i])) {
+ case 'string':
+ case 'number':
+ case 'boolean':
+ case 'null':
+ line += a[i] + ' ';
+ break;
+ default:
+ line += webrdp.o2s(a[i]) + ' ';
+ break;
+ }
+ }
+ if (0 < line.length) {
+ this.ws.send(pfx + line);
+ }
+ },
+ drop: function() {
+ },
+ debug: function() {
+ if (this.loglevel == "debug") {
+ var entry = {
+ "date": new Date(),
+ "logtype": arguments.callee.name,
+ "direction": arguments[0],
+ "data": arguments[1]
+ }
+ this.history.push(entry);
+ }
+ },
+ info: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('I:');
+ this._p.apply(this, a);
+ }
+ },
+ warn: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('W:');
+ this._p.apply(this, a);
+ }
+ },
+ err: function() {
+ if (this.ws) {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('E:');
+ this._p.apply(this, a);
+ }
+ },
+ show: function(count) {
+ if (!count) {
+ var count = 30;
+ }
+ var first = this.history.length - count > 0 ? this.history.length - count : 0;
+ for (var i = first; i < this.history.length; i++) {
+ var h = this.history[i];
+ var header = '[' + h.date.toLocaleTimeString('ru') + '.' + h.date.getMilliseconds() + ']';
+ header += '[' + h.logtype + '] ' + h.direction + ':';
+ console.log(header);
+ console.hex(h.data);
+ }
+ },
+ setWS: function(_ws) {
+ this.ws = _ws;
+ }
+});
+
+
+
+/**
+ * For debugging
+ */
+
+console.hex = (d) => console.log((Object(d).buffer instanceof ArrayBuffer ? new Uint8Array(d.buffer) :
+typeof d === 'string' ? (new TextEncoder('utf-8')).encode(d) :
+new Uint8ClampedArray(d)).reduce((p, c, i, a) => p + (i % 16 === 0 ? i.toString(16).padStart(6, 0) + ' ' : ' ') +
+c.toString(16).padStart(2, 0) + (i === a.length - 1 || i % 16 === 15 ?
+' '.repeat((15 - i % 16) * 3) + Array.from(a).splice(i - i % 16, 16).reduce((r, v) =>
+r + (v > 31 && v < 127 || v > 159 ? String.fromCharCode(v) : '.'), ' ') + '\n' : ''), ''));