Initialer Import
authorPascal Hofmann <mail@pascalhofmann.de>
Sat, 24 Jul 2010 13:29:25 +0000 (15:29 +0200)
committerPascal Hofmann <mail@pascalhofmann.de>
Sat, 24 Jul 2010 13:29:25 +0000 (15:29 +0200)
50 files changed:
TODO [new file with mode: 0644]
config.php.sample [new file with mode: 0644]
images/activity_indicator.gif [new file with mode: 0644]
include/cryptography.php [new file with mode: 0644]
include/database.php [new file with mode: 0644]
include/dtaus.php [new file with mode: 0644]
include/earnings.php [new file with mode: 0644]
include/events.php [new file with mode: 0644]
include/expenses.php [new file with mode: 0644]
include/fees.php [new file with mode: 0644]
include/finances.php [new file with mode: 0644]
include/html.php [new file with mode: 0644]
include/installation.php [new file with mode: 0644]
include/members.php [new file with mode: 0644]
include/misc.php [new file with mode: 0644]
include/payments.php [new file with mode: 0644]
index.php [new file with mode: 0644]
js/crypto-js/VERSION [new file with mode: 0644]
js/crypto-js/aes/aes-min.js [new file with mode: 0755]
js/crypto-js/aes/aes.js [new file with mode: 0755]
js/crypto-js/aes/aes_modified.js [new file with mode: 0755]
js/crypto-js/cbc/cbc-min.js [new file with mode: 0755]
js/crypto-js/cbc/cbc.js [new file with mode: 0755]
js/crypto-js/crypto-md5/crypto-md5.js [new file with mode: 0755]
js/crypto-js/crypto-sha1-hmac-pbkdf2-marc4/crypto-sha1-hmac-pbkdf2-marc4.js [new file with mode: 0755]
js/crypto-js/crypto-sha1-hmac-pbkdf2-ofb-aes/crypto-sha1-hmac-pbkdf2-ofb-aes.js [new file with mode: 0755]
js/crypto-js/crypto-sha1-hmac-pbkdf2-rabbit/crypto-sha1-hmac-pbkdf2-rabbit.js [new file with mode: 0755]
js/crypto-js/crypto-sha1-hmac-pbkdf2/crypto-sha1-hmac-pbkdf2.js [new file with mode: 0755]
js/crypto-js/crypto-sha1/crypto-sha1.js [new file with mode: 0755]
js/crypto-js/crypto-sha256/crypto-sha256.js [new file with mode: 0755]
js/crypto-js/crypto/crypto-min.js [new file with mode: 0755]
js/crypto-js/crypto/crypto.js [new file with mode: 0755]
js/crypto-js/hmac/hmac-min.js [new file with mode: 0755]
js/crypto-js/hmac/hmac.js [new file with mode: 0755]
js/crypto-js/marc4/marc4-min.js [new file with mode: 0755]
js/crypto-js/marc4/marc4.js [new file with mode: 0755]
js/crypto-js/md5/md5-min.js [new file with mode: 0755]
js/crypto-js/md5/md5.js [new file with mode: 0755]
js/crypto-js/ofb/ofb-min.js [new file with mode: 0755]
js/crypto-js/ofb/ofb.js [new file with mode: 0755]
js/crypto-js/pbkdf2/pbkdf2-min.js [new file with mode: 0755]
js/crypto-js/pbkdf2/pbkdf2.js [new file with mode: 0755]
js/crypto-js/rabbit/rabbit-min.js [new file with mode: 0755]
js/crypto-js/rabbit/rabbit.js [new file with mode: 0755]
js/crypto-js/sha1/sha1-min.js [new file with mode: 0755]
js/crypto-js/sha1/sha1.js [new file with mode: 0755]
js/crypto-js/sha256/sha256-min.js [new file with mode: 0755]
js/crypto-js/sha256/sha256.js [new file with mode: 0755]
js/dtaus.js [new file with mode: 0644]
js/jquery-1.4.2.min.js [new file with mode: 0644]

diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..8afade1
--- /dev/null
+++ b/TODO
@@ -0,0 +1 @@
+- USt.
diff --git a/config.php.sample b/config.php.sample
new file mode 100644 (file)
index 0000000..36a9cfb
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+define ('IS_INSTALLED', 0);
+define ('DB_USER',      'cccffm');
+define ('DB_PASSWORD',  'geheim');
+define ('DB_NAME',      'cccffm');
+define ('DB_HOST',      'localhost');
+define ('DB_CHARSET',   'utf8');
+define ('DB_PREFIX',    'memberdb_');
+
+define ('FOUNDING_DATE',    strtotime('02.10.2009'));
+define ('DIRECTDEBIT_DATE', strtotime('01.07.2010'));
+
+define ('ACCOUNT_HOLDER', 'CCCFFM e.V.');
+define ('ACCOUNT_NUMBER', '1234567890');
+define ('BANK_CODE',      '0987654321');
+
+
diff --git a/images/activity_indicator.gif b/images/activity_indicator.gif
new file mode 100644 (file)
index 0000000..7e8cc1b
Binary files /dev/null and b/images/activity_indicator.gif differ
diff --git a/include/cryptography.php b/include/cryptography.php
new file mode 100644 (file)
index 0000000..58a6b10
--- /dev/null
@@ -0,0 +1,406 @@
+<?php
+
+/* CRYPTOGRAPHY DOCUMENTATION {{{
+
+Derived from http://clemens.endorphin.org/TKS1-draft.pdf
+
+1. Initial setup
+    1.1 Ask for password1
+    1.2 Generate random bytes (salt1)
+    1.3 Generate random bytes (salt2)
+    1.4 Generate random bytes (secret)
+    1.6 Create userkey using PBKDF2 with password1, salt1 (1000 iterations)
+    1.5 Create masterkey using PBKDF2 with secret, salt2 (1000 iterations)
+    1.7 Encrypt masterkey using AES with userkey
+    1.8 Create sha1 hash of masterkey+userkey1+salt1
+    1.9 Store data: key=1.7 salt=1.2, control=1.8
+    
+2. Generate new key
+    2.1 Ask for password1 (existing)
+    2.2 Ask for password2 (new one)
+    2.3 iterate through valid keys
+        2.3.1 Create userkey1 using PBKDF2 with password1, salt1 (from database) - (1000 iterations)
+        2.3.2 Decrypt key1 (from database) using AES with userkey1
+        2.3.3 Create sha1 hash of masterkey+userkey1+salt1, afterwards compare with control1
+        2.3.4 if equal we have the correct row and the correct master key
+    2.4 Generate random bytes (salt2)
+    2.5 Create userkey2 using PBKDF2 with password2, salt2 (1000 iterations)
+    2.6 Encrypt masterkey (from 2.3) using AES with userkey2
+    2.7 Create sha1 hash of masterkey+userkey2+salt2
+    2.8 Store data: key=2.6 salt=2.4, control=2.7
+
+3. Delete key
+    3.1 Ask for password
+    3.2 iterate through valid keys
+        3.2.1 Create userkey1 using PBKDF2 with password, salt (from database) - (1000 iterations)
+        3.2.2 Decrypt key (from database) using AES with userkey
+        3.2.3 Create sha1 hash of masterkey+userkey+salt, afterwards compare with control
+        3.2.4 if equal we have the correct row and the correct master key
+        3.2.5 delete the row
+
+4. Encrypt/Decrypt data
+    4.1 Ask for password
+    4.2 iterate through valid keys
+        4.2.1 Create userkey using PBKDF2 with password, salt (from database) - (1000 iterations)
+        4.2.2 Decrypt key (from database) using AES with userkey
+        4.2.3 Create sha1 hash of masterkey+userkey+salt, afterwards compare with control
+        4.2.4 if equal we have the correct row and the correct master key
+    4.2 Encrypt/Decrypt data using AES with masterkey
+    
+
+PROBLEM: AES uses a random initialization vector and creates different output for same parameters
+
+}}} */
+
+
+$SQL_CREATE_CRYPTO = sprintf('
+    CREATE TABLE IF NOT EXISTS `%1$s` (
+      `description` varchar(255) NOT NULL,
+      `masterkey` blob NOT NULL,
+      `salt` blob NOT NULL,
+      `control` blob NOT NULL,
+      `created_at` datetime NOT NULL,
+      `modified_at` datetime NOT NULL
+    ) ENGINE=InnoDB DEFAULT CHARSET=%2$s
+    ', DB_TABLE_CRYPTO, DB_CHARSET);
+
+/* DB functions {{{ */
+function db_get_crypto_keys() {
+    $sql = 'SELECT * FROM %1$s';
+    return db_select_multi(sprintf($sql, DB_TABLE_CRYPTO));
+}
+
+function db_add_crypto_key($key) {
+    $key['created_at']    = db_unixtime2datetime(time());
+    $key['modified_at']   = db_unixtime2datetime(time());
+    return (db_insert_single(DB_TABLE_CRYPTO, $key));
+}
+
+/* }}} */
+
+function validate_crypto_key($userdata, &$dbdata, &$validation) {/*{{{*/
+    $fields = array(
+        'description'   => 'string',
+        'masterkey'     => 'string',
+        'salt'          => 'string',
+        'control'       => 'string'
+    );
+    $orig = $dbdata;
+    foreach ($fields as $name => $type) {
+        if (!isset($userdata[$name])) continue;
+        $dbdata[$name] = $userdata[$name];
+    }
+    return true;
+}/*}}}*/
+
+
+function action_cryptography() {/*{{{*/
+
+    $log_messages = array();
+    
+    if (isset($_POST['task'])) {
+        switch ($_POST['task']) {
+            case 'add':
+                $key = array(
+                    'description' => '',
+                    'masterkey'   => '',
+                    'salt'        => '',
+                    'control'     => ''
+                );
+                if (validate_crypto_key($_POST, $key, $log_messages)) {
+                    if (db_add_crypto_key($key)) {
+                        redirect(link_to('cryptography'));
+                    }
+                    echo db_error();
+                }
+                
+                break;
+            default:
+                break;
+        }
+    }
+
+    $keys = db_get_crypto_keys();
+
+?>
+    <h2>Liste der Schl&uuml;ssel</h2>
+    <?php if (isset($keys)) : ?>
+        <table>
+            <tr>
+                <th>Nickname</th>
+            </tr>
+        <?php foreach ($keys as $key) : ?>
+            <tr>
+                <td><?=html_escape($key['description'])?></td>
+            </tr>
+        <?php endforeach ?>
+        </table>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine Schl&uuml;ssel.</p>
+    <?php endif ?>
+<?php
+
+    form_cryptography($keys, $log_messages);
+}/*}}}*/
+
+
+
+function form_cryptography($keys = array(), $log_messages = array()) {/*{{{*/
+?>
+<?php if (empty($keys)) : ?>
+<div class="clearfix">
+    <noscript>
+        <p class="error">
+            <strong>Achtung:</strong> Ohne JavaScript geht hier gar nichts. Bitte aktivieren!
+        </p>
+    </noscript>
+    <fieldset class="clearfix">
+        <legend>Ersteinrichtung</legend>
+        <?php log_messages($log_messages); ?>
+        <?=html_text_field('Nickname', 'description_visible')?>
+        <?=html_password_field('Passwort', 'password1')?>
+        <?=html_password_field('Passwort (wdh.)', 'password2')?>
+    </fieldset>
+    <input class="submit" type="button" name="btn_initialize" value="Masterkey erzeugen und speichern" onclick="initialize()"/>
+</div>
+<form action="" method="post" id="keyform">
+    <?=html_hidden_field('description')?>
+    <?=html_hidden_field('masterkey')?>
+    <?=html_hidden_field('salt')?>
+    <?=html_hidden_field('control')?>
+    <?=html_hidden_field('task', 'add')?>
+</form>
+
+<?php js_modal_windows() ?>
+
+<script type="text/javascript">
+
+$(document).ready(function(){  
+    $('#password2').keypress( function(event) { if (event.keyCode == '13') { initialize(); } });
+});  
+
+function initialize() {/*{{{*/
+//    var t1 = new Date().getTime()
+
+    var description = $('#description_visible').val();
+    if (description.length < 1) {
+        alert('Bitte einen Nickname angeben.');
+        return;
+    }
+
+    // 1.1 Ask for password1
+    // password should be already entered into the two input fields
+    var password1 = $('#password1').val();
+    var password2 = $('#password2').val();
+    if (password1 != password2) {
+        alert('Die Passwörter sind nicht identisch.');
+        return;
+    }
+    if (password1.length < 8) {
+        alert('Das Passwort ist zu kurz (min. 8 Zeichen).');
+        return;
+    }
+
+    modal_window_show($('#please_wait'));
+    $('#password1').val('');
+    $('#password2').val('');
+
+    setTimeout(function() {
+        // 1.2 Generate random bytes (salt1)
+        var salt1       = Crypto.charenc.Binary.bytesToString(Crypto.util.randomBytes(32));
+
+        // 1.3 Generate random bytes (salt2)
+        var salt2       = Crypto.charenc.Binary.bytesToString(Crypto.util.randomBytes(32));
+
+        // 1.4 Generate random bytes (secret)
+        var secret      = Crypto.charenc.Binary.bytesToString(Crypto.util.randomBytes(32));
+
+        // 1.5 Create masterkey using PBKDF2 with secret, salt2 (1000 iterations)
+        var masterkey   = Crypto.PBKDF2(secret,    salt2, 256, { iterations: 1000, asBytes: true });
+        secret          = ''; // we don't need this anymore
+        salt2           = ''; // we don't need this anymore
+
+        // 1.6 Create userkey using PBKDF2 with password1, salt1 (1000 iterations)
+        var userkey     = Crypto.PBKDF2(password1, salt1, 256, { iterations: 1000, asBytes: true });
+        password1       = ''; // we don't need this anymore
+
+        // 1.7 Encrypt masterkey using AES with userkey
+        var crypted_masterkey = Crypto.AES.encrypt(masterkey, userkey);
+
+        // 1.8 Create sha1 hash of masterkey+userkey+salt
+        var control     = Crypto.SHA1(Crypto.charenc.Binary.bytesToString(masterkey) + Crypto.charenc.Binary.bytesToString(userkey) + salt1);
+
+    /* XXX DEBUG XXX
+        alert(
+            "masterkey: " + Crypto.charenc.Binary.bytesToString(masterkey).length + " " + masterkey + "\n" +
+            "userkey: " + userkey + "\n" + 
+            "control: " + control
+        );
+        return;
+    */
+    //    var t2 = new Date().getTime()
+    //    alert((t2 - t1) / 1000); // benchmark
+
+        // 1.9 Store data: key=1.7 salt=1.2, control=1.8
+        $('#masterkey').val(crypted_masterkey);
+        $('#salt').val(Crypto.util.bytesToHex(Crypto.charenc.Binary.stringToBytes(salt1)));
+        $('#control').val(control);
+        $('#description').val(description);
+
+        $('#keyform').submit();
+    }, 500);
+}/*}}}*/
+</script>
+
+<?php else : ?>
+
+<div class="clearfix">
+    <noscript>
+        <p class="error">
+            <strong>Achtung:</strong> Ohne JavaScript geht hier gar nichts. Bitte aktivieren!
+        </p>
+    </noscript>
+    <fieldset class="clearfix">
+        <legend>Passwort hinzuf&uuml;gen</legend>
+        <?php log_messages($log_messages); ?>
+        <?=html_password_field('Exist. Passwort', 'password1')?>
+        <small>Bestehendes Passwort</small>
+        <?=html_text_field('Nickname', 'description_visible')?>
+        <?=html_password_field('Passwort', 'password2')?>
+        <?=html_password_field('Passwort (wdh.)', 'password3')?>
+    </fieldset>
+    <input class="submit" type="button" name="btn_add_new_key" value="Passwort hinzuf&uuml;gen" onclick="add_new_key()"/>
+</div>
+<form action="" method="post" id="keyform">
+    <?=html_hidden_field('description')?>
+    <?=html_hidden_field('masterkey')?>
+    <?=html_hidden_field('salt')?>
+    <?=html_hidden_field('control')?>
+    <?=html_hidden_field('task', 'add')?>
+</form>
+
+<?php js_modal_windows() ?>
+<?php js_get_master_key() ?>
+
+<script type="text/javascript">
+
+$(document).ready(function(){  
+    $('#password3').keypress( function(event) { if (event.keyCode == '13') { add_new_key(); } });
+});  
+
+function add_new_key() {/*{{{*/
+
+    var description = $('#description_visible').val();
+    if (description.length < 1) {
+        alert('Bitte einen Nickname angeben.');
+        return;
+    }
+
+    // 2.1 Check password1 (existing)
+    var password1 = $('#password1').val();
+    if (password1.length < 1) {
+        alert('Bitte ein existierenges Passwort angeben.');
+        return;
+    }
+
+    // 2.2 Check password2 (new one)
+    var password2 = $('#password2').val();
+    var password3 = $('#password3').val();
+    if (password2 != password3) {
+        alert('Die Passwörter sind nicht identisch.');
+        return;
+    }
+    if (password2.length < 8) {
+        alert('Das Passwort ist zu kurz (min. 8 Zeichen).');
+        return;
+    }
+
+    get_master_key(password1, function(masterkey) {
+
+        $('#password1').val('');
+        $('#password2').val('');
+        $('#password3').val('');
+
+        // 2.4 Generate random bytes (salt2)
+        var salt2       = Crypto.charenc.Binary.bytesToString(Crypto.util.randomBytes(32));
+
+        // 2.5 Create userkey2 using PBKDF2 with password2, salt2 (1000 iterations)
+        var userkey2    = Crypto.PBKDF2(password2, salt2, 256, { iterations: 1000, asBytes: true });
+
+        // 2.6 Encrypt masterkey (from 2.3) using AES with userkey2
+        var crypted_masterkey = Crypto.AES.encrypt(masterkey, userkey2);
+
+        // 2.7 Create sha1 hash of masterkey+userkey2+salt2
+        var control     = Crypto.SHA1(Crypto.charenc.Binary.bytesToString(masterkey) + Crypto.charenc.Binary.bytesToString(userkey2) + salt2);
+
+        // 2.8 Store data: key=2.6 salt=2.4, control=2.7
+        $('#masterkey').val(crypted_masterkey);
+        $('#salt').val(Crypto.util.bytesToHex(Crypto.charenc.Binary.stringToBytes(salt2)));
+        $('#control').val(control);
+        $('#description').val(description);
+
+        $('#keyform').submit();
+    }, 10);
+}/*}}}*/
+</script>
+<?php endif ?>
+
+<?php
+}/*}}}*/
+
+function js_get_master_key($keys = NULL) {/*{{{*/
+    if (!isset($keys)) $keys = db_get_crypto_keys();
+    if (empty($keys)) return;
+?>
+<script type="text/javascript">
+
+var keys = new Array(
+    <?=join(",\n", array_map('json_encode', $keys)) ?>
+);
+
+function get_master_key(password, callback) {/*{{{*/
+
+    modal_window_show($("#please_wait"));
+
+    // 4.2 iterate through valid keys
+    setTimeout(function() { check_master_key(0, password, callback); }, 1000);
+
+}/*}}}*/
+
+function check_master_key(idx, password, callback) {/*{{{*/
+
+    if (idx >= keys.length) {
+        modal_window_hide();
+        alert("Falsches Passwort");
+        return;
+    }
+
+    var crypted_masterkey   = keys[idx]['masterkey'];
+    var salt                = Crypto.charenc.Binary.bytesToString(Crypto.util.hexToBytes(keys[idx]['salt']));
+    var control1            = keys[idx]['control'];
+
+    // 1 Create userkey using PBKDF2 with password, salt (from database) - (1000 iterations)
+    var userkey             = Crypto.PBKDF2(password, salt, 256, { iterations: 1000, asBytes: true });
+
+    // 2 Decrypt key (from database) using AES with userkey
+    var masterkey           = Crypto.AES.decrypt(crypted_masterkey, userkey);
+    // 3 Create sha1 hash of masterkey+userkey+salt, afterwards compare with control
+    var control2            = Crypto.SHA1(Crypto.charenc.Binary.bytesToString(masterkey) + Crypto.charenc.Binary.bytesToString(userkey) + salt);
+
+    // 4 if equal we have the correct row and the correct master key
+    if (control1 == control2) {
+        modal_window_hide();
+        callback(masterkey);
+        return;
+    }
+
+    // check next key
+    setTimeout( function() { check_master_key(idx + 1, password, callback); }, 10);
+
+}/*}}}*/
+
+</script>
+<?php
+}/*}}}*/
+
+
diff --git a/include/database.php b/include/database.php
new file mode 100644 (file)
index 0000000..571fd81
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+
+/* DATABASE TABLE NAMES {{{ */
+define ('DB_TABLE_CRYPTO',      DB_PREFIX . 'crypto');
+define ('DB_TABLE_EVENTS',      DB_PREFIX . 'events');
+define ('DB_TABLE_MEMBERS',     DB_PREFIX . 'members');
+define ('DB_TABLE_EARNINGS',    DB_PREFIX . 'earnings');
+define ('DB_TABLE_EXPENSES',    DB_PREFIX . 'expenses');
+/* }}} */
+
+
+/* COMPATIBILITY functions {{{ */
+if (function_exists('mysql_set_charset') === false) {
+    function mysql_set_charset($charset, $link_identifier = null) {
+        if ($link_identifier == null) return mysql_query('SET NAMES "' . $charset . '"');
+        return mysql_query('SET NAMES "' . $charset . '"', $link_identifier);
+    }
+}
+/* }}} */
+
+function db_connect() {
+       global $dbh;
+       if (!($dbh = @mysql_connect(DB_HOST, DB_USER, DB_PASSWORD, true))) die('<html><body>Konnte keine Verbindung zur Datenbank herstellen.</body></html>');
+       mysql_set_charset(DB_CHARSET, $dbh);
+       if (!@mysql_select_db(DB_NAME)) die('<html><body>Eine Verbindung zur Datenbank konnte hergestellt werden, aber die angegebene Datenbank konnte nicht ausgewählt werden.</body></html>');
+}
+
+// escape value for usage in mysql query
+function db_escape($value) {
+    global $dbh;
+    return mysql_real_escape_string($value, $dbh);
+}
+
+// escape each value of an array for usage in mysql query
+function db_escape_array($arr) {
+    global $dbh;
+    foreach ($arr as $key => $val) {
+        $arr[$key] = mysql_real_escape_string($val, $dbh);
+    }
+    return $arr;
+}
+
+// select one row
+function db_select_single($query) {
+    global $dbh;
+    $res = mysql_query($query, $dbh);
+    if (!$res) {
+        echo $query, ' ', db_error();
+        return NULL;
+    }
+    if (mysql_num_rows($res) != 1) return NULL;
+    $row = mysql_fetch_assoc($res);
+    mysql_free_result($res);
+    return $row;
+}
+
+// select multiple rows
+function db_select_multi($query) {
+    global $dbh;
+    $res = mysql_query($query, $dbh);
+    if (!$res) {
+        echo $query, ' ', db_error();
+        return NULL;
+    }
+    if (mysql_num_rows($res) == 0) return array();
+    $ret = array();
+    while ($row = mysql_fetch_assoc($res)) $ret[] = $row;
+    mysql_free_result($res);
+    return $ret;
+}
+
+// insert one row
+function db_insert_single($table, $fields, $ignore = false) {
+    global $dbh;
+    $query = 'INSERT INTO ' . $table;
+    if ($ignore) $query = 'INSERT IGNORE INTO ' . $table;
+    $query .= ' (' . join(',', array_keys($fields)) . ') VALUES (\'' . join('\', \'', db_escape_array($fields)) . '\')';
+    $res = mysql_query($query, $dbh);
+    if (!$res)  echo $query, ' ', db_error();
+       return $res;
+}
+
+// get automatically generated id of last record
+function db_insert_id() {
+    global $dbh;
+    return mysql_insert_id($dbh);
+}
+
+// begin transaction
+function db_begin() {
+    global $dbh;
+    return mysql_query('BEGIN');
+}
+
+// commit transaction
+function db_commit() {
+    global $dbh;
+    return mysql_query('COMMIT');
+}
+
+// rollback transaction
+function db_rollback() {
+    global $dbh;
+    return mysql_query('ROLLBACK');
+}
+
+// update multiple rows
+function db_update_multi($table, $fields, $where = '', $special = '') {
+    global $dbh;
+    $query = 'UPDATE ' . $table . ' SET ';
+    $first = true;
+    foreach ($fields as $name => $value) {
+        if (!$first) $query .= ', ';
+        else $first = false;
+        $query .= '`' . $name . '`=\'' . db_escape($value) . '\'';
+    }
+    $query .= $special;
+    if (!empty($where)) $query.= ' WHERE ' . $where;
+    $res = mysql_query($query, $dbh);
+    if (!$res)  echo $query, ' ', db_error();
+       return $res;
+}
+
+// update one rows
+function db_update_single($table, $fields, $where = '', $special = '') {
+    global $dbh;
+    $query = 'UPDATE ' . $table . ' SET ';
+    $first = true;
+    foreach ($fields as $name => $value) {
+        if (!$first) $query .= ', ';
+        else $first = false;
+        $query .= '`' . $name . '`=\'' . db_escape($value) . '\'';
+    }
+    $query .= $special;
+    if (!empty($where)) $query.= ' WHERE ' . $where;
+       $query .= ' LIMIT 1';
+    $res = mysql_query($query, $dbh);
+    if (!$res)  echo $query, ' ', db_error();
+       return $res;
+}
+
+// delete one row
+function db_delete_single($table, $where) {
+    global $dbh;
+    $query = 'DELETE FROM ' . $table . ' WHERE ' . $where . ' LIMIT 1';
+    $res = mysql_query($query, $dbh);
+    if (!$res) return NULL;
+    return (mysql_affected_rows($dbh));
+}
+
+// get error message
+function db_error() {
+    global $dbh;
+    return mysql_error($dbh);
+}
+
+function db_unixtime2date($unixtime) {
+    return strftime('%Y-%m-%d', $unixtime);
+}
+
+function db_date2unixtime($date) {
+    return strtotime($date);
+}
+
+function db_unixtime2datetime($unixtime) {
+    return strftime('%Y-%m-%d %H:%M:%S', $unixtime);
+}
+
+function db_datetime2unixtime($datetime) {
+    return strtotime($datetime);
+}
+
diff --git a/include/dtaus.php b/include/dtaus.php
new file mode 100644 (file)
index 0000000..6415efa
--- /dev/null
@@ -0,0 +1,385 @@
+<?php
+
+
+function action_dtaus() {
+    $members = db_get_members();
+    if (empty($members)) return;
+
+    $debits = array();
+    foreach ($members as $member) {
+        $info = fee_next_directdebit_for_member($member['id'], time());
+        if (empty($info)) continue;
+        $debits[] = array(
+            'member_id'     => $member['id'],
+            'member_number' => $member['number'],
+            'nickname'      => $member['nickname'],
+            'accountholder' => $member['accountholder'],
+            'accountnumber' => $member['accountnumber'],
+            'bankcode'      => $member['bankcode'],
+//          'bankname'      => $member['bankname'],
+            'amount'        => $info['value'],
+            'amountcent'    => '' . (int)round(bcmul($info['value'], 100)),
+            'purpose'       => $info['info']
+        );
+    }
+
+    js_modal_windows();
+    js_get_master_key();
+
+?>
+    <noscript>
+        <p class="error">
+            <strong>Achtung:</strong> Ohne JavaScript geht hier gar nichts. Bitte aktivieren!
+        </p>
+    </noscript>
+<h2>Bankeinzug</h2>
+<table>
+    <tr>
+        <th>Mitgliedsnummer</th>
+        <th>Nickname</th>
+        <th>Verwendungszweck</th>
+        <th style="text-align: right;">Betrag</th>
+    </tr>
+<?php foreach ($debits as $debit) : ?>
+    <tr>
+        <td><a href="<?=html_escape(link_to('fees', array('member_id'=> $debit['member_id'])))?>"><?=html_escape($debit['member_number'])?></a></td>
+        <td><?=html_escape($debit['nickname'])?></td>
+        <td><?=html_escape($debit['purpose'])?></td>
+        <td style="text-align: right;"><?=html_escape(format_money($debit['amount']))?></td>
+    </tr>
+<?php endforeach ?>
+</table>
+<input class="submit" type="button" name="btn_dtaus" value="DTAUS erzeugen" onclick="ask_for_password()" id="btn_change_bank_details"/>
+<br/>
+<br/>
+<form action="<?=link_to('create_earnings')?>" method="post" class="clearfix">
+<?php $count = 0; ?>
+<?php foreach ($debits as $debit) : ?>
+       <?=html_hidden_field(sprintf('earnings[%d][date]',        $count), format_date(time()))?>
+       <?=html_hidden_field(sprintf('earnings[%d][type]',        $count), 'fee')?>
+       <?=html_hidden_field(sprintf('earnings[%d][status]',      $count), 'paid')?>
+       <?=html_hidden_field(sprintf('earnings[%d][value]',       $count), $debit['amount'])?>
+       <?=html_hidden_field(sprintf('earnings[%d][member_id]',   $count), $debit['member_id'])?>
+       <?=html_hidden_field(sprintf('earnings[%d][description]', $count), $debit['purpose'])?>
+<?php $count++; ?>
+<?php endforeach ?>
+<input class="submit" type="submit" name="btn_create_earnings" value="Einnahmen verbuchen"/>
+</form>
+
+<div id="password_popup" class="modal_window">
+    <fieldset>
+        <legend>Passwortabfrage</legend>
+        <?=html_password_field('Passwort', 'password')?>
+        <input class="submit" type="button" name="btn_generate_dtaus" value="DTAUS erzeugen" onclick="generate_dtaus()"/>
+        <input class="submit" type="button" name="btn_cancel" value="Abbrechen" onclick="cancel_ask_for_password()"/>
+    </fieldset>
+</div>
+<div id="dtaus_popup" class="modal_window">
+    <fieldset>
+        <legend>DTAUS</legend>
+            <textarea id="dtaus_content" style="width: 100%" rows="30" readonly="readonly"></textarea>
+            <input class="submit" type="button" name="btn_dtaus_close" value="Schließen" onclick="dtaus_close()"/>
+    </fieldset>
+</div>
+
+<script type="text/javascript">
+
+var debits = new Array(
+    <?=join(",\n", array_map('json_encode', $debits)) ?>
+);
+
+
+$(document).ready(function(){  
+    $('#password').keypress( function(event) { if (event.keyCode == '13') { generate_dtaus(); } });
+    $('#btn_dtaus').focus();
+});  
+
+
+function ask_for_password() {/*{{{*/
+    modal_window_show($("#password_popup"));
+    $("#password").focus();
+    return;
+}/*}}}*/
+
+function cancel_ask_for_password() {/*{{{*/
+    modal_window_hide();
+    $("#btn_dtaus").focus();
+    return;
+}/*}}}*/
+
+
+function generate_dtaus() {/*{{{*/
+
+    var password = $('#password').val();
+    $("#password").val('');
+
+    // We pass a closure so that get_master_key may defer execution
+    get_master_key(password, function(masterkey) {
+
+        modal_window_replace($("#dtaus_popup"));
+
+        if (!DTAUS.setAccountFileSender('<?=dtaus_string(ACCOUNT_HOLDER)?>', '<?=BANK_CODE?>', '<?=ACCOUNT_NUMBER?>')) {
+            $('#dtaus_content').val(DTAUS.errormsg);
+            return;
+        }
+
+        for (var i = 0; i < debits.length; i++) {
+            var accountholder = debits[i]['accountholder'];
+            var accountnumber = debits[i]['accountnumber'];
+            var bankcode      = debits[i]['bankcode'];
+
+            // Encrypt/Decrypt data using AES with masterkey
+            if (accountholder != "") accountholder = Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(accountholder, masterkey));
+            if (accountnumber != "") accountnumber = Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(accountnumber, masterkey));
+            if (bankcode      != "") bankcode      = Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(bankcode,      masterkey));
+
+            accountholder = DTAUS.prepareString(accountholder);
+
+            if (!DTAUS.addExchange(accountholder, bankcode, accountnumber, '', debits[i]['amountcent'], debits[i]['purpose'])) {
+                $('#dtaus_content').val('Mitglied ' + debits[i]['member_number'] + ' ' + accountholder + '\n' + DTAUS.errormsg);
+                return;
+            }
+        }
+        
+        $('#dtaus_content').val(DTAUS.getFileContent());
+        $('#dtaus_content').focus();
+        $('#dtaus_content').select();
+    });
+
+}/*}}}*/
+
+function dtaus_close() {/*{{{*/
+    modal_window_hide();
+    $("#dtaus_content").val('');
+}/*}}}*/
+
+
+
+</script>
+<?
+}
+
+// borrowed from PEARs Payment_DTA
+function dtaus_string($string) {
+    if (strlen($string) == 0) return '';
+    $special_chars = array(
+        'á' => 'a',
+        'à' => 'a',
+        'ä' => 'ae',
+        'â' => 'a',
+        'ã' => 'a',
+        'å' => 'a',
+        'æ' => 'ae',
+        'ā' => 'a',
+        'ă' => 'a',
+        'ą' => 'a',
+        'ȁ' => 'a',
+        'ȃ' => 'a',
+        'Á' => 'A',
+        'À' => 'A',
+        'Ä' => 'Ae',
+        'Â' => 'A',
+        'Ã' => 'A',
+        'Å' => 'A',
+        'Æ' => 'AE',
+        'Ā' => 'A',
+        'Ă' => 'A',
+        'Ą' => 'A',
+        'Ȁ' => 'A',
+        'Ȃ' => 'A',
+        'ç' => 'c',
+        'ć' => 'c',
+        'ĉ' => 'c',
+        'ċ' => 'c',
+        'č' => 'c',
+        'Ç' => 'C',
+        'Ć' => 'C',
+        'Ĉ' => 'C',
+        'Ċ' => 'C',
+        'Č' => 'C',
+        'ď' => 'd',
+        'đ' => 'd',
+        'Ď' => 'D',
+        'Đ' => 'D',
+        'é' => 'e',
+        'è' => 'e',
+        'ê' => 'e',
+        'ë' => 'e',
+        'ē' => 'e',
+        'ĕ' => 'e',
+        'ė' => 'e',
+        'ę' => 'e',
+        'ě' => 'e',
+        'ȅ' => 'e',
+        'ȇ' => 'e',
+        'É' => 'E',
+        'È' => 'E',
+        'Ê' => 'E',
+        'Ë' => 'E',
+        'Ē' => 'E',
+        'Ĕ' => 'E',
+        'Ė' => 'E',
+        'Ę' => 'E',
+        'Ě' => 'E',
+        'Ȅ' => 'E',
+        'Ȇ' => 'E',
+        'ĝ' => 'g',
+        'ğ' => 'g',
+        'ġ' => 'g',
+        'ģ' => 'g',
+        'Ĝ' => 'G',
+        'Ğ' => 'G',
+        'Ġ' => 'G',
+        'Ģ' => 'G',
+        'ĥ' => 'h',
+        'ħ' => 'h',
+        'Ĥ' => 'H',
+        'Ħ' => 'H',
+        'ì' => 'i',
+        'ì' => 'i',
+        'î' => 'i',
+        'ï' => 'i',
+        'ĩ' => 'i',
+        'ī' => 'i',
+        'ĭ' => 'i',
+        'į' => 'i',
+        'ı' => 'i',
+        'ij' => 'ij',
+        'ȉ' => 'i',
+        'ȋ' => 'i',
+        'Í' => 'I',
+        'Ì' => 'I',
+        'Î' => 'I',
+        'Ï' => 'I',
+        'Ĩ' => 'I',
+        'Ī' => 'I',
+        'Ĭ' => 'I',
+        'Į' => 'I',
+        'İ' => 'I',
+        'IJ' => 'IJ',
+        'Ȉ' => 'I',
+        'Ȋ' => 'I',
+        'ĵ' => 'j',
+        'Ĵ' => 'J',
+        'ķ' => 'k',
+        'Ķ' => 'K',
+        'ĺ' => 'l',
+        'ļ' => 'l',
+        'ľ' => 'l',
+        'ŀ' => 'l',
+        'ł' => 'l',
+        'Ĺ' => 'L',
+        'Ļ' => 'L',
+        'Ľ' => 'L',
+        'Ŀ' => 'L',
+        'Ł' => 'L',
+        'ñ' => 'n',
+        'ń' => 'n',
+        'ņ' => 'n',
+        'ň' => 'n',
+        'ʼn' => 'n',
+        'Ñ' => 'N',
+        'Ń' => 'N',
+        'Ņ' => 'N',
+        'Ň' => 'N',
+        'ó' => 'o',
+        'ò' => 'o',
+        'ö' => 'oe',
+        'ô' => 'o',
+        'õ' => 'o',
+        'ø' => 'o',
+        'ō' => 'o',
+        'ŏ' => 'o',
+        'ő' => 'o',
+        'œ' => 'oe',
+        'ȍ' => 'o',
+        'ȏ' => 'o',
+        'Ó' => 'O',
+        'Ò' => 'O',
+        'Ö' => 'Oe',
+        'Ô' => 'O',
+        'Õ' => 'O',
+        'Ø' => 'O',
+        'Ō' => 'O',
+        'Ŏ' => 'O',
+        'Ő' => 'O',
+        'Œ' => 'OE',
+        'Ȍ' => 'O',
+        'Ȏ' => 'O',
+        'ŕ' => 'r',
+        'ř' => 'r',
+        'ȑ' => 'r',
+        'ȓ' => 'r',
+        'Ŕ' => 'R',
+        'Ř' => 'R',
+        'Ȑ' => 'R',
+        'Ȓ' => 'R',
+        'ß' => 'ss',
+        'ś' => 's',
+        'ŝ' => 's',
+        'ş' => 's',
+        'š' => 's',
+        'ș' => 's',
+        'Ś' => 'S',
+        'Ŝ' => 'S',
+        'Ş' => 'S',
+        'Š' => 'S',
+        'Ș' => 'S',
+        'ţ' => 't',
+        'ť' => 't',
+        'ŧ' => 't',
+        'ț' => 't',
+        'Ţ' => 'T',
+        'Ť' => 'T',
+        'Ŧ' => 'T',
+        'Ț' => 'T',
+        'ú' => 'u',
+        'ù' => 'u',
+        'ü' => 'ue',
+        'û' => 'u',
+        'ũ' => 'u',
+        'ū' => 'u',
+        'ŭ' => 'u',
+        'ů' => 'u',
+        'ű' => 'u',
+        'ų' => 'u',
+        'ȕ' => 'u',
+        'ȗ' => 'u',
+        'Ú' => 'U',
+        'Ù' => 'U',
+        'Ü' => 'Ue',
+        'Û' => 'U',
+        'Ũ' => 'U',
+        'Ū' => 'U',
+        'Ŭ' => 'U',
+        'Ů' => 'U',
+        'Ű' => 'U',
+        'Ų' => 'U',
+        'Ȕ' => 'U',
+        'Ȗ' => 'U',
+        'ŵ' => 'w',
+        'Ŵ' => 'W',
+        'ý' => 'y',
+        'ÿ' => 'y',
+        'ŷ' => 'y',
+        'Ý' => 'Y',
+        'Ÿ' => 'Y',
+        'Ŷ' => 'Y',
+        'ź' => 'z',
+        'ż' => 'z',
+        'ž' => 'z',
+        'Ź' => 'Z',
+        'Ż' => 'Z',
+        'Ž' => 'Z',
+    );
+
+
+    $result = strtr($string, $special_chars);   // replace known special chars
+    $result = strtoupper($result);                  // upper case
+    // make sure every special char is replaced by one space, not two or three
+    $result = mb_convert_encoding($result, 'ASCII', 'UTF-8');
+    $result = preg_replace('/[^A-Z0-9 \.,&\-\/\+\*\$%]/', ' ', $result);
+
+    return $result;
+}
+
diff --git a/include/earnings.php b/include/earnings.php
new file mode 100644 (file)
index 0000000..fd28ebd
--- /dev/null
@@ -0,0 +1,193 @@
+<?php
+
+$SQL_CREATE_EARNINGS = sprintf('
+    CREATE TABLE IF NOT EXISTS `%1$s` (
+      `id` int(10) unsigned NOT NULL auto_increment,
+      `date` date NOT NULL,
+      `type` enum(\'donation\', \'fee\', \'old_fee\') NULL,
+      `status` enum(\'open\',\'paid\') NOT NULL,
+      `account` enum(\'bank\',\'cash\') NOT NULL,
+      `value` DECIMAL(10,2) NULL,
+      `member_id` int(10) unsigned NOT NULL,
+      `description` TEXT NOT NULL,
+      `created_at` datetime NOT NULL,
+      `modified_at` datetime NOT NULL,
+      PRIMARY KEY  (`id`),
+      INDEX `idx_member_id` (`member_id`),
+      INDEX `idx_date` (`date`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=%2$s
+    ', DB_TABLE_EARNINGS, DB_CHARSET);
+
+$EARNING_TYPES = array(
+    'fee'       => 'Mitgliedsbeitrag',
+    'donation'  => 'Spende',
+    'old_fee'   => 'Mitgliedsbeiträge vor dem ' . format_date(DIRECTDEBIT_DATE)
+);
+
+
+/* DB functions {{{ */
+
+function db_get_earning_with_id($earning_id) {
+    $sql = 'SELECT * FROM %1$s WHERE id=\'%2$d\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_EARNINGS, db_escape($earning_id)));
+}
+
+function db_create_earning($earning) {
+    unset($earning['id']);
+    $earning['date']        = db_unixtime2datetime(ui_date2unixtime($earning['date']));
+    $earning['value']       = ui_money2float($earning['value']);
+    $earning['created_at']  = db_unixtime2datetime(time());
+    $earning['modified_at'] = db_unixtime2datetime(time());
+    return (db_insert_single(DB_TABLE_EARNINGS, $earning));
+}
+
+function db_change_earning($earning) {
+    $id = $earning['id'];
+    unset($earning['id']);
+    $earning['date']        = db_unixtime2datetime(ui_date2unixtime($earning['date']));
+    $earning['value']       = ui_money2float($earning['value']);
+    $earning['modified_at'] = db_unixtime2datetime(time());
+    return db_update_single(DB_TABLE_EARNINGS, $earning, sprintf('id=\'%1$d\'', db_escape($id)));
+}
+
+/* }}} */
+
+
+function validate_earning($userdata, &$dbdata, &$validation) {/*{{{*/
+    global $EARNING_TYPES, $ACCOUNT_TYPES;
+    $fields = array(
+        'id'              => 'number',
+        'date'            => 'date',
+        'type'            => $EARNING_TYPES,
+        'account'         => $ACCOUNT_TYPES,
+        'member_id'       => 'member',
+        'value'           => 'money',
+        'description'     => 'text',
+//      'status'          -  See below...
+    );
+    $orig = $dbdata;
+    $dbdata['status'] = empty($userdata['status']) ? 'open' : 'paid';
+    foreach ($fields as $name => $type) {
+        if (!isset($userdata[$name])) continue;
+        $dbdata[$name] = $userdata[$name];
+    }
+
+    if (empty($dbdata['date'])) {
+        $validation[] = 'Bitte ein Datum angeben.';
+    }
+    if ((float)$dbdata['value'] == 0) {
+        $validation[] = 'Bitte einen Betrag angeben.';
+    }
+
+    return (count($validation) == 0);
+}/*}}}*/
+
+
+function action_create_earning() {/*{{{*/
+    $earning = array();
+    $earning['date']      = format_date(time());
+    $earning['status']    = 'paid';
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        unset($_POST['id']);
+        if (validate_earning($_POST, $earning, $log_messages)) {
+            if (db_create_earning($earning)) {
+                redirect(link_to('finances'));
+            }
+            echo db_error();
+        }
+    }
+    form_earning($earning, 'create', $log_messages);
+}/*}}}*/
+
+
+function action_change_earning() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to('finances'));
+    $earning = db_get_earning_with_id($_REQUEST['id']);
+    $earning['date'] = format_date(db_date2unixtime($earning['date']));
+    $earning['value']= format_money($earning['value']);
+    if (!isset($earning)) redirect(link_to('finances'));
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        if (validate_earning($_POST, $earning, $log_messages)) {
+            if (db_change_earning($earning)) {
+                redirect(link_to('finances'));
+            }
+            echo db_error();
+        }
+    }
+
+    form_earning($earning, 'change', $log_messages);
+}/*}}}*/
+
+
+function form_earning($earning = array(), $task, $log_messages) {/*{{{*/
+    global $EARNING_TYPES, $ACCOUNT_TYPES;
+
+    $readonly = ($task == 'delete');
+
+    ?>
+    <form action="<?=link_to($task . '_earning')?>" method="post" class="clearfix" id="earning_form" style="<?php if ($_REQUEST['action'] == 'finances') : ?>float: left<?php else :?>margin-top: 2em<?php endif ?>">
+        <?php if (isset($earning['id'])) : ?><?=html_hidden_field('id', $earning)?><?php endif ?>
+        <fieldset>
+        <?php if     ($task == 'create') : ?>
+            <legend>Neue Einnahme</legend>
+        <?php elseif ($task == 'delete') : ?>
+            <legend>Einnahme l&ouml;schen</legend>
+        <?php elseif ($task == 'change') : ?>
+            <legend>Einnahme &auml;ndern</legend>
+        <?php endif ?>
+            <?php log_messages($log_messages); ?>
+            <?=html_text_field('Datum', 'date', $earning, $readonly)?>
+            <?=html_list_box('Typ', 'type', $EARNING_TYPES, $earning, $readonly)?>
+            <?=html_member_list_box('Mitglied', 'member_id', $earning, $readonly)?>
+            <?=html_money_field('Betrag', 'value', $earning, $readonly)?>
+            <?=html_text_field('Beschreibung', 'description', $earning, $readonly)?>
+            <?=html_list_box('Ziel', 'account', $ACCOUNT_TYPES, $earning, $readonly)?>
+            <?=html_checkbox('Bezahlt', 'status', $earning['status'] == 'paid', 1, $readonly)?>
+        </fieldset>
+    <?php if ($task == 'delete') : ?>
+        <input class="submit" type="submit" name="submit" value="L&ouml;schen"/>
+    <?php else : ?>
+        <input class="submit" type="submit" name="submit" value="Speichern"/>
+    <?php endif ?>
+    </form>
+    <?php if ($task == 'change') : ?>
+        <br/>
+        <a href="<?=html_escape(link_to('delete_earning', array('id' => $earning['id'])))?>">Einnahme l&ouml;schen</a>
+        <a href="<?=html_escape(link_to('finances'))?>">Neue Einnahme</a>
+    <?php elseif ($task == 'delete') : ?>
+        <br/>
+        <a href="<?=html_escape(link_to('finances'))?>">Neue Einnahme</a>
+    <?php endif ?>
+    <?php
+}/*}}}*/
+
+
+function action_create_earnings() {/*{{{*/
+    if (empty($_POST['earnings']) || !is_array($_POST['earnings'])) redirect(link_to('finances'));
+    db_begin();
+    foreach ($_POST['earnings'] as $post_data) {
+        $log_messages = array();
+        unset($post_data['id']);
+        $earning = array(
+                       'account' => 'bank'
+               );
+        if (validate_earning($post_data, $earning, $log_messages)) {
+            if (!db_create_earning($earning)) {
+                echo db_error();
+                db_rollback();
+                return;
+            }
+        }
+        else {
+            db_rollback();
+            echo '<h2>Verbuchen von Einnahmen</h2><p>Beim Verbuchen der Einnahmen ist ein Fehler aufgetreten:</p>';
+            log_messages($log_messages);
+            return;
+        }
+    }
+    db_commit();
+    redirect(link_to('finances'));
+}/*}}}*/
+
diff --git a/include/events.php b/include/events.php
new file mode 100644 (file)
index 0000000..7a56de6
--- /dev/null
@@ -0,0 +1,309 @@
+<?php
+
+$SQL_CREATE_EVENTS = sprintf('
+    CREATE TABLE IF NOT EXISTS `%1$s` (
+      `id` int(10) unsigned NOT NULL auto_increment,
+      `event_date` date NOT NULL,
+      `event_type` enum(\'joined\',\'changed\',\'leaved\') NOT NULL,
+      `member_id` int(10) unsigned NOT NULL,
+      `member_type` enum(\'regular\',\'patron\',\'honorary\') NULL,
+      `fee` DECIMAL(10,2) NULL,
+      `payment_interval` enum(\'monthly\', \'quarterly\', \'halfyearly\', \'yearly\') NULL,
+      `created_at` datetime NOT NULL,
+      `modified_at` datetime NOT NULL,
+      PRIMARY KEY  (`id`),
+      UNIQUE KEY `member_id_event_date` (`member_id`, `event_date`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=%2$s
+    ', DB_TABLE_EVENTS, DB_CHARSET);
+
+$EVENT_TYPES = array(
+    'joined'    => 'Eintritt',
+    'changed'   => 'Änderung',
+    'leaved'    => 'Austritt'
+);
+
+$PAYMENT_INTERVALS = array(
+    'monthly'   => 'monatlich',
+    'quarterly' => 'vierteljährlich',
+    'halfyearly'=> 'halbjährlich',
+    'yearly'    => 'jährlich'
+);
+
+$MEMBER_TYPES = array(
+    'regular'   => 'Ordentliches Mitglied',
+    'patron'    => 'Fördermitglied',
+    'honorary'  => 'Ehrenmitglied'
+);
+
+/* DB functions {{{ */
+
+function db_create_event($event) {
+    unset($event['id']);
+    $event['event_date']    = db_unixtime2datetime(ui_date2unixtime($event['event_date']));
+    $event['fee']                  = ui_money2float($event['fee']);
+    $event['created_at']    = db_unixtime2datetime(time());
+    $event['modified_at']   = db_unixtime2datetime(time());
+    return (db_insert_single(DB_TABLE_EVENTS, $event));
+}
+
+function db_change_event($event) {
+    $id = $event['id'];
+    unset($event['id']);
+    unset($event['member_id']);
+    $event['event_date']    = db_unixtime2datetime(ui_date2unixtime($event['event_date']));
+    $event['fee']                  = ui_money2float($event['fee']);
+    $event['modified_at']   = db_unixtime2datetime(time());
+    $special = '';
+    if (!isset($event['fee']             )) { $special .= ',`fee`=NULL';              }
+    if (!isset($event['member_type']     )) { $special .= ',`member_type`=NULL';      }
+    if (!isset($event['payment_interval'])) { $special .= ',`payment_interval`=NULL'; }
+    return db_update_single(DB_TABLE_EVENTS, $event, sprintf('id=\'%1$d\'', db_escape($id)), $special);
+}
+
+function db_delete_event($event) {
+    return db_delete_single(DB_TABLE_EVENTS, sprintf('id=\'%1$d\'', db_escape($event['id'])));
+}
+
+function db_get_events_for_member($member_id, $unixtime_start = NULL, $unixtime_end = NULL) {
+       $conditions = array(
+               sprintf('member_id=\'%d\'', db_escape($member_id))
+       );
+       if (isset($unixtime_start)) $conditions[] = sprintf('event_date>=\'%s\'', db_unixtime2date($unixtime_start));
+       if (isset($unixtime_end)  ) $conditions[] = sprintf('event_date<=\'%s\'', db_unixtime2date($unixtime_end));
+       
+    $sql = 'SELECT * FROM %1$s WHERE %2$s ORDER BY event_date ASC';
+    return db_select_multi(sprintf($sql, DB_TABLE_EVENTS, join(' AND ', $conditions)));
+}
+
+function db_get_event_with_member_and_date($member_id, $event_date) {
+    $sql = 'SELECT * FROM %1$s WHERE member_id=\'%2$d\' AND event_date=\'%3$s\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_EVENTS, db_escape($member_id), db_escape($event_date)));
+}
+
+function db_get_event_with_id($event_id) {
+    $sql = 'SELECT * FROM %1$s WHERE id=\'%2$d\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_EVENTS, db_escape($event_id)));
+}
+
+/* }}} */
+
+
+function validate_event($userdata, &$dbdata, &$validation) {/*{{{*/
+    global $EVENT_TYPES, $MEMBER_TYPES, $PAYMENT_INTERVALS;
+    $fields = array(
+        'id'              => 'number',
+        'event_date'      => 'date',
+        'fee'             => 'fee',
+        'event_type'      => $EVENT_TYPES,
+        'member_type'     => $MEMBER_TYPES,
+        'payment_interval'=> $PAYMENT_INTERVALS,
+    );
+    $orig = $dbdata;
+    foreach ($fields as $name => $type) {
+        if (!isset($userdata[$name])) continue;
+        $dbdata[$name] = $userdata[$name];
+    }
+
+
+// ids must be equal
+// member_ids must be equal
+// date must be unique for given member
+    if (isset($orig['id'])) {
+        if ($dbdata['id'] != $orig['id']) {
+            $validation[] = 'Irgendwas anderes ist sehr sehr komisch.';
+        }
+               if (empty($dbdata['event_date'])) {
+            $validation[] = 'Bitte ein Datum für das Ereignis eingeben.';
+               }
+        elseif (!validate_event_date($dbdata['member_id'], $dbdata['event_date'], $dbdata['id'])) {
+            $validation[] = 'An dem angegebenen Datum existiert schon ein Ereignis.';
+        }
+        return (count($validation) == 0);
+    }
+       if (empty($dbdata['event_date'])) {
+           $validation[] = 'Bitte ein Datum für das Ereignis eingeben.';
+       }
+    elseif (!validate_event_date($dbdata['member_id'], $dbdata['event_date'])) {
+        $validation[] = 'An dem angegebenen Datum existiert schon ein Ereignis.';
+    }
+    return (count($validation) == 0);
+}/*}}}*/
+
+
+
+
+function validate_event_date($member_id, $event_date, $event_id = NULL) {/*{{{*/
+    $event_date = db_unixtime2date(ui_date2unixtime($event_date));
+    $event      = db_get_event_with_member_and_date($member_id, $event_date);
+
+    // theres no event with this date
+    if (!isset($event)) return true;
+
+    // there is a event, but maybe its the one we got passed
+    if (isset($event_id) && $event_id == $event['id']) return true;
+
+    return false;
+}/*}}}*/
+
+
+function action_list_events($member = NULL) {/*{{{*/
+    global $EVENT_TYPES, $MEMBER_TYPES, $PAYMENT_INTERVALS;
+
+    if (!isset($member)) {
+        if (!isset($_REQUEST['member_id'])) redirect(link_to());
+        $member = db_get_member_with_id($_REQUEST['id']);
+        if (!isset($member)) redirect(link_to());
+    }
+
+    $events = db_get_events_for_member($member['id']);
+
+    ?>
+    <h3 id="events">Ereignisse</h3>
+    <?php if (isset($events)) : ?>
+        <table>
+            <tr>
+                <th>Datum</th>
+                <th>Ereignistyp</th>
+                <th>Mitgliedstyp</th>
+                <th>Beitrag</th>
+                <th>Zahlungsintervall</th>
+            </tr>
+        <?php foreach ($events as $event) : ?>
+            <tr>
+                <td><a href="<?=html_escape(link_to('change_event', array('id' => $event['id'])))?>#event_form"><?=html_escape(format_date(db_date2unixtime($event['event_date'])))?></a></td>
+                <td><?=html_escape($EVENT_TYPES[$event['event_type']])?></td>
+                <td><?=html_escape(isset($event['member_type'])? $MEMBER_TYPES[$event['member_type']] : '-')?></td>
+                <td><?=html_escape(isset($event['fee'])? format_money($event['fee']) : '-')?></td>
+                <td><?=html_escape(isset($event['payment_interval'])? $PAYMENT_INTERVALS[$event['payment_interval']] : '-')?></td>
+            </tr>
+        <?php endforeach ?>
+        </table>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine Ereignisse.</p>
+    <?php endif ?>
+    <?php
+}/*}}}*/
+
+
+
+function action_create_event($member = NULL) {/*{{{*/
+    if (!isset($member)) {
+        if (!isset($_REQUEST['member_id'])) redirect(link_to());
+        $member = db_get_member_with_id($_REQUEST['id']);
+        if (!isset($member)) redirect(link_to());
+    }
+    $event = array();
+    $event['event_date']    = format_date(time());
+    $event['member_id']     = $member['id'];
+    $events = db_get_events_for_member($member['id']);
+    if (empty($events)) {
+        $event['member_type'] = 'regular';
+    }
+    else {
+        $event['event_type'] = 'changed';
+    }
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        unset($_POST['id']);
+        if (validate_event($_POST, $event, $log_messages)) {
+            if (empty($event['fee']             )) unset($event['fee']             );
+            if (empty($event['member_type']     )) unset($event['member_type']     );
+            if (empty($event['payment_interval'])) unset($event['payment_interval']);
+            if (db_create_event($event)) {
+                redirect(link_to('view_member', array('id' => $event['member_id'])) . '#events');
+            }
+            echo db_error();
+        }
+    }
+    form_event($event, 'create', $log_messages);
+}/*}}}*/
+
+function action_change_event() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to());
+    $event = db_get_event_with_id($_REQUEST['id']);
+    $event['event_date']    = format_date(db_date2unixtime($event['event_date']));
+    $event['fee']                  = format_money($event['fee']);
+    if (!isset($event)) redirect(link_to());
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        if (validate_event($_POST, $event, $log_messages)) {
+            if (empty($event['fee']             )) unset($event['fee']             );
+            if (empty($event['member_type']     )) unset($event['member_type']     );
+            if (empty($event['payment_interval'])) unset($event['payment_interval']);
+            if (db_change_event($event)) {
+                redirect(link_to('view_member', array('id' => $event['member_id'])) . '#events');
+            }
+            echo db_error();
+        }
+    }
+
+    $member = db_get_member_with_id($event['member_id']);
+    if (!isset($member)) redirect(link_to());
+    form_member($member, true);
+    action_list_events($member);
+    form_event($event, 'change', $log_messages);
+}/*}}}*/
+
+function action_delete_event() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to());
+    $event = db_get_event_with_id($_REQUEST['id']);
+    if (!isset($event)) redirect(link_to());
+    $event['event_date']    = format_date(db_date2unixtime($event['event_date']));
+    $event['fee']                  = format_money($event['fee']);
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        if (db_delete_event($event)) {
+            redirect(link_to('view_member', array('id' => $event['member_id'])) . '#events');
+        }
+        echo db_error();
+    }
+    $member = db_get_member_with_id($event['member_id']);
+    if (!isset($member)) redirect(link_to());
+    form_member($member, true);
+    action_list_events($member);
+    form_event($event, 'delete', $log_messages);
+}/*}}}*/
+
+
+
+function form_event($event = array(), $task, $log_messages) {/*{{{*/
+    global $EVENT_TYPES, $MEMBER_TYPES, $PAYMENT_INTERVALS;
+
+    $readonly = ($task == 'delete');
+
+    ?>
+    <form action="#event_form" method="post" class="clearfix" id="event_form">
+        <?php if (isset($event['id'])) : ?><?=html_hidden_field('id', $event)?><?php endif ?>
+        <?=html_hidden_field('member_id', $event)?>
+        <fieldset>
+        <?php if     ($task == 'create') : ?>
+            <legend>Neues Ereignis</legend>
+        <?php elseif ($task == 'delete') : ?>
+            <legend>Ereignis l&ouml;schen</legend>
+        <?php elseif ($task == 'change') : ?>
+            <legend>Ereignis &auml;ndern</legend>
+        <?php endif ?>
+            <?php log_messages($log_messages); ?>
+            <?=html_text_field('Datum', 'event_date', $event, $readonly)?>
+            <?=html_list_box('Ereignistyp', 'event_type', $EVENT_TYPES, $event, $readonly)?>
+            <?=html_list_box('Mitgliedstyp', 'member_type', array_merge(array(0 => 'unverändert'), $MEMBER_TYPES), $event, $readonly)?>
+            <?=html_money_field('Beitrag', 'fee', $event, $readonly)?>
+            <small>Leer lassen wenn unver&auml;ndert.</small>
+            <?=html_list_box('Zahlungsintervall', 'payment_interval', array_merge(array(0 => 'unverändert'), $PAYMENT_INTERVALS), $event, $readonly)?>
+        </fieldset>
+    <?php if ($task == 'delete') : ?>
+        <input class="submit" type="submit" name="submit" value="L&ouml;schen"/>
+    <?php else : ?>
+        <input class="submit" type="submit" name="submit" value="Speichern"/>
+    <?php endif ?>
+    </form>
+    <?php if ($task == 'change') : ?>
+        <a href="<?=html_escape(link_to('delete_event', array('id' => $event['id'])))?>#event_form">Ereignis l&ouml;schen</a>
+        <a href="<?=html_escape(link_to('view_member', array('id' => $event['member_id'])))?>#event_form">Neues Ereignis</a>
+    <?php elseif ($task == 'delete') : ?>
+        <a href="<?=html_escape(link_to('view_member', array('id' => $event['member_id'])))?>#event_form">Neues Ereignis</a>
+    <?php endif ?>
+    <?php
+}/*}}}*/
+
+
diff --git a/include/expenses.php b/include/expenses.php
new file mode 100644 (file)
index 0000000..43c9b89
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+
+$SQL_CREATE_EXPENSES = sprintf('
+    CREATE TABLE IF NOT EXISTS `%1$s` (
+      `id` int(10) unsigned NOT NULL auto_increment,
+      `date` date NOT NULL,
+      `type` enum(\'invoice\') NULL,
+      `status` enum(\'open\',\'paid\') NOT NULL,
+      `account` enum(\'bank\',\'cash\') NOT NULL,
+      `value` DECIMAL(10,2) NULL,
+      `member_id` int(10) unsigned NOT NULL,
+      `description` TEXT NOT NULL,
+      `created_at` datetime NOT NULL,
+      `modified_at` datetime NOT NULL,
+      PRIMARY KEY  (`id`),
+      INDEX `idx_member_id` (`member_id`),
+      INDEX `idx_date` (`date`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=%2$s
+    ', DB_TABLE_EXPENSES, DB_CHARSET);
+
+$EXPENSE_TYPES = array(
+    'invoice'   => 'Rechnung'
+);
+
+
+/* DB functions {{{ */
+
+function db_get_expense_with_id($expense_id) {
+    $sql = 'SELECT * FROM %1$s WHERE id=\'%2$d\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_EXPENSES, db_escape($expense_id)));
+}
+
+function db_create_expense($expense) {
+    unset($expense['id']);
+    $expense['date']        = db_unixtime2datetime(ui_date2unixtime($expense['date']));
+    $expense['value']       = ui_money2float($expense['value']);
+    $expense['created_at']  = db_unixtime2datetime(time());
+    $expense['modified_at'] = db_unixtime2datetime(time());
+    return (db_insert_single(DB_TABLE_EXPENSES, $expense));
+}
+
+function db_change_expense($expense) {
+    $id = $expense['id'];
+    unset($expense['id']);
+    $expense['date']        = db_unixtime2datetime(ui_date2unixtime($expense['date']));
+    $expense['value']       = ui_money2float($expense['value']);
+    $expense['modified_at'] = db_unixtime2datetime(time());
+    return db_update_single(DB_TABLE_EXPENSES, $expense, sprintf('id=\'%1$d\'', db_escape($id)));
+}
+
+/* }}} */
+
+
+function validate_expense($userdata, &$dbdata, &$validation) {/*{{{*/
+    global $EXPENSE_TYPES, $ACCOUNT_TYPES;
+    $fields = array(
+        'id'              => 'number',
+        'date'            => 'date',
+        'type'            => $EXPENSE_TYPES,
+        'account'         => $ACCOUNT_TYPES,
+        'member_id'       => 'member',
+        'value'           => 'money',
+        'description'     => 'text',
+//      'status'          -  See below...
+    );
+    $orig = $dbdata;
+    $dbdata['status'] = empty($userdata['status']) ? 'open' : 'paid';
+    foreach ($fields as $name => $type) {
+        if (!isset($userdata[$name])) continue;
+        $dbdata[$name] = $userdata[$name];
+    }
+
+    if (empty($dbdata['date'])) {
+        $validation[] = 'Bitte ein Datum angeben.';
+    }
+    if ((float)$dbdata['value'] == 0) {
+        $validation[] = 'Bitte einen Betrag angeben.';
+    }
+
+    return (count($validation) == 0);
+}/*}}}*/
+
+
+function action_create_expense() {/*{{{*/
+    $expense = array();
+    $expense['date']      = format_date(time());
+    $expense['status']    = 'paid';
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        unset($_POST['id']);
+        if (validate_expense($_POST, $expense, $log_messages)) {
+            if (db_create_expense($expense)) {
+                redirect(link_to('finances'));
+            }
+            echo db_error();
+        }
+    }
+    form_expense($expense, 'create', $log_messages);
+}/*}}}*/
+
+
+function action_change_expense() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to('finances'));
+    $expense = db_get_expense_with_id($_REQUEST['id']);
+    $expense['date'] = format_date(db_date2unixtime($expense['date']));
+    $expense['value']= format_money($expense['value']);
+    if (!isset($expense)) redirect(link_to('finances'));
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        if (validate_expense($_POST, $expense, $log_messages)) {
+            if (db_change_expense($expense)) {
+                redirect(link_to('finances'));
+            }
+            echo db_error();
+        }
+    }
+
+    form_expense($expense, 'change', $log_messages);
+}/*}}}*/
+
+
+function form_expense($expense = array(), $task, $log_messages) {/*{{{*/
+    global $EXPENSE_TYPES, $ACCOUNT_TYPES;
+
+    $readonly = ($task == 'delete');
+
+    ?>
+    <form action="<?=link_to($task . '_expense')?>" method="post" class="clearfix" id="expense_form" style="<?php if ($_REQUEST['action'] == 'finances') : ?>float: left<?php else :?>margin-top: 2em<?php endif ?>">
+        <?php if (isset($expense['id'])) : ?><?=html_hidden_field('id', $expense)?><?php endif ?>
+        <fieldset>
+        <?php if     ($task == 'create') : ?>
+            <legend>Neue Ausgabe</legend>
+        <?php elseif ($task == 'delete') : ?>
+            <legend>Ausgabe l&ouml;schen</legend>
+        <?php elseif ($task == 'change') : ?>
+            <legend>Ausgabe &auml;ndern</legend>
+        <?php endif ?>
+            <?php log_messages($log_messages); ?>
+            <?=html_text_field('Datum', 'date', $expense, $readonly)?>
+            <?=html_list_box('Typ', 'type', $EXPENSE_TYPES, $expense, $readonly)?>
+            <?=html_member_list_box('Mitglied', 'member_id', $expense, $readonly)?>
+            <?=html_money_field('Betrag', 'value', $expense, $readonly)?>
+            <?=html_text_field('Beschreibung', 'description', $expense, $readonly)?>
+            <?=html_list_box('Quelle', 'account', $ACCOUNT_TYPES, $expense, $readonly)?>
+            <?=html_checkbox('Bezahlt', 'status', $expense['status'] == 'paid', 1, $readonly)?>
+        </fieldset>
+    <?php if ($task == 'delete') : ?>
+        <input class="submit" type="submit" name="submit" value="L&ouml;schen"/>
+    <?php else : ?>
+        <input class="submit" type="submit" name="submit" value="Speichern"/>
+    <?php endif ?>
+    </form>
+    <?php if ($task == 'change') : ?>
+        <br/>
+        <a href="<?=html_escape(link_to('delete_expense', array('id' => $expense['id'])))?>#expense_form">Ausgabe l&ouml;schen</a>
+        <a href="<?=html_escape(link_to('finances'))?>#expense_form">Neue Ausgabe</a>
+    <?php elseif ($task == 'delete') : ?>
+        <br/>
+        <a href="<?=html_escape(link_to('finances'))?>#expense_form">Neue Ausgabe</a>
+    <?php endif ?>
+    <?php
+}/*}}}*/
+
+
diff --git a/include/fees.php b/include/fees.php
new file mode 100644 (file)
index 0000000..4c1f6e2
--- /dev/null
@@ -0,0 +1,459 @@
+<?php
+
+
+/* HELPER functions FOR FEES {{{ */
+
+// build an empty structure for holding monthly information
+function _fees_build_month_array($start_date, $end_date, $preset = array()) {
+    $start_info = getdate($start_date);
+    $end_info   = getdate($end_date);
+
+    if ($start_info['year'] > $end_info['year']) return $preset;
+    if ($start_info['year'] == $end_info['year'] && $start_info['mon'] > $end_info['mon']) return $preset;
+
+    $ret = $preset;
+    for ($year = $start_info['year']; $year <= $end_info['year']; $year++) {
+        if (!isset($ret[$year])) $ret[$year] = array();
+        for (
+            $month  = (($year == $start_info['year']) ? $start_info['mon'] :  1);
+            $month <= (($year == $end_info['year']  ) ? $end_info['mon']   : 12);
+            $month++
+        ) {
+            if (isset($ret[$year][$month])) continue;
+            $ret[$year][$month] = array(
+                'is_member'         => NULL,
+                'member_type'       => NULL,
+                'fee'               => NULL,
+                'payment_interval'  => NULL
+            );
+        }
+    }
+    return $ret;
+}
+
+function _fees_apply_event_information(&$info, $events) {
+    if (!empty($events)) {
+
+        foreach ($events as $event) {
+
+            $timestamp = db_date2unixtime($event['event_date']);
+            if ($timestamp < FOUNDING_DATE) $timestamp = FOUNDING_DATE;
+            $date_info = getdate($timestamp);
+
+            if (!isset($info[$date_info['year']])) continue;
+            if (!isset($info[$date_info['year']][$date_info['mon']])) continue;
+
+            if ($event['fee']              !== NULL)      $info[$date_info['year']][$date_info['mon']]['fee']              = $event['fee'];
+            if ($event['member_type']      !== NULL)      $info[$date_info['year']][$date_info['mon']]['member_type']      = $event['member_type'];
+            if ($event['event_type']       !== 'changed') $info[$date_info['year']][$date_info['mon']]['is_member']        = ($event['event_type'] == 'joined' ? 1 : 0);
+            if ($event['payment_interval'] !== NULL)      $info[$date_info['year']][$date_info['mon']]['payment_interval'] = $event['payment_interval'];
+        }
+    }
+    
+    $fee              = 0;
+    $member_type      = NULL;
+    $is_member        = false;
+    $payment_interval = NULL;
+
+    foreach (array_keys($info) as $year) {
+        foreach (array_keys($info[$year]) as $month) {
+            if (!isset($info[$year][$month]['fee'])) $info[$year][$month]['fee'] = $fee;
+            else $fee = $info[$year][$month]['fee'];
+
+            if (!isset($info[$year][$month]['member_type'])) $info[$year][$month]['member_type'] = $member_type;
+            else $member_type = $info[$year][$month]['member_type'];
+
+            if (!isset($info[$year][$month]['is_member'])) $info[$year][$month]['is_member'] = $is_member;
+            else $is_member = $info[$year][$month]['is_member'];
+
+            if (!isset($info[$year][$month]['payment_interval'])) $info[$year][$month]['payment_interval'] = $payment_interval;
+            else $payment_interval = $info[$year][$month]['payment_interval'];
+        }
+    }
+    return;
+}
+
+function fees_get_list_for_member($member_id, $end_date) {
+
+    static $cache = array();
+
+    $end_date = db_date2unixtime(db_unixtime2date($end_date)); // remove hours, minutes, seconds
+
+    if (isset($cache[$member_id][$end_date])) return $cache[$member_id][$end_date];
+    if (isset($cache[$member_id])) {
+        foreach (array_reverse(array_keys($cache[$member_id])) as $cache_date) {
+            if ($cache_date <= $end_date) {
+                $ret = _fees_build_month_array($cache_date /* XXX einen Monat später wäre an dieser Stelle richtiger*/, $end_date, $cache[$member_id][$cache_date]);
+                _fees_apply_event_information($ret, db_get_events_for_member($member_id, $cache_date, $end_date));
+                $cache[$member_id][$end_date] = $ret;
+                return $ret;
+            }
+        }
+    }
+
+    $ret = _fees_build_month_array(FOUNDING_DATE, $end_date);
+    if (empty($ret)) return;
+
+    _fees_apply_event_information($ret, db_get_events_for_member($member_id));
+
+    $cache[$member_id][$end_date] = $ret;
+    return $ret;
+}
+
+function fees_sum_for_member($member_id, $end_date) {
+    $membership_info = fees_get_list_for_member($member_id, $end_date);
+    
+    $total = '0';
+
+    foreach ($membership_info as $year => $months) {
+        foreach ($months as $month => $info) {
+            if ($info['is_member']) $total = bcadd($total, $info['fee']);
+        }
+    }
+    return $total;
+}
+
+function fees_for_member_at_date($member_id, $end_date) {
+    $membership_info = fees_get_list_for_member($member_id, $end_date);
+    
+    $this_year = array_pop($membership_info);
+    $this_month = array_pop($this_year);
+    if ($this_month['is_member']) return $this_month['fee'];
+    return NULL;
+}
+
+function fees_sum_by_month($end_date) {
+    $members = db_get_members();
+    $fees = array();
+    if (empty($members)) return array();
+    foreach ($members as $member) {
+        $membership_info = fees_get_list_for_member($member['id'], $end_date);
+        foreach ($membership_info as $year => $months) {
+            foreach ($months as $month => $info) {
+                if (!isset($fees[$year][$month])) $fees[$year][$month] = '0';
+                if ($info['is_member']) $fees[$year][$month] = bcadd($fees[$year][$month], $info['fee']);
+            }
+        }
+    }
+    return $fees;
+}
+function fees_get_list_for_month($year, $month) {
+    $members = db_get_members();
+    $fees = array();
+    foreach ($members as $member) {
+        $membership_info = fees_get_list_for_member($member['id'], mktime(0, 0, 0, $month, 1, $year));
+
+        if (empty($membership_info)) continue;
+        $member['fee']       = $membership_info[$year][$month]['fee'];
+        $member['is_member'] = $membership_info[$year][$month]['is_member'];
+        $fees[] = $member;
+    }
+    return $fees;
+}
+
+function fee_next_directdebit_for_member($member_id, $max_date = NULL) {
+
+       $member = db_get_member_with_id($member_id);
+       if (!$member['directdebit']) return;
+
+    $sum_old_fees   = fees_sum_for_member($member_id, DIRECTDEBIT_DATE - 86400);
+    $sum_new_paid   = finance_get_paid_fees_for_member($member_id);
+    $year   = date('Y', DIRECTDEBIT_DATE);
+    $month  = date('n', DIRECTDEBIT_DATE);
+   
+
+
+    while (true) {
+        $start_date = mktime(0, 0, 0, $month, 1, $year);
+        if (isset($max_date) && $start_date > $max_date) return;
+
+        // check if fee is zero at the moment and skip to next event
+        // quit searching if theres no event in future
+        $current_fee = fees_for_member_at_date($member_id, $start_date);
+        if (empty($current_fee)) {
+            $events = db_get_events_for_member($member_id, $start_date + 86400);
+            if (empty($events)) return NULL;
+            $start_date = db_date2unixtime($events[0]['event_date']);
+            $month = date('n', $start_date);
+            $year  = date('Y', $start_date);
+            continue;
+        }
+
+        $sum_fees       = fees_sum_for_member($member_id, $start_date);
+        $sum_new_fees   = bcsub($sum_fees, $sum_old_fees);
+        if (bccomp($sum_new_fees, $sum_new_paid) == 1) {
+            $info = fees_get_list_for_member($member_id, $start_date);
+            $months = 1;
+            $ret = array(
+                'date'  => $start_date,
+                'value' => bcsub($sum_new_fees, $sum_new_paid),
+                'info'  => '',
+            );
+            switch ($info[$year][$month]['payment_interval']) {
+                case 'monthly'   : $months = 1;  break;
+                case 'quarterly' : $months = 3;  break;
+                case 'halfyearly': $months = 6;  break;
+                case 'yearly'    : $months = 12; break;
+            }
+            if ($months == 1) {
+                $ret['info'] = dtaus_string(sprintf('CCCFFM %d, %s', $member['number'], format_month($start_date))); 
+                return $ret;
+            }
+            $end_date = mktime(0, 0, 0, $month + $months - 1, 1, $year);
+            $sum_fee_end = fees_sum_for_member($member_id, $end_date);
+            $ret['value'] = bcadd($ret['value'], bcsub($sum_fee_end, $sum_fees));
+            $ret['info'] = dtaus_string(sprintf('CCCFFM %d, %s-%s', $member['number'], format_month($start_date), format_month($end_date)));
+            return $ret;
+        }
+        $month++;
+        if ($month == 13) { $month = 1; $year++; }
+    }
+    
+    
+}
+/* }}} */
+
+
+function action_fees() {/*{{{*/
+
+    if (isset($_REQUEST['member_id'])) {
+        render_fees_for_member($_REQUEST['member_id']);
+        return;
+    }
+    if (isset($_REQUEST['year']) && isset($_REQUEST['month'])) {
+        render_accrued_fees_for_month($_REQUEST['year'], $_REQUEST['month']);
+        return;
+    }
+
+    render_fees_by_member();
+    render_accrued_fees_by_month();
+    render_next_direct_debit();
+
+}/*}}}*/
+
+function render_fees_by_member() {/*{{{*/
+    $members = db_get_members();
+?>
+<h2>Mitgliedsbeitr&auml;ge nach Mitglied</h2>
+<table>
+    <tr>
+        <th>Mitgliedsnummer</th>
+        <th>Nickname</th>
+        <th style="text-align: right;">Angefallene Beitr&auml;ge</th>
+        <th style="text-align: right;">Aktueller Beitrag</th>
+        <th style="text-align: right;">Offener Beitrag</th>
+    </tr>
+<?php if (empty($members)) $members = array(); ?>
+<?php foreach ($members as $member) : ?>
+<?php
+       $total_fees = fees_sum_for_member($member['id'], time());
+       $current_fee = fees_for_member_at_date($member['id'], time());
+       $old_paid_fees = finance_get_paid_fees_for_member($member['id'], true);
+       $new_paid_fees = finance_get_paid_fees_for_member($member['id']);
+       $open_fees = bcsub(bcsub($total_fees, $old_paid_fees), $new_paid_fees);
+?>
+    <tr>
+        <td><a href="<?=html_escape(link_to('fees', array('member_id'=> $member['id'])))?>"><?=html_escape($member['number'])?></a></td>
+        <td><?=html_escape($member['nickname'])?></td>
+        <td style="text-align: right;"><?=format_money($total_fees)?></td>
+        <td style="text-align: right;"><?=isset($current_fee) ? format_money($current_fee) : '-' ?></td>
+        <td style="text-align: right;"><?=$open_fees > 0 ? format_money($open_fees) : '-' ?></td>
+    </tr>
+<?php endforeach ?>
+</table>
+<?php
+}/*}}}*/
+
+function render_accrued_fees_by_month() {/*{{{*/
+    $fees = fees_sum_by_month(time());
+    $fees = array_reverse($fees, true);
+?>
+<h2>Angefallene Mitgliedsbeitr&auml;ge nach Monat</h2>
+<table>
+    <tr>
+        <th>Monat</th>
+        <th style="text-align: right;">Mitgliedsbeitrag</th>
+    </tr>
+<?php foreach ($fees as $year => $months) : ?>
+    <?php $months = array_reverse($months, true); ?>
+    <?php foreach ($months as $month => $fee) : ?>
+    <tr>
+        <td><a href="<?=html_escape(link_to('fees', array('year' => $year, 'month'=> $month)))?>"><?=html_escape(format_month($year, $month))?></a></td>
+        <td style="text-align: right;"><?=html_escape(format_money($fee))?></td>
+    </tr>
+    <?php endforeach ?>
+<?php endforeach ?>
+</table>
+<?php
+}/*}}}*/
+
+function render_accrued_fees_for_month($year, $month) {/*{{{*/
+    $fees = fees_get_list_for_month($year, $month);
+?>
+<h2>Angefallene Mitgliedsbeitr&auml;ge f&uuml;r <?=format_month($year, $month)?></h2>
+<table>
+    <tr>
+        <th>Mitgliedsnummer</th>
+        <th>Nickname</th>
+        <th style="text-align: right;">Mitgliedsbeitrag</th>
+    </tr>
+<?php foreach ($fees as $info) : ?>
+    <tr>
+        <td><a href="<?=html_escape(link_to('fees', array('member_id'=> $info['id'])))?>"><?=html_escape($info['number'])?></a></td>
+        <td><?=html_escape($info['nickname'])?></td>
+        <td style="text-align: right;"><?=html_escape($info['is_member'] ? format_money($info['fee']) : '-')?></td>
+    </tr>
+<?php endforeach ?>
+</table>
+<p><a href="<?=html_escape(link_to('fees'))?>">Alle angefallenen Mitgliedsbeitr&auml;ge</a></p>
+<?php
+}/*}}}*/
+
+function render_next_direct_debit() {/*{{{*/
+    $members = db_get_members();
+?>
+<h2>Nächste Abbuchungen nach Mitglied</h2>
+<table>
+    <tr>
+        <th>Mitgliedsnummer</th>
+        <th>Nickname</th>
+        <th style="text-align: right;">Verwendungszweck</th>
+        <th style="text-align: right;">Betrag</th>
+    </tr>
+<?php if (empty($members)) $members = array(); ?>
+<?php foreach ($members as $member) : ?>
+    <?php $next_debit = fee_next_directdebit_for_member($member['id']); ?>
+    <tr>
+        <td><a href="<?=html_escape(link_to('fees', array('member_id'=> $member['id'])))?>"><?=html_escape($member['number'])?></a></td>
+        <td><?=html_escape($member['nickname'])?></td>
+<?php if (empty($next_debit)) : ?>
+        <td>-</td>
+        <td style="text-align: right;">-</td>
+<?php else : ?>
+        <td><?=html_escape($next_debit['info'])?></td>
+        <td style="text-align: right;"><?=format_money($next_debit['value'])?></td>
+<?php endif ?>
+    </tr>
+<?php endforeach ?>
+</table>
+<?php
+}
+
+function render_fees_for_member($member_id) {/*{{{*/
+    global $MEMBER_TYPES, $EARNING_TYPES;
+
+    $member      = db_get_member_with_id($member_id);
+    if (!isset($member)) redirect(link_to('fees'));
+
+    $membership_info = fees_get_list_for_member($member_id, time());
+    $membership_info = array_reverse($membership_info, true);
+
+    $paid_fees = array_reverse(finance_list_paid_fees_for_member($member_id, time(), true));
+
+    $sum_new_paid = finance_get_paid_fees_for_member($member_id);
+    $sum_old_paid = finance_get_paid_fees_for_member($member_id, true);
+    $sum_old_fees = fees_sum_for_member($member_id, DIRECTDEBIT_DATE - 86400);
+    $sum_fees     = fees_sum_for_member($member_id, time());
+    $sum_new_fees = bcsub($sum_fees, $sum_old_fees);
+
+    $state = '';
+    $new_open = 0;
+    $old_open = 0;
+    if (bccomp($sum_new_fees, $sum_new_paid) == 1) $new_open = 1;
+    if (bccomp($sum_old_fees, $sum_old_paid) == 1) $old_open = 1;
+
+    if ($new_open && $old_open) {
+        $state = sprintf('Es sind noch %1$s Mitgliedsbeitrag offen, davon %2$s für die Zeit vor dem %3$s und %4$s für danach.',
+            format_money(bcadd(bcsub($sum_old_fees, $sum_old_paid), bcsub($sum_new_fees, $sum_new_paid))),
+            format_money(bcsub($sum_old_fees, $sum_old_paid)),
+            format_date(DIRECTDEBIT_DATE),
+            format_money(bcsub($sum_new_fees, $sum_new_paid))
+        );
+    }
+    elseif($new_open) {
+        $state = sprintf('Es sind noch %1$s Mitgliedsbeitrag offen.', format_money(bcsub($sum_new_fees, $sum_new_paid)));
+    }
+    elseif($old_open) {
+        $state = sprintf('Für die Zeit vor dem %1$s sind noch %2$s Mitgliedsbeitrag offen.', format_date(DIRECTDEBIT_DATE), format_money(bcsub($sum_old_fees, $sum_old_paid)));
+    }
+
+    $next_debit = fee_next_directdebit_for_member($member_id);
+
+?>
+<h2>Mitgliedsbeitr&auml;ge von <?=html_escape(!empty($member['nickname']) ? $member['nickname'] : sprintf('Mitglied Nr. %d', $member['number']))?></h2>
+<h3>Mitgliedsdetails</h3>
+<table>
+    <tr>
+        <th>Mitgliedsnummer</th>
+        <th>Nickname</th>
+        <th>Status</th>
+    </tr>
+    <tr>
+        <td><a href="<?=html_escape(link_to('view_member', array('id'=> $member['id'])))?>"><?=html_escape($member['number'])?></a></td>
+        <td><?=html_escape($member['nickname'])?></strong></p></td>
+        <td>
+        <?php if (empty($state)) : ?>
+            Kein Beitragsrückstand
+        <?php else : ?>
+            <?=wordwrap(html_escape($state), 70, '<br/>')?>
+        <?php endif ?>
+</td>
+    </tr>
+</table>
+<div style="float: left">
+<h3>Angefallene Mitgliedsbeitr&auml;ge</h3>
+<table>
+    <tr>
+        <th>Monat</th>
+        <th>Mitgliedsart</th>
+        <th style="text-align: right;">Mitgliedsbeitrag</th>
+    </tr>
+<?php foreach ($membership_info as $year => $months) : ?>
+    <?php $months = array_reverse($months, true); ?>
+    <?php foreach ($months as $month => $info) : ?>
+    <tr>
+        <td><?=html_escape(format_month($year, $month))?></td>
+        <td><?=html_escape($info['is_member'] ? $MEMBER_TYPES[$info['member_type']] : 'Kein Mitglied')?></td>
+        <td style="text-align: right;"><?=html_escape($info['is_member'] ? format_money($info['fee']) : '-')?></td>
+    </tr>
+    <?php endforeach ?>
+<?php endforeach ?>
+</table>
+<p><a href="<?=html_escape(link_to('fees'))?>">Alle angefallenen Mitgliedsbeitr&auml;ge</a></p>
+</div>
+<div style="float: left; margin-left: 1em;">
+<h3>Nächste Abbuchung</h3>
+<table>
+    <tr>
+        <th>Verwendungszweck</th>
+        <th style="text-align: right;">Betrag</th>
+    </tr>
+<?php if (empty($next_debit)) : ?>
+        <td>-</td>
+        <td style="text-align: right;">-</td>
+<?php else : ?>
+        <td><?=html_escape($next_debit['info'])?></td>
+        <td style="text-align: right;"><?=format_money($next_debit['value'])?></td>
+<?php endif ?> 
+</table>
+<h3>Bezahlte Mitgliedsbeitr&auml;ge</h3>
+<table>
+    <tr>
+        <th>Monat</th>
+        <th style="text-align: right;">Typ</th>
+        <th style="text-align: right;">Betrag</th>
+    </tr>
+<?php foreach ($paid_fees as $payment) : ?>
+    <tr>
+        <td><?=html_escape(format_date(db_date2unixtime($payment['date'])))?></td>
+        <td><?=$EARNING_TYPES[$payment['type']]?></td>
+        <td style="text-align: right;"><?=format_money($payment['value'])?></td>
+    </tr>
+<?php endforeach ?>
+</table>
+</div>
+<br style="clear: left;"/>
+<?php
+}/*}}}*/
+
+
diff --git a/include/finances.php b/include/finances.php
new file mode 100644 (file)
index 0000000..179f206
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+
+function finance_get_paid_fees_for_member($member_id, $old_fees = false) {
+    // $old_fees: Beiträge die nicht durch Lastschrift eingezogen werden.
+    //            Falls zu viel bezahlt, müssen diese auf die per Lastschrift
+    //            eingezogenen Beiträge angerechnet werden.
+    $row = db_select_single(sprintf('SELECT SUM(value) as paid_fees FROM %1$s WHERE member_id=\'%2$d\' AND type=\'%3$s\' AND status=\'paid\'',
+        DB_TABLE_EARNINGS,
+        db_escape($member_id),
+        $old_fees ? 'old_fee' : 'fee'
+    ));
+
+    $paid_fees = $row['paid_fees'];
+
+    if ($old_fees) return $paid_fees;
+
+    $invoiced_fees      = fees_sum_for_member($member_id, DIRECTDEBIT_DATE - 86400);
+    $paid_invoiced_fees = finance_get_paid_fees_for_member($member_id, true);
+    if (bccomp($paid_invoiced_fees, $invoiced_fees) == 1) $paid_fees = bcadd($paid_fees, bcsub($paid_invoiced_fees, $invoiced_fees));
+
+    return $paid_fees;
+}
+
+function finance_list_paid_fees_for_member($member_id) {
+    return db_select_multi(sprintf('SELECT * FROM %1$s WHERE member_id=\'%2$d\' ORDER BY `date` ASC',
+        DB_TABLE_EARNINGS,
+        db_escape($member_id)
+    ));
+}
+
+function action_finances() {/*{{{*/
+?>
+    <h2>Finanzen</h2>
+<?php
+    action_create_expense();
+    action_create_earning();
+       echo '<br style="clear: left;"/>';
+    render_open_payments();
+    render_finished_payments();
+}/*}}}*/
+
diff --git a/include/html.php b/include/html.php
new file mode 100644 (file)
index 0000000..274f680
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+function html_escape($val) {
+    return htmlentities($val, ENT_COMPAT, 'UTF-8');
+}
+
+function html_hidden_field($name, $value = '', $id = NULL) {
+    if (!isset($id)) $id = $name;
+    if (is_array($value)) {
+        if (isset($value[$name])) $value = $value[$name];
+        else $value = '';
+    }
+    return sprintf('<input type="hidden" name="%1$s" id="%3$s" value="%2$s"/>', $name, html_escape($value), $id) . "\n";
+}
+
+function html_text_field($label, $name, $value = '', $readonly = false, $id = NULL) {
+    if (!isset($id)) $id = $name;
+    if (is_array($value)) {
+        if (isset($value[$name])) $value = $value[$name];
+        else $value = '';
+    }
+    if ($readonly) $readonly = ' readonly="readonly"';
+    else $readonly = '';
+    return sprintf('<label for="%4$s">%1$s:</label><input type="text" name="%2$s" id="%4$s" value="%3$s"%5$s/>', html_escape($label), $name, html_escape($value), $id, $readonly) . "\n";
+}
+
+function html_money_field($label, $name, $value = '', $readonly = false, $id = NULL) {
+    if (!isset($id)) $id = $name;
+    if (is_array($value)) {
+        if (isset($value[$name])) $value = $value[$name];
+        else $value = '';
+    }
+    $value = str_replace(array('.', '€'), array(',', ''), $value);
+    if ($readonly) $readonly = ' readonly="readonly"';
+    else $readonly = '';
+    return sprintf('<label for="%4$s">%1$s:</label><input type="text" name="%2$s" id="%4$s" value="%3$s"%5$s/>', html_escape($label), $name, html_escape($value), $id, $readonly) . "\n";
+}
+
+function html_password_field($label, $name, $id = NULL) {
+    if (!isset($id)) $id = $name;
+    return sprintf('<label for="%3$s">%1$s:</label><input type="password" name="%2$s" id="%3$s"/>', html_escape($label), $name, $id) . "\n";
+}
+
+function html_checkbox($label, $name, $checked, $value, $readonly = false, $id = NULL) {
+    if (!isset($id)) $id = $name;
+    if (is_array($checked)) {
+        if (isset($checked[$name])) $checked = $checked[$name];
+        else $checked = false;
+    }
+    if ($checked) $checked = ' checked="checked"';
+    else $checked = '';
+    if ($readonly) $readonly = ' disabled="disabled"';
+    else $readonly = '';
+    return sprintf('<div class="checkbox"><input type="checkbox" name="%2$s" id="%4$s" value="%3$s"%5$s%6$s/><label for="%4$s">%1$s</label></div>', html_escape($label), $name, html_escape($value), $id, $checked, $readonly) . "\n";
+}
+
+function html_list_box($label, $name, $list, $selected, $readonly = false, $id = NULL, $tag_params = '') {
+    if (!isset($id)) $id = $name;
+    if (!empty($tag_params)) $tag_params = ' ' . $tag_params;
+    if (is_array($selected)) {
+        if (isset($selected[$name])) $selected = $selected[$name];
+        else $selected = '';
+    }
+    if ($readonly) return html_text_field($label, $name, isset($list[$selected]) ? $list[$selected] : '-', true);
+
+    $ret = sprintf('<label for="%2$s">%1$s:</label>', html_escape($label), $id) . "\n";
+    $ret .= sprintf('<select id="%1$s" name="%1$s"%2$s>', html_escape($name), $tag_params);
+    foreach ($list as $key => $value) {
+        if ($key == $selected) $ret .= sprintf('<option value="%1$s" selected="selected">%2$s</option>', html_escape($key), str_replace(' ', '&nbsp;', html_escape($value)));
+        else $ret .= sprintf('<option value="%1$s">%2$s</option>', html_escape($key), str_replace(' ', '&nbsp;', html_escape($value)));
+    }
+    $ret .= '</select>';
+    return $ret;
+}
+
diff --git a/include/installation.php b/include/installation.php
new file mode 100644 (file)
index 0000000..f57ccae
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+function install_database() {
+    if (!mysql_query($GLOBALS['SQL_CREATE_MEMBERS']))  echo db_error();
+    if (!mysql_query($GLOBALS['SQL_CREATE_EVENTS']))   echo db_error();
+    if (!mysql_query($GLOBALS['SQL_CREATE_CRYPTO']))   echo db_error();
+    if (!mysql_query($GLOBALS['SQL_CREATE_EARNINGS'])) echo db_error();
+    if (!mysql_query($GLOBALS['SQL_CREATE_EXPENSES'])) echo db_error();
+}
+
+function install_clear_database() {
+    mysql_query('DROP TABLE ' . DB_TABLE_MEMBERS);
+    mysql_query('DROP TABLE ' . DB_TABLE_EVENTS);
+    mysql_query('DROP TABLE ' . DB_TABLE_CRYPTO);
+    mysql_query('DROP TABLE ' . DB_TABLE_EARNINGS);
+    mysql_query('DROP TABLE ' . DB_TABLE_EXPENSES);
+}
+
diff --git a/include/members.php b/include/members.php
new file mode 100644 (file)
index 0000000..7abcc9d
--- /dev/null
@@ -0,0 +1,550 @@
+<?php
+
+$SQL_CREATE_MEMBERS = sprintf('
+    CREATE TABLE IF NOT EXISTS `%1$s` (
+      `id` int(10) unsigned NOT NULL auto_increment,
+      `number` int(10) unsigned NOT NULL,
+      `firstname` varchar(255) NOT NULL,
+      `lastname` varchar(255) NOT NULL,
+      `nickname` varchar(255) NOT NULL,
+      `accountholder` blob NOT NULL,
+      `accountnumber` blob NOT NULL,
+      `bankcode` blob NOT NULL,
+      `bankname` blob NOT NULL,
+      `directdebit` tinyint(1) NOT NULL,
+      `street` varchar(255) NOT NULL,
+      `housenumber` varchar(255) NOT NULL,
+      `postcode` varchar(255) NOT NULL,
+      `city` varchar(255) NOT NULL,
+      `country` varchar(255) NOT NULL,
+      `email` varchar(255) NOT NULL,
+      `phone` varchar(255) NOT NULL,
+      `fax` varchar(255) NOT NULL,
+      `mobile` varchar(255) NOT NULL,
+      `jabber` varchar(255) NOT NULL,
+      `icq` varchar(255) NOT NULL,
+      `msn` varchar(255) NOT NULL,
+      `aim` varchar(255) NOT NULL,
+      `skype` varchar(255) NOT NULL,
+      `url` varchar(255) NOT NULL,
+      `twitter` varchar(255) NOT NULL,
+      `created_at` datetime NOT NULL,
+      `modified_at` datetime NOT NULL,
+      PRIMARY KEY  (`id`),
+      UNIQUE KEY `number` (`number`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=%2$s
+    ', DB_TABLE_MEMBERS, DB_CHARSET);
+
+/* DB functions {{{ */
+
+function db_create_member($member) {
+    unset($member['id']);
+    $member['created_at']   = db_unixtime2datetime(time());
+    $member['modified_at']  = db_unixtime2datetime(time());
+    if (!db_insert_single(DB_TABLE_MEMBERS, $member)) return false;
+    return db_insert_id();
+}
+
+function db_change_member($member) {
+    $id = $member['id'];
+    unset($member['id']);
+    $member['modified_at']  = db_unixtime2datetime(time());
+    return db_update_single(DB_TABLE_MEMBERS, $member, sprintf('id=\'%1$d\'', db_escape($id)));
+}
+
+function db_get_members() {
+    $sql = 'SELECT * FROM %1$s ORDER BY number ASC';
+    return db_select_multi(sprintf($sql, DB_TABLE_MEMBERS));
+}
+
+function db_get_member_with_id($member_id) {
+    $sql = 'SELECT * FROM %1$s WHERE id=\'%2$d\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_MEMBERS, db_escape($member_id)));
+}
+
+function db_get_next_free_member_number() {/*{{{*/
+    $row = db_select_single(sprintf('SELECT MAX(number) AS number FROM %1$s', DB_TABLE_MEMBERS));
+    if (!isset($row)) return 2;
+
+    $number = $row['number'] + 1;
+    // Some numbers are reserved for honorary members
+    $number = (int) $number;
+    switch ($number) {
+        case 1:
+        case 23:
+        case 42:
+        case 666:
+        case 1337:
+        case 31337:
+            return $number + 1;
+    }
+    return $number;
+};/*}}}*/
+
+
+function db_get_member_with_number($member_number) {
+    $sql = 'SELECT * FROM %1$s WHERE number=\'%2$d\' LIMIT 1';
+    return db_select_single(sprintf($sql, DB_TABLE_MEMBERS, db_escape($member_number)));
+}
+
+/* }}} */
+
+function validate_member($userdata, &$dbdata, &$validation) {/*{{{*/
+    $fields = array(
+        'id'            => 'number',
+        'number'        => 'number',
+        'firstname'     => 'string',
+        'lastname'      => 'string',
+        'nickname'      => 'string',
+        'accountholder' => 'string',
+        'accountnumber' => 'string',
+        'bankcode'      => 'string',
+        'bankname'      => 'string',
+        'directdebit'   => 'bool',
+        'street'        => 'string',
+        'housenumber'   => 'string',
+        'postcode'      => 'string',
+        'city'          => 'string',
+        'country'       => 'string',
+        'email'         => 'string',
+        'phone'         => 'string',
+        'fax'           => 'string',
+        'mobile'        => 'string',
+        'jabber'        => 'string',
+        'icq'           => 'string',
+        'msn'           => 'string',
+        'aim'           => 'string',
+        'skype'         => 'string',
+        'url'           => 'string',
+        'twitter'       => 'string',
+    );
+    $orig = $dbdata;
+    foreach ($fields as $name => $type) {
+        if ($type == 'bool') {
+            $dbdata[$name] = !empty($userdata[$name]);
+            continue;
+        }
+        if (!isset($userdata[$name])) continue;
+        $dbdata[$name] = $userdata[$name];
+    }
+//      ids must be equal
+//      number must be equal. if changed - check if free and allowed
+    if (isset($orig['id'])) {
+        if ($dbdata['id'] != $orig['id']) {
+            $validation[] = 'Irgendwas ist sehr sehr komisch.';
+        }
+        if (!validate_member_number($dbdata['number'], $dbdata['id'])) {
+            $validation[] = 'Die Mitgliedsnummer ist schon vergeben.';
+        }
+        return (count($validation) == 0);
+    }
+    if (!validate_member_number($dbdata['number'])) {
+        $validation[] = 'Die Mitgliedsnummer ist schon vergeben.';
+    }
+    return (count($validation) == 0);
+}/*}}}*/
+
+function validate_member_number($member_number, $member_id = NULL) {/*{{{*/
+    $member = db_get_member_with_number($member_number);
+
+    // theres no member with this number
+    if (!isset($member)) return true;
+
+    // there is a member, but maybe its the one we got passed
+    if (isset($member_id) && $member_id == $member['id']) return true;
+
+    return false;
+}/*}}}*/
+
+
+function action_list_members() {/*{{{*/
+    $members = db_get_members();
+    ?>
+    <h2>Liste der Mitglieder</h2>
+    <?php if (isset($members)) : ?>
+        <table>
+            <tr>
+                <th>Mitgliedsnummer</th>
+                <th>Nickname</th>
+                <th>Vorname</th>
+                <th>Nachname</th>
+            </tr>
+        <?php foreach ($members as $member) : ?>
+            <tr>
+                <td><a href="<?=html_escape(link_to('view_member', array('id'=> $member['id'])))?>"><?=html_escape($member['number'])?></a></td>
+                <td><?=html_escape($member['nickname'])?></td>
+                <td><?=html_escape($member['firstname'])?></td>
+                <td><?=html_escape($member['lastname'])?></td>
+            </tr>
+        <?php endforeach ?>
+        </table>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine Mitglieder.</p>
+    <?php endif ?>
+        <a href="<?=link_to('create_member')?>">Neues Mitglied</a>
+        <a href="<?=link_to('export_email')?>">E-Mail-Adressen</a>
+    <?php
+}/*}}}*/
+
+function action_view_member() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to());
+    $member = db_get_member_with_id($_REQUEST['id']);
+    if (!isset($member)) redirect(link_to());
+
+    form_member($member, true);
+    action_list_events($member);
+    action_create_event($member);
+}/*}}}*/
+
+function action_create_member() {/*{{{*/
+    $member = array();
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        unset($_POST['id']);
+        if (validate_member($_POST, $member, $log_messages)) {
+            if (empty($member['number'])) $member['number'] = db_get_next_free_member_number();
+            if ($member_id = db_create_member($member)) {
+                redirect(link_to('view_member', array('id' => $member_id)) . '#events');
+            }
+            echo db_error();
+        }
+    }
+    form_member($member, false, $log_messages);
+}/*}}}*/
+
+function action_change_member() {/*{{{*/
+    if (!isset($_REQUEST['id'])) redirect(link_to());
+    $member = db_get_member_with_id($_REQUEST['id']);
+    if (!isset($member)) redirect(link_to());
+    $log_messages = array();
+    if (isset($_POST['submit'])) {
+        if (validate_member($_POST, $member, $log_messages)) {
+            if (empty($member['number'])) $member['number'] = db_get_next_free_member_number();
+            if (db_change_member($member)) {
+                redirect(link_to('view_member', array('id' => $member['id'])));
+            }
+            echo db_error();
+        }
+    }
+    form_member($member, false, $log_messages);
+}/*}}}*/
+
+
+function action_export_email() {/*{{{*/
+    $members = db_get_members();
+    $first = true;
+    ?>
+    <h2>E-Mail-Adressen</h2>
+    <?php if (isset($members)) : ?>
+        <?php foreach ($members as $member) : ?><?php if (empty($member['email'])) continue; ?><?php if ($first) : $first = false; else :?>,<?php endif ?> <?=$member['email']?><?php endforeach ?>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine Mitglieder.</p>
+    <?php endif ?>
+    <?php
+}/*}}}*/
+
+
+function html_member_list_box($label, $name, $selected, $readonly = false, $id = NULL, $tag_params = '') {
+    $list = array(0 => '-');
+    $members = db_get_members();
+    if (!empty($members)) {
+        foreach ($members as $member) {
+            $list[$member['id']] = $member['number'] . ' ' . $member['nickname'];
+        }
+    }
+    return html_list_box($label, $name, $list, $selected, $readonly, $id, $tag_params);
+}
+
+function form_member($member = array(), $readonly = false, $log_messages = array()) {/*{{{*/
+    
+    $keys = db_get_crypto_keys();
+    if (empty($keys)) {
+?>
+        <p class="error">
+            <strong>Achtung:</strong> Bitte zuerst <a href="<?=link_to('cryptography')?>">Kryptographie einrichten</a>.
+        </p>
+<?php
+        return;
+    }
+
+    js_modal_windows();
+    js_get_master_key();
+
+    ?>
+    <noscript>
+        <p class="error">
+            <strong>Achtung:</strong> Ohne JavaScript geht hier gar nichts. Bitte aktivieren!
+        </p>
+    </noscript>
+    <?php if (!isset($member['id'])) : ?>
+        <h2>Neues Mitglied</h2>
+    <?php elseif ($readonly) : ?>
+        <h2>Mitgliedsdetails</h2>
+    <?php else : ?>
+        <h2>Mitglied &auml;ndern</h2>
+    <?php endif ?>
+    <?php log_messages($log_messages); ?>
+    <form action="" method="post" class="clearfix">
+        <?php if (isset($member['id']) && !$readonly) : ?><?=html_hidden_field('id', $member)?><?php endif ?>
+        <fieldset>
+            <legend>Allgemein</legend>
+            <?=html_text_field('Mitgliedsnummer', 'number', $member, $readonly)?>
+            <?php if (!$readonly) : ?><small>Wird automatisch vergeben wenn leer.</small><?php endif ?>
+            <?=html_text_field('Nickname', 'nickname', $member, $readonly)?>
+            <?=html_text_field('E-Mail', 'email', $member, $readonly)?>
+        </fieldset>
+        <fieldset id="bank_details">
+            <legend>Bankverbindung</legend>
+            <?=html_text_field('Kontoinhaber', 'accountholder', $member, true)?>
+            <?=html_text_field('Kontonummer', 'accountnumber', $member, true)?>
+            <?=html_text_field('Bankleitzahl', 'bankcode', $member, true)?>
+            <?=html_text_field('Name der Bank', 'bankname', $member, true)?>
+            <?=html_checkbox('Lastschrifteinzug', 'directdebit', $member, 1, $readonly)?>
+            <?php if (!isset($member['id'])) : ?>
+                <input class="submit" type="button" name="btn_bank_details" value="Bankverbindung erfassen" onclick="bank_details_ask_pass()" id="btn_bank_details"/>
+            <?php elseif ($readonly) : ?>
+                <input class="submit" type="button" name="btn_bank_details" value="Bankverbindung betrachten" onclick="bank_details_ask_pass()" id="btn_bank_details"/>
+            <?php else : ?>
+                <input class="submit" type="button" name="btn_bank_details" value="Bankverbindung &auml;ndern" onclick="bank_details_ask_pass()" id="btn_bank_details"/>
+            <?php endif ?>
+        </fieldset>
+        <fieldset style="clear: left;">
+            <legend>Anschrift</legend>
+            <?=html_text_field('Vorname', 'firstname', $member, $readonly)?>
+            <?=html_text_field('Nachname', 'lastname', $member, $readonly)?>
+            <?=html_text_field('Straße', 'street', $member, $readonly)?>
+            <?=html_text_field('Hausnummer', 'housenumber', $member, $readonly)?>
+            <?=html_text_field('PLZ', 'postcode', $member, $readonly)?>
+            <?=html_text_field('Ort', 'city', $member, $readonly)?>
+            <?=html_text_field('Land', 'country', $member, $readonly)?>
+        </fieldset>
+        <fieldset>
+            <legend>Sonstiges</legend>
+            <?=html_text_field('Telefon', 'phone', $member, $readonly)?>
+            <?=html_text_field('Fax', 'fax', $member, $readonly)?>
+            <?=html_text_field('Handy', 'mobile', $member, $readonly)?>
+            <?=html_text_field('Jabber', 'jabber', $member, $readonly)?>
+            <?=html_text_field('ICQ', 'icq', $member, $readonly)?>
+            <?=html_text_field('MSN', 'msn', $member, $readonly)?>
+            <?=html_text_field('AIM', 'aim', $member, $readonly)?>
+            <?=html_text_field('Skype', 'skype', $member, $readonly)?>
+            <?=html_text_field('Website', 'url', $member, $readonly)?>
+            <?=html_text_field('Twitter', 'twitter', $member, $readonly)?>
+        </fieldset>
+        <?php if (!$readonly) : ?>
+            <input class="submit" type="submit" name="submit" value="Speichern"/>
+        <?php else : ?>
+            <br style="clear: left;"/>
+            <a href="<?=html_escape(link_to('change_member', array('id' => $member['id'])))?>">Mitglied &auml;ndern</a>
+        <?php endif ?>
+    </form>
+    <?php if (!isset($member['id'])) : ?>
+    <input class="submit" type="button" name="btn_import_qrcode1" value="QR-Code importieren" onclick="import_qrcode_ask_pass()" id="btn_import_qrcode1"/>
+    <div id="password_popup2" class="modal_window">
+        <fieldset>
+            <legend>Passwortabfrage</legend>
+            <?=html_password_field('Passwort', 'password2')?>
+            <input class="submit" type="button" name="btn_import_qrcode2" value="QR-Code importieren" onclick="import_qrcode()"/>
+        <input class="submit" type="button" name="btn_cancel_import_qrcode" value="Abbrechen" onclick="cancel_import_qrcode()"/>
+        </fieldset>
+    </div>
+    <div id="qrcode_popup" class="modal_window">
+        <fieldset>
+            <legend>QR-Code</legend>
+            <textarea id="qrcode_content" style="width: 100%" rows="30"></textarea>
+            <input class="submit" type="button" name="btn_qrcode_close" value="Importieren" onclick="qrcode_close()"/>
+        </fieldset>
+    </div>
+    <?php endif ?>
+    
+    <div id="password_popup" class="modal_window">
+        <fieldset>
+            <legend>Passwortabfrage</legend>
+            <?=html_password_field('Passwort', 'password1')?>
+            <input class="submit" type="button" name="btn_decrypt_bank_details" value="Bankdaten entschl&uuml;sseln" onclick="decrypt_bank_details()"/>
+        <input class="submit" type="button" name="btn_cancel" value="Abbrechen" onclick="cancel_change_bank_details()"/>
+        </fieldset>
+    </div>
+    <div id="bank_detail_popup" class="modal_window">
+        <fieldset>
+            <legend>Bankverbindung</legend>
+            <?=html_text_field('Kontoinhaber', 'tmp_accountholder', $member, $readonly)?>
+            <?=html_text_field('Kontonummer', 'tmp_accountnumber', $member, $readonly)?>
+            <?=html_text_field('Bankleitzahl', 'tmp_bankcode', $member, $readonly)?>
+            <?=html_text_field('Name der Bank', 'tmp_bankname', $member, $readonly)?>
+            <?php if (!isset($member['id'])) : ?>
+                <input class="submit" type="button" name="btn_bank_details_save" value="&Uuml;bernehmen" onclick="bank_details_save()"/>
+                <input class="submit" type="button" name="btn_bank_details_cancel" value="Abbrechen" onclick="bank_details_cancel()"/>
+            <?php elseif ($readonly) : ?>
+                <input class="submit" type="button" name="btn_bank_details_cancel" value="Zur&uuml;ck" onclick="bank_details_cancel()" id="btn_bank_details_cancel"/>
+            <?php else : ?>
+                <input class="submit" type="button" name="btn_bank_details_save" value="&Uuml;bernehmen" onclick="bank_details_save()"/>
+                <input class="submit" type="button" name="btn_bank_details_cancel" value="Abbrechen" onclick="bank_details_cancel()"/>
+            <?php endif ?>
+        </fieldset>
+    </div>
+<script type="text/javascript">
+
+
+var the_masterkey;
+
+$(document).ready(function(){  
+    $('#accountholder').focus( function() { $("#btn_bank_details").focus(); });
+<?php if ($readonly) : ?>
+    $('#btn_bank_details_cancel').keypress( function(event) { if (event.keyCode == '13') { bank_details_cancel(); } });
+<?php else : ?>
+    $('#tmp_bankname').keypress( function(event) { if (event.keyCode == '13') { bank_details_save(); } });
+<?php endif ?>
+    $('#password1').keypress( function(event) { if (event.keyCode == '13') { decrypt_bank_details(); } });
+    <?php if (!isset($member['id'])) : ?>
+    $('#password2').keypress( function(event) { if (event.keyCode == '13') { import_qrcode(); } });
+<?php endif ?>
+});  
+
+function bank_details_ask_pass() {/*{{{*/
+    modal_window_show($("#password_popup"));
+    $("#password1").focus();
+    return;
+}/*}}}*/
+
+
+function import_qrcode_ask_pass() {/*{{{*/
+    modal_window_show($("#password_popup2"));
+    $("#password2").focus();
+    return;
+}/*}}}*/
+
+function cancel_change_bank_details() {/*{{{*/
+    modal_window_hide();
+    $("#btn_bank_details").focus();
+    $("#password1").val('');
+    return;
+}/*}}}*/
+
+function cancel_import_qrcode() {/*{{{*/
+    modal_window_hide();
+    $("#password2").val('');
+    return;
+}/*}}}*/
+
+function decrypt_bank_details() {/*{{{*/
+
+    var password = $('#password1').val();
+    $("#password1").val('');
+
+    // We pass a closure so that get_master_key may defer execution
+    get_master_key(password, function(masterkey) {
+
+        modal_window_replace($("#bank_detail_popup"));
+<?php if ($readonly) : ?>
+        $('#btn_bank_details_cancel').focus();
+<?php else : ?>
+        $('#tmp_accountholder').focus();
+<?php endif ?>
+
+        var accountholder = $('#accountholder').val();
+        var accountnumber = $('#accountnumber').val();
+        var bankcode      = $('#bankcode').val();
+        var bankname      = $('#bankname').val();
+
+        // Encrypt/Decrypt data using AES with masterkey
+        if (accountholder != "") { $('#tmp_accountholder').val(Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(accountholder, masterkey))); }
+        if (accountnumber != "") { $('#tmp_accountnumber').val(Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(accountnumber, masterkey))); }
+        if (bankcode      != "") { $('#tmp_bankcode'     ).val(Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(bankcode,      masterkey))); }
+        if (bankname      != "") { $('#tmp_bankname'     ).val(Crypto.charenc.UTF8.bytesToString(Crypto.AES.decrypt(bankname,      masterkey))); }
+        the_masterkey = masterkey; // cache masterkey until popup is closed
+    });
+
+}/*}}}*/
+
+function import_qrcode() {/*{{{*/
+
+    var password = $('#password2').val();
+    $("#password2").val('');
+
+    // We pass a closure so that get_master_key may defer execution
+    get_master_key(password, function(masterkey) {
+
+        modal_window_replace($("#qrcode_popup"));
+        the_masterkey = masterkey; // cache masterkey until popup is closed
+    });
+
+}/*}}}*/
+
+function qrcode_close() {/*{{{*/
+
+    modal_window_hide();
+
+    var qrcode_content = $('#qrcode_content').val();
+    $("#qrcode_content").val('');
+    var qrcode = qrcode_content.split(';');
+    if (qrcode.length >= 17) { 
+        $("#nickname").val(qrcode[0]);
+        // XXX birthdate - unused
+        $("#email").val(qrcode[2]);
+        // XXX cccmember - unused
+        $("#firstname").val(qrcode[4]);
+        $("#lastname").val(qrcode[5]);
+        $("#street").val(qrcode[6]);
+        $("#housenumber").val(qrcode[7]);
+        $("#postcode").val(qrcode[8]);
+        $("#city").val(qrcode[9]);
+        $("#country").val(qrcode[10]);
+        // XXX fee unused
+        $("#paymentinterval").val(qrcode[12]);
+        var accountholder = qrcode[13];
+        var accountnumber = qrcode[14];
+        var bankcode = qrcode[15];
+        var bankname = qrcode[16];
+        // XXX amount unused - wurde erst am 4.5. ergänzt, war vorher leer.
+    }
+
+
+    // 4.2 Encrypt/Decrypt data using AES with masterkey
+    if (accountholder != "") { $('#accountholder').val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(accountholder), the_masterkey)); } else { $('#accountholder').val(''); }
+    if (accountnumber != "") { $('#accountnumber').val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(accountnumber), the_masterkey)); } else { $('#accountnumber').val(''); }
+    if (bankcode      != "") { $('#bankcode'     ).val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(bankcode),      the_masterkey)); } else { $('#bankcode').val(''); }
+    if (bankname      != "") { $('#bankname'     ).val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(bankname),      the_masterkey)); } else { $('#bankname').val(''); }
+    the_masterkey = '';
+
+}/*}}}*/
+
+function bank_details_save() {/*{{{*/
+
+    modal_window_hide();
+
+    var accountholder = $('#tmp_accountholder').val();
+    var accountnumber = $('#tmp_accountnumber').val();
+    var bankcode      = $('#tmp_bankcode').val();
+    var bankname      = $('#tmp_bankname').val();
+    $("#tmp_accountholder").val('');
+    $("#tmp_accountnumber").val('');
+    $("#tmp_bankcode").val('');
+    $("#tmp_bankname").val('');
+
+    // 4.2 Encrypt/Decrypt data using AES with masterkey
+    if (accountholder != "") { $('#accountholder').val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(accountholder), the_masterkey)); } else { $('#accountholder').val(''); }
+    if (accountnumber != "") { $('#accountnumber').val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(accountnumber), the_masterkey)); } else { $('#accountnumber').val(''); }
+    if (bankcode      != "") { $('#bankcode'     ).val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(bankcode),      the_masterkey)); } else { $('#bankcode').val(''); }
+    if (bankname      != "") { $('#bankname'     ).val(Crypto.AES.encrypt(Crypto.charenc.UTF8.stringToBytes(bankname),      the_masterkey)); } else { $('#bankname').val(''); }
+    the_masterkey = '';
+
+}/*}}}*/
+
+function bank_details_cancel() {/*{{{*/
+
+    modal_window_hide();
+
+    $("#tmp_accountholder").val('');
+    $("#tmp_accountnumber").val('');
+    $("#tmp_bankcode").val('');
+    $("#tmp_bankname").val('');
+
+    the_masterkey = '';
+}/*}}}*/
+
+</script>
+    <?php
+}/*}}}*/
+
+
diff --git a/include/misc.php b/include/misc.php
new file mode 100644 (file)
index 0000000..9dbed18
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+
+function action_404() {/*{{{*/
+    header('HTTP/1.0 404 Not Found');
+    ?>
+    <h2>Seite nicht gefunden!</h2>
+    <p>Was immer du auch suchst, es ist nicht hier.</p>
+    <?php
+}/*}}}*/
+
+function redirect($url) {/*{{{*/
+    header('HTTP/1.1 301 Moved Permanently');
+    header('Location: ' . $url);
+    die();
+}/*}}}*/
+
+function link_to($action = '', $params = array()) {/*{{{*/
+    if (!empty($action)) $params = array_merge(array('action' => $action), $params);
+       $base = str_replace('index.php', '', $_SERVER['SCRIPT_NAME']);
+    if (empty($params)) return $base;
+    $query_string = '';
+    foreach ($params as $key => $val) {
+        if ($query_string != '') $query_string .= '&';
+        $query_string .= urlencode($key) . '=' . urlencode($val);
+    }
+    return $base . '?' . $query_string;
+}/*}}}*/
+
+function log_messages($log_messages) {/*{{{*/
+    if (empty($log_messages)) return;
+?>
+    <ul class="log_messages">
+    <?php foreach ($log_messages as $log_message) : ?>
+        <li><?=html_escape($log_message)?></li>
+    <?php endforeach ?>
+    </ul>
+<?php
+}/*}}}*/
+
+function format_date($unixtime) {
+    return strftime('%d.%m.%Y', $unixtime);
+}
+
+function format_month() {
+    switch (func_num_args()) {
+        case 1: $ts    = func_get_arg(0);
+                $year  = date('Y', $ts);
+                $month = date('n', $ts);
+            break;
+        case 2: $year  = func_get_arg(0);
+                $month = func_get_arg(1);
+            break;
+        default: return NULL;
+    }
+    return sprintf('%02d/%04d', $month, $year);
+}
+
+function format_money($amount) {
+    return number_format($amount, 2, ',', '') . '€';
+}
+
+function ui_date2unixtime($date) {
+    return strtotime($date);
+}
+function ui_money2float($amount) {
+    return str_replace(',', '.', $amount);
+}
+
+function paginate($url, $current, $max) {
+?>
+<?php if ($max > 1) : ?>
+<div class="pagination">
+       <?php if ($current != 1) : ?>
+       <div class="prev"><a href="<?=html_escape(sprintf($url, $current - 1))?>">&laquo;</a></div>
+       <?php endif ?>
+       <?php if ($current != $max) : ?>
+       <div class="next"><a href="<?=html_escape(sprintf($url, $current + 1))?>">&raquo;</a></div>
+       <?php endif ?>
+
+       <?php $skipped = false; ?>
+
+       <?php for ($page = 1; $page <= $max; $page++) : ?>
+           <?php if ($page != 1 && $page != $max && ($page < $current - 3 || $page > $current + 3)) { $skipped = true; continue; } ?>
+           <?php if ($skipped) : ?> ... <?php endif ?>
+               <?php if ($page == $current) : ?>
+            <?=html_escape($page)?>
+        <?php else : ?>
+            <a href="<?=html_escape(sprintf($url, $page))?>"><?=html_escape($page)?></a>
+        <?php endif ?>
+        <?php $skipped = false; ?>
+       <?php endfor ?>
+</div>
+<?php endif ?>
+<?
+}
+
+
+function js_modal_windows() {/*{{{*/
+?>
+<script type="text/javascript">
+
+$(document).ready(function(){  
+    var window_width = $(window).width();  
+    var window_height = $(window).height();  
+    $('.modal_window').each(function(){  
+        var modal_height = $(this).outerHeight();  
+        var modal_width = $(this).outerWidth();  
+        var top = (window_height-modal_height)/2;  
+        var left = (window_width-modal_width)/2;  
+        $(this).css({'top' : top , 'left' : left});  
+    });  
+});  
+
+var modal_windows = new Array();
+function modal_window_show(modal_window) {
+    var length = modal_windows.length;
+    if (length > 0) modal_windows[length - 1].fadeOut(500);
+    else $("#mask").fadeTo(500, 0.8);
+    modal_windows.push(modal_window);
+    modal_window.fadeIn(500);
+}
+function modal_window_hide() {
+    var length = modal_windows.length;
+    if (length <= 0) return;
+    modal_windows[length - 1].fadeOut(500);
+    if (length == 1) {
+        $("#mask").fadeOut(500);
+    }
+    else {
+        modal_windows[length - 2].fadeIn(500);
+    }
+    modal_windows.pop();
+}
+function modal_window_replace(modal_window) {
+    var length = modal_windows.length;
+    if (length > 0) {
+        modal_windows[length - 1].fadeOut(500);
+        modal_windows.pop();
+    }
+    modal_windows.push(modal_window);
+    modal_window.fadeIn(500);
+}
+
+</script>
+<div id="mask"></div>  
+<div id="please_wait" class="modal_window" style="text-align:center;">
+    <strong>Bitte warten...</strong><br/>
+    <img src="images/activity_indicator.gif"/>
+</div>
+<?php
+}/*}}}*/
+
+
diff --git a/include/payments.php b/include/payments.php
new file mode 100644 (file)
index 0000000..37e2f0e
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+// NOTE: combination of earnings and expenses
+
+$ACCOUNT_TYPES = array(
+    'bank' => 'Konto',
+    'cash' => 'Kasse'
+);
+
+/* DB functions {{{ */
+function db_get_payments($offset, $count, $where) {
+    $limit = '';
+    if (isset($offset) && isset($count)) $limit = sprintf(' LIMIT %1$d,%2$d', (int)$offset, (int)$count);
+    elseif (isset($offset)) $limit = sprintf(' LIMIT %1$d', (int)$count);
+    $sql = 'SELECT t.*, %4$s.nickname AS nickname FROM ((SELECT \'earning\' as ptype, %1$s.* FROM %1$s %3$s) UNION (SELECT \'expense\', %2$s.* FROM %2$s %3$s)) AS t LEFT JOIN %4$s ON t.member_id=%4$s.id ORDER BY `date` DESC, t.id DESC' . $limit;
+    return db_select_multi(sprintf($sql, DB_TABLE_EARNINGS, DB_TABLE_EXPENSES, $where, DB_TABLE_MEMBERS));
+}
+
+function db_get_finished_payments($offset, $count) {
+    $where = ' WHERE status=\'paid\'';
+    $payments = db_get_payments($offset, $count, $where);
+
+    $bank  = 0;
+    $cash  = 0;
+    $total = 0;
+    if ($count != 0) { // XXX TODO hä? - An dieser Stelle gäbe es sinnigere Bedinungen
+        $limit = sprintf(' LIMIT %d,18446744073709551615', (int)($count + $offset));
+        $sql = 'SELECT SUM(IF(`account`=\'bank\', `value`, 0)) as `bank`, SUM(IF(`account`=\'cash\', `value`, 0)) as `cash`, SUM(`value`) AS `total` 
+                FROM (
+                    SELECT `value`, `account` 
+                    FROM (( SELECT `value`,                 `id`, `date`, `account` FROM %1$s %3$s) 
+                    UNION ( SELECT `value` * -1 AS `value`, `id`, `date`, `account` FROM %2$s %3$s)
+                    ) AS t
+                    ORDER BY `date` DESC, t.id DESC' . $limit . '
+                ) AS u';
+        $row = db_select_single(sprintf($sql, DB_TABLE_EARNINGS, DB_TABLE_EXPENSES, $where));
+        $bank  = $row['bank'];
+        $cash  = $row['cash'];
+        $total = $row['total'];
+    }
+    for ($i = count($payments) - 1; $i >= 0; $i--) {
+        $value = $payments[$i]['value'];
+        if ($payments[$i]['ptype'] == 'expense') $value = bcmul($value, '-1');
+        switch ($payments[$i]['account']) {
+            case 'bank': $bank = bcadd($bank, $value); break;
+            case 'cash': $cash = bcadd($cash, $value); break;
+            default: break; // TODO XXX die?
+        }
+        $total = bcadd($total, $value);
+        $payments[$i]['bank']   = $bank;
+        $payments[$i]['cash']   = $cash;
+        $payments[$i]['total']  = $total;
+    }
+    return $payments;
+}
+
+function db_count_finished_payments() {
+    $where = ' WHERE status=\'paid\'';
+    $sql = 'SELECT count(*) as `count` FROM ((SELECT * FROM %1$s %3$s) UNION (SELECT * FROM %2$s %3$s)) AS t';
+    $row = db_select_single(sprintf($sql, DB_TABLE_EARNINGS, DB_TABLE_EXPENSES, $where));
+    return $row['count'];
+}
+
+function db_get_open_payments($offset, $count) {
+    return db_get_payments($offset, $count, ' WHERE status=\'open\'');
+}
+
+/* }}} */
+
+function render_finished_payments($count = 25) {/*{{{*/
+    global $EXPENSE_TYPES, $EARNING_TYPES, $ACCOUNT_TYPES;
+    $page = 1;
+    if (!empty($_REQUEST['page'])) $page = max(1, (int)$_REQUEST['page']);
+    $payments = db_get_finished_payments(($page - 1) * $count, $count);
+    $max = db_count_finished_payments();
+    ?>
+    <h3 id="finished">Abgeschlossene Zahlungen</h3>
+    <?php if (isset($payments)) : ?>
+        <table>
+            <tr>
+                <th>Id</th>
+                <th>Datum</th>
+                <th>Typ</th>
+                <th>Mitglied</th>
+                <th>Betrag</th>
+                <th>Beschreibung</th>
+                <th>Konto</th>
+                <th>Kasse</th>
+                <th>Gesamt</th>
+            </tr>
+        <?php foreach ($payments as $payment) : ?>
+            <tr class="<?=$payment['ptype']?>">
+                <td style="text-align: right;"><a href="<?=html_escape(link_to('change_' . $payment['ptype'], array('id' => $payment['id'])))?>"><?=html_escape($payment['id'])?></a></td>
+                <td><?=html_escape(format_date(db_date2unixtime($payment['date'])))?></td>
+                <td><?=html_escape($payment['ptype'] == 'earning'? $EARNING_TYPES[$payment['type']] : $EXPENSE_TYPES[$payment['type']])?></td>
+                <td><?php if ($payment['member_id'] != 0) : ?><a href="<?=html_escape(link_to('view_member', array('id'=> $payment['member_id'])))?>"><?=html_escape($payment['nickname'])?><?php endif ?></a></td>
+                <td style="text-align: right;"><?=html_escape(format_money(($payment['ptype'] == 'earning' ? 1 : -1)  * $payment['value']))?></td>
+                <td><?=html_escape($payment['description'])?></td>
+                <td style="text-align: right;"><?=html_escape(format_money($payment['bank']))?></td>
+                <td style="text-align: right;"><?=html_escape(format_money($payment['cash']))?></td>
+                <td style="text-align: right;"><?=html_escape(format_money($payment['total']))?></td>
+            </tr>
+        <?php endforeach ?>
+        </table>
+        <?php paginate(link_to('finances') . '&page=%d#finished', $page, ceil($max / $count)); ?>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine abgeschlossenen Zahlungen.</p>
+    <?php endif ?>
+    <?php
+
+}/*}}}*/
+
+function render_open_payments($offset = 0, $count = 25) {/*{{{*/
+    global $EXPENSE_TYPES, $EARNING_TYPES, $ACCOUNT_TYPES;
+    global $EXPENSE_TYPES, $EARNING_TYPES;
+    $payments = db_get_open_payments($offset, $count);
+    ?>
+    <h3>Offene Zahlungen</h3>
+    <?php if (isset($payments)) : ?>
+        <table>
+            <tr>
+                <th>Id</th>
+                <th>Datum</th>
+                <th>Typ</th>
+                <th>Mitglied</th>
+                <th>Quelle/Ziel</th>
+                <th>Betrag</th>
+                <th>Beschreibung</th>
+            </tr>
+        <?php foreach ($payments as $payment) : ?>
+            <tr class="<?=$payment['ptype']?>">
+                <td style="text-align: right;"><a href="<?=html_escape(link_to('change_' . $payment['ptype'], array('id' => $payment['id'])))?>"><?=html_escape($payment['id'])?></a></td>
+                <td><?=html_escape(format_date(db_date2unixtime($payment['date'])))?></td>
+                <td><?=html_escape($payment['ptype'] == 'earning'? $EARNING_TYPES[$payment['type']] : $EXPENSE_TYPES[$payment['type']])?></td>
+                <td><?php if ($payment['member_id'] != 0) : ?><a href="<?=html_escape(link_to('view_member', array('id'=> $payment['member_id'])))?>"><?=html_escape($payment['nickname'])?><?php endif ?></a></td>
+                <td><?=html_escape($ACCOUNT_TYPES[$payment['account']])?></td>
+                <td style="text-align: right;"><?=html_escape(format_money(($payment['ptype'] == 'earning'? 1 : -1)  * $payment['value']))?></td>
+                <td><?=html_escape($payment['description'])?></td>
+            </tr>
+        <?php endforeach ?>
+        </table>
+    <?php else : ?>
+        <p>Bisher gibt's noch keine abgeschlossenen Zahlungen.</p>
+    <?php endif ?>
+    <?php
+
+}/*}}}*/
+
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..0cdb1ac
--- /dev/null
+++ b/index.php
@@ -0,0 +1,142 @@
+<?php
+
+header('Content-type: text/html; charset=utf-8');
+if (function_exists('date_default_timezone_set')) date_default_timezone_set('UTC');
+bcscale(2);
+
+require_once('config.php');
+require_once('include/database.php');
+require_once('include/misc.php');
+require_once('include/installation.php');
+require_once('include/html.php');
+
+require_once('include/finances.php');
+require_once('include/fees.php');
+require_once('include/cryptography.php');
+require_once('include/earnings.php');
+require_once('include/events.php');
+require_once('include/expenses.php');
+require_once('include/members.php');
+require_once('include/payments.php');
+require_once('include/dtaus.php');
+
+db_connect();
+
+if (!defined('IS_INSTALLED') || !IS_INSTALLED) install_database();
+
+$action = 'list_members';
+if (isset($_REQUEST['action'])) $action = $_REQUEST['action'];
+
+ob_start();
+
+switch ($action) {
+    case 'list_members'   : action_list_members();    break;
+    case 'view_member'    : action_view_member();     break;
+    case 'export_email'   : action_export_email();    break;
+    case 'create_member'  : action_create_member();   break;
+    case 'change_member'  : action_change_member();   break;
+    case 'create_event'   : action_create_event();    break;
+    case 'change_event'   : action_change_event();    break;
+    case 'delete_event'   : action_delete_event();    break;
+    case 'create_earnings': action_create_earnings(); break;
+    case 'create_earning' : action_create_earning();  break;
+    case 'change_earning' : action_change_earning();  break;
+    case 'delete_earning' : action_delete_earning();  break;
+    case 'create_expense' : action_create_expense();  break;
+    case 'change_expense' : action_change_expense();  break;
+    case 'delete_expense' : action_delete_expense();  break;
+    case 'cryptography'   : action_cryptography();    break;
+    case 'fees'           : action_fees();            break;
+    case 'dtaus'          : action_dtaus();           break;
+    case 'finances'       : action_finances();        break;
+    default: action_404();
+}
+
+$content = ob_get_contents();
+ob_end_clean();
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de">
+    <head>
+        <title>Mitgliedsverwaltung - Chaos Computer Club Frankfurt am Main</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+        <style type="text/css">
+            form  { line-height: 2em; }
+            fieldset { line-height: 2em; border: 1px solid #ccc; margin-bottom: 1em; float:left; border-width: 1px 0 0 0; width: 27em; }
+            /*fieldset { border: 1px solid #ccc; margin-bottom: 1em; float:left; border-width: 1px 0 0 0; } */
+            legend { font-size: 1.4em; }
+            label { clear: left; display: block; width: 10em; float: left; }
+            input, select { display: block; width: 20em; float: left; }
+            form small { clear: left; display: block; text-align: right; margin-right: 1.8em; line-height: 1em; margin-bottom: 1.5em; }
+            fieldset small { clear: left; display: block; text-align: right; margin-right: 1.8em; line-height: 1em; margin-bottom: 1.5em; }
+            .checkbox label { clear: none; margin-left: 0.5em; }
+            .checkbox input { clear: left; width: auto;  }
+            #header { margin: 0; padding: 1px 0; background-color: #464646; }
+            #content { font-size: 1.4em; }
+            .content { margin: 0 auto; width: 960px; }
+            .submit { clear: left; }
+            h1 { color: #fff; }
+            body { margin: 0; padding: 0; font-family:  Arial, Verdana, Tahoma; font-size: 62.5%; color: #333; }
+            html { margin: 0; padding: 0; }
+            table { border-collapse: collapse; margin-bottom: 1em; border: 1px solid #ccc; }
+            th, td { padding: 0.3em 2em 0.3em 0.3em;}
+            tr:hover { background-color: #eee; }
+            tr.earning { background-color: #efe; }
+            tr.expense { background-color: #fee; }
+            tr.earning:hover { background-color: #dfd; }
+            tr.expense:hover { background-color: #fdd; }
+            tr.total { background-color: #eee; font-weight: bold; }
+            th { text-align: left; background-color: #eee; font-weight: normal; border-bottom: 1px solid #ccc;  }
+            h1, h2, h3, legend { font-family: Georgia, Sans-Serif, Verdana; font-weight: normal; color: #333;}
+            #header h1 { font-size: 4em; color: #fff; }
+            h2 { font-size: 2.2em; }
+            h3 { font-size: 1.8em; }
+            #pagemenu { background-color: #e0e0e0; border-top: 1px solid #e4f2fd; font-size: 1.4em; }
+            #pagemenu ul { list-style: none; padding: 0px 10px; display:block; margin-top: 6px; }
+            #pagemenu li { line-height:28px; font-weight: bold; display: inline; float: left; margin-left: 5px; }
+            #pagemenu li a { display: block; color: #888; background-color: #eee; padding: 0px 20px; text-decoration: none; }
+            #pagemenu li a:hover { color: #000; background: #fff; }
+            #footer { background-color: #eee; border-top: 1px solid #ddd; height: 50px; margin-top: 3em; }
+            .clearfix:after { content: " "; display: block; clear: both; visibility: hidden; line-height: 0; height: 0; }
+            .clearfix { display: inline-block; }
+            html[xmlns] .clearfix { display: block; }
+            * html .clearfix { height: 1%; }
+            .log_messages { color: #cc0000; }
+            p.error { border: 1px solid #900;  padding: 1em; color: #cc0000; background-color: #fff9f9;}
+
+            #content a { text-decoration: none; padding: 0 1px; color: #21759B; border-bottom: 1px solid #ccc; }
+            #content a:hover { color: #D54E21; background: #F5D1C5; border-bottom: 1px solid #D54E21; }
+            .modal_window { position:absolute; display:none; background-color: #fff; padding: 1em; }
+            #mask { position:absolute; top:0px; left:0px; height:100%; width:100%; display:none; background-color: black; }
+
+                       .pagination { text-align: center; margin: 10px 0; font-weight: bold; }
+                       .pagination .prev { float: left; }
+                       .pagination .next { float: right; }
+
+        </style>
+        <script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/crypto/crypto-min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/sha1/sha1-min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/hmac/hmac-min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/pbkdf2/pbkdf2-min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/ofb/ofb-min.js"></script>
+        <script type="text/javascript" src="js/crypto-js/aes/aes_modified.js"></script>
+        <script type="text/javascript" src="js/dtaus.js"></script>
+    </head>
+    <body>
+    <div id="header"><div class="content"><h1>Mitgliedsverwaltung</h1></div></div>
+    <div id="pagemenu">
+        <ul class="content clearfix">
+            <li><a href="<?=link_to()?>">Mitglieder</a></li>
+            <li><a href="<?=link_to('finances')?>">Finanzen</a></li>
+            <li><a href="<?=link_to('fees')?>">Mitgliedsbeitr&auml;ge</a></li>
+            <li><a href="<?=link_to('dtaus')?>">Bankeinzug</a></li>
+            <li><a href="<?=link_to('cryptography')?>">Kryptographie</a></li>
+        </ul>
+    </div>
+    <div id="content" class="content"><?=$content?></div>
+    <div id="footer"></div>
+    </body>
+</html>
diff --git a/js/crypto-js/VERSION b/js/crypto-js/VERSION
new file mode 100644 (file)
index 0000000..1449f33
--- /dev/null
@@ -0,0 +1 @@
+2.0.0 modified
diff --git a/js/crypto-js/aes/aes-min.js b/js/crypto-js/aes/aes-min.js
new file mode 100755 (executable)
index 0000000..4e2c043
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var l=Crypto,a=l.util,u=l.charenc,s=u.UTF8,j=u.Binary;var v=[99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22];for(var n=[],r=0;r<256;r++){n[v[r]]=r}var q=[],p=[],m=[],h=[],g=[],e=[];function f(y,x){for(var w=0,z=0;z<8;z++){if(x&1){w^=y}var A=y&128;y=(y<<1)&255;if(A){y^=27}x>>>=1}return w}for(var r=0;r<256;r++){q[r]=f(r,2);p[r]=f(r,3);m[r]=f(r,9);h[r]=f(r,11);g[r]=f(r,13);e[r]=f(r,14)}var k=[0,1,2,4,8,16,32,64,128,27,54];var c=[[],[],[],[]],d,b,t;var o=l.AES={encrypt:function(A,z,y){var i=s.stringToBytes(A),x=a.randomBytes(o._blocksize*4),w=z.constructor==String?l.PBKDF2(z,x,32,{asBytes:true}):z;mode=y&&y.mode||l.mode.OFB;o._init(w);mode.encrypt(o,i,x);return a.bytesToBase64(x.concat(i))},decrypt:function(z,y,x){var A=a.base64ToBytes(z),w=A.splice(0,o._blocksize*4),i=y.constructor==String?l.PBKDF2(y,w,32,{asBytes:true}):y;mode=x&&x.mode||l.mode.OFB;o._init(i);mode.decrypt(o,A,w);return s.bytesToString(A)},_blocksize:4,_encryptblock:function(w,x){for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){c[D][i]=w[x+i*4+D]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[i][D]}}for(var C=1;C<b;C++){for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=v[c[D][i]]}}c[1].push(c[1].shift());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].unshift(c[3].pop());for(var i=0;i<4;i++){var B=c[0][i],A=c[1][i],z=c[2][i],y=c[3][i];c[0][i]=q[B]^p[A]^z^y;c[1][i]=B^q[A]^p[z]^y;c[2][i]=B^A^q[z]^p[y];c[3][i]=p[B]^A^z^q[y]}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[C*4+i][D]}}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=v[c[D][i]]}}c[1].push(c[1].shift());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].unshift(c[3].pop());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[b*4+i][D]}}for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){w[x+i*4+D]=c[D][i]}}},_decryptblock:function(x,w){for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){c[D][i]=x[w+i*4+D]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[b*4+i][D]}}for(var C=1;C<b;C++){c[1].unshift(c[1].pop());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].push(c[3].shift());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=n[c[D][i]]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[(b-C)*4+i][D]}}for(var i=0;i<4;i++){var B=c[0][i],A=c[1][i],z=c[2][i],y=c[3][i];c[0][i]=e[B]^h[A]^g[z]^m[y];c[1][i]=m[B]^e[A]^h[z]^g[y];c[2][i]=g[B]^m[A]^e[z]^h[y];c[3][i]=h[B]^g[A]^m[z]^e[y]}}c[1].unshift(c[1].pop());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].push(c[3].shift());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=n[c[D][i]]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[i][D]}}for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){x[w+i*4+D]=c[D][i]}}},_init:function(i){d=i.length/4;b=d+6;o._keyexpansion(i)},_keyexpansion:function(w){t=[];for(var x=0;x<d;x++){t[x]=[w[x*4],w[x*4+1],w[x*4+2],w[x*4+3]]}for(var x=d;x<o._blocksize*(b+1);x++){var i=[t[x-1][0],t[x-1][1],t[x-1][2],t[x-1][3]];if(x%d==0){i.push(i.shift());i[0]=v[i[0]];i[1]=v[i[1]];i[2]=v[i[2]];i[3]=v[i[3]];i[0]^=k[x/d]}else{if(d>6&&x%d==4){i[0]=v[i[0]];i[1]=v[i[1]];i[2]=v[i[2]];i[3]=v[i[3]]}}t[x]=[t[x-d][0]^i[0],t[x-d][1]^i[1],t[x-d][2]^i[2],t[x-d][3]^i[3]]}}}})();
\ No newline at end of file
diff --git a/js/crypto-js/aes/aes.js b/js/crypto-js/aes/aes.js
new file mode 100755 (executable)
index 0000000..214e634
--- /dev/null
@@ -0,0 +1,385 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Precomputed SBOX\r
+var SBOX = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,\r
+             0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\r
+             0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,\r
+             0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\r
+             0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,\r
+             0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\r
+             0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,\r
+             0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\r
+             0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,\r
+             0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\r
+             0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,\r
+             0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\r
+             0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,\r
+             0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\r
+             0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,\r
+             0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\r
+             0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,\r
+             0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\r
+             0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,\r
+             0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\r
+             0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,\r
+             0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\r
+             0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,\r
+             0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\r
+             0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,\r
+             0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\r
+             0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,\r
+             0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\r
+             0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,\r
+             0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\r
+             0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,\r
+             0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ];\r
+\r
+// Compute inverse SBOX lookup table\r
+for (var INVSBOX = [], i = 0; i < 256; i++) INVSBOX[SBOX[i]] = i;\r
+\r
+// Compute mulitplication in GF(2^8) lookup tables\r
+var MULT2 = [],\r
+    MULT3 = [],\r
+    MULT9 = [],\r
+    MULTB = [],\r
+    MULTD = [],\r
+    MULTE = [];\r
+\r
+function xtime(a, b) {\r
+       for (var result = 0, i = 0; i < 8; i++) {\r
+               if (b & 1) result ^= a;\r
+               var hiBitSet = a & 0x80;\r
+               a = (a << 1) & 0xFF;\r
+               if (hiBitSet) a ^= 0x1b;\r
+               b >>>= 1;\r
+       }\r
+       return result;\r
+}\r
+\r
+for (var i = 0; i < 256; i++) {\r
+       MULT2[i] = xtime(i,2);\r
+       MULT3[i] = xtime(i,3);\r
+       MULT9[i] = xtime(i,9);\r
+       MULTB[i] = xtime(i,0xB);\r
+       MULTD[i] = xtime(i,0xD);\r
+       MULTE[i] = xtime(i,0xE);\r
+}\r
+\r
+// Precomputed RCon lookup\r
+var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];\r
+\r
+// Inner state\r
+var state = [[], [], [], []],\r
+    keylength,\r
+    nrounds,\r
+    keyschedule;\r
+\r
+var AES = C.AES = {\r
+\r
+       /**\r
+        * Public API\r
+        */\r
+\r
+       encrypt: function (message, password, options) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   m = UTF8.stringToBytes(message),\r
+\r
+                   // Generate random IV\r
+                   iv = util.randomBytes(AES._blocksize * 4),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Determine mode\r
+               mode = options && options.mode || C.mode.OFB;\r
+\r
+               // Encrypt\r
+               AES._init(k);\r
+               mode.encrypt(AES, m, iv);\r
+\r
+               // Return ciphertext\r
+               return util.bytesToBase64(iv.concat(m));\r
+\r
+       },\r
+\r
+       decrypt: function (ciphertext, password, options) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   c = util.base64ToBytes(ciphertext),\r
+\r
+                   // Separate IV and message\r
+                   iv = c.splice(0, AES._blocksize * 4),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Determine mode\r
+               mode = options && options.mode || C.mode.OFB;\r
+\r
+               // Decrypt\r
+               AES._init(k);\r
+               mode.decrypt(AES, c, iv);\r
+\r
+               // Return plaintext\r
+               return UTF8.bytesToString(c);\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Package private methods and properties\r
+        */\r
+\r
+       _blocksize: 4,\r
+\r
+       _encryptblock: function (m, offset) {\r
+\r
+               // Set input\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = m[offset + col * 4 + row];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[col][row];\r
+               }\r
+\r
+               for (var round = 1; round < nrounds; round++) {\r
+\r
+                       // Sub bytes\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] = SBOX[state[row][col]];\r
+                       }\r
+\r
+                       // Shift rows\r
+                       state[1].push(state[1].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[3].unshift(state[3].pop());\r
+\r
+                       // Mix columns\r
+                       for (var col = 0; col < 4; col++) {\r
+\r
+                               var s0 = state[0][col],\r
+                                   s1 = state[1][col],\r
+                                   s2 = state[2][col],\r
+                                   s3 = state[3][col];\r
+\r
+                               state[0][col] = MULT2[s0] ^ MULT3[s1] ^ s2 ^ s3;\r
+                               state[1][col] = s0 ^ MULT2[s1] ^ MULT3[s2] ^ s3;\r
+                               state[2][col] = s0 ^ s1 ^ MULT2[s2] ^ MULT3[s3];\r
+                               state[3][col] = MULT3[s0] ^ s1 ^ s2 ^ MULT2[s3];\r
+\r
+                       }\r
+\r
+                       // Add round key\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] ^= keyschedule[round * 4 + col][row];\r
+                       }\r
+\r
+               }\r
+\r
+               // Sub bytes\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = SBOX[state[row][col]];\r
+               }\r
+\r
+               // Shift rows\r
+               state[1].push(state[1].shift());\r
+               state[2].push(state[2].shift());\r
+               state[2].push(state[2].shift());\r
+               state[3].unshift(state[3].pop());\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[nrounds * 4 + col][row];\r
+               }\r
+\r
+               // Set output\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               m[offset + col * 4 + row] = state[row][col];\r
+               }\r
+\r
+       },\r
+\r
+       _decryptblock: function (c, offset) {\r
+\r
+               // Set input\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = c[offset + col * 4 + row];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[nrounds * 4 + col][row];\r
+               }\r
+\r
+               for (var round = 1; round < nrounds; round++) {\r
+\r
+                       // Inv shift rows\r
+                       state[1].unshift(state[1].pop());\r
+                       state[2].push(state[2].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[3].push(state[3].shift());\r
+\r
+                       // Inv sub bytes\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] = INVSBOX[state[row][col]];\r
+                       }\r
+\r
+                       // Add round key\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] ^= keyschedule[(nrounds - round) * 4 + col][row];\r
+                       }\r
+\r
+                       // Inv mix columns\r
+                       for (var col = 0; col < 4; col++) {\r
+\r
+                               var s0 = state[0][col],\r
+                                   s1 = state[1][col],\r
+                                   s2 = state[2][col],\r
+                                   s3 = state[3][col];\r
+\r
+                               state[0][col] = MULTE[s0] ^ MULTB[s1] ^ MULTD[s2] ^ MULT9[s3];\r
+                               state[1][col] = MULT9[s0] ^ MULTE[s1] ^ MULTB[s2] ^ MULTD[s3];\r
+                               state[2][col] = MULTD[s0] ^ MULT9[s1] ^ MULTE[s2] ^ MULTB[s3];\r
+                               state[3][col] = MULTB[s0] ^ MULTD[s1] ^ MULT9[s2] ^ MULTE[s3];\r
+\r
+                       }\r
+\r
+               }\r
+\r
+               // Inv shift rows\r
+               state[1].unshift(state[1].pop());\r
+               state[2].push(state[2].shift());\r
+               state[2].push(state[2].shift());\r
+               state[3].push(state[3].shift());\r
+\r
+               // Inv sub bytes\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = INVSBOX[state[row][col]];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[col][row];\r
+               }\r
+\r
+               // Set output\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               c[offset + col * 4 + row] = state[row][col];\r
+               }\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Private methods\r
+        */\r
+\r
+       _init: function (k) {\r
+               keylength = k.length / 4;\r
+               nrounds = keylength + 6;\r
+               AES._keyexpansion(k);\r
+       },\r
+\r
+       // Generate a key schedule\r
+       _keyexpansion: function (k) {\r
+\r
+               keyschedule = [];\r
+\r
+               for (var row = 0; row < keylength; row++) {\r
+                       keyschedule[row] = [\r
+                               k[row * 4],\r
+                               k[row * 4 + 1],\r
+                               k[row * 4 + 2],\r
+                               k[row * 4 + 3]\r
+                       ];\r
+               }\r
+\r
+               for (var row = keylength; row < AES._blocksize * (nrounds + 1); row++) {\r
+\r
+                       var temp = [\r
+                               keyschedule[row - 1][0],\r
+                               keyschedule[row - 1][1],\r
+                               keyschedule[row - 1][2],\r
+                               keyschedule[row - 1][3]\r
+                       ];\r
+\r
+                       if (row % keylength == 0) {\r
+\r
+                               // Rot word\r
+                               temp.push(temp.shift());\r
+\r
+                               // Sub word\r
+                               temp[0] = SBOX[temp[0]];\r
+                               temp[1] = SBOX[temp[1]];\r
+                               temp[2] = SBOX[temp[2]];\r
+                               temp[3] = SBOX[temp[3]];\r
+\r
+                               temp[0] ^= RCON[row / keylength];\r
+\r
+                       } else if (keylength > 6 && row % keylength == 4) {\r
+\r
+                               // Sub word\r
+                               temp[0] = SBOX[temp[0]];\r
+                               temp[1] = SBOX[temp[1]];\r
+                               temp[2] = SBOX[temp[2]];\r
+                               temp[3] = SBOX[temp[3]];\r
+\r
+                       }\r
+\r
+                       keyschedule[row] = [\r
+                               keyschedule[row - keylength][0] ^ temp[0],\r
+                               keyschedule[row - keylength][1] ^ temp[1],\r
+                               keyschedule[row - keylength][2] ^ temp[2],\r
+                               keyschedule[row - keylength][3] ^ temp[3]\r
+                       ];\r
+\r
+               }\r
+\r
+       }\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/aes/aes_modified.js b/js/crypto-js/aes/aes_modified.js
new file mode 100755 (executable)
index 0000000..872166f
--- /dev/null
@@ -0,0 +1,385 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Precomputed SBOX\r
+var SBOX = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,\r
+             0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,\r
+             0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,\r
+             0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,\r
+             0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,\r
+             0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,\r
+             0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,\r
+             0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,\r
+             0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,\r
+             0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,\r
+             0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,\r
+             0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,\r
+             0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,\r
+             0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,\r
+             0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,\r
+             0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,\r
+             0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,\r
+             0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,\r
+             0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,\r
+             0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,\r
+             0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,\r
+             0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,\r
+             0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,\r
+             0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,\r
+             0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,\r
+             0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,\r
+             0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,\r
+             0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,\r
+             0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,\r
+             0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,\r
+             0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,\r
+             0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ];\r
+\r
+// Compute inverse SBOX lookup table\r
+for (var INVSBOX = [], i = 0; i < 256; i++) INVSBOX[SBOX[i]] = i;\r
+\r
+// Compute mulitplication in GF(2^8) lookup tables\r
+var MULT2 = [],\r
+    MULT3 = [],\r
+    MULT9 = [],\r
+    MULTB = [],\r
+    MULTD = [],\r
+    MULTE = [];\r
+\r
+function xtime(a, b) {\r
+       for (var result = 0, i = 0; i < 8; i++) {\r
+               if (b & 1) result ^= a;\r
+               var hiBitSet = a & 0x80;\r
+               a = (a << 1) & 0xFF;\r
+               if (hiBitSet) a ^= 0x1b;\r
+               b >>>= 1;\r
+       }\r
+       return result;\r
+}\r
+\r
+for (var i = 0; i < 256; i++) {\r
+       MULT2[i] = xtime(i,2);\r
+       MULT3[i] = xtime(i,3);\r
+       MULT9[i] = xtime(i,9);\r
+       MULTB[i] = xtime(i,0xB);\r
+       MULTD[i] = xtime(i,0xD);\r
+       MULTE[i] = xtime(i,0xE);\r
+}\r
+\r
+// Precomputed RCon lookup\r
+var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];\r
+\r
+// Inner state\r
+var state = [[], [], [], []],\r
+    keylength,\r
+    nrounds,\r
+    keyschedule;\r
+\r
+var AES = C.AES = {\r
+\r
+       /**\r
+        * Public API\r
+        */\r
+\r
+       encrypt: function (message, password, options) {\r
+\r
+               var\r
+\r
+                   // Create copy to work on\r
+                   m = message.slice(),\r
+\r
+                   // Generate random IV\r
+                   iv = util.randomBytes(AES._blocksize * 4),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Determine mode\r
+               mode = options && options.mode || C.mode.OFB;\r
+\r
+               // Encrypt\r
+               AES._init(k);\r
+               mode.encrypt(AES, m, iv);\r
+\r
+               // Return ciphertext\r
+               return util.bytesToBase64(iv.concat(m));\r
+\r
+       },\r
+\r
+       decrypt: function (ciphertext, password, options) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   c = util.base64ToBytes(ciphertext),\r
+\r
+                   // Separate IV and message\r
+                   iv = c.splice(0, AES._blocksize * 4),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Determine mode\r
+               mode = options && options.mode || C.mode.OFB;\r
+\r
+               // Decrypt\r
+               AES._init(k);\r
+               mode.decrypt(AES, c, iv);\r
+\r
+               // Return bytes\r
+               return c;\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Package private methods and properties\r
+        */\r
+\r
+       _blocksize: 4,\r
+\r
+       _encryptblock: function (m, offset) {\r
+\r
+               // Set input\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = m[offset + col * 4 + row];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[col][row];\r
+               }\r
+\r
+               for (var round = 1; round < nrounds; round++) {\r
+\r
+                       // Sub bytes\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] = SBOX[state[row][col]];\r
+                       }\r
+\r
+                       // Shift rows\r
+                       state[1].push(state[1].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[3].unshift(state[3].pop());\r
+\r
+                       // Mix columns\r
+                       for (var col = 0; col < 4; col++) {\r
+\r
+                               var s0 = state[0][col],\r
+                                   s1 = state[1][col],\r
+                                   s2 = state[2][col],\r
+                                   s3 = state[3][col];\r
+\r
+                               state[0][col] = MULT2[s0] ^ MULT3[s1] ^ s2 ^ s3;\r
+                               state[1][col] = s0 ^ MULT2[s1] ^ MULT3[s2] ^ s3;\r
+                               state[2][col] = s0 ^ s1 ^ MULT2[s2] ^ MULT3[s3];\r
+                               state[3][col] = MULT3[s0] ^ s1 ^ s2 ^ MULT2[s3];\r
+\r
+                       }\r
+\r
+                       // Add round key\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] ^= keyschedule[round * 4 + col][row];\r
+                       }\r
+\r
+               }\r
+\r
+               // Sub bytes\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = SBOX[state[row][col]];\r
+               }\r
+\r
+               // Shift rows\r
+               state[1].push(state[1].shift());\r
+               state[2].push(state[2].shift());\r
+               state[2].push(state[2].shift());\r
+               state[3].unshift(state[3].pop());\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[nrounds * 4 + col][row];\r
+               }\r
+\r
+               // Set output\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               m[offset + col * 4 + row] = state[row][col];\r
+               }\r
+\r
+       },\r
+\r
+       _decryptblock: function (c, offset) {\r
+\r
+               // Set input\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = c[offset + col * 4 + row];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[nrounds * 4 + col][row];\r
+               }\r
+\r
+               for (var round = 1; round < nrounds; round++) {\r
+\r
+                       // Inv shift rows\r
+                       state[1].unshift(state[1].pop());\r
+                       state[2].push(state[2].shift());\r
+                       state[2].push(state[2].shift());\r
+                       state[3].push(state[3].shift());\r
+\r
+                       // Inv sub bytes\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] = INVSBOX[state[row][col]];\r
+                       }\r
+\r
+                       // Add round key\r
+                       for (var row = 0; row < 4; row++) {\r
+                               for (var col = 0; col < 4; col++)\r
+                                       state[row][col] ^= keyschedule[(nrounds - round) * 4 + col][row];\r
+                       }\r
+\r
+                       // Inv mix columns\r
+                       for (var col = 0; col < 4; col++) {\r
+\r
+                               var s0 = state[0][col],\r
+                                   s1 = state[1][col],\r
+                                   s2 = state[2][col],\r
+                                   s3 = state[3][col];\r
+\r
+                               state[0][col] = MULTE[s0] ^ MULTB[s1] ^ MULTD[s2] ^ MULT9[s3];\r
+                               state[1][col] = MULT9[s0] ^ MULTE[s1] ^ MULTB[s2] ^ MULTD[s3];\r
+                               state[2][col] = MULTD[s0] ^ MULT9[s1] ^ MULTE[s2] ^ MULTB[s3];\r
+                               state[3][col] = MULTB[s0] ^ MULTD[s1] ^ MULT9[s2] ^ MULTE[s3];\r
+\r
+                       }\r
+\r
+               }\r
+\r
+               // Inv shift rows\r
+               state[1].unshift(state[1].pop());\r
+               state[2].push(state[2].shift());\r
+               state[2].push(state[2].shift());\r
+               state[3].push(state[3].shift());\r
+\r
+               // Inv sub bytes\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] = INVSBOX[state[row][col]];\r
+               }\r
+\r
+               // Add round key\r
+               for (var row = 0; row < 4; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               state[row][col] ^= keyschedule[col][row];\r
+               }\r
+\r
+               // Set output\r
+               for (var row = 0; row < AES._blocksize; row++) {\r
+                       for (var col = 0; col < 4; col++)\r
+                               c[offset + col * 4 + row] = state[row][col];\r
+               }\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Private methods\r
+        */\r
+\r
+       _init: function (k) {\r
+               keylength = k.length / 4;\r
+               nrounds = keylength + 6;\r
+               AES._keyexpansion(k);\r
+       },\r
+\r
+       // Generate a key schedule\r
+       _keyexpansion: function (k) {\r
+\r
+               keyschedule = [];\r
+\r
+               for (var row = 0; row < keylength; row++) {\r
+                       keyschedule[row] = [\r
+                               k[row * 4],\r
+                               k[row * 4 + 1],\r
+                               k[row * 4 + 2],\r
+                               k[row * 4 + 3]\r
+                       ];\r
+               }\r
+\r
+               for (var row = keylength; row < AES._blocksize * (nrounds + 1); row++) {\r
+\r
+                       var temp = [\r
+                               keyschedule[row - 1][0],\r
+                               keyschedule[row - 1][1],\r
+                               keyschedule[row - 1][2],\r
+                               keyschedule[row - 1][3]\r
+                       ];\r
+\r
+                       if (row % keylength == 0) {\r
+\r
+                               // Rot word\r
+                               temp.push(temp.shift());\r
+\r
+                               // Sub word\r
+                               temp[0] = SBOX[temp[0]];\r
+                               temp[1] = SBOX[temp[1]];\r
+                               temp[2] = SBOX[temp[2]];\r
+                               temp[3] = SBOX[temp[3]];\r
+\r
+                               temp[0] ^= RCON[row / keylength];\r
+\r
+                       } else if (keylength > 6 && row % keylength == 4) {\r
+\r
+                               // Sub word\r
+                               temp[0] = SBOX[temp[0]];\r
+                               temp[1] = SBOX[temp[1]];\r
+                               temp[2] = SBOX[temp[2]];\r
+                               temp[3] = SBOX[temp[3]];\r
+\r
+                       }\r
+\r
+                       keyschedule[row] = [\r
+                               keyschedule[row - keylength][0] ^ temp[0],\r
+                               keyschedule[row - keylength][1] ^ temp[1],\r
+                               keyschedule[row - keylength][2] ^ temp[2],\r
+                               keyschedule[row - keylength][3] ^ temp[3]\r
+                       ];\r
+\r
+               }\r
+\r
+       }\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/cbc/cbc-min.js b/js/crypto-js/cbc/cbc-min.js
new file mode 100755 (executable)
index 0000000..69f06d0
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+Crypto.mode.CBC={encrypt:function(b,a,c){var f=b._blocksize*4;a.push(128);for(var e=0;e<a.length;e+=f){if(e==0){for(var d=0;d<f;d++){a[d]^=c[d]}}else{for(var d=0;d<f;d++){a[e+d]^=a[e+d-f]}}b._encryptblock(a,e)}},decrypt:function(a,j,d){var h=a._blocksize*4;for(var g=0;g<j.length;g+=h){var b=j.slice(g,g+h);a._decryptblock(j,g);if(g==0){for(var e=0;e<h;e++){j[e]^=d[e]}}else{for(var e=0;e<h;e++){j[g+e]^=f[e]}}var f=b}while(j.pop()!=128){}}};
\ No newline at end of file
diff --git a/js/crypto-js/cbc/cbc.js b/js/crypto-js/cbc/cbc.js
new file mode 100755 (executable)
index 0000000..3425f41
--- /dev/null
@@ -0,0 +1,71 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+Crypto.mode.CBC = {\r
+\r
+       encrypt: function (cipher, m, iv) {\r
+\r
+               var blockSizeInBytes = cipher._blocksize * 4;\r
+\r
+               // Pad\r
+               m.push(0x80);\r
+\r
+               // Encrypt each block\r
+               for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {\r
+\r
+                       if (offset == 0) {\r
+                               // XOR first block using IV\r
+                               for (var i = 0; i < blockSizeInBytes; i++)\r
+                                       m[i] ^= iv[i];\r
+                       }\r
+                       else {\r
+                               // XOR this block using previous crypted block\r
+                               for (var i = 0; i < blockSizeInBytes; i++)\r
+                                       m[offset + i] ^= m[offset + i - blockSizeInBytes];\r
+                       }\r
+\r
+                       // Encrypt block\r
+                       cipher._encryptblock(m, offset);\r
+\r
+               }\r
+\r
+       },\r
+\r
+       decrypt: function (cipher, c, iv) {\r
+\r
+               var blockSizeInBytes = cipher._blocksize * 4;\r
+\r
+               // Decrypt each block\r
+               for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {\r
+\r
+                       // Save this crypted block\r
+                       var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);\r
+\r
+                       // Decrypt block\r
+                       cipher._decryptblock(c, offset);\r
+\r
+                       if (offset == 0) {\r
+                               // XOR first block using IV\r
+                               for (var i = 0; i < blockSizeInBytes; i++)\r
+                                       c[i] ^= iv[i];\r
+                       }\r
+                       else {\r
+                               // XOR decrypted block using previous crypted block\r
+                               for (var i = 0; i < blockSizeInBytes; i++)\r
+                                       c[offset + i] ^= prevCryptedBlock[i];\r
+                       }\r
+\r
+                       // This crypted block is the new previous crypted block\r
+                       var prevCryptedBlock = thisCryptedBlock;\r
+\r
+               }\r
+\r
+               // Strip padding\r
+               while (c.pop() != 0x80) ;\r
+\r
+       }\r
+\r
+};\r
diff --git a/js/crypto-js/crypto-md5/crypto-md5.js b/js/crypto-js/crypto-md5/crypto-md5.js
new file mode 100755 (executable)
index 0000000..e1676ab
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.MD5=function(i,g){var h=a.wordsToBytes(c._md5(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._md5=function(y){if(y.constructor==String){y=e.stringToBytes(y)}var k=a.bytesToWords(y),n=y.length*8,v=1732584193,u=-271733879,t=-1732584194,s=271733878;for(var p=0;p<k.length;p++){k[p]=((k[p]<<8)|(k[p]>>>24))&16711935|((k[p]<<24)|(k[p]>>>8))&4278255360}k[n>>>5]|=128<<(n%32);k[(((n+64)>>>9)<<4)+14]=n;var q=c._ff,h=c._gg,w=c._hh,o=c._ii;for(var p=0;p<k.length;p+=16){var g=v,r=u,j=t,x=s;v=q(v,u,t,s,k[p+0],7,-680876936);s=q(s,v,u,t,k[p+1],12,-389564586);t=q(t,s,v,u,k[p+2],17,606105819);u=q(u,t,s,v,k[p+3],22,-1044525330);v=q(v,u,t,s,k[p+4],7,-176418897);s=q(s,v,u,t,k[p+5],12,1200080426);t=q(t,s,v,u,k[p+6],17,-1473231341);u=q(u,t,s,v,k[p+7],22,-45705983);v=q(v,u,t,s,k[p+8],7,1770035416);s=q(s,v,u,t,k[p+9],12,-1958414417);t=q(t,s,v,u,k[p+10],17,-42063);u=q(u,t,s,v,k[p+11],22,-1990404162);v=q(v,u,t,s,k[p+12],7,1804603682);s=q(s,v,u,t,k[p+13],12,-40341101);t=q(t,s,v,u,k[p+14],17,-1502002290);u=q(u,t,s,v,k[p+15],22,1236535329);v=h(v,u,t,s,k[p+1],5,-165796510);s=h(s,v,u,t,k[p+6],9,-1069501632);t=h(t,s,v,u,k[p+11],14,643717713);u=h(u,t,s,v,k[p+0],20,-373897302);v=h(v,u,t,s,k[p+5],5,-701558691);s=h(s,v,u,t,k[p+10],9,38016083);t=h(t,s,v,u,k[p+15],14,-660478335);u=h(u,t,s,v,k[p+4],20,-405537848);v=h(v,u,t,s,k[p+9],5,568446438);s=h(s,v,u,t,k[p+14],9,-1019803690);t=h(t,s,v,u,k[p+3],14,-187363961);u=h(u,t,s,v,k[p+8],20,1163531501);v=h(v,u,t,s,k[p+13],5,-1444681467);s=h(s,v,u,t,k[p+2],9,-51403784);t=h(t,s,v,u,k[p+7],14,1735328473);u=h(u,t,s,v,k[p+12],20,-1926607734);v=w(v,u,t,s,k[p+5],4,-378558);s=w(s,v,u,t,k[p+8],11,-2022574463);t=w(t,s,v,u,k[p+11],16,1839030562);u=w(u,t,s,v,k[p+14],23,-35309556);v=w(v,u,t,s,k[p+1],4,-1530992060);s=w(s,v,u,t,k[p+4],11,1272893353);t=w(t,s,v,u,k[p+7],16,-155497632);u=w(u,t,s,v,k[p+10],23,-1094730640);v=w(v,u,t,s,k[p+13],4,681279174);s=w(s,v,u,t,k[p+0],11,-358537222);t=w(t,s,v,u,k[p+3],16,-722521979);u=w(u,t,s,v,k[p+6],23,76029189);v=w(v,u,t,s,k[p+9],4,-640364487);s=w(s,v,u,t,k[p+12],11,-421815835);t=w(t,s,v,u,k[p+15],16,530742520);u=w(u,t,s,v,k[p+2],23,-995338651);v=o(v,u,t,s,k[p+0],6,-198630844);s=o(s,v,u,t,k[p+7],10,1126891415);t=o(t,s,v,u,k[p+14],15,-1416354905);u=o(u,t,s,v,k[p+5],21,-57434055);v=o(v,u,t,s,k[p+12],6,1700485571);s=o(s,v,u,t,k[p+3],10,-1894986606);t=o(t,s,v,u,k[p+10],15,-1051523);u=o(u,t,s,v,k[p+1],21,-2054922799);v=o(v,u,t,s,k[p+8],6,1873313359);s=o(s,v,u,t,k[p+15],10,-30611744);t=o(t,s,v,u,k[p+6],15,-1560198380);u=o(u,t,s,v,k[p+13],21,1309151649);v=o(v,u,t,s,k[p+4],6,-145523070);s=o(s,v,u,t,k[p+11],10,-1120210379);t=o(t,s,v,u,k[p+2],15,718787259);u=o(u,t,s,v,k[p+9],21,-343485551);v=(v+g)>>>0;u=(u+r)>>>0;t=(t+j)>>>0;s=(s+x)>>>0}return a.endian([v,u,t,s])};c._ff=function(i,h,o,l,g,k,j){var m=i+(h&o|~h&l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._gg=function(i,h,o,l,g,k,j){var m=i+(h&l|o&~l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._hh=function(i,h,o,l,g,k,j){var m=i+(h^o^l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._ii=function(i,h,o,l,g,k,j){var m=i+(o^(h|~l))+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha1-hmac-pbkdf2-marc4/crypto-sha1-hmac-pbkdf2-marc4.js b/js/crypto-js/crypto-sha1-hmac-pbkdf2-marc4/crypto-sha1-hmac-pbkdf2-marc4.js
new file mode 100755 (executable)
index 0000000..789cddc
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.HMAC=function(l,m,k,h){if(m.constructor==String){m=d.stringToBytes(m)}if(k.constructor==String){k=d.stringToBytes(k)}if(k.length>l._blocksize*4){k=l(k,{asBytes:true})}var g=k.slice(0),n=k.slice(0);for(var j=0;j<l._blocksize*4;j++){g[j]^=92;n[j]^=54}var f=l(g.concat(l(n.concat(m),{asBytes:true})),{asBytes:true});return h&&h.asBytes?f:h&&h.asString?c.bytesToString(f):a.bytesToHex(f)}})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.PBKDF2=function(q,o,f,t){if(q.constructor==String){q=d.stringToBytes(q)}if(o.constructor==String){o=d.stringToBytes(o)}var s=t&&t.hasher||e.SHA1,k=t&&t.iterations||1;function p(i,j){return e.HMAC(s,j,i,{asBytes:true})}var h=[],g=1;while(h.length<f){var l=p(q,o.concat(a.wordsToBytes([g])));for(var r=l,n=1;n<k;n++){r=p(q,r);for(var m=0;m<l.length;m++){l[m]^=r[m]}}h=h.concat(l);g++}h.length=f;return t&&t.asBytes?h:t&&t.asString?c.bytesToString(h):a.bytesToHex(h)}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,c=b.Binary;var d=f.MARC4={encrypt:function(l,j){var g=e.stringToBytes(l),i=a.randomBytes(16),h=j.constructor==String?f.PBKDF2(j,i,32,{asBytes:true}):j;d._marc4(g,h,1536);return a.bytesToBase64(i.concat(g))},decrypt:function(j,i){var l=a.base64ToBytes(j),h=l.splice(0,16),g=i.constructor==String?f.PBKDF2(i,h,32,{asBytes:true}):i;d._marc4(l,g,1536);return e.bytesToString(l)},_marc4:function(g,n,l){var p,o,q,h;for(p=0,q=[];p<256;p++){q[p]=p}for(p=0,o=0;p<256;p++){o=(o+q[p]+n[p%n.length])%256;h=q[p];q[p]=q[o];q[o]=h}p=o=0;for(var n=-l;n<g.length;n++){p=(p+1)%256;o=(o+q[p])%256;h=q[p];q[p]=q[o];q[o]=h;if(n<0){continue}g[n]^=q[(q[p]+q[o])%256]}}}})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha1-hmac-pbkdf2-ofb-aes/crypto-sha1-hmac-pbkdf2-ofb-aes.js b/js/crypto-js/crypto-sha1-hmac-pbkdf2-ofb-aes/crypto-sha1-hmac-pbkdf2-ofb-aes.js
new file mode 100755 (executable)
index 0000000..dc2c745
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.HMAC=function(l,m,k,h){if(m.constructor==String){m=d.stringToBytes(m)}if(k.constructor==String){k=d.stringToBytes(k)}if(k.length>l._blocksize*4){k=l(k,{asBytes:true})}var g=k.slice(0),n=k.slice(0);for(var j=0;j<l._blocksize*4;j++){g[j]^=92;n[j]^=54}var f=l(g.concat(l(n.concat(m),{asBytes:true})),{asBytes:true});return h&&h.asBytes?f:h&&h.asString?c.bytesToString(f):a.bytesToHex(f)}})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.PBKDF2=function(q,o,f,t){if(q.constructor==String){q=d.stringToBytes(q)}if(o.constructor==String){o=d.stringToBytes(o)}var s=t&&t.hasher||e.SHA1,k=t&&t.iterations||1;function p(i,j){return e.HMAC(s,j,i,{asBytes:true})}var h=[],g=1;while(h.length<f){var l=p(q,o.concat(a.wordsToBytes([g])));for(var r=l,n=1;n<k;n++){r=p(q,r);for(var m=0;m<l.length;m++){l[m]^=r[m]}}h=h.concat(l);g++}h.length=f;return t&&t.asBytes?h:t&&t.asString?c.bytesToString(h):a.bytesToHex(h)}})();(function(){Crypto.mode.OFB={encrypt:a,decrypt:a};function a(c,b,d){var g=c._blocksize*4,f=d.slice(0);for(var e=0;e<b.length;e++){if(e%g==0){c._encryptblock(f,0)}b[e]^=f[e%g]}}})();(function(){var l=Crypto,a=l.util,u=l.charenc,s=u.UTF8,j=u.Binary;var v=[99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22];for(var n=[],r=0;r<256;r++){n[v[r]]=r}var q=[],p=[],m=[],h=[],g=[],e=[];function f(y,x){for(var w=0,z=0;z<8;z++){if(x&1){w^=y}var A=y&128;y=(y<<1)&255;if(A){y^=27}x>>>=1}return w}for(var r=0;r<256;r++){q[r]=f(r,2);p[r]=f(r,3);m[r]=f(r,9);h[r]=f(r,11);g[r]=f(r,13);e[r]=f(r,14)}var k=[0,1,2,4,8,16,32,64,128,27,54];var c=[[],[],[],[]],d,b,t;var o=l.AES={encrypt:function(A,z,y){var i=s.stringToBytes(A),x=a.randomBytes(o._blocksize*4),w=z.constructor==String?l.PBKDF2(z,x,32,{asBytes:true}):z;mode=y&&y.mode||l.mode.OFB;o._init(w);mode.encrypt(o,i,x);return a.bytesToBase64(x.concat(i))},decrypt:function(z,y,x){var A=a.base64ToBytes(z),w=A.splice(0,o._blocksize*4),i=y.constructor==String?l.PBKDF2(y,w,32,{asBytes:true}):y;mode=x&&x.mode||l.mode.OFB;o._init(i);mode.decrypt(o,A,w);return s.bytesToString(A)},_blocksize:4,_encryptblock:function(w,x){for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){c[D][i]=w[x+i*4+D]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[i][D]}}for(var C=1;C<b;C++){for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=v[c[D][i]]}}c[1].push(c[1].shift());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].unshift(c[3].pop());for(var i=0;i<4;i++){var B=c[0][i],A=c[1][i],z=c[2][i],y=c[3][i];c[0][i]=q[B]^p[A]^z^y;c[1][i]=B^q[A]^p[z]^y;c[2][i]=B^A^q[z]^p[y];c[3][i]=p[B]^A^z^q[y]}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[C*4+i][D]}}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=v[c[D][i]]}}c[1].push(c[1].shift());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].unshift(c[3].pop());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[b*4+i][D]}}for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){w[x+i*4+D]=c[D][i]}}},_decryptblock:function(x,w){for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){c[D][i]=x[w+i*4+D]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[b*4+i][D]}}for(var C=1;C<b;C++){c[1].unshift(c[1].pop());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].push(c[3].shift());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=n[c[D][i]]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[(b-C)*4+i][D]}}for(var i=0;i<4;i++){var B=c[0][i],A=c[1][i],z=c[2][i],y=c[3][i];c[0][i]=e[B]^h[A]^g[z]^m[y];c[1][i]=m[B]^e[A]^h[z]^g[y];c[2][i]=g[B]^m[A]^e[z]^h[y];c[3][i]=h[B]^g[A]^m[z]^e[y]}}c[1].unshift(c[1].pop());c[2].push(c[2].shift());c[2].push(c[2].shift());c[3].push(c[3].shift());for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]=n[c[D][i]]}}for(var D=0;D<4;D++){for(var i=0;i<4;i++){c[D][i]^=t[i][D]}}for(var D=0;D<o._blocksize;D++){for(var i=0;i<4;i++){x[w+i*4+D]=c[D][i]}}},_init:function(i){d=i.length/4;b=d+6;o._keyexpansion(i)},_keyexpansion:function(w){t=[];for(var x=0;x<d;x++){t[x]=[w[x*4],w[x*4+1],w[x*4+2],w[x*4+3]]}for(var x=d;x<o._blocksize*(b+1);x++){var i=[t[x-1][0],t[x-1][1],t[x-1][2],t[x-1][3]];if(x%d==0){i.push(i.shift());i[0]=v[i[0]];i[1]=v[i[1]];i[2]=v[i[2]];i[3]=v[i[3]];i[0]^=k[x/d]}else{if(d>6&&x%d==4){i[0]=v[i[0]];i[1]=v[i[1]];i[2]=v[i[2]];i[3]=v[i[3]]}}t[x]=[t[x-d][0]^i[0],t[x-d][1]^i[1],t[x-d][2]^i[2],t[x-d][3]^i[3]]}}}})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha1-hmac-pbkdf2-rabbit/crypto-sha1-hmac-pbkdf2-rabbit.js b/js/crypto-js/crypto-sha1-hmac-pbkdf2-rabbit/crypto-sha1-hmac-pbkdf2-rabbit.js
new file mode 100755 (executable)
index 0000000..ff88234
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.HMAC=function(l,m,k,h){if(m.constructor==String){m=d.stringToBytes(m)}if(k.constructor==String){k=d.stringToBytes(k)}if(k.length>l._blocksize*4){k=l(k,{asBytes:true})}var g=k.slice(0),n=k.slice(0);for(var j=0;j<l._blocksize*4;j++){g[j]^=92;n[j]^=54}var f=l(g.concat(l(n.concat(m),{asBytes:true})),{asBytes:true});return h&&h.asBytes?f:h&&h.asString?c.bytesToString(f):a.bytesToHex(f)}})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.PBKDF2=function(q,o,f,t){if(q.constructor==String){q=d.stringToBytes(q)}if(o.constructor==String){o=d.stringToBytes(o)}var s=t&&t.hasher||e.SHA1,k=t&&t.iterations||1;function p(i,j){return e.HMAC(s,j,i,{asBytes:true})}var h=[],g=1;while(h.length<f){var l=p(q,o.concat(a.wordsToBytes([g])));for(var r=l,n=1;n<k;n++){r=p(q,r);for(var m=0;m<l.length;m++){l[m]^=r[m]}}h=h.concat(l);g++}h.length=f;return t&&t.asBytes?h:t&&t.asString?c.bytesToString(h):a.bytesToHex(h)}})();(function(){var a=Crypto,f=a.util,j=a.charenc,d=j.UTF8,e=j.Binary;var i=[],g=[],h;var k=a.Rabbit={encrypt:function(o,n){var b=d.stringToBytes(o),l=f.randomBytes(8),c=n.constructor==String?a.PBKDF2(n,l,32,{asBytes:true}):n;k._rabbit(b,c,f.bytesToWords(l));return f.bytesToBase64(l.concat(b))},decrypt:function(n,m){var o=f.base64ToBytes(n),l=o.splice(0,8),b=m.constructor==String?a.PBKDF2(m,l,32,{asBytes:true}):m;k._rabbit(o,b,f.bytesToWords(l));return d.bytesToString(o)},_rabbit:function(l,n,p){k._keysetup(n);if(p){k._ivsetup(p)}for(var r=[],q=0;q<l.length;q++){if(q%16==0){k._nextstate();r[0]=i[0]^(i[5]>>>16)^(i[3]<<16);r[1]=i[2]^(i[7]>>>16)^(i[5]<<16);r[2]=i[4]^(i[1]>>>16)^(i[7]<<16);r[3]=i[6]^(i[3]>>>16)^(i[1]<<16);for(var o=0;o<4;o++){r[o]=((r[o]<<8)|(r[o]>>>24))&16711935|((r[o]<<24)|(r[o]>>>8))&4278255360}for(var c=120;c>=0;c-=8){r[c/8]=(r[c>>>5]>>>(24-c%32))&255}}l[q]^=r[q%16]}},_keysetup:function(b){i[0]=b[0];i[2]=b[1];i[4]=b[2];i[6]=b[3];i[1]=(b[3]<<16)|(b[2]>>>16);i[3]=(b[0]<<16)|(b[3]>>>16);i[5]=(b[1]<<16)|(b[0]>>>16);i[7]=(b[2]<<16)|(b[1]>>>16);g[0]=f.rotl(b[2],16);g[2]=f.rotl(b[3],16);g[4]=f.rotl(b[0],16);g[6]=f.rotl(b[1],16);g[1]=(b[0]&4294901760)|(b[1]&65535);g[3]=(b[1]&4294901760)|(b[2]&65535);g[5]=(b[2]&4294901760)|(b[3]&65535);g[7]=(b[3]&4294901760)|(b[0]&65535);h=0;for(var c=0;c<4;c++){k._nextstate()}for(var c=0;c<8;c++){g[c]^=i[(c+4)&7]}},_ivsetup:function(b){var o=f.endian(b[0]),m=f.endian(b[1]),n=(o>>>16)|(m&4294901760),l=(m<<16)|(o&65535);g[0]^=o;g[1]^=n;g[2]^=m;g[3]^=l;g[4]^=o;g[5]^=n;g[6]^=m;g[7]^=l;for(var c=0;c<4;c++){k._nextstate()}},_nextstate:function(){for(var c=[],l=0;l<8;l++){c[l]=g[l]}g[0]=(g[0]+1295307597+h)>>>0;g[1]=(g[1]+3545052371+((g[0]>>>0)<(c[0]>>>0)?1:0))>>>0;g[2]=(g[2]+886263092+((g[1]>>>0)<(c[1]>>>0)?1:0))>>>0;g[3]=(g[3]+1295307597+((g[2]>>>0)<(c[2]>>>0)?1:0))>>>0;g[4]=(g[4]+3545052371+((g[3]>>>0)<(c[3]>>>0)?1:0))>>>0;g[5]=(g[5]+886263092+((g[4]>>>0)<(c[4]>>>0)?1:0))>>>0;g[6]=(g[6]+1295307597+((g[5]>>>0)<(c[5]>>>0)?1:0))>>>0;g[7]=(g[7]+3545052371+((g[6]>>>0)<(c[6]>>>0)?1:0))>>>0;h=(g[7]>>>0)<(c[7]>>>0)?1:0;for(var m=[],l=0;l<8;l++){var o=(i[l]+g[l])>>>0;var q=o&65535,n=o>>>16;var b=((((q*q)>>>17)+q*n)>>>15)+n*n,p=(((o&4294901760)*o)>>>0)+(((o&65535)*o)>>>0)>>>0;m[l]=b^p}i[0]=m[0]+((m[7]<<16)|(m[7]>>>16))+((m[6]<<16)|(m[6]>>>16));i[1]=m[1]+((m[0]<<8)|(m[0]>>>24))+m[7];i[2]=m[2]+((m[1]<<16)|(m[1]>>>16))+((m[0]<<16)|(m[0]>>>16));i[3]=m[3]+((m[2]<<8)|(m[2]>>>24))+m[1];i[4]=m[4]+((m[3]<<16)|(m[3]>>>16))+((m[2]<<16)|(m[2]>>>16));i[5]=m[5]+((m[4]<<8)|(m[4]>>>24))+m[3];i[6]=m[6]+((m[5]<<16)|(m[5]>>>16))+((m[4]<<16)|(m[4]>>>16));i[7]=m[7]+((m[6]<<8)|(m[6]>>>24))+m[5]}}})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha1-hmac-pbkdf2/crypto-sha1-hmac-pbkdf2.js b/js/crypto-js/crypto-sha1-hmac-pbkdf2/crypto-sha1-hmac-pbkdf2.js
new file mode 100755 (executable)
index 0000000..b2a5e71
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.HMAC=function(l,m,k,h){if(m.constructor==String){m=d.stringToBytes(m)}if(k.constructor==String){k=d.stringToBytes(k)}if(k.length>l._blocksize*4){k=l(k,{asBytes:true})}var g=k.slice(0),n=k.slice(0);for(var j=0;j<l._blocksize*4;j++){g[j]^=92;n[j]^=54}var f=l(g.concat(l(n.concat(m),{asBytes:true})),{asBytes:true});return h&&h.asBytes?f:h&&h.asString?c.bytesToString(f):a.bytesToHex(f)}})();(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.PBKDF2=function(q,o,f,t){if(q.constructor==String){q=d.stringToBytes(q)}if(o.constructor==String){o=d.stringToBytes(o)}var s=t&&t.hasher||e.SHA1,k=t&&t.iterations||1;function p(i,j){return e.HMAC(s,j,i,{asBytes:true})}var h=[],g=1;while(h.length<f){var l=p(q,o.concat(a.wordsToBytes([g])));for(var r=l,n=1;n<k;n++){r=p(q,r);for(var m=0;m<l.length;m++){l[m]^=r[m]}}h=h.concat(l);g++}h.length=f;return t&&t.asBytes?h:t&&t.asString?c.bytesToString(h):a.bytesToHex(h)}})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha1/crypto-sha1.js b/js/crypto-js/crypto-sha1/crypto-sha1.js
new file mode 100755 (executable)
index 0000000..aa5dd1c
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto-sha256/crypto-sha256.js b/js/crypto-js/crypto-sha256/crypto-sha256.js
new file mode 100755 (executable)
index 0000000..320b2f8
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();(function(){var g=Crypto,b=g.util,c=g.charenc,f=c.UTF8,e=c.Binary;var a=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];var d=g.SHA256=function(j,h){var i=b.wordsToBytes(d._sha256(j));return h&&h.asBytes?i:h&&h.asString?e.bytesToString(i):b.bytesToHex(i)};d._sha256=function(q){if(q.constructor==String){q=f.stringToBytes(q)}var y=b.bytesToWords(q),z=q.length*8,r=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],s=[],K,J,I,G,F,E,D,C,B,A,p,o;y[z>>5]|=128<<(24-z%32);y[((z+64>>9)<<4)+15]=z;for(var B=0;B<y.length;B+=16){K=r[0];J=r[1];I=r[2];G=r[3];F=r[4];E=r[5];D=r[6];C=r[7];for(var A=0;A<64;A++){if(A<16){s[A]=y[A+B]}else{var n=s[A-15],u=s[A-2],M=((n<<25)|(n>>>7))^((n<<14)|(n>>>18))^(n>>>3),L=((u<<15)|(u>>>17))^((u<<13)|(u>>>19))^(u>>>10);s[A]=M+(s[A-7]>>>0)+L+(s[A-16]>>>0)}var t=F&E^~F&D,k=K&J^K&I^J&I,x=((K<<30)|(K>>>2))^((K<<19)|(K>>>13))^((K<<10)|(K>>>22)),v=((F<<26)|(F>>>6))^((F<<21)|(F>>>11))^((F<<7)|(F>>>25));p=(C>>>0)+v+t+(a[A])+(s[A]>>>0);o=x+k;C=D;D=E;E=F;F=G+p;G=I;I=J;J=K;K=p+o}r[0]+=K;r[1]+=J;r[2]+=I;r[3]+=G;r[4]+=F;r[5]+=E;r[6]+=D;r[7]+=C}return r};d._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto/crypto-min.js b/js/crypto-js/crypto/crypto-min.js
new file mode 100755 (executable)
index 0000000..dc5b80d
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var d=window.Crypto={};var a=d.util={rotl:function(h,g){return(h<<g)|(h>>>(32-g))},rotr:function(h,g){return(h<<(32-g))|(h>>>g)},endian:function(h){if(h.constructor==Number){return a.rotl(h,8)&16711935|a.rotl(h,24)&4278255360}for(var g=0;g<h.length;g++){h[g]=a.endian(h[g])}return h},randomBytes:function(h){for(var g=[];h>0;h--){g.push(Math.floor(Math.random()*256))}return g},bytesToWords:function(h){for(var k=[],j=0,g=0;j<h.length;j++,g+=8){k[g>>>5]|=h[j]<<(24-g%32)}return k},wordsToBytes:function(i){for(var h=[],g=0;g<i.length*32;g+=8){h.push((i[g>>>5]>>>(24-g%32))&255)}return h},bytesToHex:function(g){for(var j=[],h=0;h<g.length;h++){j.push((g[h]>>>4).toString(16));j.push((g[h]&15).toString(16))}return j.join("")},hexToBytes:function(h){for(var g=[],i=0;i<h.length;i+=2){g.push(parseInt(h.substr(i,2),16))}return g},bytesToBase64:function(h){if(typeof btoa=="function"){return btoa(e.bytesToString(h))}for(var g=[],l=0;l<h.length;l+=3){var m=(h[l]<<16)|(h[l+1]<<8)|h[l+2];for(var k=0;k<4;k++){if(l*8+k*6<=h.length*8){g.push(c.charAt((m>>>6*(3-k))&63))}else{g.push("=")}}}return g.join("")},base64ToBytes:function(h){if(typeof atob=="function"){return e.stringToBytes(atob(h))}h=h.replace(/[^A-Z0-9+\/]/ig,"");for(var g=[],j=0,k=0;j<h.length;k=++j%4){if(k==0){continue}g.push(((c.indexOf(h.charAt(j-1))&(Math.pow(2,-2*k+8)-1))<<(k*2))|(c.indexOf(h.charAt(j))>>>(6-k*2)))}return g}};d.mode={};var b=d.charenc={};var f=b.UTF8={stringToBytes:function(g){return e.stringToBytes(unescape(encodeURIComponent(g)))},bytesToString:function(g){return decodeURIComponent(escape(e.bytesToString(g)))}};var e=b.Binary={stringToBytes:function(j){for(var g=[],h=0;h<j.length;h++){g.push(j.charCodeAt(h))}return g},bytesToString:function(g){for(var j=[],h=0;h<g.length;h++){j.push(String.fromCharCode(g[h]))}return j.join("")}}})();
\ No newline at end of file
diff --git a/js/crypto-js/crypto/crypto.js b/js/crypto-js/crypto/crypto.js
new file mode 100755 (executable)
index 0000000..1f0f089
--- /dev/null
@@ -0,0 +1,160 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+// Global Crypto object\r
+var Crypto = window.Crypto = {};\r
+\r
+// Crypto utilities\r
+var util = Crypto.util = {\r
+\r
+       // Bit-wise rotate left\r
+       rotl: function (n, b) {\r
+               return (n << b) | (n >>> (32 - b));\r
+       },\r
+\r
+       // Bit-wise rotate right\r
+       rotr: function (n, b) {\r
+               return (n << (32 - b)) | (n >>> b);\r
+       },\r
+\r
+       // Swap big-endian to little-endian and vice versa\r
+       endian: function (n) {\r
+\r
+               // If number given, swap endian\r
+               if (n.constructor == Number) {\r
+                       return util.rotl(n,  8) & 0x00FF00FF |\r
+                              util.rotl(n, 24) & 0xFF00FF00;\r
+               }\r
+\r
+               // Else, assume array and swap all items\r
+               for (var i = 0; i < n.length; i++)\r
+                       n[i] = util.endian(n[i]);\r
+               return n;\r
+\r
+       },\r
+\r
+       // Generate an array of any length of random bytes\r
+       randomBytes: function (n) {\r
+               for (var bytes = []; n > 0; n--)\r
+                       bytes.push(Math.floor(Math.random() * 256));\r
+               return bytes;\r
+       },\r
+\r
+       // Convert a byte array to big-endian 32-bit words\r
+       bytesToWords: function (bytes) {\r
+               for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)\r
+                       words[b >>> 5] |= bytes[i] << (24 - b % 32);\r
+               return words;\r
+       },\r
+\r
+       // Convert big-endian 32-bit words to a byte array\r
+       wordsToBytes: function (words) {\r
+               for (var bytes = [], b = 0; b < words.length * 32; b += 8)\r
+                       bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);\r
+               return bytes;\r
+       },\r
+\r
+       // Convert a byte array to a hex string\r
+       bytesToHex: function (bytes) {\r
+               for (var hex = [], i = 0; i < bytes.length; i++) {\r
+                       hex.push((bytes[i] >>> 4).toString(16));\r
+                       hex.push((bytes[i] & 0xF).toString(16));\r
+               }\r
+               return hex.join("");\r
+       },\r
+\r
+       // Convert a hex string to a byte array\r
+       hexToBytes: function (hex) {\r
+               for (var bytes = [], c = 0; c < hex.length; c += 2)\r
+                       bytes.push(parseInt(hex.substr(c, 2), 16));\r
+               return bytes;\r
+       },\r
+\r
+       // Convert a byte array to a base-64 string\r
+       bytesToBase64: function (bytes) {\r
+\r
+               // Use browser-native function if it exists\r
+               if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes));\r
+\r
+               for(var base64 = [], i = 0; i < bytes.length; i += 3) {\r
+                       var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];\r
+                       for (var j = 0; j < 4; j++) {\r
+                               if (i * 8 + j * 6 <= bytes.length * 8)\r
+                                       base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));\r
+                               else base64.push("=");\r
+                       }\r
+               }\r
+\r
+               return base64.join("");\r
+\r
+       },\r
+\r
+       // Convert a base-64 string to a byte array\r
+       base64ToBytes: function (base64) {\r
+\r
+               // Use browser-native function if it exists\r
+               if (typeof atob == "function") return Binary.stringToBytes(atob(base64));\r
+\r
+               // Remove non-base-64 characters\r
+               base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");\r
+\r
+               for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {\r
+                       if (imod4 == 0) continue;\r
+                       bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) |\r
+                                  (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));\r
+               }\r
+\r
+               return bytes;\r
+\r
+       }\r
+\r
+};\r
+\r
+// Crypto mode namespace\r
+Crypto.mode = {};\r
+\r
+// Crypto character encodings\r
+var charenc = Crypto.charenc = {};\r
+\r
+// UTF-8 encoding\r
+var UTF8 = charenc.UTF8 = {\r
+\r
+       // Convert a string to a byte array\r
+       stringToBytes: function (str) {\r
+               return Binary.stringToBytes(unescape(encodeURIComponent(str)));\r
+       },\r
+\r
+       // Convert a byte array to a string\r
+       bytesToString: function (bytes) {\r
+               return decodeURIComponent(escape(Binary.bytesToString(bytes)));\r
+       }\r
+\r
+};\r
+\r
+// Binary encoding\r
+var Binary = charenc.Binary = {\r
+\r
+       // Convert a string to a byte array\r
+       stringToBytes: function (str) {\r
+               for (var bytes = [], i = 0; i < str.length; i++)\r
+                       bytes.push(str.charCodeAt(i));\r
+               return bytes;\r
+       },\r
+\r
+       // Convert a byte array to a string\r
+       bytesToString: function (bytes) {\r
+               for (var str = [], i = 0; i < bytes.length; i++)\r
+                       str.push(String.fromCharCode(bytes[i]));\r
+               return str.join("");\r
+       }\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/hmac/hmac-min.js b/js/crypto-js/hmac/hmac-min.js
new file mode 100755 (executable)
index 0000000..01984cc
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.HMAC=function(l,m,k,h){if(m.constructor==String){m=d.stringToBytes(m)}if(k.constructor==String){k=d.stringToBytes(k)}if(k.length>l._blocksize*4){k=l(k,{asBytes:true})}var g=k.slice(0),n=k.slice(0);for(var j=0;j<l._blocksize*4;j++){g[j]^=92;n[j]^=54}var f=l(g.concat(l(n.concat(m),{asBytes:true})),{asBytes:true});return h&&h.asBytes?f:h&&h.asString?c.bytesToString(f):a.bytesToHex(f)}})();
\ No newline at end of file
diff --git a/js/crypto-js/hmac/hmac.js b/js/crypto-js/hmac/hmac.js
new file mode 100755 (executable)
index 0000000..4be28f3
--- /dev/null
@@ -0,0 +1,43 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+C.HMAC = function (hasher, message, key, options) {\r
+\r
+       // Convert to byte arrays\r
+       if (message.constructor == String) message = UTF8.stringToBytes(message);\r
+       if (key.constructor == String) key = UTF8.stringToBytes(key);\r
+       /* else, assume byte arrays already */\r
+\r
+       // Allow arbitrary length keys\r
+       if (key.length > hasher._blocksize * 4)\r
+               key = hasher(key, { asBytes: true });\r
+\r
+       // XOR keys with pad constants\r
+       var okey = key.slice(0),\r
+           ikey = key.slice(0);\r
+       for (var i = 0; i < hasher._blocksize * 4; i++) {\r
+               okey[i] ^= 0x5C;\r
+               ikey[i] ^= 0x36;\r
+       }\r
+\r
+       var hmacbytes = hasher(okey.concat(hasher(ikey.concat(message), { asBytes: true })), { asBytes: true });\r
+\r
+       return options && options.asBytes ? hmacbytes :\r
+              options && options.asString ? Binary.bytesToString(hmacbytes) :\r
+              util.bytesToHex(hmacbytes);\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/marc4/marc4-min.js b/js/crypto-js/marc4/marc4-min.js
new file mode 100755 (executable)
index 0000000..646176c
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,c=b.Binary;var d=f.MARC4={encrypt:function(l,j){var g=e.stringToBytes(l),i=a.randomBytes(16),h=j.constructor==String?f.PBKDF2(j,i,32,{asBytes:true}):j;d._marc4(g,h,1536);return a.bytesToBase64(i.concat(g))},decrypt:function(j,i){var l=a.base64ToBytes(j),h=l.splice(0,16),g=i.constructor==String?f.PBKDF2(i,h,32,{asBytes:true}):i;d._marc4(l,g,1536);return e.bytesToString(l)},_marc4:function(g,n,l){var p,o,q,h;for(p=0,q=[];p<256;p++){q[p]=p}for(p=0,o=0;p<256;p++){o=(o+q[p]+n[p%n.length])%256;h=q[p];q[p]=q[o];q[o]=h}p=o=0;for(var n=-l;n<g.length;n++){p=(p+1)%256;o=(o+q[p])%256;h=q[p];q[p]=q[o];q[o]=h;if(n<0){continue}g[n]^=q[(q[p]+q[o])%256]}}}})();
\ No newline at end of file
diff --git a/js/crypto-js/marc4/marc4.js b/js/crypto-js/marc4/marc4.js
new file mode 100755 (executable)
index 0000000..f027b0e
--- /dev/null
@@ -0,0 +1,122 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+var MARC4 = C.MARC4 = {\r
+\r
+       /**\r
+        * Public API\r
+        */\r
+\r
+       encrypt: function (message, password) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   m = UTF8.stringToBytes(message),\r
+\r
+                   // Generate random IV\r
+                   iv = util.randomBytes(16),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Encrypt\r
+               MARC4._marc4(m, k, 1536);\r
+\r
+               // Return ciphertext\r
+               return util.bytesToBase64(iv.concat(m));\r
+\r
+       },\r
+\r
+       decrypt: function (ciphertext, password) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   c = util.base64ToBytes(ciphertext),\r
+\r
+                   // Separate IV and message\r
+                   iv = c.splice(0, 16),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Decrypt\r
+               MARC4._marc4(c, k, 1536);\r
+\r
+               // Return plaintext\r
+               return UTF8.bytesToString(c);\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Internal methods\r
+        */\r
+\r
+       // The core\r
+       _marc4: function (m, k, drop) {\r
+\r
+               // State variables\r
+               var i, j, s, temp;\r
+\r
+               // Key setup\r
+               for (i = 0, s = []; i < 256; i++) s[i] = i;\r
+               for (i = 0, j = 0;  i < 256; i++) {\r
+\r
+                       j = (j + s[i] + k[i % k.length]) % 256;\r
+\r
+                       // Swap\r
+                       temp = s[i];\r
+                       s[i] = s[j];\r
+                       s[j] = temp;\r
+\r
+               }\r
+\r
+               // Clear counters\r
+               i = j = 0;\r
+\r
+               // Encryption\r
+               for (var k = -drop; k < m.length; k++) {\r
+\r
+                       i = (i + 1) % 256;\r
+                       j = (j + s[i]) % 256;\r
+\r
+                       // Swap\r
+                       temp = s[i];\r
+                       s[i] = s[j];\r
+                       s[j] = temp;\r
+\r
+                       // Stop here if we're still dropping keystream\r
+                       if (k < 0) continue;\r
+\r
+                       // Encrypt\r
+                       m[k] ^= s[(s[i] + s[j]) % 256];\r
+\r
+               }\r
+\r
+       }\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/md5/md5-min.js b/js/crypto-js/md5/md5-min.js
new file mode 100755 (executable)
index 0000000..9bb803a
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.MD5=function(i,g){var h=a.wordsToBytes(c._md5(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._md5=function(y){if(y.constructor==String){y=e.stringToBytes(y)}var k=a.bytesToWords(y),n=y.length*8,v=1732584193,u=-271733879,t=-1732584194,s=271733878;for(var p=0;p<k.length;p++){k[p]=((k[p]<<8)|(k[p]>>>24))&16711935|((k[p]<<24)|(k[p]>>>8))&4278255360}k[n>>>5]|=128<<(n%32);k[(((n+64)>>>9)<<4)+14]=n;var q=c._ff,h=c._gg,w=c._hh,o=c._ii;for(var p=0;p<k.length;p+=16){var g=v,r=u,j=t,x=s;v=q(v,u,t,s,k[p+0],7,-680876936);s=q(s,v,u,t,k[p+1],12,-389564586);t=q(t,s,v,u,k[p+2],17,606105819);u=q(u,t,s,v,k[p+3],22,-1044525330);v=q(v,u,t,s,k[p+4],7,-176418897);s=q(s,v,u,t,k[p+5],12,1200080426);t=q(t,s,v,u,k[p+6],17,-1473231341);u=q(u,t,s,v,k[p+7],22,-45705983);v=q(v,u,t,s,k[p+8],7,1770035416);s=q(s,v,u,t,k[p+9],12,-1958414417);t=q(t,s,v,u,k[p+10],17,-42063);u=q(u,t,s,v,k[p+11],22,-1990404162);v=q(v,u,t,s,k[p+12],7,1804603682);s=q(s,v,u,t,k[p+13],12,-40341101);t=q(t,s,v,u,k[p+14],17,-1502002290);u=q(u,t,s,v,k[p+15],22,1236535329);v=h(v,u,t,s,k[p+1],5,-165796510);s=h(s,v,u,t,k[p+6],9,-1069501632);t=h(t,s,v,u,k[p+11],14,643717713);u=h(u,t,s,v,k[p+0],20,-373897302);v=h(v,u,t,s,k[p+5],5,-701558691);s=h(s,v,u,t,k[p+10],9,38016083);t=h(t,s,v,u,k[p+15],14,-660478335);u=h(u,t,s,v,k[p+4],20,-405537848);v=h(v,u,t,s,k[p+9],5,568446438);s=h(s,v,u,t,k[p+14],9,-1019803690);t=h(t,s,v,u,k[p+3],14,-187363961);u=h(u,t,s,v,k[p+8],20,1163531501);v=h(v,u,t,s,k[p+13],5,-1444681467);s=h(s,v,u,t,k[p+2],9,-51403784);t=h(t,s,v,u,k[p+7],14,1735328473);u=h(u,t,s,v,k[p+12],20,-1926607734);v=w(v,u,t,s,k[p+5],4,-378558);s=w(s,v,u,t,k[p+8],11,-2022574463);t=w(t,s,v,u,k[p+11],16,1839030562);u=w(u,t,s,v,k[p+14],23,-35309556);v=w(v,u,t,s,k[p+1],4,-1530992060);s=w(s,v,u,t,k[p+4],11,1272893353);t=w(t,s,v,u,k[p+7],16,-155497632);u=w(u,t,s,v,k[p+10],23,-1094730640);v=w(v,u,t,s,k[p+13],4,681279174);s=w(s,v,u,t,k[p+0],11,-358537222);t=w(t,s,v,u,k[p+3],16,-722521979);u=w(u,t,s,v,k[p+6],23,76029189);v=w(v,u,t,s,k[p+9],4,-640364487);s=w(s,v,u,t,k[p+12],11,-421815835);t=w(t,s,v,u,k[p+15],16,530742520);u=w(u,t,s,v,k[p+2],23,-995338651);v=o(v,u,t,s,k[p+0],6,-198630844);s=o(s,v,u,t,k[p+7],10,1126891415);t=o(t,s,v,u,k[p+14],15,-1416354905);u=o(u,t,s,v,k[p+5],21,-57434055);v=o(v,u,t,s,k[p+12],6,1700485571);s=o(s,v,u,t,k[p+3],10,-1894986606);t=o(t,s,v,u,k[p+10],15,-1051523);u=o(u,t,s,v,k[p+1],21,-2054922799);v=o(v,u,t,s,k[p+8],6,1873313359);s=o(s,v,u,t,k[p+15],10,-30611744);t=o(t,s,v,u,k[p+6],15,-1560198380);u=o(u,t,s,v,k[p+13],21,1309151649);v=o(v,u,t,s,k[p+4],6,-145523070);s=o(s,v,u,t,k[p+11],10,-1120210379);t=o(t,s,v,u,k[p+2],15,718787259);u=o(u,t,s,v,k[p+9],21,-343485551);v=(v+g)>>>0;u=(u+r)>>>0;t=(t+j)>>>0;s=(s+x)>>>0}return a.endian([v,u,t,s])};c._ff=function(i,h,o,l,g,k,j){var m=i+(h&o|~h&l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._gg=function(i,h,o,l,g,k,j){var m=i+(h&l|o&~l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._hh=function(i,h,o,l,g,k,j){var m=i+(h^o^l)+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._ii=function(i,h,o,l,g,k,j){var m=i+(o^(h|~l))+(g>>>0)+j;return((m<<k)|(m>>>(32-k)))+h};c._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/md5/md5.js b/js/crypto-js/md5/md5.js
new file mode 100755 (executable)
index 0000000..359b609
--- /dev/null
@@ -0,0 +1,161 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Public API\r
+var MD5 = C.MD5 = function (message, options) {\r
+       var digestbytes = util.wordsToBytes(MD5._md5(message));\r
+       return options && options.asBytes ? digestbytes :\r
+              options && options.asString ? Binary.bytesToString(digestbytes) :\r
+              util.bytesToHex(digestbytes);\r
+};\r
+\r
+// The core\r
+MD5._md5 = function (message) {\r
+\r
+       // Convert to byte array\r
+       if (message.constructor == String) message = UTF8.stringToBytes(message);\r
+       /* else, assume byte array already */\r
+\r
+       var m = util.bytesToWords(message),\r
+           l = message.length * 8,\r
+           a =  1732584193,\r
+           b = -271733879,\r
+           c = -1732584194,\r
+           d =  271733878;\r
+\r
+       // Swap endian\r
+       for (var i = 0; i < m.length; i++) {\r
+               m[i] = ((m[i] <<  8) | (m[i] >>> 24)) & 0x00FF00FF |\r
+                      ((m[i] << 24) | (m[i] >>>  8)) & 0xFF00FF00;\r
+       }\r
+\r
+       // Padding\r
+       m[l >>> 5] |= 0x80 << (l % 32);\r
+       m[(((l + 64) >>> 9) << 4) + 14] = l;\r
+\r
+       // Method shortcuts\r
+       var FF = MD5._ff,\r
+           GG = MD5._gg,\r
+           HH = MD5._hh,\r
+           II = MD5._ii;\r
+\r
+       for (var i = 0; i < m.length; i += 16) {\r
+\r
+               var aa = a,\r
+                   bb = b,\r
+                   cc = c,\r
+                   dd = d;\r
+\r
+               a = FF(a, b, c, d, m[i+ 0],  7, -680876936);\r
+               d = FF(d, a, b, c, m[i+ 1], 12, -389564586);\r
+               c = FF(c, d, a, b, m[i+ 2], 17,  606105819);\r
+               b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);\r
+               a = FF(a, b, c, d, m[i+ 4],  7, -176418897);\r
+               d = FF(d, a, b, c, m[i+ 5], 12,  1200080426);\r
+               c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);\r
+               b = FF(b, c, d, a, m[i+ 7], 22, -45705983);\r
+               a = FF(a, b, c, d, m[i+ 8],  7,  1770035416);\r
+               d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);\r
+               c = FF(c, d, a, b, m[i+10], 17, -42063);\r
+               b = FF(b, c, d, a, m[i+11], 22, -1990404162);\r
+               a = FF(a, b, c, d, m[i+12],  7,  1804603682);\r
+               d = FF(d, a, b, c, m[i+13], 12, -40341101);\r
+               c = FF(c, d, a, b, m[i+14], 17, -1502002290);\r
+               b = FF(b, c, d, a, m[i+15], 22,  1236535329);\r
+\r
+               a = GG(a, b, c, d, m[i+ 1],  5, -165796510);\r
+               d = GG(d, a, b, c, m[i+ 6],  9, -1069501632);\r
+               c = GG(c, d, a, b, m[i+11], 14,  643717713);\r
+               b = GG(b, c, d, a, m[i+ 0], 20, -373897302);\r
+               a = GG(a, b, c, d, m[i+ 5],  5, -701558691);\r
+               d = GG(d, a, b, c, m[i+10],  9,  38016083);\r
+               c = GG(c, d, a, b, m[i+15], 14, -660478335);\r
+               b = GG(b, c, d, a, m[i+ 4], 20, -405537848);\r
+               a = GG(a, b, c, d, m[i+ 9],  5,  568446438);\r
+               d = GG(d, a, b, c, m[i+14],  9, -1019803690);\r
+               c = GG(c, d, a, b, m[i+ 3], 14, -187363961);\r
+               b = GG(b, c, d, a, m[i+ 8], 20,  1163531501);\r
+               a = GG(a, b, c, d, m[i+13],  5, -1444681467);\r
+               d = GG(d, a, b, c, m[i+ 2],  9, -51403784);\r
+               c = GG(c, d, a, b, m[i+ 7], 14,  1735328473);\r
+               b = GG(b, c, d, a, m[i+12], 20, -1926607734);\r
+\r
+               a = HH(a, b, c, d, m[i+ 5],  4, -378558);\r
+               d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);\r
+               c = HH(c, d, a, b, m[i+11], 16,  1839030562);\r
+               b = HH(b, c, d, a, m[i+14], 23, -35309556);\r
+               a = HH(a, b, c, d, m[i+ 1],  4, -1530992060);\r
+               d = HH(d, a, b, c, m[i+ 4], 11,  1272893353);\r
+               c = HH(c, d, a, b, m[i+ 7], 16, -155497632);\r
+               b = HH(b, c, d, a, m[i+10], 23, -1094730640);\r
+               a = HH(a, b, c, d, m[i+13],  4,  681279174);\r
+               d = HH(d, a, b, c, m[i+ 0], 11, -358537222);\r
+               c = HH(c, d, a, b, m[i+ 3], 16, -722521979);\r
+               b = HH(b, c, d, a, m[i+ 6], 23,  76029189);\r
+               a = HH(a, b, c, d, m[i+ 9],  4, -640364487);\r
+               d = HH(d, a, b, c, m[i+12], 11, -421815835);\r
+               c = HH(c, d, a, b, m[i+15], 16,  530742520);\r
+               b = HH(b, c, d, a, m[i+ 2], 23, -995338651);\r
+\r
+               a = II(a, b, c, d, m[i+ 0],  6, -198630844);\r
+               d = II(d, a, b, c, m[i+ 7], 10,  1126891415);\r
+               c = II(c, d, a, b, m[i+14], 15, -1416354905);\r
+               b = II(b, c, d, a, m[i+ 5], 21, -57434055);\r
+               a = II(a, b, c, d, m[i+12],  6,  1700485571);\r
+               d = II(d, a, b, c, m[i+ 3], 10, -1894986606);\r
+               c = II(c, d, a, b, m[i+10], 15, -1051523);\r
+               b = II(b, c, d, a, m[i+ 1], 21, -2054922799);\r
+               a = II(a, b, c, d, m[i+ 8],  6,  1873313359);\r
+               d = II(d, a, b, c, m[i+15], 10, -30611744);\r
+               c = II(c, d, a, b, m[i+ 6], 15, -1560198380);\r
+               b = II(b, c, d, a, m[i+13], 21,  1309151649);\r
+               a = II(a, b, c, d, m[i+ 4],  6, -145523070);\r
+               d = II(d, a, b, c, m[i+11], 10, -1120210379);\r
+               c = II(c, d, a, b, m[i+ 2], 15,  718787259);\r
+               b = II(b, c, d, a, m[i+ 9], 21, -343485551);\r
+\r
+               a = (a + aa) >>> 0;\r
+               b = (b + bb) >>> 0;\r
+               c = (c + cc) >>> 0;\r
+               d = (d + dd) >>> 0;\r
+\r
+       }\r
+\r
+       return util.endian([a, b, c, d]);\r
+\r
+};\r
+\r
+// Auxiliary functions\r
+MD5._ff  = function (a, b, c, d, x, s, t) {\r
+       var n = a + (b & c | ~b & d) + (x >>> 0) + t;\r
+       return ((n << s) | (n >>> (32 - s))) + b;\r
+};\r
+MD5._gg  = function (a, b, c, d, x, s, t) {\r
+       var n = a + (b & d | c & ~d) + (x >>> 0) + t;\r
+       return ((n << s) | (n >>> (32 - s))) + b;\r
+};\r
+MD5._hh  = function (a, b, c, d, x, s, t) {\r
+       var n = a + (b ^ c ^ d) + (x >>> 0) + t;\r
+       return ((n << s) | (n >>> (32 - s))) + b;\r
+};\r
+MD5._ii  = function (a, b, c, d, x, s, t) {\r
+       var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;\r
+       return ((n << s) | (n >>> (32 - s))) + b;\r
+};\r
+\r
+// Package private blocksize\r
+MD5._blocksize = 16;\r
+\r
+})();\r
diff --git a/js/crypto-js/ofb/ofb-min.js b/js/crypto-js/ofb/ofb-min.js
new file mode 100755 (executable)
index 0000000..c2f3229
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){Crypto.mode.OFB={encrypt:a,decrypt:a};function a(c,b,d){var g=c._blocksize*4,f=d.slice(0);for(var e=0;e<b.length;e++){if(e%g==0){c._encryptblock(f,0)}b[e]^=f[e%g]}}})();
\ No newline at end of file
diff --git a/js/crypto-js/ofb/ofb.js b/js/crypto-js/ofb/ofb.js
new file mode 100755 (executable)
index 0000000..403df04
--- /dev/null
@@ -0,0 +1,35 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Public API\r
+Crypto.mode.OFB = {\r
+       encrypt: OFB,\r
+       decrypt: OFB\r
+};\r
+\r
+// The mode function\r
+function OFB(cipher, m, iv) {\r
+\r
+       var blockSizeInBytes = cipher._blocksize * 4,\r
+           keystream = iv.slice(0);\r
+\r
+       // Encrypt each byte\r
+       for (var i = 0; i < m.length; i++) {\r
+\r
+               // Generate keystream\r
+               if (i % blockSizeInBytes == 0)\r
+                       cipher._encryptblock(keystream, 0);\r
+\r
+               // Encrypt byte\r
+               m[i] ^= keystream[i % blockSizeInBytes];\r
+\r
+       }\r
+\r
+}\r
+\r
+})();\r
diff --git a/js/crypto-js/pbkdf2/pbkdf2-min.js b/js/crypto-js/pbkdf2/pbkdf2-min.js
new file mode 100755 (executable)
index 0000000..b8bcea1
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var e=Crypto,a=e.util,b=e.charenc,d=b.UTF8,c=b.Binary;e.PBKDF2=function(q,o,f,t){if(q.constructor==String){q=d.stringToBytes(q)}if(o.constructor==String){o=d.stringToBytes(o)}var s=t&&t.hasher||e.SHA1,k=t&&t.iterations||1;function p(i,j){return e.HMAC(s,j,i,{asBytes:true})}var h=[],g=1;while(h.length<f){var l=p(q,o.concat(a.wordsToBytes([g])));for(var r=l,n=1;n<k;n++){r=p(q,r);for(var m=0;m<l.length;m++){l[m]^=r[m]}}h=h.concat(l);g++}h.length=f;return t&&t.asBytes?h:t&&t.asString?c.bytesToString(h):a.bytesToHex(h)}})();
\ No newline at end of file
diff --git a/js/crypto-js/pbkdf2/pbkdf2.js b/js/crypto-js/pbkdf2/pbkdf2.js
new file mode 100755 (executable)
index 0000000..127c1d0
--- /dev/null
@@ -0,0 +1,54 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+C.PBKDF2 = function (password, salt, keylen, options) {\r
+\r
+       // Convert to byte arrays\r
+       if (password.constructor == String) password = UTF8.stringToBytes(password);\r
+       if (salt.constructor == String) salt = UTF8.stringToBytes(salt);\r
+       /* else, assume byte arrays already */\r
+\r
+       // Defaults\r
+       var hasher = options && options.hasher || C.SHA1,\r
+           iterations = options && options.iterations || 1;\r
+\r
+       // Pseudo-random function\r
+       function PRF(password, salt) {\r
+               return C.HMAC(hasher, salt, password, { asBytes: true });\r
+       }\r
+\r
+       // Generate key\r
+       var derivedKeyBytes = [],\r
+           blockindex = 1;\r
+       while (derivedKeyBytes.length < keylen) {\r
+               var block = PRF(password, salt.concat(util.wordsToBytes([blockindex])));\r
+               for (var u = block, i = 1; i < iterations; i++) {\r
+                       u = PRF(password, u);\r
+                       for (var j = 0; j < block.length; j++) block[j] ^= u[j];\r
+               }\r
+               derivedKeyBytes = derivedKeyBytes.concat(block);\r
+               blockindex++;\r
+       }\r
+\r
+       // Truncate excess bytes\r
+       derivedKeyBytes.length = keylen;\r
+\r
+       return options && options.asBytes ? derivedKeyBytes :\r
+              options && options.asString ? Binary.bytesToString(derivedKeyBytes) :\r
+              util.bytesToHex(derivedKeyBytes);\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/rabbit/rabbit-min.js b/js/crypto-js/rabbit/rabbit-min.js
new file mode 100755 (executable)
index 0000000..faceb22
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var a=Crypto,f=a.util,j=a.charenc,d=j.UTF8,e=j.Binary;var i=[],g=[],h;var k=a.Rabbit={encrypt:function(o,n){var b=d.stringToBytes(o),l=f.randomBytes(8),c=n.constructor==String?a.PBKDF2(n,l,32,{asBytes:true}):n;k._rabbit(b,c,f.bytesToWords(l));return f.bytesToBase64(l.concat(b))},decrypt:function(n,m){var o=f.base64ToBytes(n),l=o.splice(0,8),b=m.constructor==String?a.PBKDF2(m,l,32,{asBytes:true}):m;k._rabbit(o,b,f.bytesToWords(l));return d.bytesToString(o)},_rabbit:function(l,n,p){k._keysetup(n);if(p){k._ivsetup(p)}for(var r=[],q=0;q<l.length;q++){if(q%16==0){k._nextstate();r[0]=i[0]^(i[5]>>>16)^(i[3]<<16);r[1]=i[2]^(i[7]>>>16)^(i[5]<<16);r[2]=i[4]^(i[1]>>>16)^(i[7]<<16);r[3]=i[6]^(i[3]>>>16)^(i[1]<<16);for(var o=0;o<4;o++){r[o]=((r[o]<<8)|(r[o]>>>24))&16711935|((r[o]<<24)|(r[o]>>>8))&4278255360}for(var c=120;c>=0;c-=8){r[c/8]=(r[c>>>5]>>>(24-c%32))&255}}l[q]^=r[q%16]}},_keysetup:function(b){i[0]=b[0];i[2]=b[1];i[4]=b[2];i[6]=b[3];i[1]=(b[3]<<16)|(b[2]>>>16);i[3]=(b[0]<<16)|(b[3]>>>16);i[5]=(b[1]<<16)|(b[0]>>>16);i[7]=(b[2]<<16)|(b[1]>>>16);g[0]=f.rotl(b[2],16);g[2]=f.rotl(b[3],16);g[4]=f.rotl(b[0],16);g[6]=f.rotl(b[1],16);g[1]=(b[0]&4294901760)|(b[1]&65535);g[3]=(b[1]&4294901760)|(b[2]&65535);g[5]=(b[2]&4294901760)|(b[3]&65535);g[7]=(b[3]&4294901760)|(b[0]&65535);h=0;for(var c=0;c<4;c++){k._nextstate()}for(var c=0;c<8;c++){g[c]^=i[(c+4)&7]}},_ivsetup:function(b){var o=f.endian(b[0]),m=f.endian(b[1]),n=(o>>>16)|(m&4294901760),l=(m<<16)|(o&65535);g[0]^=o;g[1]^=n;g[2]^=m;g[3]^=l;g[4]^=o;g[5]^=n;g[6]^=m;g[7]^=l;for(var c=0;c<4;c++){k._nextstate()}},_nextstate:function(){for(var c=[],l=0;l<8;l++){c[l]=g[l]}g[0]=(g[0]+1295307597+h)>>>0;g[1]=(g[1]+3545052371+((g[0]>>>0)<(c[0]>>>0)?1:0))>>>0;g[2]=(g[2]+886263092+((g[1]>>>0)<(c[1]>>>0)?1:0))>>>0;g[3]=(g[3]+1295307597+((g[2]>>>0)<(c[2]>>>0)?1:0))>>>0;g[4]=(g[4]+3545052371+((g[3]>>>0)<(c[3]>>>0)?1:0))>>>0;g[5]=(g[5]+886263092+((g[4]>>>0)<(c[4]>>>0)?1:0))>>>0;g[6]=(g[6]+1295307597+((g[5]>>>0)<(c[5]>>>0)?1:0))>>>0;g[7]=(g[7]+3545052371+((g[6]>>>0)<(c[6]>>>0)?1:0))>>>0;h=(g[7]>>>0)<(c[7]>>>0)?1:0;for(var m=[],l=0;l<8;l++){var o=(i[l]+g[l])>>>0;var q=o&65535,n=o>>>16;var b=((((q*q)>>>17)+q*n)>>>15)+n*n,p=(((o&4294901760)*o)>>>0)+(((o&65535)*o)>>>0)>>>0;m[l]=b^p}i[0]=m[0]+((m[7]<<16)|(m[7]>>>16))+((m[6]<<16)|(m[6]>>>16));i[1]=m[1]+((m[0]<<8)|(m[0]>>>24))+m[7];i[2]=m[2]+((m[1]<<16)|(m[1]>>>16))+((m[0]<<16)|(m[0]>>>16));i[3]=m[3]+((m[2]<<8)|(m[2]>>>24))+m[1];i[4]=m[4]+((m[3]<<16)|(m[3]>>>16))+((m[2]<<16)|(m[2]>>>16));i[5]=m[5]+((m[4]<<8)|(m[4]>>>24))+m[3];i[6]=m[6]+((m[5]<<16)|(m[5]>>>16))+((m[4]<<16)|(m[4]>>>16));i[7]=m[7]+((m[6]<<8)|(m[6]>>>24))+m[5]}}})();
\ No newline at end of file
diff --git a/js/crypto-js/rabbit/rabbit.js b/js/crypto-js/rabbit/rabbit.js
new file mode 100755 (executable)
index 0000000..5b76ee8
--- /dev/null
@@ -0,0 +1,226 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Inner state\r
+var x = [],\r
+    c = [],\r
+    b;\r
+\r
+var Rabbit = C.Rabbit = {\r
+\r
+       /**\r
+        * Public API\r
+        */\r
+\r
+       encrypt: function (message, password) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   m = UTF8.stringToBytes(message),\r
+\r
+                   // Generate random IV\r
+                   iv = util.randomBytes(8),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Encrypt\r
+               Rabbit._rabbit(m, k, util.bytesToWords(iv));\r
+\r
+               // Return ciphertext\r
+               return util.bytesToBase64(iv.concat(m));\r
+\r
+       },\r
+\r
+       decrypt: function (ciphertext, password) {\r
+\r
+               var\r
+\r
+                   // Convert to bytes\r
+                   c = util.base64ToBytes(ciphertext),\r
+\r
+                   // Separate IV and message\r
+                   iv = c.splice(0, 8),\r
+\r
+                   // Generate key\r
+                   k = password.constructor == String ?\r
+                       // Derive key from passphrase\r
+                       C.PBKDF2(password, iv, 32, { asBytes: true }) :\r
+                       // else, assume byte array representing cryptographic key\r
+                       password;\r
+\r
+               // Decrypt\r
+               Rabbit._rabbit(c, k, util.bytesToWords(iv));\r
+\r
+               // Return plaintext\r
+               return UTF8.bytesToString(c);\r
+\r
+       },\r
+\r
+\r
+       /**\r
+        * Internal methods\r
+        */\r
+\r
+       // Encryption/decryption scheme\r
+       _rabbit: function (m, k, iv) {\r
+\r
+               Rabbit._keysetup(k);\r
+               if (iv) Rabbit._ivsetup(iv);\r
+\r
+               for (var s = [], i = 0; i < m.length; i++) {\r
+\r
+                       if (i % 16 == 0) {\r
+\r
+                               // Iterate the system\r
+                               Rabbit._nextstate();\r
+\r
+                               // Generate 16 bytes of pseudo-random data\r
+                               s[0] = x[0] ^ (x[5] >>> 16) ^ (x[3] << 16);\r
+                               s[1] = x[2] ^ (x[7] >>> 16) ^ (x[5] << 16);\r
+                               s[2] = x[4] ^ (x[1] >>> 16) ^ (x[7] << 16);\r
+                               s[3] = x[6] ^ (x[3] >>> 16) ^ (x[1] << 16);\r
+\r
+                               // Swap endian\r
+                               for (var j = 0; j < 4; j++) {\r
+                                       s[j] = ((s[j] <<  8) | (s[j] >>> 24)) & 0x00FF00FF |\r
+                                              ((s[j] << 24) | (s[j] >>>  8)) & 0xFF00FF00;\r
+                               }\r
+\r
+                               // Convert words to bytes\r
+                               for (var b = 120; b >= 0; b -= 8)\r
+                                       s[b / 8] = (s[b >>> 5] >>> (24 - b % 32)) & 0xFF;\r
+\r
+                       }\r
+\r
+                       m[i] ^= s[i % 16];\r
+\r
+               }\r
+\r
+       },\r
+\r
+       // Key setup scheme\r
+       _keysetup: function (k) {\r
+\r
+               // Generate initial state values\r
+               x[0] = k[0];\r
+               x[2] = k[1];\r
+               x[4] = k[2];\r
+               x[6] = k[3];\r
+               x[1] = (k[3] << 16) | (k[2] >>> 16);\r
+               x[3] = (k[0] << 16) | (k[3] >>> 16);\r
+               x[5] = (k[1] << 16) | (k[0] >>> 16);\r
+               x[7] = (k[2] << 16) | (k[1] >>> 16);\r
+\r
+               // Generate initial counter values\r
+               c[0] = util.rotl(k[2], 16);\r
+               c[2] = util.rotl(k[3], 16);\r
+               c[4] = util.rotl(k[0], 16);\r
+               c[6] = util.rotl(k[1], 16);\r
+               c[1] = (k[0] & 0xFFFF0000) | (k[1] & 0xFFFF);\r
+               c[3] = (k[1] & 0xFFFF0000) | (k[2] & 0xFFFF);\r
+               c[5] = (k[2] & 0xFFFF0000) | (k[3] & 0xFFFF);\r
+               c[7] = (k[3] & 0xFFFF0000) | (k[0] & 0xFFFF);\r
+\r
+               // Clear carry bit\r
+               b = 0;\r
+\r
+               // Iterate the system four times\r
+               for (var i = 0; i < 4; i++) Rabbit._nextstate();\r
+\r
+               // Modify the counters\r
+               for (var i = 0; i < 8; i++) c[i] ^= x[(i + 4) & 7];\r
+\r
+       },\r
+\r
+       // IV setup scheme\r
+       _ivsetup: function (iv) {\r
+\r
+               // Generate four subvectors\r
+               var i0 = util.endian(iv[0]),\r
+                   i2 = util.endian(iv[1]),\r
+                   i1 = (i0 >>> 16) | (i2 & 0xFFFF0000),\r
+                   i3 = (i2 <<  16) | (i0 & 0x0000FFFF);\r
+\r
+               // Modify counter values\r
+               c[0] ^= i0;\r
+               c[1] ^= i1;\r
+               c[2] ^= i2;\r
+               c[3] ^= i3;\r
+               c[4] ^= i0;\r
+               c[5] ^= i1;\r
+               c[6] ^= i2;\r
+               c[7] ^= i3;\r
+\r
+               // Iterate the system four times\r
+               for (var i = 0; i < 4; i++) Rabbit._nextstate();\r
+\r
+       },\r
+\r
+       // Next-state function\r
+       _nextstate: function () {\r
+\r
+               // Save old counter values\r
+               for (var c_old = [], i = 0; i < 8; i++) c_old[i] = c[i];\r
+\r
+               // Calculate new counter values\r
+               c[0] = (c[0] + 0x4D34D34D + b) >>> 0;\r
+               c[1] = (c[1] + 0xD34D34D3 + ((c[0] >>> 0) < (c_old[0] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[2] = (c[2] + 0x34D34D34 + ((c[1] >>> 0) < (c_old[1] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[3] = (c[3] + 0x4D34D34D + ((c[2] >>> 0) < (c_old[2] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[4] = (c[4] + 0xD34D34D3 + ((c[3] >>> 0) < (c_old[3] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[5] = (c[5] + 0x34D34D34 + ((c[4] >>> 0) < (c_old[4] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[6] = (c[6] + 0x4D34D34D + ((c[5] >>> 0) < (c_old[5] >>> 0) ? 1 : 0)) >>> 0;\r
+               c[7] = (c[7] + 0xD34D34D3 + ((c[6] >>> 0) < (c_old[6] >>> 0) ? 1 : 0)) >>> 0;\r
+               b = (c[7] >>> 0) < (c_old[7] >>> 0) ? 1 : 0;\r
+\r
+               // Calculate the g-values\r
+               for (var g = [], i = 0; i < 8; i++) {\r
+\r
+                       var gx = (x[i] + c[i]) >>> 0;\r
+\r
+                       // Construct high and low argument for squaring\r
+                       var ga = gx & 0xFFFF,\r
+                           gb = gx >>> 16;\r
+\r
+                       // Calculate high and low result of squaring\r
+                       var gh = ((((ga * ga) >>> 17) + ga * gb) >>> 15) + gb * gb,\r
+                           gl = (((gx & 0xFFFF0000) * gx) >>> 0) + (((gx & 0x0000FFFF) * gx) >>> 0) >>> 0;\r
+\r
+                       // High XOR low\r
+                       g[i] = gh ^ gl;\r
+\r
+               }\r
+\r
+               // Calculate new state values\r
+               x[0] = g[0] + ((g[7] << 16) | (g[7] >>> 16)) + ((g[6] << 16) | (g[6] >>> 16));\r
+               x[1] = g[1] + ((g[0] <<  8) | (g[0] >>> 24)) + g[7];\r
+               x[2] = g[2] + ((g[1] << 16) | (g[1] >>> 16)) + ((g[0] << 16) | (g[0] >>> 16));\r
+               x[3] = g[3] + ((g[2] <<  8) | (g[2] >>> 24)) + g[1];\r
+               x[4] = g[4] + ((g[3] << 16) | (g[3] >>> 16)) + ((g[2] << 16) | (g[2] >>> 16));\r
+               x[5] = g[5] + ((g[4] <<  8) | (g[4] >>> 24)) + g[3];\r
+               x[6] = g[6] + ((g[5] << 16) | (g[5] >>> 16)) + ((g[4] << 16) | (g[4] >>> 16));\r
+               x[7] = g[7] + ((g[6] <<  8) | (g[6] >>> 24)) + g[5];\r
+\r
+       }\r
+\r
+};\r
+\r
+})();\r
diff --git a/js/crypto-js/sha1/sha1-min.js b/js/crypto-js/sha1/sha1-min.js
new file mode 100755 (executable)
index 0000000..30091b1
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var f=Crypto,a=f.util,b=f.charenc,e=b.UTF8,d=b.Binary;var c=f.SHA1=function(i,g){var h=a.wordsToBytes(c._sha1(i));return g&&g.asBytes?h:g&&g.asString?d.bytesToString(h):a.bytesToHex(h)};c._sha1=function(o){if(o.constructor==String){o=e.stringToBytes(o)}var v=a.bytesToWords(o),x=o.length*8,p=[],r=1732584193,q=-271733879,k=-1732584194,h=271733878,g=-1009589776;v[x>>5]|=128<<(24-x%32);v[((x+64>>>9)<<4)+15]=x;for(var z=0;z<v.length;z+=16){var E=r,D=q,C=k,B=h,A=g;for(var y=0;y<80;y++){if(y<16){p[y]=v[z+y]}else{var u=p[y-3]^p[y-8]^p[y-14]^p[y-16];p[y]=(u<<1)|(u>>>31)}var s=((r<<5)|(r>>>27))+g+(p[y]>>>0)+(y<20?(q&k|~q&h)+1518500249:y<40?(q^k^h)+1859775393:y<60?(q&k|q&h|k&h)-1894007588:(q^k^h)-899497514);g=h;h=k;k=(q<<30)|(q>>>2);q=r;r=s}r+=E;q+=D;k+=C;h+=B;g+=A}return[r,q,k,h,g]};c._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/sha1/sha1.js b/js/crypto-js/sha1/sha1.js
new file mode 100755 (executable)
index 0000000..729bdb8
--- /dev/null
@@ -0,0 +1,89 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Public API\r
+var SHA1 = C.SHA1 = function (message, options) {\r
+       var digestbytes = util.wordsToBytes(SHA1._sha1(message));\r
+       return options && options.asBytes ? digestbytes :\r
+              options && options.asString ? Binary.bytesToString(digestbytes) :\r
+              util.bytesToHex(digestbytes);\r
+};\r
+\r
+// The core\r
+SHA1._sha1 = function (message) {\r
+\r
+       // Convert to byte array\r
+       if (message.constructor == String) message = UTF8.stringToBytes(message);\r
+       /* else, assume byte array already */\r
+\r
+       var m  = util.bytesToWords(message),\r
+           l  = message.length * 8,\r
+           w  =  [],\r
+           H0 =  1732584193,\r
+           H1 = -271733879,\r
+           H2 = -1732584194,\r
+           H3 =  271733878,\r
+           H4 = -1009589776;\r
+\r
+       // Padding\r
+       m[l >> 5] |= 0x80 << (24 - l % 32);\r
+       m[((l + 64 >>> 9) << 4) + 15] = l;\r
+\r
+       for (var i = 0; i < m.length; i += 16) {\r
+\r
+               var a = H0,\r
+                   b = H1,\r
+                   c = H2,\r
+                   d = H3,\r
+                   e = H4;\r
+\r
+               for (var j = 0; j < 80; j++) {\r
+\r
+                       if (j < 16) w[j] = m[i + j];\r
+                       else {\r
+                               var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];\r
+                               w[j] = (n << 1) | (n >>> 31);\r
+                       }\r
+\r
+                       var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (\r
+                                j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :\r
+                                j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :\r
+                                j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :\r
+                                         (H1 ^ H2 ^ H3) - 899497514);\r
+\r
+                       H4 =  H3;\r
+                       H3 =  H2;\r
+                       H2 = (H1 << 30) | (H1 >>> 2);\r
+                       H1 =  H0;\r
+                       H0 =  t;\r
+\r
+               }\r
+\r
+               H0 += a;\r
+               H1 += b;\r
+               H2 += c;\r
+               H3 += d;\r
+               H4 += e;\r
+\r
+       }\r
+\r
+       return [H0, H1, H2, H3, H4];\r
+\r
+};\r
+\r
+// Package private blocksize\r
+SHA1._blocksize = 16;\r
+\r
+})();\r
diff --git a/js/crypto-js/sha256/sha256-min.js b/js/crypto-js/sha256/sha256-min.js
new file mode 100755 (executable)
index 0000000..d3a4118
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Crypto-JS v2.0.0
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+(function(){var g=Crypto,b=g.util,c=g.charenc,f=c.UTF8,e=c.Binary;var a=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];var d=g.SHA256=function(j,h){var i=b.wordsToBytes(d._sha256(j));return h&&h.asBytes?i:h&&h.asString?e.bytesToString(i):b.bytesToHex(i)};d._sha256=function(q){if(q.constructor==String){q=f.stringToBytes(q)}var y=b.bytesToWords(q),z=q.length*8,r=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],s=[],K,J,I,G,F,E,D,C,B,A,p,o;y[z>>5]|=128<<(24-z%32);y[((z+64>>9)<<4)+15]=z;for(var B=0;B<y.length;B+=16){K=r[0];J=r[1];I=r[2];G=r[3];F=r[4];E=r[5];D=r[6];C=r[7];for(var A=0;A<64;A++){if(A<16){s[A]=y[A+B]}else{var n=s[A-15],u=s[A-2],M=((n<<25)|(n>>>7))^((n<<14)|(n>>>18))^(n>>>3),L=((u<<15)|(u>>>17))^((u<<13)|(u>>>19))^(u>>>10);s[A]=M+(s[A-7]>>>0)+L+(s[A-16]>>>0)}var t=F&E^~F&D,k=K&J^K&I^J&I,x=((K<<30)|(K>>>2))^((K<<19)|(K>>>13))^((K<<10)|(K>>>22)),v=((F<<26)|(F>>>6))^((F<<21)|(F>>>11))^((F<<7)|(F>>>25));p=(C>>>0)+v+t+(a[A])+(s[A]>>>0);o=x+k;C=D;D=E;E=F;F=G+p;G=I;I=J;J=K;K=p+o}r[0]+=K;r[1]+=J;r[2]+=I;r[3]+=G;r[4]+=F;r[5]+=E;r[6]+=D;r[7]+=C}return r};d._blocksize=16})();
\ No newline at end of file
diff --git a/js/crypto-js/sha256/sha256.js b/js/crypto-js/sha256/sha256.js
new file mode 100755 (executable)
index 0000000..c5a2d01
--- /dev/null
@@ -0,0 +1,133 @@
+/*!\r
+ * Crypto-JS v2.0.0\r
+ * http://code.google.com/p/crypto-js/\r
+ * Copyright (c) 2009, Jeff Mott. All rights reserved.\r
+ * http://code.google.com/p/crypto-js/wiki/License\r
+ */\r
+(function(){\r
+\r
+// Shortcuts\r
+var C = Crypto,\r
+    util = C.util,\r
+    charenc = C.charenc,\r
+    UTF8 = charenc.UTF8,\r
+    Binary = charenc.Binary;\r
+\r
+// Constants\r
+var K = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,\r
+          0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,\r
+          0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,\r
+          0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,\r
+          0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,\r
+          0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,\r
+          0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,\r
+          0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,\r
+          0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,\r
+          0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,\r
+          0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,\r
+          0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,\r
+          0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,\r
+          0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,\r
+          0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,\r
+          0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ];\r
+\r
+// Public API\r
+var SHA256 = C.SHA256 = function (message, options) {\r
+       var digestbytes = util.wordsToBytes(SHA256._sha256(message));\r
+       return options && options.asBytes ? digestbytes :\r
+              options && options.asString ? Binary.bytesToString(digestbytes) :\r
+              util.bytesToHex(digestbytes);\r
+};\r
+\r
+// The core\r
+SHA256._sha256 = function (message) {\r
+\r
+       // Convert to byte array\r
+       if (message.constructor == String) message = UTF8.stringToBytes(message);\r
+       /* else, assume byte array already */\r
+\r
+       var m = util.bytesToWords(message),\r
+           l = message.length * 8,\r
+           H = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,\r
+                 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ],\r
+           w = [],\r
+           a, b, c, d, e, f, g, h, i, j,\r
+           t1, t2;\r
+\r
+       // Padding\r
+       m[l >> 5] |= 0x80 << (24 - l % 32);\r
+       m[((l + 64 >> 9) << 4) + 15] = l;\r
+\r
+       for (var i = 0; i < m.length; i += 16) {\r
+\r
+               a = H[0];\r
+               b = H[1];\r
+               c = H[2];\r
+               d = H[3];\r
+               e = H[4];\r
+               f = H[5];\r
+               g = H[6];\r
+               h = H[7];\r
+\r
+               for (var j = 0; j < 64; j++) {\r
+\r
+                       if (j < 16) w[j] = m[j + i];\r
+                       else {\r
+\r
+                               var gamma0x = w[j - 15],\r
+                                   gamma1x = w[j - 2],\r
+                                   gamma0  = ((gamma0x << 25) | (gamma0x >>>  7)) ^\r
+                                             ((gamma0x << 14) | (gamma0x >>> 18)) ^\r
+                                              (gamma0x >>> 3),\r
+                                   gamma1  = ((gamma1x <<  15) | (gamma1x >>> 17)) ^\r
+                                             ((gamma1x <<  13) | (gamma1x >>> 19)) ^\r
+                                              (gamma1x >>> 10);\r
+\r
+                               w[j] = gamma0 + (w[j - 7] >>> 0) +\r
+                                      gamma1 + (w[j - 16] >>> 0);\r
+\r
+                       }\r
+\r
+                       var ch  = e & f ^ ~e & g,\r
+                           maj = a & b ^ a & c ^ b & c,\r
+                           sigma0 = ((a << 30) | (a >>>  2)) ^\r
+                                    ((a << 19) | (a >>> 13)) ^\r
+                                    ((a << 10) | (a >>> 22)),\r
+                           sigma1 = ((e << 26) | (e >>>  6)) ^\r
+                                    ((e << 21) | (e >>> 11)) ^\r
+                                    ((e <<  7) | (e >>> 25));\r
+\r
+\r
+                       t1 = (h >>> 0) + sigma1 + ch + (K[j]) + (w[j] >>> 0);\r
+                       t2 = sigma0 + maj;\r
+\r
+                       h = g;\r
+                       g = f;\r
+                       f = e;\r
+                       e = d + t1;\r
+                       d = c;\r
+                       c = b;\r
+                       b = a;\r
+                       a = t1 + t2;\r
+\r
+               }\r
+\r
+               H[0] += a;\r
+               H[1] += b;\r
+               H[2] += c;\r
+               H[3] += d;\r
+               H[4] += e;\r
+               H[5] += f;\r
+               H[6] += g;\r
+               H[7] += h;\r
+\r
+       }\r
+\r
+       return H;\r
+\r
+};\r
+\r
+// Package private blocksize\r
+SHA256._blocksize = 16;\r
+\r
+})();\r
diff --git a/js/dtaus.js b/js/dtaus.js
new file mode 100644 (file)
index 0000000..61f4a1a
--- /dev/null
@@ -0,0 +1,333 @@
+function str_pad_left(input, len, str) {
+       input += ''
+       str += '';
+       var cur = input.length;
+       if (cur >= len) {
+               return input;
+       }
+       while (cur < len) {
+               input = str + input;
+               cur++;
+       }
+       return input;
+}
+function str_pad_right(input, len, str) {
+       input += ''
+       str += '';
+       var cur = input.length;
+       if (cur >= len) return input;
+       while (cur < len) {
+               input += str;
+               cur++;
+       }
+       return input;
+}
+
+function str_replace(search, replace, subject) {
+       return subject.split(search).join(replace);
+}
+
+
+
+// Javascript ltrim borrowed from http://www.webtoolkit.info/
+function ltrim(str, chars) {
+       chars = chars || "\\s";
+       return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
+}
+
+function toInt(val) {
+       val = ltrim(val, '0');
+       if (val == '') return 0;
+       return parseInt(val);
+}
+
+// ported from PEARs Payment_DTA
+var DTAUS = {
+
+       account_file_sender: null,
+       timestamp: new Date(),
+       exchanges: [],
+       errormsg: '',
+
+    sum_bank_codes:                    0,
+    sum_account_numbers:       0,
+       sum_amounts:                    0,
+
+       invalid_chars: new RegExp('[^A-Z0-9 \.,&\-\/\+\*\$%]'),
+       invalid_digits: new RegExp('[^0-9]'),
+
+       validString: function(str) {
+               if (str.match(this.invalid_chars)) return false;
+               return true;
+       },      
+
+       prepareString: function(str) {
+               str = str.toUpperCase();
+               str = str_replace('Ö', 'OE', str);
+               str = str_replace('Ä', 'AE', str);
+               str = str_replace('Ü', 'UE', str);
+               str = str_replace('ß', 'SS', str);
+               return str;
+       },
+       
+    setAccountFileSender: function(sender_name, bank_code, account_number, additional_name) {
+               // TODO additional_name wird derzeit einfach verworfen
+
+               this.account_file_sender= null;
+               this.timestamp                  = new Date();
+               this.exchanges                  = [];
+               this.errormsg                   = '';
+
+       this.sum_bank_codes             = 0;
+       this.sum_account_numbers= 0;
+               this.sum_amounts                = 0;
+
+               account_number = '' + account_number;
+        bank_code      = '' + bank_code;
+               if (!additional_name) additional_name = '';
+
+               var errors = [];
+               var res;
+
+        if (sender_name.length    <=  0)                                               errors.push('Kein Absender angegeben.');
+               if (res = sender_name.match(this.invalid_chars))                errors.push('Absender "' + sender_name + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+        if (bank_code.length      <=  0)                                               errors.push('Keine Absender-BLZ angegeben.');
+        if (bank_code.length       >  8)                                               errors.push('Absender-BLZ "' + bank_code + '" ist zu lang (max 8 Zeichen).');
+               if (res = bank_code.match(this.invalid_digits))                 errors.push('Absender-BLZ "' + bank_code + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+        if (account_number.length <=  0)                                               errors.push('Keine Absender-Kontonummer angegeben.');
+        if (account_number.length  > 10)                                               errors.push('Absender-Kontonummer "' + account_number + '" ist zu lang (max 8 Zeichen).');
+               if (res = account_number.match(this.invalid_digits))    errors.push('Absender-Kontonummer "' + account_number + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+               if (res = additional_name.match(this.invalid_chars))    errors.push('Absender-Zusatz "' + additional_name + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+               if (errors.length == 0) {
+                       this.account_file_sender = [];
+                       this.account_file_sender['name']            = sender_name.substr(0, 27);
+                       this.account_file_sender['bank_code']       = bank_code;
+                       this.account_file_sender['account_number']  = account_number;
+                       this.account_file_sender['additional_name'] = additional_name.substr(0, 27);
+                       return true;
+        }
+               this.errormsg = 'Fehler beim setzen des Senders:\n';
+               for (var i = 0; i < errors.length; i++) {
+                       this.errormsg += ' - ' + errors[i]  +'\n';
+               }
+               return false;
+    },
+       /*
+               NOTE: For debits the receiver is the one who pays[SIC!] the money!
+
+               receiver_name           string, allowed characters: "A-Z0-9 .,&-/+*$%", up to 27 characters
+               bank_code                       up to 8 digits
+               account_number          up to 10 digits
+               additional_name         string, allowed characters: "A-Z0-9 .,&-/+*$%", up to 27 characters
+               amount                          amount in cent
+               purpose                         string, allowed characters: "A-Z0-9 .,&-/+*$%", up to 27 characters
+       */
+       addExchange: function(receiver_name, bank_code, account_number, additional_name, amount, purpose) {
+               // TODO Derzeit wird nur ein Verwendungszweck unterstützt (purpose theoretisch auch ein array sein)
+               // TODO additional_name wird derzeit einfach verworfen
+
+               if (!this.account_file_sender) return;
+
+               account_number = '' + account_number;
+        bank_code      = '' + bank_code;
+               if (!additional_name) additional_name = '';
+
+               var errors = [];
+               var res;
+
+        if (receiver_name.length  <=  0)                                               errors.push('Kein Empfänger angegeben.');
+               if (res = receiver_name.match(this.invalid_chars))              errors.push('Empfänger "' + receiver_name + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+        if (bank_code.length      <=  0)                                               errors.push('Keine Empfänger-BLZ angegeben.');
+        if (bank_code.length       >  8)                                               errors.push('Empfänger-BLZ "' + bank_code + '" ist zu lang (max 8 Zeichen).');
+               if (res = bank_code.match(this.invalid_digits))                 errors.push('Empfänger-BLZ "' + bank_code + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+        if (account_number.length <=  0)                                               errors.push('Keine Empfänger-Kontonummer angegeben.');
+        if (account_number.length  > 10)                                               errors.push('Empfänger-Kontonummer "' + account_number + '" ist zu lang (max 8 Zeichen).');
+               if (res = account_number.match(this.invalid_digits))    errors.push('Empfänger-Kontonummer "' + account_number + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+               if (res = additional_name.match(this.invalid_chars))    errors.push('Empfänger-Zusatz "' + additional_name + '" enthält ungültige Zeichen. (' + res[0] + ')');
+               if (res = amount.match(this.invalid_digits))                    errors.push('Betrag "' + amount + '" enthält ungültige Zeichen. (' + res[0] + ')');
+               if (res = purpose.match(this.invalid_chars))                    errors.push('Verwendungszweck "' + purpose + '" enthält ungültige Zeichen. (' + res[0] + ')');
+
+               if (errors.length == 0) {
+                       this.sum_amounts                += toInt(amount);
+                       this.sum_bank_codes             += toInt(bank_code);
+                       this.sum_account_numbers+= toInt(account_number);
+
+                       idx = this.exchanges.length;
+                       this.exchanges[idx] = [];
+                       this.exchanges[idx]['sender_name']                              = this.account_file_sender['name'];
+                       this.exchanges[idx]['sender_bank_code']                 = this.account_file_sender['bank_code']
+                       this.exchanges[idx]['sender_account_number']    = this.account_file_sender['account_number'];
+                       this.exchanges[idx]['sender_additional_name']   = this.account_file_sender['additional_name']
+                       this.exchanges[idx]['receiver_name']            = receiver_name.substr(0, 27);
+                       this.exchanges[idx]['receiver_bank_code']       = bank_code;
+                       this.exchanges[idx]['receiver_account_number']  = account_number;
+                       this.exchanges[idx]['receiver_additional_name'] = additional_name.substr(0, 27);
+                       this.exchanges[idx]['amount']                                   = amount;
+                       this.exchanges[idx]['purpose']                                  = purpose;
+                       return true;
+               }
+               this.errormsg = 'Fehler beim Hinzufügen eines Empfängers:\n';
+               for (var i = 0; i < errors.length; i++) {
+                       this.errormsg += ' - ' + errors[i]  +'\n';
+               }
+               return false;
+       },
+
+       getFileContent: function() {
+               // TODO additional_name wird derzeit einfach verworfen
+
+               var content = '';
+
+               sum_account_numbers = toInt('0');
+               sum_bank_codes          = toInt('0');
+               sum_amounts                     = toInt('0');
+
+        // (field numbers according to ebics-zka.de specification)
+
+        /**
+         * data record A
+         */
+               content += str_pad_left('128', 4, '0');                                                                                 // A1   record length (128 Bytes)
+        content += "A";                                                                                                                                        // A2   record type
+        content += "LK";                                                                                                                               // A3   file mode (credit "G" or debit "L") and Customer File ("K") / Bank File ("B")
+        content += str_pad_left(this.account_file_sender['bank_code'], 8, '0');                        // A4   sender's bank code
+               content += str_pad_right('', 8, '0');                                                                                   // A5   only used if Bank File, otherwise NULL
+        content += str_pad_right(this.account_file_sender['name'], 27, ' ');                   // A6   sender's name
+        content += str_pad_left(this.timestamp.getDate(), 2, '0')                                              // A7   date of file creation
+                                + str_pad_left(this.timestamp.getMonth() + 1, 2, '0')
+                                + this.timestamp.getFullYear().toString().substr(2,2);
+               content += str_pad_right('', 4, ' ');                                                                                   // A8   free (bank internal)
+        content += str_pad_left(this.account_file_sender['account_number'], 10, '0');  // A9   sender's account number
+               content += str_pad_right('', 10, '0');                                                                                  // A10  sender's reference number (optional)
+               content += str_pad_right('', 15, ' ');                                                                                  // A11a free (reserve)
+               content += str_pad_right('', 8, ' ');                                                                                   // A11b execution date ("DDMMYYYY", optional)
+               content += str_pad_right('', 24, ' ');                                                                                  // A11c free (reserve)
+        content += '1';                                                                                                                                        // A12  currency (1 = Euro)
+
+               if (content.length != 128) {
+                       alert('Fehlerhafter A-Satz: ' + content.length + ' Zeichen');
+                       return content;
+               }
+
+        /**
+         * data record(s) C
+         */
+               for (var i = 0; i < this.exchanges.length; i++) {
+                       var exchange = this.exchanges[i];
+
+                       sum_account_numbers     += toInt(exchange['receiver_account_number']);
+                       sum_bank_codes          += toInt(exchange['receiver_bank_code']);
+                       sum_amounts                     += toInt(exchange['amount']);
+
+                       content += str_pad_left('187', 4, '0');                                                                         // C1   record length (187 Bytes + 29 Bytes for each additional part)
+            content += 'C';                                                                                                                            // C2   record type
+               content += str_pad_left(exchange['sender_bank_code'], 8, '0');                          // C3   first involved bank
+               content += str_pad_left(exchange['receiver_bank_code'], 8, '0');                        // C4   receiver's bank code
+               content += str_pad_left(exchange['receiver_account_number'], 10, '0');          // C5   receiver's account number
+            content += str_pad_right('', 13, '0');                                                                             // C6   internal customer number ('0' + 11 chars + '0')
+                       content += '05';                                                                                                                        // C7a  payment mode (text key) (debit '05' / credit '51')
+                       content += str_pad_right('',  3, '0');                                                                          // C7b  additional text key
+                       content += ' ';                                                                                                                         // C8   bank internal
+            content += str_pad_right('', 11, '0');                                                                             // C9   free (reserve)
+               content += str_pad_left(exchange['sender_bank_code'], 8, '0');                          // C10  sender's bank code
+               content += str_pad_left(exchange['sender_account_number'], 10, '0');            // C11  sender's account number
+               content += str_pad_left(exchange['amount'], 11, '0');                                           // C12  amount
+                       content += str_pad_right('',  3, ' ');                                                                          // C13  free (reserve)
+               content += str_pad_right(exchange['receiver_name'], 27, ' ');                           // C14a receiver's name
+                       content += str_pad_right('',  8, ' ');                                                                          // C14b delimitation
+            /* first part/128 chars full */
+               content += str_pad_right(exchange['sender_name'], 27, ' ');                                     // C15  sender's name
+               content += str_pad_right(exchange['purpose'], 27, ' ');                                         // C16  first line of purpose
+               content += '1';                                                                                                                         // C17a currency (1 = Euro)
+                       content += str_pad_right('',  2, ' ');                                                                          // C17b free (reserve)
+            content += str_pad_right('',  2, '0');                                                                             // C18  number of additional parts (00-15)
+
+            /*
+             * End of the constant part (187 chars),
+             * now up to 15 extensions with 29 chars each might follow.
+                        * NOTE: Not implemented
+             */
+
+            // no extension, pad to fill the part to 2*128 chars
+            content += str_pad_right('', 256-187, ' ');
+                       if (content.length % 128 != 0) {
+                               alert('Fehlerhafter C-Satz: ' + content.length + ' Zeichen');
+                               return content;
+                       }
+               }
+
+        /**
+         * data record E
+         */
+
+               if (this.sum_amounts         != sum_amounts
+                || this.sum_bank_codes      != sum_bank_codes
+                || this.sum_account_numbers != sum_account_numbers
+               ) {
+                       alert('Fehlerhafte Summe');
+                       return content;
+               }
+
+               content += str_pad_left('128', 4, '0');                                                                                 // E1   record length (128 bytes)
+        content += 'E';                                                                                                                                        // E2   record type
+               content += str_pad_right('',  5, ' ');                                                                                  // E3   free (reserve)
+               content += str_pad_left(this.exchanges.length, 7, '0');                                                 // E4   number of records type C
+               content += str_pad_right('', 13, '0');                                                                                  // E5   free (reserve)
+        content += str_pad_left(sum_account_numbers, 17, '0');                                                 // E6   sum of account numbers
+        content += str_pad_left(sum_bank_codes,      17, '0');                                                 // E7   sum of bank codes
+        content += str_pad_left(sum_amounts,         13, '0');                                                 // E8   sum of amounts
+               content += str_pad_right('',  51, ' ');                                                                                 // E9   delimitation
+
+
+               if (content.length % 128 != 0) {
+                       alert('Fehlerhafter E-Satz');
+                       return content;
+               }
+
+        return content;
+    }
+}
+
+
+/**
+* Function : dump()
+* Arguments: The data - array,hash(associative array),object
+*    The level - OPTIONAL
+* Returns  : The textual representation of the array.
+* This function was inspired by the print_r function of PHP.
+* This will accept some data as the argument and return a
+* text that will be a more readable version of the
+* array/hash/object that is given.
+*/
+function dump(arr,level) {
+var dumped_text = "";
+if(!level) level = 0;
+
+//The padding given at the beginning of the line.
+var level_padding = "";
+for(var j=0;j<level+1;j++) level_padding += "    ";
+
+if(typeof(arr) == 'object') { //Array/Hashes/Objects
+ for(var item in arr) {
+  var value = arr[item];
+  if(typeof(value) == 'object') { //If it is an array,
+   dumped_text += level_padding + "'" + item + "' ...\n";
+   dumped_text += dump(value,level+1);
+  } else {
+   dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
+  }
+ }
+} else { //Stings/Chars/Numbers etc.
+ dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
+}
+return dumped_text;
+} 
+
diff --git a/js/jquery-1.4.2.min.js b/js/jquery-1.4.2.min.js
new file mode 100644 (file)
index 0000000..7c24308
--- /dev/null
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);