From a676d712611c6263a8507e5bcf105e3df42def33 Mon Sep 17 00:00:00 2001 From: Ben Adida Date: Sat, 21 Aug 2010 22:25:57 -0700 Subject: [PATCH] re-added helios django app as part of the same git project --- helios/README.txt | 6 + helios/__init__.py | 20 + helios/counters.py | 100 ++ helios/crypto/__init__.py | 0 helios/crypto/algs.py | 729 +++++++++ helios/crypto/electionalgs.py | 731 +++++++++ helios/crypto/number.py | 201 +++ helios/crypto/numtheory.py | 1434 +++++++++++++++++ helios/crypto/randpool.py | 422 +++++ helios/crypto/utils.py | 25 + helios/datetimewidget.py | 101 ++ helios/election_urls.py | 78 + helios/fields.py | 30 + helios/forms.py | 28 + helios/management/__init__.py | 5 + helios/management/commands/__init__.py | 3 + .../commands/helios_trustee_decrypt.py | 37 + .../management/commands/load_voter_files.py | 90 ++ .../management/commands/verify_cast_votes.py | 38 + .../ui-bg_diagonals-thick_18_b81900_40x40.png | Bin 0 -> 260 bytes .../ui-bg_diagonals-thick_20_666666_40x40.png | Bin 0 -> 251 bytes .../images/ui-bg_flat_10_000000_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_100_f6f6f6_1x400.png | Bin 0 -> 104 bytes .../images/ui-bg_glass_100_fdf5ce_1x400.png | Bin 0 -> 125 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../ui-bg_gloss-wave_35_f6a828_500x100.png | Bin 0 -> 3762 bytes .../ui-bg_highlight-soft_100_eeeeee_1x100.png | Bin 0 -> 90 bytes .../ui-bg_highlight-soft_75_ffe45c_1x100.png | Bin 0 -> 129 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_228ef1_256x240.png | Bin 0 -> 5355 bytes .../images/ui-icons_ef8c08_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffd27a_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 4369 bytes .../ui-lightness/jquery-ui-1.8.1.custom.css | 486 ++++++ helios/media/helios/bigint.class | Bin 0 -> 797 bytes helios/media/helios/bigint.java | 25 + helios/media/helios/bigint.js | 179 ++ helios/media/helios/class.js | 65 + helios/media/helios/elgamal.js | 538 +++++++ helios/media/helios/helios.js | 609 +++++++ helios/media/helios/jquery-1.2.2.min.js | 31 + helios/media/helios/jquery-jtemplates.js | 3 + helios/media/helios/jquery.json.min.js | 35 + helios/media/helios/prettydate.js | 36 + helios/media/helios/random.js | 48 + helios/media/helios/sha1.js | 202 +++ helios/media/helios/sha2.js | 144 ++ helios/media/js/jqsplitdatetime.js | 3 + helios/media/js/jquery-1.4.2.min.js | 154 ++ helios/media/js/jquery-ui-1.8.1.custom.min.js | 756 +++++++++ helios/media/loading.gif | Bin 0 -> 3208 bytes helios/media/main.css | 68 + helios/media/static_templates/question.html | 145 ++ helios/models.py | 706 ++++++++ helios/security.py | 145 ++ helios/signals.py | 16 + helios/tasks.py | 124 ++ helios/templates/base.html | 44 + helios/templates/cast_done.html | 26 + helios/templates/cryptobase.html | 15 + .../templates/election_audited_ballots.html | 31 + helios/templates/election_bboard.html | 47 + helios/templates/election_build.html | 68 + helios/templates/election_cast_confirm.html | 119 ++ helios/templates/election_compute_tally.html | 26 + helios/templates/election_edit.html | 22 + helios/templates/election_freeze.html | 44 + helios/templates/election_keygenerator.html | 146 ++ helios/templates/election_new.html | 22 + helios/templates/election_new_2.html | 92 ++ helios/templates/election_questions.html | 132 ++ helios/templates/election_register.html | 27 + helios/templates/election_tallied.html | 14 + helios/templates/election_view.html | 273 ++++ helios/templates/elections_administered.html | 11 + helios/templates/email/result_body.txt | 12 + helios/templates/email/result_subject.txt | 1 + helios/templates/email/vote_body.txt | 24 + helios/templates/email/vote_subject.txt | 1 + helios/templates/list_trustees.html | 59 + helios/templates/new_trustee.html | 13 + helios/templates/notification/result.txt | 1 + helios/templates/socialbuttons.html | 5 + helios/templates/stats.html | 13 + helios/templates/trustee_check_sk.html | 78 + .../templates/trustee_decrypt_and_prove.html | 209 +++ helios/templates/trustee_home.html | 32 + helios/templates/voters_email.html | 48 + helios/templates/voters_list.html | 112 ++ helios/templates/voters_manage.html | 39 + helios/templates/voters_search.html | 17 + helios/templates/voters_upload.html | 30 + helios/test.py | 19 + helios/urls.py | 32 + helios/utils.py | 160 ++ helios/view_utils.py | 87 + helios/views.py | 995 ++++++++++++ helios/widgets.py | 191 +++ 98 files changed, 11933 insertions(+) create mode 100644 helios/README.txt create mode 100644 helios/__init__.py create mode 100644 helios/counters.py create mode 100644 helios/crypto/__init__.py create mode 100644 helios/crypto/algs.py create mode 100644 helios/crypto/electionalgs.py create mode 100644 helios/crypto/number.py create mode 100644 helios/crypto/numtheory.py create mode 100644 helios/crypto/randpool.py create mode 100644 helios/crypto/utils.py create mode 100644 helios/datetimewidget.py create mode 100644 helios/election_urls.py create mode 100644 helios/fields.py create mode 100644 helios/forms.py create mode 100644 helios/management/__init__.py create mode 100644 helios/management/commands/__init__.py create mode 100644 helios/management/commands/helios_trustee_decrypt.py create mode 100644 helios/management/commands/load_voter_files.py create mode 100644 helios/management/commands/verify_cast_votes.py create mode 100644 helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png create mode 100644 helios/media/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png create mode 100644 helios/media/css/ui-lightness/images/ui-icons_222222_256x240.png create mode 100644 helios/media/css/ui-lightness/images/ui-icons_228ef1_256x240.png create mode 100644 helios/media/css/ui-lightness/images/ui-icons_ef8c08_256x240.png create mode 100644 helios/media/css/ui-lightness/images/ui-icons_ffd27a_256x240.png create mode 100644 helios/media/css/ui-lightness/images/ui-icons_ffffff_256x240.png create mode 100644 helios/media/css/ui-lightness/jquery-ui-1.8.1.custom.css create mode 100644 helios/media/helios/bigint.class create mode 100644 helios/media/helios/bigint.java create mode 100644 helios/media/helios/bigint.js create mode 100644 helios/media/helios/class.js create mode 100644 helios/media/helios/elgamal.js create mode 100644 helios/media/helios/helios.js create mode 100644 helios/media/helios/jquery-1.2.2.min.js create mode 100644 helios/media/helios/jquery-jtemplates.js create mode 100644 helios/media/helios/jquery.json.min.js create mode 100644 helios/media/helios/prettydate.js create mode 100644 helios/media/helios/random.js create mode 100644 helios/media/helios/sha1.js create mode 100644 helios/media/helios/sha2.js create mode 100644 helios/media/js/jqsplitdatetime.js create mode 100644 helios/media/js/jquery-1.4.2.min.js create mode 100644 helios/media/js/jquery-ui-1.8.1.custom.min.js create mode 100644 helios/media/loading.gif create mode 100644 helios/media/main.css create mode 100644 helios/media/static_templates/question.html create mode 100644 helios/models.py create mode 100644 helios/security.py create mode 100644 helios/signals.py create mode 100644 helios/tasks.py create mode 100644 helios/templates/base.html create mode 100644 helios/templates/cast_done.html create mode 100644 helios/templates/cryptobase.html create mode 100644 helios/templates/election_audited_ballots.html create mode 100644 helios/templates/election_bboard.html create mode 100644 helios/templates/election_build.html create mode 100644 helios/templates/election_cast_confirm.html create mode 100644 helios/templates/election_compute_tally.html create mode 100644 helios/templates/election_edit.html create mode 100644 helios/templates/election_freeze.html create mode 100644 helios/templates/election_keygenerator.html create mode 100644 helios/templates/election_new.html create mode 100644 helios/templates/election_new_2.html create mode 100644 helios/templates/election_questions.html create mode 100644 helios/templates/election_register.html create mode 100644 helios/templates/election_tallied.html create mode 100644 helios/templates/election_view.html create mode 100644 helios/templates/elections_administered.html create mode 100644 helios/templates/email/result_body.txt create mode 100644 helios/templates/email/result_subject.txt create mode 100644 helios/templates/email/vote_body.txt create mode 100644 helios/templates/email/vote_subject.txt create mode 100644 helios/templates/list_trustees.html create mode 100644 helios/templates/new_trustee.html create mode 100644 helios/templates/notification/result.txt create mode 100644 helios/templates/socialbuttons.html create mode 100644 helios/templates/stats.html create mode 100644 helios/templates/trustee_check_sk.html create mode 100644 helios/templates/trustee_decrypt_and_prove.html create mode 100644 helios/templates/trustee_home.html create mode 100644 helios/templates/voters_email.html create mode 100644 helios/templates/voters_list.html create mode 100644 helios/templates/voters_manage.html create mode 100644 helios/templates/voters_search.html create mode 100644 helios/templates/voters_upload.html create mode 100644 helios/test.py create mode 100644 helios/urls.py create mode 100644 helios/utils.py create mode 100644 helios/view_utils.py create mode 100644 helios/views.py create mode 100644 helios/widgets.py diff --git a/helios/README.txt b/helios/README.txt new file mode 100644 index 000000000..20aa3985e --- /dev/null +++ b/helios/README.txt @@ -0,0 +1,6 @@ +The Helios Django App +===================== + +LICENSE: this code is released under the GPL v3 or later. + +NOTE: this used to be a separate git submodule, but now it's not. diff --git a/helios/__init__.py b/helios/__init__.py new file mode 100644 index 000000000..bf3c8ddc5 --- /dev/null +++ b/helios/__init__.py @@ -0,0 +1,20 @@ + +from django.conf import settings + +TEMPLATE_BASE = settings.HELIOS_TEMPLATE_BASE or "helios/templates/base.html" + +# a setting to ensure that only admins can create an election +ADMIN_ONLY = settings.HELIOS_ADMIN_ONLY + +# allow upload of voters via CSV? +VOTERS_UPLOAD = settings.HELIOS_VOTERS_UPLOAD + +# allow emailing of voters? +VOTERS_EMAIL = settings.HELIOS_VOTERS_EMAIL + +from django.core.urlresolvers import reverse + +# get the short path for the URL +def get_election_url(election): + from views import election_shortcut + return settings.URL_HOST + reverse(election_shortcut, args=[election.short_name]) diff --git a/helios/counters.py b/helios/counters.py new file mode 100644 index 000000000..3f6c1c04a --- /dev/null +++ b/helios/counters.py @@ -0,0 +1,100 @@ +# Copyright 2008 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from google.appengine.api import memcache +from google.appengine.ext import db +from google.appengine.api.datastore import _CurrentTransactionKey +import random + +class GeneralCounterShardConfig(db.Model): + """Tracks the number of shards for each named counter.""" + name = db.StringProperty(required=True) + num_shards = db.IntegerProperty(required=True, default=20) + + +class GeneralCounterShard(db.Model): + """Shards for each named counter""" + name = db.StringProperty(required=True) + count = db.IntegerProperty(required=True, default=0) + +def get_config(name): + if _CurrentTransactionKey(): + config = GeneralCounterShardConfig.get_by_key_name(name) + if not config: + config = GeneralCounterShardConfig(name=name) + config.put() + return config + else: + return GeneralCounterShardConfig.get_or_insert(name, name=name) + +def get_count(name): + """Retrieve the value for a given sharded counter. + + Parameters: + name - The name of the counter + """ + total = memcache.get(name) + if total is None: + total = 0 + for counter in GeneralCounterShard.all().filter('name = ', name): + total += counter.count + memcache.add(name, str(total), 60) + return total + + +def increment(name): + """Increment the value for a given sharded counter. + + Parameters: + name - The name of the counter + """ + config = get_config(name) + def txn(): + index = random.randint(0, config.num_shards - 1) + shard_name = name + str(index) + counter = GeneralCounterShard.get_by_key_name(shard_name) + if counter is None: + counter = GeneralCounterShard(key_name=shard_name, name=name) + counter.count += 1 + counter.put() + + if _CurrentTransactionKey(): + txn() + else: + db.run_in_transaction(txn) + + memcache.incr(name) + + +def increase_shards(name, num): + """Increase the number of shards for a given sharded counter. + Will never decrease the number of shards. + + Parameters: + name - The name of the counter + num - How many shards to use + + """ + config = get_config(name) + def txn(): + if config.num_shards < num: + config.num_shards = num + config.put() + + if _CurrentTransactionKey(): + txn() + else: + db.run_in_transaction(txn) + diff --git a/helios/crypto/__init__.py b/helios/crypto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/helios/crypto/algs.py b/helios/crypto/algs.py new file mode 100644 index 000000000..78e83c2a8 --- /dev/null +++ b/helios/crypto/algs.py @@ -0,0 +1,729 @@ +""" +Crypto Algorithms for the Helios Voting System + +FIXME: improve random number generation. + +Ben Adida +ben@adida.net +""" + +import math, sha, logging +import randpool, number + +import numtheory + +# some utilities +class Utils: + RAND = randpool.RandomPool() + + @classmethod + def random_seed(cls, data): + cls.RAND.add_event(data) + + @classmethod + def random_mpz(cls, n_bits): + low = 2**(n_bits-1) + high = low * 2 + + # increment and find a prime + # return randrange(low, high) + + return number.getRandomNumber(n_bits, cls.RAND.get_bytes) + + @classmethod + def random_mpz_lt(cls, max): + # return randrange(0, max) + n_bits = int(math.floor(math.log(max, 2))) + return (number.getRandomNumber(n_bits, cls.RAND.get_bytes) % max) + + @classmethod + def random_prime(cls, n_bits): + return number.getPrime(n_bits, cls.RAND.get_bytes) + + @classmethod + def is_prime(cls, mpz): + #return numtheory.miller_rabin(mpz) + return number.isPrime(mpz) + + @classmethod + def xgcd(cls, a, b): + """ + Euclid's Extended GCD algorithm + """ + mod = a%b + + if mod == 0: + return 0,1 + else: + x,y = cls.xgcd(b, mod) + return y, x-(y*(a/b)) + + @classmethod + def inverse(cls, mpz, mod): + # return cls.xgcd(mpz,mod)[0] + return number.inverse(mpz, mod) + + @classmethod + def random_safe_prime(cls, n_bits): + p = None + q = None + + while True: + p = cls.random_prime(n_bits) + q = (p-1)/2 + if cls.is_prime(q): + return p + + @classmethod + def random_special_prime(cls, q_n_bits, p_n_bits): + p = None + q = None + + z_n_bits = p_n_bits - q_n_bits + + q = cls.random_prime(q_n_bits) + + while True: + z = cls.random_mpz(z_n_bits) + p = q*z + 1 + if cls.is_prime(p): + return p, q, z + + +class ElGamal: + def __init__(self): + self.p = None + self.q = None + self.g = None + + @classmethod + def generate(cls, n_bits): + """ + generate an El-Gamal environment. Returns an instance + of ElGamal(), with prime p, group size q, and generator g + """ + + EG = ElGamal() + + # find a prime p such that (p-1)/2 is prime q + EG.p = Utils.random_safe_prime(n_bits) + + # q is the order of the group + # FIXME: not always p-1/2 + EG.q = (EG.p-1)/2 + + # find g that generates the q-order subgroup + while True: + EG.g = Utils.random_mpz_lt(EG.p) + if pow(EG.g, EG.q, EG.p) == 1: + break + + return EG + + def generate_keypair(self): + """ + generates a keypair in the setting + """ + + keypair = EGKeyPair() + keypair.generate(self.p, self.q, self.g) + + return keypair + + def toJSONDict(self): + return {'p': str(self.p), 'q': str(self.q), 'g': str(self.g)} + + @classmethod + def fromJSONDict(cls, d): + eg = cls() + eg.p = int(d['p']) + eg.q = int(d['q']) + eg.g = int(d['g']) + return eg + +class EGKeyPair: + def __init__(self): + self.pk = EGPublicKey() + self.sk = EGSecretKey() + + def generate(self, p, q, g): + """ + Generate an ElGamal keypair + """ + self.pk.g = g + self.pk.p = p + self.pk.q = q + + self.sk.x = Utils.random_mpz_lt(p) + self.pk.y = pow(g, self.sk.x, p) + + self.sk.pk = self.pk + +class EGPublicKey: + def __init__(self): + self.y = None + self.p = None + self.g = None + self.q = None + + def encrypt_with_r(self, plaintext, r, encode_message= False): + """ + expecting plaintext.m to be a big integer + """ + ciphertext = EGCiphertext() + ciphertext.pk = self + + # make sure m is in the right subgroup + if encode_message: + y = plaintext.m + 1 + if pow(y, self.q, self.p) == 1: + m = y + else: + m = -y % self.p + else: + m = plaintext.m + + ciphertext.alpha = pow(self.g, r, self.p) + ciphertext.beta = (m * pow(self.y, r, self.p)) % self.p + + return ciphertext + + def encrypt_return_r(self, plaintext): + """ + Encrypt a plaintext and return the randomness just generated and used. + """ + r = Utils.random_mpz_lt(self.q) + ciphertext = self.encrypt_with_r(plaintext, r) + + return [ciphertext, r] + + def encrypt(self, plaintext): + """ + Encrypt a plaintext, obscure the randomness. + """ + return self.encrypt_return_r(plaintext)[0] + + def to_dict(self): + """ + Serialize to dictionary. + """ + return {'y' : str(self.y), 'p' : str(self.p), 'g' : str(self.g) , 'q' : str(self.q)} + + toJSONDict = to_dict + + # quick hack FIXME + def toJSON(self): + import utils + return utils.to_json(self.toJSONDict()) + + def __mul__(self,other): + if other == 0 or other == 1: + return self + + # check p and q + if self.p != other.p or self.q != other.q or self.g != other.g: + raise Exception("incompatible public keys") + + result = EGPublicKey() + result.p = self.p + result.q = self.q + result.g = self.g + result.y = (self.y * other.y) % result.p + return result + + def verify_sk_proof(self, dlog_proof, challenge_generator = None): + """ + verify the proof of knowledge of the secret key + g^response = commitment * y^challenge + """ + left_side = pow(self.g, dlog_proof.response, self.p) + right_side = (dlog_proof.commitment * pow(self.y, dlog_proof.challenge, self.p)) % self.p + + expected_challenge = challenge_generator(dlog_proof.commitment) % self.q + + return ((left_side == right_side) and (dlog_proof.challenge == expected_challenge)) + + @classmethod + def from_dict(cls, d): + """ + Deserialize from dictionary. + """ + pk = cls() + pk.y = int(d['y']) + pk.p = int(d['p']) + pk.g = int(d['g']) + pk.q = int(d['q']) + return pk + + fromJSONDict = from_dict + +class EGSecretKey: + def __init__(self): + self.x = None + self.pk = None + + def decryption_factor(self, ciphertext): + """ + provide the decryption factor, not yet inverted because of needed proof + """ + return pow(ciphertext.alpha, self.x, self.pk.p) + + def decryption_factor_and_proof(self, ciphertext, challenge_generator=None): + """ + challenge generator is almost certainly + EG_fiatshamir_challenge_generator + """ + if not challenge_generator: + challenge_generator = EG_fiatshamir_challenge_generator + + dec_factor = self.decryption_factor(ciphertext) + + proof = EGZKProof.generate(self.pk.g, ciphertext.alpha, self.x, self.pk.p, self.pk.q, challenge_generator) + + return dec_factor, proof + + def decrypt(self, ciphertext, dec_factor = None, decode_m=False): + """ + Decrypt a ciphertext. Optional parameter decides whether to encode the message into the proper subgroup. + """ + if not dec_factor: + dec_factor = self.decryption_factor(ciphertext) + + m = (Utils.inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p + + if decode_m: + # get m back from the q-order subgroup + if m < self.pk.q: + y = m + else: + y = -m % self.pk.p + + return EGPlaintext(y-1, self.pk) + else: + return EGPlaintext(m, self.pk) + + def prove_decryption(self, ciphertext): + """ + given g, y, alpha, beta/(encoded m), prove equality of discrete log + with Chaum Pedersen, and that discrete log is x, the secret key. + + Prover sends a=g^w, b=alpha^w for random w + Challenge c = sha1(a,b) with and b in decimal form + Prover sends t = w + xc + + Verifier will check that g^t = a * y^c + and alpha^t = b * beta/m ^ c + """ + + m = (Utils.inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p + beta_over_m = (ciphertext.beta * Utils.inverse(m, self.pk.p)) % self.pk.p + + # pick a random w + w = Utils.random_mpz_lt(self.pk.q) + a = pow(self.pk.g, w, self.pk.p) + b = pow(ciphertext.alpha, w, self.pk.p) + + c = int(sha.new(str(a) + "," + str(b)).hexdigest(),16) + + t = (w + self.x * c) % self.pk.q + + return m, { + 'commitment' : {'A' : str(a), 'B': str(b)}, + 'challenge' : str(c), + 'response' : str(t) + } + + def to_dict(self): + return {'x' : str(self.x), 'public_key' : self.pk.to_dict()} + + toJSONDict = to_dict + + def prove_sk(self, challenge_generator): + """ + Generate a PoK of the secret key + Prover generates w, a random integer modulo q, and computes commitment = g^w mod p. + Verifier provides challenge modulo q. + Prover computes response = w + x*challenge mod q, where x is the secret key. + """ + w = Utils.random_mpz_lt(self.pk.q) + commitment = pow(self.pk.g, w, self.pk.p) + challenge = challenge_generator(commitment) % self.pk.q + response = (w + (self.x * challenge)) % self.pk.q + + return DLogProof(commitment, challenge, response) + + + @classmethod + def from_dict(cls, d): + if not d: + return None + + sk = cls() + sk.x = int(d['x']) + if d.has_key('public_key'): + sk.pk = EGPublicKey.from_dict(d['public_key']) + else: + sk.pk = None + return sk + + fromJSONDict = from_dict + +class EGPlaintext: + def __init__(self, m = None, pk = None): + self.m = m + self.pk = pk + + def to_dict(self): + return {'m' : self.m} + + @classmethod + def from_dict(cls, d): + r = cls() + r.m = d['m'] + return r + + +class EGCiphertext: + def __init__(self, alpha=None, beta=None, pk=None): + self.pk = pk + self.alpha = alpha + self.beta = beta + + def __mul__(self,other): + """ + Homomorphic Multiplication of ciphertexts. + """ + if type(other) == int and (other == 0 or other == 1): + return self + + if self.pk != other.pk: + logging.info(self.pk) + logging.info(other.pk) + raise Exception('different PKs!') + + new = EGCiphertext() + + new.pk = self.pk + new.alpha = (self.alpha * other.alpha) % self.pk.p + new.beta = (self.beta * other.beta) % self.pk.p + + return new + + def reenc_with_r(self, r): + """ + We would do this homomorphically, except + that's no good when we do plaintext encoding of 1. + """ + new_c = EGCiphertext() + new_c.alpha = (self.alpha * pow(self.pk.g, r, self.pk.p)) % self.pk.p + new_c.beta = (self.beta * pow(self.pk.y, r, self.pk.p)) % self.pk.p + new_c.pk = self.pk + + return new_c + + def reenc_return_r(self): + """ + Reencryption with fresh randomness, which is returned. + """ + r = Utils.random_mpz_lt(self.pk.q) + new_c = self.reenc_with_r(r) + return [new_c, r] + + def reenc(self): + """ + Reencryption with fresh randomness, which is kept obscured (unlikely to be useful.) + """ + return self.reenc_return_r()[0] + + def __eq__(self, other): + """ + Check for ciphertext equality. + """ + if other == None: + return False + + return (self.alpha == other.alpha and self.beta == other.beta) + + def generate_encryption_proof(self, plaintext, randomness, challenge_generator): + """ + Generate the disjunctive encryption proof of encryption + """ + # random W + w = Utils.random_mpz_lt(self.pk.q) + + # build the proof + proof = EGZKProof() + + # compute A=g^w, B=y^w + proof.commitment['A'] = pow(self.pk.g, w, self.pk.p) + proof.commitment['B'] = pow(self.pk.y, w, self.pk.p) + + # generate challenge + proof.challenge = challenge_generator(proof.commitment); + + # Compute response = w + randomness * challenge + proof.response = (w + (randomness * proof.challenge)) % self.pk.q; + + return proof; + + def simulate_encryption_proof(self, plaintext, challenge=None): + # generate a random challenge if not provided + if not challenge: + challenge = Utils.random_mpz_lt(self.pk.q) + + proof = EGZKProof() + proof.challenge = challenge + + # compute beta/plaintext, the completion of the DH tuple + beta_over_plaintext = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p + + # random response, does not even need to depend on the challenge + proof.response = Utils.random_mpz_lt(self.pk.q); + + # now we compute A and B + proof.commitment['A'] = (Utils.inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.g, proof.response, self.pk.p)) % self.pk.p + proof.commitment['B'] = (Utils.inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.y, proof.response, self.pk.p)) % self.pk.p + + return proof + + def generate_disjunctive_encryption_proof(self, plaintexts, real_index, randomness, challenge_generator): + # note how the interface is as such so that the result does not reveal which is the real proof. + + proofs = [None for p in plaintexts] + + # go through all plaintexts and simulate the ones that must be simulated. + for p_num in range(len(plaintexts)): + if p_num != real_index: + proofs[p_num] = self.simulate_encryption_proof(plaintexts[p_num]) + + # the function that generates the challenge + def real_challenge_generator(commitment): + # set up the partial real proof so we're ready to get the hash + proofs[real_index] = EGZKProof() + proofs[real_index].commitment = commitment + + # get the commitments in a list and generate the whole disjunctive challenge + commitments = [p.commitment for p in proofs] + disjunctive_challenge = challenge_generator(commitments); + + # now we must subtract all of the other challenges from this challenge. + real_challenge = disjunctive_challenge + for p_num in range(len(proofs)): + if p_num != real_index: + real_challenge = real_challenge - proofs[p_num].challenge + + # make sure we mod q, the exponent modulus + return real_challenge % self.pk.q + + # do the real proof + real_proof = self.generate_encryption_proof(plaintexts[real_index], randomness, real_challenge_generator) + + # set the real proof + proofs[real_index] = real_proof + + return EGZKDisjunctiveProof(proofs) + + def verify_encryption_proof(self, plaintext, proof): + """ + Checks for the DDH tuple g, y, alpha, beta/plaintext. + (PoK of randomness r.) + + Proof contains commitment = {A, B}, challenge, response + """ + + # check that g^response = A * alpha^challenge + first_check = (pow(self.pk.g, proof.response, self.pk.p) == ((pow(self.alpha, proof.challenge, self.pk.p) * proof.commitment['A']) % self.pk.p)) + + # check that y^response = B * (beta/m)^challenge + beta_over_m = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p + second_check = (pow(self.pk.y, proof.response, self.pk.p) == ((pow(beta_over_m, proof.challenge, self.pk.p) * proof.commitment['B']) % self.pk.p)) + + # print "1,2: %s %s " % (first_check, second_check) + return (first_check and second_check) + + def verify_disjunctive_encryption_proof(self, plaintexts, proof, challenge_generator): + """ + plaintexts and proofs are all lists of equal length, with matching. + + overall_challenge is what all of the challenges combined should yield. + """ + for i in range(len(plaintexts)): + # if a proof fails, stop right there + if not self.verify_encryption_proof(plaintexts[i], proof.proofs[i]): + print "bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i]) + return False + + # logging.info("made it past the two encryption proofs") + + # check the overall challenge + return (challenge_generator([p.commitment for p in proof.proofs]) == (sum([p.challenge for p in proof.proofs]) % self.pk.q)) + + def verify_decryption_proof(self, plaintext, proof): + """ + Checks for the DDH tuple g, alpha, y, beta/plaintext + (PoK of secret key x.) + """ + return False + + def verify_decryption_factor(self, dec_factor, dec_proof, public_key): + """ + when a ciphertext is decrypted by a dec factor, the proof needs to be checked + """ + pass + + def decrypt(self, decryption_factors, public_key): + """ + decrypt a ciphertext given a list of decryption factors (from multiple trustees) + For now, no support for threshold + """ + running_decryption = self.beta + for dec_factor in decryption_factors: + running_decryption = (running_decryption * Utils.inverse(dec_factor, public_key.p)) % public_key.p + + return running_decryption + + def to_dict(self): + return {'alpha': str(self.alpha), 'beta': str(self.beta)} + + toJSONDict= to_dict + + def to_string(self): + return "%s,%s" % (self.alpha, self.beta) + + @classmethod + def from_dict(cls, d, pk = None): + result = cls() + result.alpha = int(d['alpha']) + result.beta = int(d['beta']) + result.pk = pk + return result + + fromJSONDict = from_dict + + @classmethod + def from_string(cls, str): + """ + expects alpha,beta + """ + split = str.split(",") + return cls.from_dict({'alpha' : split[0], 'beta' : split[1]}) + +class EGZKProof(object): + def __init__(self): + self.commitment = {'A':None, 'B':None} + self.challenge = None + self.response = None + + @classmethod + def generate(cls, little_g, little_h, x, p, q, challenge_generator): + """ + generate a DDH tuple proof, where challenge generator is + almost certainly EG_fiatshamir_challenge_generator + """ + + # generate random w + w = Utils.random_mpz_lt(q) + + # create proof instance + proof = cls() + + # compute A = little_g^w, B=little_h^w + proof.commitment['A'] = pow(little_g, w, p) + proof.commitment['B'] = pow(little_h, w, p) + + # get challenge + proof.challenge = challenge_generator(proof.commitment) + + # compute response + proof.response = (w + (x * proof.challenge)) % q + + # return proof + return proof + + @classmethod + def from_dict(cls, d): + p = cls() + p.commitment = {'A': int(d['commitment']['A']), 'B': int(d['commitment']['B'])} + p.challenge = int(d['challenge']) + p.response = int(d['response']) + return p + + fromJSONDict = from_dict + + def to_dict(self): + return { + 'commitment' : {'A' : str(self.commitment['A']), 'B' : str(self.commitment['B'])}, + 'challenge': str(self.challenge), + 'response': str(self.response) + } + + def verify(self, little_g, little_h, big_g, big_h, p, q, challenge_generator=None): + """ + Verify a DH tuple proof + """ + # check that little_g^response = A * big_g^challenge + first_check = (pow(little_g, self.response, p) == ((pow(big_g, self.challenge, p) * self.commitment['A']) % p)) + + # check that little_h^response = B * big_h^challenge + second_check = (pow(little_h, self.response, p) == ((pow(big_h, self.challenge, p) * self.commitment['B']) % p)) + + # check the challenge? + third_check = True + + if challenge_generator: + third_check = (self.challenge == challenge_generator(self.commitment)) + + return (first_check and second_check and third_check) + + toJSONDict = to_dict + +class EGZKDisjunctiveProof: + def __init__(self, proofs = None): + self.proofs = proofs + + @classmethod + def from_dict(cls, d): + dp = cls() + dp.proofs = [EGZKProof.from_dict(p) for p in d] + return dp + + def to_dict(self): + return [p.to_dict() for p in self.proofs] + + toJSONDict = to_dict + +class DLogProof(object): + def __init__(self, commitment, challenge, response): + self.commitment = commitment + self.challenge = challenge + self.response = response + + def to_dict(self): + return {'challenge': str(self.challenge), 'commitment': str(self.commitment), 'response' : str(self.response)} + + toJSONDict = to_dict + + @classmethod + def from_dict(cls, d): + dlp = cls(int(d['commitment']), int(d['challenge']), int(d['response'])) + return dlp + + fromJSONDict = from_dict + +def EG_disjunctive_challenge_generator(commitments): + array_to_hash = [] + for commitment in commitments: + array_to_hash.append(str(commitment['A'])) + array_to_hash.append(str(commitment['B'])) + + string_to_hash = ",".join(array_to_hash) + return int(sha.new(string_to_hash).hexdigest(),16) + +# a challenge generator for Fiat-Shamir with A,B commitment +def EG_fiatshamir_challenge_generator(commitment): + return EG_disjunctive_challenge_generator([commitment]) + +def DLog_challenge_generator(commitment): + string_to_hash = str(commitment) + return int(sha.new(string_to_hash).hexdigest(),16) + diff --git a/helios/crypto/electionalgs.py b/helios/crypto/electionalgs.py new file mode 100644 index 000000000..4b5bb490a --- /dev/null +++ b/helios/crypto/electionalgs.py @@ -0,0 +1,731 @@ +""" +Election-specific algorithms for Helios + +Ben Adida +2008-08-30 +""" + +import algs +import logging +import utils +import uuid +import datetime + +class HeliosObject(object): + """ + A base class to ease serialization and de-serialization + crypto objects are kept as full-blown crypto objects, serialized to jsonobjects on the way out + and deserialized from jsonobjects on the way in + """ + FIELDS = [] + JSON_FIELDS = None + + def __init__(self, **kwargs): + self.set_from_args(**kwargs) + + # generate uuid if need be + if 'uuid' in self.FIELDS and (not hasattr(self, 'uuid') or self.uuid == None): + self.uuid = str(uuid.uuid4()) + + def set_from_args(self, **kwargs): + for f in self.FIELDS: + if kwargs.has_key(f): + new_val = self.process_value_in(f, kwargs[f]) + setattr(self, f, new_val) + else: + setattr(self, f, None) + + def set_from_other_object(self, o): + for f in self.FIELDS: + if hasattr(o, f): + setattr(self, f, self.process_value_in(f, getattr(o,f))) + else: + setattr(self, f, None) + + def toJSON(self): + return utils.to_json(self.toJSONDict()) + + def toJSONDict(self, alternate_fields=None): + val = {} + for f in (alternate_fields or self.JSON_FIELDS or self.FIELDS): + val[f] = self.process_value_out(f, getattr(self, f)) + return val + + @classmethod + def fromJSONDict(cls, d): + # go through the keys and fix them + new_d = {} + for k in d.keys(): + new_d[str(k)] = d[k] + + return cls(**new_d) + + @classmethod + def fromOtherObject(cls, o): + obj = cls() + obj.set_from_other_object(o) + return obj + + def toOtherObject(self, o): + for f in self.FIELDS: + # FIXME: why isn't this working? + if hasattr(o, f): + # BIG HAMMER + try: + setattr(o, f, self.process_value_out(f, getattr(self,f))) + except: + pass + + @property + def hash(self): + s = utils.to_json(self.toJSONDict()) + return utils.hash_b64(s) + + def process_value_in(self, field_name, field_value): + """ + process some fields on the way into the object + """ + if field_value == None: + return None + + val = self._process_value_in(field_name, field_value) + if val != None: + return val + else: + return field_value + + def _process_value_in(self, field_name, field_value): + return None + + def process_value_out(self, field_name, field_value): + """ + process some fields on the way out of the object + """ + if field_value == None: + return None + + val = self._process_value_out(field_name, field_value) + if val != None: + return val + else: + return field_value + + def _process_value_out(self, field_name, field_value): + return None + + def __eq__(self, other): + if not hasattr(self, 'uuid'): + return super(HeliosObject,self) == other + + return other != None and self.uuid == other.uuid + +class EncryptedAnswer(HeliosObject): + """ + An encrypted answer to a single election question + """ + + FIELDS = ['choices', 'individual_proofs', 'overall_proof', 'randomness', 'answer'] + + # FIXME: remove this constructor and use only named-var constructor from HeliosObject + def __init__(self, choices=None, individual_proofs=None, overall_proof=None, randomness=None, answer=None): + self.choices = choices + self.individual_proofs = individual_proofs + self.overall_proof = overall_proof + self.randomness = randomness + self.answer = answer + + @classmethod + def generate_plaintexts(cls, pk, min=0, max=1): + plaintexts = [] + running_product = 1 + + # run the product up to the min + for i in range(max+1): + # if we're in the range, add it to the array + if i >= min: + plaintexts.append(algs.EGPlaintext(running_product, pk)) + + # next value in running product + running_product = (running_product * pk.g) % pk.p + + return plaintexts + + def verify_plaintexts_and_randomness(self, pk): + """ + this applies only if the explicit answers and randomness factors are given + we do not verify the proofs here, that is the verify() method + """ + if not hasattr(self, 'answer'): + return False + + for choice_num in range(len(self.choices)): + choice = self.choices[choice_num] + choice.pk = pk + + # redo the encryption + # WORK HERE (paste from below encryption) + + return False + + def verify(self, pk, min=0, max=1): + possible_plaintexts = self.generate_plaintexts(pk) + homomorphic_sum = 0 + + for choice_num in range(len(self.choices)): + choice = self.choices[choice_num] + choice.pk = pk + individual_proof = self.individual_proofs[choice_num] + + # verify the proof on the encryption of that choice + if not choice.verify_disjunctive_encryption_proof(possible_plaintexts, individual_proof, algs.EG_disjunctive_challenge_generator): + return False + + # compute homomorphic sum if needed + if max != None: + homomorphic_sum = choice * homomorphic_sum + + if max != None: + # determine possible plaintexts for the sum + sum_possible_plaintexts = self.generate_plaintexts(pk, min=min, max=max) + + # verify the sum + return homomorphic_sum.verify_disjunctive_encryption_proof(sum_possible_plaintexts, self.overall_proof, algs.EG_disjunctive_challenge_generator) + else: + # approval voting, no need for overall proof verification + return True + + def toJSONDict(self, with_randomness=False): + value = { + 'choices': [c.to_dict() for c in self.choices], + 'individual_proofs' : [p.to_dict() for p in self.individual_proofs] + } + + if self.overall_proof: + value['overall_proof'] = self.overall_proof.to_dict() + else: + value['overall_proof'] = None + + if with_randomness: + value['randomness'] = [str(r) for r in self.randomness] + value['answer'] = self.answer + + return value + + @classmethod + def fromJSONDict(cls, d, pk=None): + ea = cls() + + ea.choices = [algs.EGCiphertext.from_dict(c, pk) for c in d['choices']] + ea.individual_proofs = [algs.EGZKDisjunctiveProof.from_dict(p) for p in d['individual_proofs']] + + if d['overall_proof']: + ea.overall_proof = algs.EGZKDisjunctiveProof.from_dict(d['overall_proof']) + else: + ea.overall_proof = None + + if d.has_key('randomness'): + ea.randomness = [int(r) for r in d['randomness']] + ea.answer = d['answer'] + + return ea + + @classmethod + def fromElectionAndAnswer(cls, election, question_num, answer_indexes): + """ + Given an election, a question number, and a list of answers to that question + in the form of an array of 0-based indexes into the answer array, + produce an EncryptedAnswer that works. + """ + question = election.questions[question_num] + answers = question['answers'] + pk = election.public_key + + # initialize choices, individual proofs, randomness and overall proof + choices = [None for a in range(len(answers))] + individual_proofs = [None for a in range(len(answers))] + overall_proof = None + randomness = [None for a in range(len(answers))] + + # possible plaintexts [0, 1] + plaintexts = cls.generate_plaintexts(pk) + + # keep track of number of options selected. + num_selected_answers = 0; + + # homomorphic sum of all + homomorphic_sum = 0 + randomness_sum = 0 + + # min and max for number of answers, useful later + min_answers = 0 + if question.has_key('min'): + min_answers = question['min'] + max_answers = question['max'] + + # go through each possible answer and encrypt either a g^0 or a g^1. + for answer_num in range(len(answers)): + plaintext_index = 0 + + # assuming a list of answers + if answer_num in answer_indexes: + plaintext_index = 1 + num_selected_answers += 1 + + # randomness and encryption + randomness[answer_num] = algs.Utils.random_mpz_lt(pk.q) + choices[answer_num] = pk.encrypt_with_r(plaintexts[plaintext_index], randomness[answer_num]) + + # generate proof + individual_proofs[answer_num] = choices[answer_num].generate_disjunctive_encryption_proof(plaintexts, plaintext_index, + randomness[answer_num], algs.EG_disjunctive_challenge_generator) + + # sum things up homomorphically if needed + if max_answers != None: + homomorphic_sum = choices[answer_num] * homomorphic_sum + randomness_sum = (randomness_sum + randomness[answer_num]) % pk.q + + # prove that the sum is 0 or 1 (can be "blank vote" for this answer) + # num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded + + if num_selected_answers < min_answers: + raise Exception("Need to select at least %s answer(s)" % min_answers) + + if max_answers != None: + sum_plaintexts = cls.generate_plaintexts(pk, min=min_answers, max=max_answers) + + # need to subtract the min from the offset + overall_proof = homomorphic_sum.generate_disjunctive_encryption_proof(sum_plaintexts, num_selected_answers - min_answers, randomness_sum, algs.EG_disjunctive_challenge_generator); + else: + # approval voting + overall_proof = None + + return cls(choices, individual_proofs, overall_proof, randomness, answer_indexes) + +class EncryptedVote(HeliosObject): + """ + An encrypted ballot + """ + FIELDS = ['encrypted_answers', 'election_hash', 'election_uuid'] + + def verify(self, election): + # right number of answers + if len(self.encrypted_answers) != len(election.questions): + return False + + # check hash + if self.election_hash != election.hash: + # print "%s / %s " % (self.election_hash, election.hash) + return False + + # check ID + if self.election_uuid != election.uuid: + return False + + # check proofs on all of answers + for question_num in range(len(election.questions)): + ea = self.encrypted_answers[question_num] + + question = election.questions[question_num] + min_answers = 0 + if question.has_key('min'): + min_answers = question['min'] + + if not ea.verify(election.public_key, min=min_answers, max=question['max']): + return False + + return True + + def get_hash(self): + return utils.hash_b64(utils.to_json(self.toJSONDict())) + + def toJSONDict(self, with_randomness=False): + return { + 'answers': [a.toJSONDict(with_randomness) for a in self.encrypted_answers], + 'election_hash': self.election_hash, + 'election_uuid': self.election_uuid + } + + @classmethod + def fromJSONDict(cls, d, pk=None): + ev = cls() + + ev.encrypted_answers = [EncryptedAnswer.fromJSONDict(ea, pk) for ea in d['answers']] + ev.election_hash = d['election_hash'] + ev.election_uuid = d['election_uuid'] + + return ev + + @classmethod + def fromElectionAndAnswers(cls, election, answers): + pk = election.public_key + + # each answer is an index into the answer array + encrypted_answers = [EncryptedAnswer.fromElectionAndAnswer(election, answer_num, answers[answer_num]) for answer_num in range(len(answers))] + return cls(encrypted_answers=encrypted_answers, election_hash=election.hash, election_uuid = election.uuid) + +class Election(HeliosObject): + + FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', + 'frozen_at', 'public_key', 'private_key', 'cast_url', 'result', 'result_proof', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at'] + JSON_FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', + 'frozen_at', 'public_key', 'cast_url', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at'] + + def init_tally(self): + return Tally(election=self) + + def _process_value_in(self, field_name, field_value): + if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': + if type(field_value) == str or type(field_value) == unicode: + return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') + + if field_name == 'public_key': + return algs.EGPublicKey.fromJSONDict(field_value) + + if field_name == 'private_key': + return algs.EGSecretKey.fromJSONDict(field_value) + + def _process_value_out(self, field_name, field_value): + # the date + if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': + return str(field_value) + + if field_name == 'public_key' or field_name == 'private_key': + return field_value.toJSONDict() + + @property + def registration_status_pretty(self): + if self.openreg: + return "Open" + else: + return "Closed" + + @property + def pretty_result(self): + if not self.result: + return None + + raw_result = self.result + prettified_result = [] + + # loop through questions + for i in range(len(self.questions)): + q = self.questions[i] + pretty_question = [] + + # go through answers + for j in range(len(q['answers'])): + a = q['answers'][j] + count = raw_result[i][j] + pretty_question.append({'answer': a, 'count': count}) + + prettified_result.append({'question': q['short_name'], 'answers': pretty_question}) + + return prettified_result + + +class Voter(HeliosObject): + """ + A voter in an election + """ + FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id', 'name', 'alias'] + JSON_FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id', 'name'] + + # alternative, for when the voter is aliased + ALIASED_VOTER_JSON_FIELDS = ['election_uuid', 'uuid', 'alias'] + + def toJSONDict(self): + fields = None + if self.alias != None: + return super(Voter, self).toJSONDict(self.ALIASED_VOTER_JSON_FIELDS) + else: + return super(Voter,self).toJSONDict() + +class Trustee(HeliosObject): + """ + a trustee + """ + FIELDS = ['uuid', 'public_key', 'public_key_hash', 'pok', 'decryption_factors', 'decryption_proofs', 'email'] + + def _process_value_in(self, field_name, field_value): + if field_name == 'public_key': + return algs.EGPublicKey.fromJSONDict(field_value) + + if field_name == 'pok': + return algs.DLogProof.fromJSONDict(field_value) + + def _process_value_out(self, field_name, field_value): + if field_name == 'public_key' or field_name == 'pok': + return field_value.toJSONDict() + +class CastVote(HeliosObject): + """ + A cast vote, which includes an encrypted vote and some cast metadata + """ + FIELDS = ['vote', 'cast_at', 'voter_uuid', 'voter_hash', 'vote_hash'] + + def __init__(self, *args, **kwargs): + super(CastVote, self).__init__(*args, **kwargs) + self.election = None + + @classmethod + def fromJSONDict(cls, d, election=None): + o = cls() + o.election = election + o.set_from_args(**d) + return o + + def toJSONDict(self, include_vote=True): + result = super(CastVote,self).toJSONDict() + if not include_vote: + del result['vote'] + return result + + @classmethod + def fromOtherObject(cls, o, election): + obj = cls() + obj.election = election + obj.set_from_other_object(o) + return obj + + def _process_value_in(self, field_name, field_value): + if field_name == 'cast_at': + if type(field_value) == str: + return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') + + if field_name == 'vote': + return EncryptedVote.fromJSONDict(field_value, self.election.public_key) + + def _process_value_out(self, field_name, field_value): + # the date + if field_name == 'cast_at': + return str(field_value) + + if field_name == 'vote': + return field_value.toJSONDict() + + def issues(self, election): + """ + Look for consistency problems + """ + issues = [] + + # check the election + if self.vote.election_uuid != election.uuid: + issues.append("the vote's election UUID does not match the election for which this vote is being cast") + + return issues + +class DLogTable(object): + """ + Keeping track of discrete logs + """ + + def __init__(self, base, modulus): + self.dlogs = {} + self.dlogs[1] = 0 + self.last_dlog_result = 1 + self.counter = 0 + + self.base = base + self.modulus = modulus + + def increment(self): + self.counter += 1 + + # new value + new_value = (self.last_dlog_result * self.base) % self.modulus + + # record the discrete log + self.dlogs[new_value] = self.counter + + # record the last value + self.last_dlog_result = new_value + + def precompute(self, up_to): + while self.counter < up_to: + self.increment() + + def lookup(self, value): + return self.dlogs.get(value, None) + + +class Tally(HeliosObject): + """ + A running homomorphic tally + """ + + FIELDS = ['num_tallied', 'tally'] + JSON_FIELDS = ['num_tallied', 'tally'] + + def __init__(self, *args, **kwargs): + super(Tally, self).__init__(*args, **kwargs) + + self.election = kwargs.get('election',None) + + if self.election: + self.init_election(self.election) + else: + self.questions = None + self.public_key = None + + if not self.tally: + self.tally = None + + # initialize + if self.num_tallied == None: + self.num_tallied = 0 + + def init_election(self, election): + """ + given the election, initialize some params + """ + self.questions = election.questions + self.public_key = election.public_key + + if not self.tally: + self.tally = [[0 for a in q['answers']] for q in self.questions] + + def add_vote_batch(self, encrypted_votes, verify_p=True): + """ + Add a batch of votes. Eventually, this will be optimized to do an aggregate proof verification + rather than a whole proof verif for each vote. + """ + for vote in encrypted_votes: + self.add_vote(vote, verify_p) + + def add_vote(self, encrypted_vote, verify_p=True): + # do we verify? + if verify_p: + if not encrypted_vote.verify(self.election): + raise Exception('Bad Vote') + + # for each question + for question_num in range(len(self.questions)): + question = self.questions[question_num] + answers = question['answers'] + + # for each possible answer to each question + for answer_num in range(len(answers)): + # do the homomorphic addition into the tally + enc_vote_choice = encrypted_vote.encrypted_answers[question_num].choices[answer_num] + enc_vote_choice.pk = self.public_key + self.tally[question_num][answer_num] = encrypted_vote.encrypted_answers[question_num].choices[answer_num] * self.tally[question_num][answer_num] + + self.num_tallied += 1 + + def decryption_factors_and_proofs(self, sk): + """ + returns an array of decryption factors and a corresponding array of decryption proofs. + makes the decryption factors into strings, for general Helios / JS compatibility. + """ + # for all choices of all questions (double list comprehension) + decryption_factors = [] + decryption_proof = [] + + for question_num, question in enumerate(self.questions): + answers = question['answers'] + question_factors = [] + question_proof = [] + + for answer_num, answer in enumerate(answers): + # do decryption and proof of it + dec_factor, proof = sk.decryption_factor_and_proof(self.tally[question_num][answer_num]) + + # look up appropriate discrete log + # this is the string conversion + question_factors.append(str(dec_factor)) + question_proof.append(proof.toJSONDict()) + + decryption_factors.append(question_factors) + decryption_proof.append(question_proof) + + return decryption_factors, decryption_proof + + def decrypt_and_prove(self, sk, discrete_logs=None): + """ + returns an array of tallies and a corresponding array of decryption proofs. + """ + + # who's keeping track of discrete logs? + if not discrete_logs: + discrete_logs = self.discrete_logs + + # for all choices of all questions (double list comprehension) + decrypted_tally = [] + decryption_proof = [] + + for question_num in range(len(self.questions)): + question = self.questions[question_num] + answers = question['answers'] + question_tally = [] + question_proof = [] + + for answer_num in range(len(answers)): + # do decryption and proof of it + plaintext, proof = sk.prove_decryption(self.tally[question_num][answer_num]) + + # look up appropriate discrete log + question_tally.append(discrete_logs[plaintext]) + question_proof.append(proof) + + decrypted_tally.append(question_tally) + decryption_proof.append(question_proof) + + return decrypted_tally, decryption_proof + + def verify_decryption_proofs(self, decryption_factors, decryption_proofs, public_key, challenge_generator): + """ + decryption_factors is a list of lists of dec factors + decryption_proofs are the corresponding proofs + public_key is, of course, the public key of the trustee + """ + + # go through each one + for q_num, q in enumerate(self.tally): + for a_num, answer_tally in enumerate(q): + # parse the proof + proof = algs.EGZKProof.fromJSONDict(decryption_proofs[q_num][a_num]) + + # check that g, alpha, y, dec_factor is a DH tuple + if not proof.verify(public_key.g, answer_tally.alpha, public_key.y, int(decryption_factors[q_num][a_num]), public_key.p, public_key.q, challenge_generator): + return False + + return True + + def decrypt_from_factors(self, decryption_factors, public_key): + """ + decrypt a tally given decryption factors + + The decryption factors are a list of decryption factor sets, for each trustee. + Each decryption factor set is a list of lists of decryption factors (questions/answers). + """ + + # pre-compute a dlog table + dlog_table = DLogTable(base = public_key.g, modulus = public_key.p) + dlog_table.precompute(self.num_tallied) + + result = [] + + # go through each one + for q_num, q in enumerate(self.tally): + q_result = [] + + for a_num, a in enumerate(q): + # coalesce the decryption factors into one list + dec_factor_list = [df[q_num][a_num] for df in decryption_factors] + raw_value = self.tally[q_num][a_num].decrypt(dec_factor_list, public_key) + + q_result.append(dlog_table.lookup(raw_value)) + + result.append(q_result) + + return result + + def _process_value_in(self, field_name, field_value): + if field_name == 'tally': + return [[algs.EGCiphertext.fromJSONDict(a) for a in q] for q in field_value] + + def _process_value_out(self, field_name, field_value): + if field_name == 'tally': + return [[a.toJSONDict() for a in q] for q in field_value] + diff --git a/helios/crypto/number.py b/helios/crypto/number.py new file mode 100644 index 000000000..9d50563e9 --- /dev/null +++ b/helios/crypto/number.py @@ -0,0 +1,201 @@ +# +# number.py : Number-theoretic functions +# +# Part of the Python Cryptography Toolkit +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +__revision__ = "$Id: number.py,v 1.13 2003/04/04 18:21:07 akuchling Exp $" + +bignum = long +try: + from Crypto.PublicKey import _fastmath +except ImportError: + _fastmath = None + +# Commented out and replaced with faster versions below +## def long2str(n): +## s='' +## while n>0: +## s=chr(n & 255)+s +## n=n>>8 +## return s + +## import types +## def str2long(s): +## if type(s)!=types.StringType: return s # Integers will be left alone +## return reduce(lambda x,y : x*256+ord(y), s, 0L) + +def size (N): + """size(N:long) : int + Returns the size of the number N in bits. + """ + bits, power = 0,1L + while N >= power: + bits += 1 + power = power << 1 + return bits + +def getRandomNumber(N, randfunc): + """getRandomNumber(N:int, randfunc:callable):long + Return an N-bit random number.""" + + S = randfunc(N/8) + odd_bits = N % 8 + if odd_bits != 0: + char = ord(randfunc(1)) >> (8-odd_bits) + S = chr(char) + S + value = bytes_to_long(S) + value |= 2L ** (N-1) # Ensure high bit is set + assert size(value) >= N + return value + +def GCD(x,y): + """GCD(x:long, y:long): long + Return the GCD of x and y. + """ + x = abs(x) ; y = abs(y) + while x > 0: + x, y = y % x, x + return y + +def inverse(u, v): + """inverse(u:long, u:long):long + Return the inverse of u mod v. + """ + u3, v3 = long(u), long(v) + u1, v1 = 1L, 0L + while v3 > 0: + q=u3 / v3 + u1, v1 = v1, u1 - v1*q + u3, v3 = v3, u3 - v3*q + while u1<0: + u1 = u1 + v + return u1 + +# Given a number of bits to generate and a random generation function, +# find a prime number of the appropriate size. + +def getPrime(N, randfunc): + """getPrime(N:int, randfunc:callable):long + Return a random N-bit prime number. + """ + + number=getRandomNumber(N, randfunc) | 1 + while (not isPrime(number)): + number=number+2 + return number + +def isPrime(N): + """isPrime(N:long):bool + Return true if N is prime. + """ + if N == 1: + return 0 + if N in sieve: + return 1 + for i in sieve: + if (N % i)==0: + return 0 + + # Use the accelerator if available + if _fastmath is not None: + return _fastmath.isPrime(N) + + # Compute the highest bit that's set in N + N1 = N - 1L + n = 1L + while (n> 1L + + # Rabin-Miller test + for c in sieve[:7]: + a=long(c) ; d=1L ; t=n + while (t): # Iterate over the bits in N1 + x=(d*d) % N + if x==1L and d!=1L and d!=N1: + return 0 # Square root of 1 found + if N1 & t: + d=(x*a) % N + else: + d=x + t = t >> 1L + if d!=1L: + return 0 + return 1 + +# Small primes used for checking primality; these are all the primes +# less than 256. This should be enough to eliminate most of the odd +# numbers before needing to do a Rabin-Miller test at all. + +sieve=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 251] + +# Improved conversion functions contributed by Barry Warsaw, after +# careful benchmarking + +import struct + +def long_to_bytes(n, blocksize=0): + """long_to_bytes(n:long, blocksize:int) : string + Convert a long integer to a byte string. + + If optional blocksize is given and greater than zero, pad the front of the + byte string with binary zeros so that the length is a multiple of + blocksize. + """ + # after much testing, this algorithm was deemed to be the fastest + s = '' + n = long(n) + pack = struct.pack + while n > 0: + s = pack('>I', n & 0xffffffffL) + s + n = n >> 32 + # strip off leading zeros + for i in range(len(s)): + if s[i] != '\000': + break + else: + # only happens when n == 0 + s = '\000' + i = 0 + s = s[i:] + # add back some pad bytes. this could be done more efficiently w.r.t. the + # de-padding being done above, but sigh... + if blocksize > 0 and len(s) % blocksize: + s = (blocksize - len(s) % blocksize) * '\000' + s + return s + +def bytes_to_long(s): + """bytes_to_long(string) : long + Convert a byte string to a long integer. + + This is (essentially) the inverse of long_to_bytes(). + """ + acc = 0L + unpack = struct.unpack + length = len(s) + if length % 4: + extra = (4 - length % 4) + s = '\000' * extra + s + length = length + extra + for i in range(0, length, 4): + acc = (acc << 32) + unpack('>I', s[i:i+4])[0] + return acc + +# For backwards compatibility... +import warnings +def long2str(n, blocksize=0): + warnings.warn("long2str() has been replaced by long_to_bytes()") + return long_to_bytes(n, blocksize) +def str2long(s): + warnings.warn("str2long() has been replaced by bytes_to_long()") + return bytes_to_long(s) diff --git a/helios/crypto/numtheory.py b/helios/crypto/numtheory.py new file mode 100644 index 000000000..16fcf9aa0 --- /dev/null +++ b/helios/crypto/numtheory.py @@ -0,0 +1,1434 @@ +################################################## +# ent.py -- Element Number Theory +# (c) William Stein, 2004 +################################################## + + + + +from random import randrange +from math import log, sqrt + + + + +################################################## +## Greatest Common Divisors +################################################## + +def gcd(a, b): # (1) + """ + Returns the greatest commond divisor of a and b. + Input: + a -- an integer + b -- an integer + Output: + an integer, the gcd of a and b + Examples: + >>> gcd(97,100) + 1 + >>> gcd(97 * 10**15, 19**20 * 97**2) # (2) + 97L + """ + if a < 0: a = -a + if b < 0: b = -b + if a == 0: return b + if b == 0: return a + while b != 0: + (a, b) = (b, a%b) + return a + + + +################################################## +## Enumerating Primes +################################################## + +def primes(n): + """ + Returns a list of the primes up to n, computed + using the Sieve of Eratosthenes. + Input: + n -- a positive integer + Output: + list -- a list of the primes up to n + Examples: + >>> primes(10) + [2, 3, 5, 7] + >>> primes(45) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43] + """ + if n <= 1: return [] + X = [i for i in range(3,n+1) if i%2 != 0] # (1) + P = [2] # (2) + sqrt_n = sqrt(n) # (3) + while len(X) > 0 and X[0] <= sqrt_n: # (4) + p = X[0] # (5) + P.append(p) # (6) + X = [a for a in X if a%p != 0] # (7) + return P + X # (8) + + + + +################################################## +## Integer Factorization +################################################## + +def trial_division(n, bound=None): + """ + Return the smallest prime divisor <= bound of the + positive integer n, or n if there is no such prime. + If the optional argument bound is omitted, then bound=n. + Input: + n -- a positive integer + bound - (optional) a positive integer + Output: + int -- a prime p<=bound that divides n, or n if + there is no such prime. + Examples: + >>> trial_division(15) + 3 + >>> trial_division(91) + 7 + >>> trial_division(11) + 11 + >>> trial_division(387833, 300) + 387833 + >>> # 300 is not big enough to split off a + >>> # factor, but 400 is. + >>> trial_division(387833, 400) + 389 + """ + if n == 1: return 1 + for p in [2, 3, 5]: + if n%p == 0: return p + if bound == None: bound = n + dif = [6, 4, 2, 4, 2, 4, 6, 2] + m = 7; i = 1 + while m <= bound and m*m <= n: + if n%m == 0: + return m + m += dif[i%8] + i += 1 + return n + +def factor(n): + """ + Returns the factorization of the integer n as + a sorted list of tuples (p,e), where the integers p + are output by the split algorithm. + Input: + n -- an integer + Output: + list -- factorization of n + Examples: + >>> factor(500) + [(2, 2), (5, 3)] + >>> factor(-20) + [(2, 2), (5, 1)] + >>> factor(1) + [] + >>> factor(2004) + [(2, 2), (3, 1), (167, 1)] + """ + if n in [-1, 0, 1]: return [] + if n < 0: n = -n + F = [] + while n != 1: + p = trial_division(n) + e = 1 + n /= p + while n%p == 0: + e += 1; n /= p + F.append((p,e)) + F.sort() + return F + + + +################################################## +## Linear Equations Modulo $n$ +################################################## + +def xgcd(a, b): + """ + Returns g, x, y such that g = x*a + y*b = gcd(a,b). + Input: + a -- an integer + b -- an integer + Output: + g -- an integer, the gcd of a and b + x -- an integer + y -- an integer + Examples: + >>> xgcd(2,3) + (1, -1, 1) + >>> xgcd(10, 12) + (2, -1, 1) + >>> g, x, y = xgcd(100, 2004) + >>> print g, x, y + 4 -20 1 + >>> print x*100 + y*2004 + 4 + """ + if a == 0 and b == 0: return (0, 0, 1) + if a == 0: return (abs(b), 0, b/abs(b)) + if b == 0: return (abs(a), a/abs(a), 0) + x_sign = 1; y_sign = 1 + if a < 0: a = -a; x_sign = -1 + if b < 0: b = -b; y_sign = -1 + x = 1; y = 0; r = 0; s = 1 + while b != 0: + (c, q) = (a%b, a/b) + (a, b, r, s, x, y) = (b, c, x-q*r, y-q*s, r, s) + return (a, x*x_sign, y*y_sign) + +def inversemod(a, n): + """ + Returns the inverse of a modulo n, normalized to + lie between 0 and n-1. If a is not coprime to n, + raise an exception (this will be useful later for + the elliptic curve factorization method). + Input: + a -- an integer coprime to n + n -- a positive integer + Output: + an integer between 0 and n-1. + Examples: + >>> inversemod(1,1) + 0 + >>> inversemod(2,5) + 3 + >>> inversemod(5,8) + 5 + >>> inversemod(37,100) + 73 + """ + g, x, y = xgcd(a, n) + if g != 1: + raise ZeroDivisionError, (a,n) + assert g == 1, "a must be coprime to n." + return x%n + +def solve_linear(a,b,n): + """ + If the equation ax = b (mod n) has a solution, return a + solution normalized to lie between 0 and n-1, otherwise + returns None. + Input: + a -- an integer + b -- an integer + n -- an integer + Output: + an integer or None + Examples: + >>> solve_linear(4, 2, 10) + 8 + >>> solve_linear(2, 1, 4) == None + True + """ + g, c, _ = xgcd(a,n) # (1) + if b%g != 0: return None + return ((b/g)*c) % n + +def crt(a, b, m, n): + """ + Return the unique integer between 0 and m*n - 1 + that reduces to a modulo n and b modulo m, where + the integers m and n are coprime. + Input: + a, b, m, n -- integers, with m and n coprime + Output: + int -- an integer between 0 and m*n - 1. + Examples: + >>> crt(1, 2, 3, 4) + 10 + >>> crt(4, 5, 10, 3) + 14 + >>> crt(-1, -1, 100, 101) + 10099 + """ + g, c, _ = xgcd(m, n) + assert g == 1, "m and n must be coprime." + return (a + (b-a)*c*m) % (m*n) + + +################################################## +## Computation of Powers +################################################## + +def powermod(a, m, n): + """ + The m-th power of a modulo n. + Input: + a -- an integer + m -- a nonnegative integer + n -- a positive integer + Output: + int -- an integer between 0 and n-1 + Examples: + >>> powermod(2,25,30) + 2 + >>> powermod(19,12345,100) + 99 + """ + assert m >= 0, "m must be nonnegative." # (1) + assert n >= 1, "n must be positive." # (2) + ans = 1 + apow = a + while m != 0: + if m%2 != 0: + ans = (ans * apow) % n # (3) + apow = (apow * apow) % n # (4) + m /= 2 + return ans % n + + +################################################## +## Finding a Primitive Root +################################################## + +def primitive_root(p): + """ + Returns first primitive root modulo the prime p. + (If p is not prime, this return value of this function + is not meaningful.) + Input: + p -- an integer that is assumed prime + Output: + int -- a primitive root modulo p + Examples: + >>> primitive_root(7) + 3 + >>> primitive_root(389) + 2 + >>> primitive_root(5881) + 31 + """ + if p == 2: return 1 + F = factor(p-1) + a = 2 + while a < p: + generates = True + for q, _ in F: + if powermod(a, (p-1)/q, p) == 1: + generates = False + break + if generates: return a + a += 1 + assert False, "p must be prime." + + +################################################## +## Determining Whether a Number is Prime +################################################## + +def is_pseudoprime(n, bases = [2,3,5,7]): + """ + Returns True if n is a pseudoprime to the given bases, + in the sense that n>1 and b**(n-1) = 1 (mod n) for each + elements b of bases, with b not a multiple of n, and + False otherwise. + Input: + n -- an integer + bases -- a list of integers + Output: + bool + Examples: + >>> is_pseudoprime(91) + False + >>> is_pseudoprime(97) + True + >>> is_pseudoprime(1) + False + >>> is_pseudoprime(-2) + True + >>> s = [x for x in range(10000) if is_pseudoprime(x)] + >>> t = primes(10000) + >>> s == t + True + >>> is_pseudoprime(29341) # first non-prime pseudoprime + True + >>> factor(29341) + [(13, 1), (37, 1), (61, 1)] + """ + if n < 0: n = -n + if n <= 1: return False + for b in bases: + if b%n != 0 and powermod(b, n-1, n) != 1: + return False + return True + + +def miller_rabin(n, num_trials=4): + """ + True if n is likely prime, and False if n + is definitely not prime. Increasing num_trials + increases the probability of correctness. + (One can prove that the probability that this + function returns True when it should return + False is at most (1/4)**num_trials.) + Input: + n -- an integer + num_trials -- the number of trials with the + primality test. + Output: + bool -- whether or not n is probably prime. + Examples: + >>> miller_rabin(91) + False #rand + >>> miller_rabin(97) + True #rand + >>> s = [x for x in range(1000) if miller_rabin(x, 1)] + >>> t = primes(1000) + >>> print len(s), len(t) # so 1 in 25 wrong + 175 168 #rand + >>> s = [x for x in range(1000) if miller_rabin(x)] + >>> s == t + True #rand + """ + if n < 0: n = -n + if n in [2,3]: return True + if n <= 4: return False + m = n - 1 + k = 0 + while m%2 == 0: + k += 1; m /= 2 + # Now n - 1 = (2**k) * m with m odd + for i in range(num_trials): + a = randrange(2,n-1) # (1) + apow = powermod(a, m, n) + if not (apow in [1, n-1]): + some_minus_one = False + for r in range(k-1): # (2) + apow = (apow**2)%n + if apow == n-1: + some_minus_one = True + break # (3) + if (apow in [1, n-1]) or some_minus_one: + prob_prime = True + else: + return False + return True + + +################################################## +## The Diffie-Hellman Key Exchange +################################################## + +def random_prime(num_digits, is_prime = miller_rabin): + """ + Returns a random prime with num_digits digits. + Input: + num_digits -- a positive integer + is_prime -- (optional argment) + a function of one argument n that + returns either True if n is (probably) + prime and False otherwise. + Output: + int -- an integer + Examples: + >>> random_prime(10) + 8599796717L #rand + >>> random_prime(40) + 1311696770583281776596904119734399028761L #rand + """ + n = randrange(10**(num_digits-1), 10**num_digits) + if n%2 == 0: n += 1 + while not is_prime(n): n += 2 + return n + +def dh_init(p): + """ + Generates and returns a random positive + integer n < p and the power 2^n (mod p). + Input: + p -- an integer that is prime + Output: + int -- a positive integer < p, a secret + int -- 2^n (mod p), send to other user + Examples: + >>> p = random_prime(20) + >>> dh_init(p) + (15299007531923218813L, 4715333264598442112L) #rand + """ + n = randrange(2,p) + return n, powermod(2,n,p) + +def dh_secret(p, n, mpow): + """ + Computes the shared Diffie-Hellman secret key. + Input: + p -- an integer that is prime + n -- an integer: output by dh_init for this user + mpow-- an integer: output by dh_init for other user + Output: + int -- the shared secret key. + Examples: + >>> p = random_prime(20) + >>> n, npow = dh_init(p) + >>> m, mpow = dh_init(p) + >>> dh_secret(p, n, mpow) + 15695503407570180188L #rand + >>> dh_secret(p, m, npow) + 15695503407570180188L #rand + """ + return powermod(mpow,n,p) + + + + + + +################################################## +## Encoding Strings as Lists of Integers +################################################## + +def str_to_numlist(s, bound): + """ + Returns a sequence of integers between 0 and bound-1 + that encodes the string s. Randomization is included, + so the same string is very likely to encode differently + each time this function is called. + Input: + s -- a string + bound -- an integer >= 256 + Output: + list -- encoding of s as a list of integers + Examples: + >>> str_to_numlist("Run!", 1000) + [82, 117, 110, 33] #rand + >>> str_to_numlist("TOP SECRET", 10**20) + [4995371940984439512L, 92656709616492L] #rand + """ + assert bound >= 256, "bound must be at least 256." + n = int(log(bound) / log(256)) # (1) + salt = min(int(n/8) + 1, n-1) # (2) + i = 0; v = [] + while i < len(s): # (3) + c = 0; pow = 1 + for j in range(n): # (4) + if j < salt: + c += randrange(1,256)*pow # (5) + else: + if i >= len(s): break + c += ord(s[i])*pow # (6) + i += 1 + pow *= 256 + v.append(c) + return v + +def numlist_to_str(v, bound): + """ + Returns the string that the sequence v of + integers encodes. + Input: + v -- list of integers between 0 and bound-1 + bound -- an integer >= 256 + Output: + str -- decoding of v as a string + Examples: + >>> print numlist_to_str([82, 117, 110, 33], 1000) + Run! + >>> x = str_to_numlist("TOP SECRET MESSAGE", 10**20) + >>> print numlist_to_str(x, 10**20) + TOP SECRET MESSAGE + """ + assert bound >= 256, "bound must be at least 256." + n = int(log(bound) / log(256)) + s = "" + salt = min(int(n/8) + 1, n-1) + for x in v: + for j in range(n): + y = x%256 + if y > 0 and j >= salt: + s += chr(y) + x /= 256 + return s + + +################################################## +## The RSA Cryptosystem +################################################## + +def rsa_init(p, q): + """ + Returns defining parameters (e, d, n) for the RSA + cryptosystem defined by primes p and q. The + primes p and q may be computed using the + random_prime functions. + Input: + p -- a prime integer + q -- a prime integer + Output: + Let m be (p-1)*(q-1). + e -- an encryption key, which is a randomly + chosen integer between 2 and m-1 + d -- the inverse of e modulo eulerphi(p*q), + as an integer between 2 and m-1 + n -- the product p*q. + Examples: + >>> p = random_prime(20); q = random_prime(20) + >>> print p, q + 37999414403893878907L 25910385856444296437L #rand + >>> e, d, n = rsa_init(p, q) + >>> e + 5 #rand + >>> d + 787663591619054108576589014764921103213L #rand + >>> n + 984579489523817635784646068716489554359L #rand + """ + m = (p-1)*(q-1) + e = 3 + while gcd(e, m) != 1: e += 1 + d = inversemod(e, m) + return e, d, p*q + +def rsa_encrypt(plain_text, e, n): + """ + Encrypt plain_text using the encrypt + exponent e and modulus n. + Input: + plain_text -- arbitrary string + e -- an integer, the encryption exponent + n -- an integer, the modulus + Output: + str -- the encrypted cipher text + Examples: + >>> e = 1413636032234706267861856804566528506075 + >>> n = 2109029637390047474920932660992586706589 + >>> rsa_encrypt("Run Nikita!", e, n) + [78151883112572478169375308975376279129L] #rand + >>> rsa_encrypt("Run Nikita!", e, n) + [1136438061748322881798487546474756875373L] #rand + """ + plain = str_to_numlist(plain_text, n) + return [powermod(x, e, n) for x in plain] + +def rsa_decrypt(cipher, d, n): + """ + Decrypt the cipher_text using the decryption + exponent d and modulus n. + Input: + cipher_text -- list of integers output + by rsa_encrypt + Output: + str -- the unencrypted plain text + Examples: + >>> d = 938164637865370078346033914094246201579 + >>> n = 2109029637390047474920932660992586706589 + >>> msg1 = [1071099761433836971832061585353925961069] + >>> msg2 = [1336506586627416245118258421225335020977] + >>> rsa_decrypt(msg1, d, n) + 'Run Nikita!' + >>> rsa_decrypt(msg2, d, n) + 'Run Nikita!' + """ + plain = [powermod(x, d, n) for x in cipher] + return numlist_to_str(plain, n) + + +################################################## +## Computing the Legendre Symbol +################################################## + +def legendre(a, p): + """ + Returns the Legendre symbol a over p, where + p is an odd prime. + Input: + a -- an integer + p -- an odd prime (primality not checked) + Output: + int: -1 if a is not a square mod p, + 0 if gcd(a,p) is not 1 + 1 if a is a square mod p. + Examples: + >>> legendre(2, 5) + -1 + >>> legendre(3, 3) + 0 + >>> legendre(7, 2003) + -1 + """ + assert p%2 == 1, "p must be an odd prime." + b = powermod(a, (p-1)/2, p) + if b == 1: return 1 + elif b == p-1: return -1 + return 0 + + +################################################## +## In this section we implement the algorithm +################################################## + +def sqrtmod(a, p): + """ + Returns a square root of a modulo p. + Input: + a -- an integer that is a perfect + square modulo p (this is checked) + p -- a prime + Output: + int -- a square root of a, as an integer + between 0 and p-1. + Examples: + >>> sqrtmod(4, 5) # p == 1 (mod 4) + 3 #rand + >>> sqrtmod(13, 23) # p == 3 (mod 4) + 6 #rand + >>> sqrtmod(997, 7304723089) # p == 1 (mod 4) + 761044645L #rand + """ + a %= p + if p == 2: return a + assert legendre(a, p) == 1, "a must be a square mod p." + if p%4 == 3: return powermod(a, (p+1)/4, p) + + def mul(x, y): # multiplication in R # (1) + return ((x[0]*y[0] + a*y[1]*x[1]) % p, \ + (x[0]*y[1] + x[1]*y[0]) % p) + def pow(x, n): # exponentiation in R # (2) + ans = (1,0) + xpow = x + while n != 0: + if n%2 != 0: ans = mul(ans, xpow) + xpow = mul(xpow, xpow) + n /= 2 + return ans + + while True: + z = randrange(2,p) + u, v = pow((1,z), (p-1)/2) + if v != 0: + vinv = inversemod(v, p) + for x in [-u*vinv, (1-u)*vinv, (-1-u)*vinv]: + if (x*x)%p == a: return x%p + assert False, "Bug in sqrtmod." + + +################################################## +## Continued Fractions +################################################## + +def convergents(v): + """ + Returns the partial convergents of the continued + fraction v. + Input: + v -- list of integers [a0, a1, a2, ..., am] + Output: + list -- list [(p0,q0), (p1,q1), ...] + of pairs (pm,qm) such that the mth + convergent of v is pm/qm. + Examples: + >>> convergents([1, 2]) + [(1, 1), (3, 2)] + >>> convergents([3, 7, 15, 1, 292]) + [(3, 1), (22, 7), (333, 106), (355, 113), (103993, 33102)] + """ + w = [(0,1), (1,0)] + for n in range(len(v)): + pn = v[n]*w[n+1][0] + w[n][0] + qn = v[n]*w[n+1][1] + w[n][1] + w.append((pn, qn)) + del w[0]; del w[0] # remove first entries of w + return w + +def contfrac_rat(numer, denom): + """ + Returns the continued fraction of the rational + number numer/denom. + Input: + numer -- an integer + denom -- a positive integer coprime to num + Output + list -- the continued fraction [a0, a1, ..., am] + of the rational number num/denom. + Examples: + >>> contfrac_rat(3, 2) + [1, 2] + >>> contfrac_rat(103993, 33102) + [3, 7, 15, 1, 292] + """ + assert denom > 0, "denom must be positive" + a = numer; b = denom + v = [] + while b != 0: + v.append(a/b) + (a, b) = (b, a%b) + return v + +def contfrac_float(x): + """ + Returns the continued fraction of the floating + point number x, computed using the continued + fraction procedure, and the sequence of partial + convergents. + Input: + x -- a floating point number (decimal) + Output: + list -- the continued fraction [a0, a1, ...] + obtained by applying the continued + fraction procedure to x to the + precision of this computer. + list -- the list [(p0,q0), (p1,q1), ...] + of pairs (pm,qm) such that the mth + convergent of continued fraction + is pm/qm. + Examples: + >>> v, w = contfrac_float(3.14159); print v + [3, 7, 15, 1, 25, 1, 7, 4] + >>> v, w = contfrac_float(2.718); print v + [2, 1, 2, 1, 1, 4, 1, 12] + >>> contfrac_float(0.3) + ([0, 3, 2, 1], [(0, 1), (1, 3), (2, 7), (3, 10)]) + """ + v = [] + w = [(0,1), (1,0)] # keep track of convergents + start = x + while True: + a = int(x) # (1) + v.append(a) + n = len(v)-1 + pn = v[n]*w[n+1][0] + w[n][0] + qn = v[n]*w[n+1][1] + w[n][1] + w.append((pn, qn)) + x -= a + if abs(start - float(pn)/float(qn)) == 0: # (2) + del w[0]; del w[0] # (3) + return v, w + x = 1/x + +def sum_of_two_squares(p): + """ + Uses continued fractions to efficiently compute + a representation of the prime p as a sum of + two squares. The prime p must be 1 modulo 4. + Input: + p -- a prime congruent 1 modulo 4. + Output: + integers a, b such that p is a*a + b*b + Examples: + >>> sum_of_two_squares(5) + (1, 2) + >>> sum_of_two_squares(389) + (10, 17) + >>> sum_of_two_squares(86295641057493119033) + (789006548L, 9255976973L) + """ + assert p%4 == 1, "p must be 1 modulo 4" + r = sqrtmod(-1, p) # (1) + v = contfrac_rat(-r, p) # (2) + n = int(sqrt(p)) + for a, b in convergents(v): # (3) + c = r*b + p*a # (4) + if -n <= c and c <= n: return (abs(b),abs(c)) + assert False, "Bug in sum_of_two_squares." # (5) + + +################################################## +## Arithmetic +################################################## + +def ellcurve_add(E, P1, P2): + """ + Returns the sum of P1 and P2 on the elliptic + curve E. + Input: + E -- an elliptic curve over Z/pZ, given by a + triple of integers (a, b, p), with p odd. + P1 --a pair of integers (x, y) or the + string "Identity". + P2 -- same type as P1 + Output: + R -- same type as P1 + Examples: + >>> E = (1, 0, 7) # y**2 = x**3 + x over Z/7Z + >>> P1 = (1, 3); P2 = (3, 3) + >>> ellcurve_add(E, P1, P2) + (3, 4) + >>> ellcurve_add(E, P1, (1, 4)) + 'Identity' + >>> ellcurve_add(E, "Identity", P2) + (3, 3) + """ + a, b, p = E + assert p > 2, "p must be odd." + if P1 == "Identity": return P2 + if P2 == "Identity": return P1 + x1, y1 = P1; x2, y2 = P2 + x1 %= p; y1 %= p; x2 %= p; y2 %= p + if x1 == x2 and y1 == p-y2: return "Identity" + if P1 == P2: + if y1 == 0: return "Identity" + lam = (3*x1**2+a) * inversemod(2*y1,p) + else: + lam = (y1 - y2) * inversemod(x1 - x2, p) + x3 = lam**2 - x1 - x2 + y3 = -lam*x3 - y1 + lam*x1 + return (x3%p, y3%p) + +def ellcurve_mul(E, m, P): + """ + Returns the multiple m*P of the point P on + the elliptic curve E. + Input: + E -- an elliptic curve over Z/pZ, given by a + triple (a, b, p). + m -- an integer + P -- a pair of integers (x, y) or the + string "Identity" + Output: + A pair of integers or the string "Identity". + Examples: + >>> E = (1, 0, 7) + >>> P = (1, 3) + >>> ellcurve_mul(E, 5, P) + (1, 3) + >>> ellcurve_mul(E, 9999, P) + (1, 4) + """ + assert m >= 0, "m must be nonnegative." + power = P + mP = "Identity" + while m != 0: + if m%2 != 0: mP = ellcurve_add(E, mP, power) + power = ellcurve_add(E, power, power) + m /= 2 + return mP + + +################################################## +## Integer Factorization +################################################## + +def lcm_to(B): + """ + Returns the least common multiple of all + integers up to B. + Input: + B -- an integer + Output: + an integer + Examples: + >>> lcm_to(5) + 60 + >>> lcm_to(20) + 232792560 + >>> lcm_to(100) + 69720375229712477164533808935312303556800L + """ + ans = 1 + logB = log(B) + for p in primes(B): + ans *= p**int(logB/log(p)) + return ans + +def pollard(N, m): + """ + Use Pollard's (p-1)-method to try to find a + nontrivial divisor of N. + Input: + N -- a positive integer + m -- a positive integer, the least common + multiple of the integers up to some + bound, computed using lcm_to. + Output: + int -- an integer divisor of n + Examples: + >>> pollard(5917, lcm_to(5)) + 61 + >>> pollard(779167, lcm_to(5)) + 779167 + >>> pollard(779167, lcm_to(15)) + 2003L + >>> pollard(187, lcm_to(15)) + 11 + >>> n = random_prime(5)*random_prime(5)*random_prime(5) + >>> pollard(n, lcm_to(100)) + 315873129119929L #rand + >>> pollard(n, lcm_to(1000)) + 3672986071L #rand + """ + for a in [2, 3]: + x = powermod(a, m, N) - 1 + g = gcd(x, N) + if g != 1 and g != N: + return g + return N + +def randcurve(p): + """ + Construct a somewhat random elliptic curve + over Z/pZ and a random point on that curve. + Input: + p -- a positive integer + Output: + tuple -- a triple E = (a, b, p) + P -- a tuple (x,y) on E + Examples: + >>> p = random_prime(20); p + 17758176404715800329L #rand + >>> E, P = randcurve(p) + >>> print E + (15299007531923218813L, 1, 17758176404715800329L) #rand + >>> print P + (0, 1) + """ + assert p > 2, "p must be > 2." + a = randrange(p) + while gcd(4*a**3 + 27, p) != 1: + a = randrange(p) + return (a, 1, p), (0,1) + +def elliptic_curve_method(N, m, tries=5): + """ + Use the elliptic curve method to try to find a + nontrivial divisor of N. + Input: + N -- a positive integer + m -- a positive integer, the least common + multiple of the integers up to some + bound, computed using lcm_to. + tries -- a positive integer, the number of + different elliptic curves to try + Output: + int -- a divisor of n + Examples: + >>> elliptic_curve_method(5959, lcm_to(20)) + 59L #rand + >>> elliptic_curve_method(10007*20011, lcm_to(100)) + 10007L #rand + >>> p = random_prime(9); q = random_prime(9) + >>> n = p*q; n + 117775675640754751L #rand + >>> elliptic_curve_method(n, lcm_to(100)) + 117775675640754751L #rand + >>> elliptic_curve_method(n, lcm_to(500)) + 117775675640754751L #rand + """ + for _ in range(tries): # (1) + E, P = randcurve(N) # (2) + try: # (3) + Q = ellcurve_mul(E, m, P) # (4) + except ZeroDivisionError, x: # (5) + g = gcd(x[0],N) # (6) + if g != 1 or g != N: return g # (7) + return N + + +################################################## +## ElGamal Elliptic Curve Cryptosystem +################################################## + +def elgamal_init(p): + """ + Constructs an ElGamal cryptosystem over Z/pZ, by + choosing a random elliptic curve E over Z/pZ, a + point B in E(Z/pZ), and a random integer n. This + function returns the public key as a 4-tuple + (E, B, n*B) and the private key n. + Input: + p -- a prime number + Output: + tuple -- the public key as a 3-tuple + (E, B, n*B), where E = (a, b, p) is an + elliptic curve over Z/pZ, B = (x, y) is + a point on E, and n*B = (x',y') is + the sum of B with itself n times. + int -- the private key, which is the pair (E, n) + Examples: + >>> p = random_prime(20); p + 17758176404715800329L #rand + >>> public, private = elgamal_init(p) + >>> print "E =", public[0] + E = (15299007531923218813L, 1, 17758176404715800329L) #rand + >>> print "B =", public[1] + B = (0, 1) + >>> print "nB =", public[2] + nB = (5619048157825840473L, 151469105238517573L) #rand + >>> print "n =", private[1] + n = 12608319787599446459 #rand + """ + E, B = randcurve(p) + n = randrange(2,p) + nB = ellcurve_mul(E, n, B) + return (E, B, nB), (E, n) + +def elgamal_encrypt(plain_text, public_key): + """ + Encrypt a message using the ElGamal cryptosystem + with given public_key = (E, B, n*B). + Input: + plain_text -- a string + public_key -- a triple (E, B, n*B), as output + by elgamal_init. + Output: + list -- a list of pairs of points on E that + represent the encrypted message + Examples: + >>> public, private = elgamal_init(random_prime(20)) + >>> elgamal_encrypt("RUN", public) + [((6004308617723068486L, 15578511190582849677L), \ #rand + (7064405129585539806L, 8318592816457841619L))] #rand + """ + E, B, nB = public_key + a, b, p = E + assert p > 10000, "p must be at least 10000." + v = [1000*x for x in \ + str_to_numlist(plain_text, p/1000)] # (1) + cipher = [] + for x in v: + while not legendre(x**3+a*x+b, p)==1: # (2) + x = (x+1)%p + y = sqrtmod(x**3+a*x+b, p) # (3) + P = (x,y) + r = randrange(1,p) + encrypted = (ellcurve_mul(E, r, B), \ + ellcurve_add(E, P, ellcurve_mul(E,r,nB))) + cipher.append(encrypted) + return cipher + +def elgamal_decrypt(cipher_text, private_key): + """ + Encrypt a message using the ElGamal cryptosystem + with given public_key = (E, B, n*B). + Input: + cipher_text -- list of pairs of points on E output + by elgamal_encrypt. + Output: + str -- the unencrypted plain text + Examples: + >>> public, private = elgamal_init(random_prime(20)) + >>> v = elgamal_encrypt("TOP SECRET MESSAGE!", public) + >>> print elgamal_decrypt(v, private) + TOP SECRET MESSAGE! + """ + E, n = private_key + p = E[2] + plain = [] + for rB, P_plus_rnB in cipher_text: + nrB = ellcurve_mul(E, n, rB) + minus_nrB = (nrB[0], -nrB[1]) + P = ellcurve_add(E, minus_nrB, P_plus_rnB) + plain.append(P[0]/1000) + return numlist_to_str(plain, p/1000) + + +################################################## +## Associativity of the Group Law +################################################## + +# The variable order is x1, x2, x3, y1, y2, y3, a, b +class Poly: # (1) + def __init__(self, d): # (2) + self.v = dict(d) + def __cmp__(self, other): # (3) + self.normalize(); other.normalize() # (4) + if self.v == other.v: return 0 + return -1 + + def __add__(self, other): # (5) + w = Poly(self.v) + for m in other.monomials(): + w[m] += other[m] + return w + def __sub__(self, other): + w = Poly(self.v) + for m in other.monomials(): + w[m] -= other[m] + return w + def __mul__(self, other): + if len(self.v) == 0 or len(other.v) == 0: + return Poly([]) + m1 = self.monomials(); m2 = other.monomials() + r = Poly([]) + for m1 in self.monomials(): + for m2 in other.monomials(): + z = [m1[i] + m2[i] for i in range(8)] + r[z] += self[m1]*other[m2] + return r + def __neg__(self): + v = {} + for m in self.v.keys(): + v[m] = -self.v[m] + return Poly(v) + def __div__(self, other): + return Frac(self, other) + + def __getitem__(self, m): # (6) + m = tuple(m) + if not self.v.has_key(m): self.v[m] = 0 + return self.v[m] + def __setitem__(self, m, c): + self.v[tuple(m)] = c + def __delitem__(self, m): + del self.v[tuple(m)] + + def monomials(self): # (7) + return self.v.keys() + def normalize(self): # (8) + while True: + finished = True + for m in self.monomials(): + if self[m] == 0: + del self[m] + continue + for i in range(3): + if m[3+i] >= 2: + finished = False + nx0 = list(m); nx0[3+i] -= 2; + nx0[7] += 1 + nx1 = list(m); nx1[3+i] -= 2; + nx1[i] += 1; nx1[6] += 1 + nx3 = list(m); nx3[3+i] -= 2; + nx3[i] += 3 + c = self[m] + del self[m] + self[nx0] += c; + self[nx1] += c; + self[nx3] += c + # end for + # end for + if finished: return + # end while + +one = Poly({(0,0,0,0,0,0,0,0):1}) # (9) + +class Frac: # (10) + def __init__(self, num, denom=one): + self.num = num; self.denom = denom + def __cmp__(self, other): # (11) + if self.num * other.denom == self.denom * other.num: + return 0 + return -1 + + def __add__(self, other): # (12) + return Frac(self.num*other.denom + \ + self.denom*other.num, + self.denom*other.denom) + def __sub__(self, other): + return Frac(self.num*other.denom - \ + self.denom*other.num, + self.denom*other.denom) + def __mul__(self, other): + return Frac(self.num*other.num, \ + self.denom*other.denom) + def __div__(self, other): + return Frac(self.num*other.denom, \ + self.denom*other.num) + def __neg__(self): + return Frac(-self.num,self.denom) + +def var(i): # (14) + v = [0,0,0,0,0,0,0,0]; v[i]=1; + return Frac(Poly({tuple(v):1})) + +def prove_associative(): # (15) + x1 = var(0); x2 = var(1); x3 = var(2) + y1 = var(3); y2 = var(4); y3 = var(5) + a = var(6); b = var(7) + + lambda12 = (y1 - y2)/(x1 - x2) + x4 = lambda12*lambda12 - x1 - x2 + nu12 = y1 - lambda12*x1 + y4 = -lambda12*x4 - nu12 + lambda23 = (y2 - y3)/(x2 - x3) + x5 = lambda23*lambda23 - x2 - x3 + nu23 = y2 - lambda23*x2 + y5 = -lambda23*x5 - nu23 + s1 = (x1 - x5)*(x1 - x5)*((y3 - y4)*(y3 - y4) \ + - (x3 + x4)*(x3 - x4)*(x3 - x4)) + s2 = (x3 - x4)*(x3 - x4)*((y1 - y5)*(y1 - y5) \ + - (x1 + x5)*(x1 - x5)*(x1 - x5)) + print "Associative?" + print s1 == s2 # (17) + + + + + + + + + + + + + + + +########################################################## +# The following are all the examples not in functions. # +########################################################## + +def examples(): + """ + >>> from ent import * + >>> 7/5 + 1 + >>> -2/3 + -1 + >>> 1.0/3 + 0.33333333333333331 + >>> float(2)/3 + 0.66666666666666663 + >>> 100**2 + 10000 + >>> 10**20 + 100000000000000000000L + >>> range(10) # range(n) is from 0 to n-1 + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> range(3,10) # range(a,b) is from a to b-1 + [3, 4, 5, 6, 7, 8, 9] + >>> [x**2 for x in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + >>> [x**2 for x in range(10) if x%4 == 1] + [1, 25, 81] + >>> [1,2,3] + [5,6,7] # concatenation + [1, 2, 3, 5, 6, 7] + >>> len([1,2,3,4,5]) # length of a list + 5 + >>> x = [4,7,10,'gcd'] # mixing types is fine + >>> x[0] # 0-based indexing + 4 + >>> x[3] + 'gcd' + >>> x[3] = 'lagrange' # assignment + >>> x.append("fermat") # append to end of list + >>> x + [4, 7, 10, 'lagrange', 'fermat'] + >>> del x[3] # delete entry 3 from list + >>> x + [4, 7, 10, 'fermat'] + >>> v = primes(10000) + >>> len(v) # this is pi(10000) + 1229 + >>> len([x for x in v if x < 1000]) # pi(1000) + 168 + >>> len([x for x in v if x < 5000]) # pi(5000) + 669 + >>> x=(1, 2, 3) # creation + >>> x[1] + 2 + >>> (1, 2, 3) + (4, 5, 6) # concatenation + (1, 2, 3, 4, 5, 6) + >>> (a, b) = (1, 2) # assignment assigns to each member + >>> print a, b + 1 2 + >>> for (c, d) in [(1,2), (5,6)]: + ... print c, d + 1 2 + 5 6 + >>> x = 1, 2 # parentheses optional in creation + >>> x + (1, 2) + >>> c, d = x # parentheses also optional + >>> print c, d + 1 2 + >>> P = [p for p in range(200000) if is_pseudoprime(p)] + >>> Q = primes(200000) + >>> R = [x for x in P if not (x in Q)]; print R + [29341, 46657, 75361, 115921, 162401] + >>> [n for n in R if is_pseudoprime(n,[2,3,5,7,11,13])] + [162401] + >>> factor(162401) + [(17, 1), (41, 1), (233, 1)] + >>> p = random_prime(50) + >>> p + 13537669335668960267902317758600526039222634416221L #rand + >>> n, npow = dh_init(p) + >>> n + 8520467863827253595224582066095474547602956490963L #rand + >>> npow + 3206478875002439975737792666147199399141965887602L #rand + >>> m, mpow = dh_init(p) + >>> m + 3533715181946048754332697897996834077726943413544L #rand + >>> mpow + 3465862701820513569217254081716392362462604355024L #rand + >>> dh_secret(p, n, mpow) + 12931853037327712933053975672241775629043437267478L #rand + >>> dh_secret(p, m, npow) + 12931853037327712933053975672241775629043437267478L #rand + >>> prove_associative() + Associative? + True + >>> len(primes(10000)) + 1229 + >>> 10000/log(10000) + 1085.73620476 + >>> powermod(3,45,100) + 43 + >>> inversemod(37, 112) + 109 + >>> powermod(102, 70, 113) + 98 + >>> powermod(99, 109, 113) + 60 + >>> P = primes(1000) + >>> Q = [p for p in P if primitive_root(p) == 2] + >>> print len(Q), len(P) + 67 168 + >>> P = primes(50000) + >>> Q = [primitive_root(p) for p in P] + >>> Q.index(37) + 3893 + >>> P[3893] + 36721 + >>> for n in range(97): + ... if powermod(5,n,97)==3: print n + 70 + >>> factor(5352381469067) + [(141307, 1), (37877681L, 1)] + >>> d=inversemod(4240501142039, (141307-1)*(37877681-1)) + >>> d + 5195621988839L + >>> convergents([-3,1,1,1,1,3]) + [(-3, 1), (-2, 1), (-5, 2), (-7, 3), \ + (-12, 5), (-43, 18)] + >>> convergents([0,2,4,1,8,2]) + [(0, 1), (1, 2), (4, 9), (5, 11), \ + (44, 97), (93, 205)] + >>> import math + >>> e = math.exp(1) + >>> v, convs = contfrac_float(e) + >>> [(a,b) for a, b in convs if \ + abs(e - a*1.0/b) < 1/(math.sqrt(5)*b**2)] + [(3, 1), (19, 7), (193, 71), (2721, 1001),\ + (49171, 18089), (1084483, 398959),\ + (28245729, 10391023), (325368125, 119696244)] + >>> factor(12345) + [(3, 1), (5, 1), (823, 1)] + >>> factor(729) + [(3, 6)] + >>> factor(5809961789) + [(5809961789L, 1)] + >>> 5809961789 % 4 + 1L + >>> sum_of_two_squares(5809961789) + (51542L, 56155L) + >>> N = [60 + s for s in range(-15,16)] + >>> def is_powersmooth(B, x): + ... for p, e in factor(x): + ... if p**e > B: return False + ... return True + >>> Ns = [x for x in N if is_powersmooth(20, x)] + >>> print len(Ns), len(N), len(Ns)*1.0/len(N) + 14 31 0.451612903226 + >>> P = [x for x in range(10**12, 10**12+1000)\ + if miller_rabin(x)] + >>> Ps = [x for x in P if \ + is_powersmooth(10000, x-1)] + >>> print len(Ps), len(P), len(Ps)*1.0/len(P) + 2 37 0.0540540540541 + + """ + + +if __name__ == '__main__': + import doctest, sys + doctest.testmod(sys.modules[__name__]) diff --git a/helios/crypto/randpool.py b/helios/crypto/randpool.py new file mode 100644 index 000000000..389d41e8b --- /dev/null +++ b/helios/crypto/randpool.py @@ -0,0 +1,422 @@ +# +# randpool.py : Cryptographically strong random number generation +# +# Part of the Python Cryptography Toolkit +# +# Distribute and use freely; there are no restrictions on further +# dissemination and usage except those imposed by the laws of your +# country of residence. This software is provided "as is" without +# warranty of fitness for use or suitability for any purpose, express +# or implied. Use at your own risk or not at all. +# + +__revision__ = "$Id: randpool.py,v 1.14 2004/05/06 12:56:54 akuchling Exp $" + +import time, array, types, warnings, os.path +from number import long_to_bytes +try: + import Crypto.Util.winrandom as winrandom +except: + winrandom = None + +STIRNUM = 3 + +class RandomPool: + """randpool.py : Cryptographically strong random number generation. + + The implementation here is similar to the one in PGP. To be + cryptographically strong, it must be difficult to determine the RNG's + output, whether in the future or the past. This is done by using + a cryptographic hash function to "stir" the random data. + + Entropy is gathered in the same fashion as PGP; the highest-resolution + clock around is read and the data is added to the random number pool. + A conservative estimate of the entropy is then kept. + + If a cryptographically secure random source is available (/dev/urandom + on many Unixes, Windows CryptGenRandom on most Windows), then use + it. + + Instance Attributes: + bits : int + Maximum size of pool in bits + bytes : int + Maximum size of pool in bytes + entropy : int + Number of bits of entropy in this pool. + + Methods: + add_event([s]) : add some entropy to the pool + get_bytes(int) : get N bytes of random data + randomize([N]) : get N bytes of randomness from external source + """ + + + def __init__(self, numbytes = 160, cipher=None, hash=None): + if hash is None: + import sha as hash + + # The cipher argument is vestigial; it was removed from + # version 1.1 so RandomPool would work even in the limited + # exportable subset of the code + if cipher is not None: + warnings.warn("'cipher' parameter is no longer used") + + if isinstance(hash, types.StringType): + # ugly hack to force __import__ to give us the end-path module + hash = __import__('Crypto.Hash.'+hash, + None, None, ['new']) + warnings.warn("'hash' parameter should now be a hashing module") + + self.bytes = numbytes + self.bits = self.bytes*8 + self.entropy = 0 + self._hash = hash + + # Construct an array to hold the random pool, + # initializing it to 0. + self._randpool = array.array('B', [0]*self.bytes) + + self._event1 = self._event2 = 0 + self._addPos = 0 + self._getPos = hash.digest_size + self._lastcounter=time.time() + self.__counter = 0 + + self._measureTickSize() # Estimate timer resolution + self._randomize() + + def _updateEntropyEstimate(self, nbits): + self.entropy += nbits + if self.entropy < 0: + self.entropy = 0 + elif self.entropy > self.bits: + self.entropy = self.bits + + def _randomize(self, N = 0, devname = '/dev/urandom'): + """_randomize(N, DEVNAME:device-filepath) + collects N bits of randomness from some entropy source (e.g., + /dev/urandom on Unixes that have it, Windows CryptoAPI + CryptGenRandom, etc) + DEVNAME is optional, defaults to /dev/urandom. You can change it + to /dev/random if you want to block till you get enough + entropy. + """ + data = '' + if N <= 0: + nbytes = int((self.bits - self.entropy)/8+0.5) + else: + nbytes = int(N/8+0.5) + if winrandom: + # Windows CryptGenRandom provides random data. + data = winrandom.new().get_bytes(nbytes) + # GAE fix, benadida + #elif os.path.exists(devname): + # # Many OSes support a /dev/urandom device + # try: + # f=open(devname) + # data=f.read(nbytes) + # f.close() + # except IOError, (num, msg): + # if num!=2: raise IOError, (num, msg) + # # If the file wasn't found, ignore the error + if data: + self._addBytes(data) + # Entropy estimate: The number of bits of + # data obtained from the random source. + self._updateEntropyEstimate(8*len(data)) + self.stir_n() # Wash the random pool + + def randomize(self, N=0): + """randomize(N:int) + use the class entropy source to get some entropy data. + This is overridden by KeyboardRandomize(). + """ + return self._randomize(N) + + def stir_n(self, N = STIRNUM): + """stir_n(N) + stirs the random pool N times + """ + for i in xrange(N): + self.stir() + + def stir (self, s = ''): + """stir(s:string) + Mix up the randomness pool. This will call add_event() twice, + but out of paranoia the entropy attribute will not be + increased. The optional 's' parameter is a string that will + be hashed with the randomness pool. + """ + + entropy=self.entropy # Save inital entropy value + self.add_event() + + # Loop over the randomness pool: hash its contents + # along with a counter, and add the resulting digest + # back into the pool. + for i in range(self.bytes / self._hash.digest_size): + h = self._hash.new(self._randpool) + h.update(str(self.__counter) + str(i) + str(self._addPos) + s) + self._addBytes( h.digest() ) + self.__counter = (self.__counter + 1) & 0xFFFFffffL + + self._addPos, self._getPos = 0, self._hash.digest_size + self.add_event() + + # Restore the old value of the entropy. + self.entropy=entropy + + + def get_bytes (self, N): + """get_bytes(N:int) : string + Return N bytes of random data. + """ + + s='' + i, pool = self._getPos, self._randpool + h=self._hash.new() + dsize = self._hash.digest_size + num = N + while num > 0: + h.update( self._randpool[i:i+dsize] ) + s = s + h.digest() + num = num - dsize + i = (i + dsize) % self.bytes + if i>1, bits+1 + if bits>8: bits=8 + + self._event1, self._event2 = event, self._event1 + + self._updateEntropyEstimate(bits) + return bits + + # Private functions + def _noise(self): + # Adds a bit of noise to the random pool, by adding in the + # current time and CPU usage of this process. + # The difference from the previous call to _noise() is taken + # in an effort to estimate the entropy. + t=time.time() + delta = (t - self._lastcounter)/self._ticksize*1e6 + self._lastcounter = t + self._addBytes(long_to_bytes(long(1000*time.time()))) + self._addBytes(long_to_bytes(long(1000*time.clock()))) + self._addBytes(long_to_bytes(long(1000*time.time()))) + self._addBytes(long_to_bytes(long(delta))) + + # Reduce delta to a maximum of 8 bits so we don't add too much + # entropy as a result of this call. + delta=delta % 0xff + return int(delta) + + + def _measureTickSize(self): + # _measureTickSize() tries to estimate a rough average of the + # resolution of time that you can see from Python. It does + # this by measuring the time 100 times, computing the delay + # between measurements, and taking the median of the resulting + # list. (We also hash all the times and add them to the pool) + interval = [None] * 100 + h = self._hash.new(`(id(self),id(interval))`) + + # Compute 100 differences + t=time.time() + h.update(`t`) + i = 0 + j = 0 + while i < 100: + t2=time.time() + h.update(`(i,j,t2)`) + j += 1 + delta=int((t2-t)*1e6) + if delta: + interval[i] = delta + i += 1 + t=t2 + + # Take the median of the array of intervals + interval.sort() + self._ticksize=interval[len(interval)/2] + h.update(`(interval,self._ticksize)`) + # mix in the measurement times and wash the random pool + self.stir(h.digest()) + + def _addBytes(self, s): + "XOR the contents of the string S into the random pool" + i, pool = self._addPos, self._randpool + for j in range(0, len(s)): + pool[i]=pool[i] ^ ord(s[j]) + i=(i+1) % self.bytes + self._addPos = i + + # Deprecated method names: remove in PCT 2.1 or later. + def getBytes(self, N): + warnings.warn("getBytes() method replaced by get_bytes()", + DeprecationWarning) + return self.get_bytes(N) + + def addEvent (self, event, s=""): + warnings.warn("addEvent() method replaced by add_event()", + DeprecationWarning) + return self.add_event(s + str(event)) + +class PersistentRandomPool (RandomPool): + def __init__ (self, filename=None, *args, **kwargs): + RandomPool.__init__(self, *args, **kwargs) + self.filename = filename + if filename: + try: + # the time taken to open and read the file might have + # a little disk variability, modulo disk/kernel caching... + f=open(filename, 'rb') + self.add_event() + data = f.read() + self.add_event() + # mix in the data from the file and wash the random pool + self.stir(data) + f.close() + except IOError: + # Oh, well; the file doesn't exist or is unreadable, so + # we'll just ignore it. + pass + + def save(self): + if self.filename == "": + raise ValueError, "No filename set for this object" + # wash the random pool before save, provides some forward secrecy for + # old values of the pool. + self.stir_n() + f=open(self.filename, 'wb') + self.add_event() + f.write(self._randpool.tostring()) + f.close() + self.add_event() + # wash the pool again, provide some protection for future values + self.stir() + +# non-echoing Windows keyboard entry +_kb = 0 +if not _kb: + try: + import msvcrt + class KeyboardEntry: + def getch(self): + c = msvcrt.getch() + if c in ('\000', '\xe0'): + # function key + c += msvcrt.getch() + return c + def close(self, delay = 0): + if delay: + time.sleep(delay) + while msvcrt.kbhit(): + msvcrt.getch() + _kb = 1 + except: + pass + +# non-echoing Posix keyboard entry +if not _kb: + try: + import termios + class KeyboardEntry: + def __init__(self, fd = 0): + self._fd = fd + self._old = termios.tcgetattr(fd) + new = termios.tcgetattr(fd) + new[3]=new[3] & ~termios.ICANON & ~termios.ECHO + termios.tcsetattr(fd, termios.TCSANOW, new) + def getch(self): + termios.tcflush(0, termios.TCIFLUSH) # XXX Leave this in? + return os.read(self._fd, 1) + def close(self, delay = 0): + if delay: + time.sleep(delay) + termios.tcflush(self._fd, termios.TCIFLUSH) + termios.tcsetattr(self._fd, termios.TCSAFLUSH, self._old) + _kb = 1 + except: + pass + +class KeyboardRandomPool (PersistentRandomPool): + def __init__(self, *args, **kwargs): + PersistentRandomPool.__init__(self, *args, **kwargs) + + def randomize(self, N = 0): + "Adds N bits of entropy to random pool. If N is 0, fill up pool." + import os, string, time + if N <= 0: + bits = self.bits - self.entropy + else: + bits = N*8 + if bits == 0: + return + print bits,'bits of entropy are now required. Please type on the keyboard' + print 'until enough randomness has been accumulated.' + kb = KeyboardEntry() + s='' # We'll save the characters typed and add them to the pool. + hash = self._hash + e = 0 + try: + while e < bits: + temp=str(bits-e).rjust(6) + os.write(1, temp) + s=s+kb.getch() + e += self.add_event(s) + os.write(1, 6*chr(8)) + self.add_event(s+hash.new(s).digest() ) + finally: + kb.close() + print '\n\007 Enough. Please wait a moment.\n' + self.stir_n() # wash the random pool. + kb.close(4) + +if __name__ == '__main__': + pool = RandomPool() + print 'random pool entropy', pool.entropy, 'bits' + pool.add_event('something') + print `pool.get_bytes(100)` + import tempfile, os + fname = tempfile.mktemp() + pool = KeyboardRandomPool(filename=fname) + print 'keyboard random pool entropy', pool.entropy, 'bits' + pool.randomize() + print 'keyboard random pool entropy', pool.entropy, 'bits' + pool.randomize(128) + pool.save() + saved = open(fname, 'rb').read() + print 'saved', `saved` + print 'pool ', `pool._randpool.tostring()` + newpool = PersistentRandomPool(fname) + print 'persistent random pool entropy', pool.entropy, 'bits' + os.remove(fname) diff --git a/helios/crypto/utils.py b/helios/crypto/utils.py new file mode 100644 index 000000000..f34583ebf --- /dev/null +++ b/helios/crypto/utils.py @@ -0,0 +1,25 @@ +""" +Crypto Utils +""" + +import sha, hmac, base64 + +from django.utils import simplejson + +from hashlib import sha256 + +def hash_b64(s): + """ + hash the string using sha1 and produce a base64 output + removes the trailing "=" + """ + hasher = sha256(s) + result= base64.b64encode(hasher.digest())[:-1] + return result + +def to_json(d): + return simplejson.dumps(d, sort_keys=True) + +def from_json(json_str): + if not json_str: return None + return simplejson.loads(json_str) diff --git a/helios/datetimewidget.py b/helios/datetimewidget.py new file mode 100644 index 000000000..5a9e0d40a --- /dev/null +++ b/helios/datetimewidget.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# utils/widgets.py + +''' +DateTimeWidget using JSCal2 from http://www.dynarch.com/projects/calendar/ + +django snippets 1629 +''' + +from django.utils.encoding import force_unicode +from django.conf import settings +from django import forms +import datetime, time +from django.utils.safestring import mark_safe + +# DATETIMEWIDGET +calbtn = u'''calendar +''' + +class DateTimeWidget(forms.widgets.TextInput): + class Media: + css = { + 'all': ( + '/static/helios/jscal/css/jscal2.css', + '/static/helios/jscal/css/border-radius.css', + '/static/helios/jscal/css/win2k/win2k.css', + ) + } + js = ( + '/static/helios/jscal/js/jscal2.js', + '/static/helios/jscal/js/lang/en.js', + ) + + dformat = '%Y-%m-%d %H:%M' + def render(self, name, value, attrs=None): + if value is None: value = '' + final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) + if value != '': + try: + final_attrs['value'] = \ + force_unicode(value.strftime(self.dformat)) + except: + final_attrs['value'] = \ + force_unicode(value) + if not final_attrs.has_key('id'): + final_attrs['id'] = u'%s_id' % (name) + id = final_attrs['id'] + + jsdformat = self.dformat #.replace('%', '%%') + cal = calbtn % (settings.MEDIA_URL, id, id, jsdformat, id) + a = u'%s%s' % (forms.util.flatatt(final_attrs), self.media, cal) + return mark_safe(a) + + def value_from_datadict(self, data, files, name): + dtf = forms.fields.DEFAULT_DATETIME_INPUT_FORMATS + empty_values = forms.fields.EMPTY_VALUES + + value = data.get(name, None) + if value in empty_values: + return None + if isinstance(value, datetime.datetime): + return value + if isinstance(value, datetime.date): + return datetime.datetime(value.year, value.month, value.day) + for format in dtf: + try: + return datetime.datetime(*time.strptime(value, format)[:6]) + except ValueError: + continue + return None + + def _has_changed(self, initial, data): + """ + Return True if data differs from initial. + Copy of parent's method, but modify value with strftime function before final comparsion + """ + if data is None: + data_value = u'' + else: + data_value = data + + if initial is None: + initial_value = u'' + else: + initial_value = initial + + try: + if force_unicode(initial_value.strftime(self.dformat)) != force_unicode(data_value.strftime(self.dformat)): + return True + except: + if force_unicode(initial_value) != force_unicode(data_value): + return True + + return False diff --git a/helios/election_urls.py b/helios/election_urls.py new file mode 100644 index 000000000..2e2580ed6 --- /dev/null +++ b/helios/election_urls.py @@ -0,0 +1,78 @@ +""" +Helios URLs for Election related stuff + +Ben Adida (ben@adida.net) +""" + +from django.conf.urls.defaults import * + +from helios.views import * + +urlpatterns = patterns('', + (r'^$', one_election), + + # edit election params + (r'^/edit$', one_election_edit), + (r'^/schedule$', one_election_schedule), + + # adding trustees + (r'^/trustees/$', list_trustees), + (r'^/trustees/view$', list_trustees_view), + (r'^/trustees/new$', new_trustee), + (r'^/trustees/add-helios$', new_trustee_helios), + (r'^/trustees/delete$', delete_trustee), + + # trustee pages + (r'^/trustees/(?P[^/]+)/home$', trustee_home), + (r'^/trustees/(?P[^/]+)/sendurl$', trustee_send_url), + (r'^/trustees/(?P[^/]+)/keygenerator$', trustee_keygenerator), + (r'^/trustees/(?P[^/]+)/check-sk$', trustee_check_sk), + (r'^/trustees/(?P[^/]+)/upoad-pk$', trustee_upload_pk), + (r'^/trustees/(?P[^/]+)/decrypt-and-prove$', trustee_decrypt_and_prove), + (r'^/trustees/(?P[^/]+)/upload-decryption$', trustee_upload_decryption), + + # election voting-process actions + (r'^/view$', one_election_view), + (r'^/result$', one_election_result), + (r'^/result_proof$', one_election_result_proof), + (r'^/bboard$', one_election_bboard), + (r'^/audited-ballots/$', one_election_audited_ballots), + + # server-side encryption + (r'^/encrypt-ballot$', encrypt_ballot), + + # construct election + (r'^/questions$', one_election_questions), + (r'^/set_reg$', one_election_set_reg), + (r'^/set_featured$', one_election_set_featured), + (r'^/save_questions$', one_election_save_questions), + (r'^/register$', one_election_register), + (r'^/freeze$', one_election_freeze), # includes freeze_2 as POST target + + # computing tally + (r'^/compute_tally$', one_election_compute_tally), + (r'^/combine_decryptions$', combine_decryptions), + + # casting a ballot before we know who the voter is + (r'^/cast$', one_election_cast), + (r'^/cast_confirm$', one_election_cast_confirm), + (r'^/cast_done$', one_election_cast_done), + + # post audited ballot + (r'^/post-audited-ballot', post_audited_ballot), + + # managing voters + (r'^/voters/$', voter_list), + (r'^/voters/upload$', voters_upload), + (r'^/voters/list$', voters_list_pretty), + (r'^/voters/search$', voters_search), + (r'^/voters/email$', voters_email), + (r'^/voters/(?P[^/]+)$', one_voter), + (r'^/voters/(?P[^/]+)/delete$', voter_delete), + + # ballots + (r'^/ballots/$', ballot_list), + (r'^/ballots/(?P[^/]+)/all$', voter_votes), + (r'^/ballots/(?P[^/]+)/last$', voter_last_vote), + +) diff --git a/helios/fields.py b/helios/fields.py new file mode 100644 index 000000000..676a50608 --- /dev/null +++ b/helios/fields.py @@ -0,0 +1,30 @@ +from time import strptime, strftime +import datetime +from django import forms +from django.db import models +from django.forms import fields +from widgets import SplitSelectDateTimeWidget + +class SplitDateTimeField(fields.MultiValueField): + widget = SplitSelectDateTimeWidget + + def __init__(self, *args, **kwargs): + """ + Have to pass a list of field types to the constructor, else we + won't get any data to our compress method. + """ + all_fields = (fields.DateField(), fields.TimeField()) + super(SplitDateTimeField, self).__init__(all_fields, *args, **kwargs) + + def compress(self, data_list): + """ + Takes the values from the MultiWidget and passes them as a + list to this function. This function needs to compress the + list into a single object to save. + """ + if data_list: + if not (data_list[0] and data_list[1]): + raise forms.ValidationError("Field is missing data.") + return datetime.datetime.combine(*data_list) + return None + diff --git a/helios/forms.py b/helios/forms.py new file mode 100644 index 000000000..de3f435c9 --- /dev/null +++ b/helios/forms.py @@ -0,0 +1,28 @@ +""" +Forms for Helios +""" + +from django import forms +from models import Election +from widgets import * +from fields import * + +class ElectionForm(forms.Form): + short_name = forms.SlugField(max_length=25, help_text='no spaces, will be part of the URL for your election, e.g. my-club-2010') + name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'size':60}), help_text='the pretty name for your election, e.g. My Club 2010 Election') + description = forms.CharField(max_length=2000, widget=forms.Textarea(attrs={'cols': 70, 'wrap': 'soft'})) + use_voter_aliases = forms.BooleanField(required=False, initial=False, help_text='if selected, voter identities will be replaced with aliases, e.g. "V12", in the ballot tracking center') + + +class ElectionTimesForm(forms.Form): + # times + voting_starts_at = SplitDateTimeField(help_text = 'UTC date and time when voting begins', + widget=SplitSelectDateTimeWidget) + voting_ends_at = SplitDateTimeField(help_text = 'UTC date and time when voting ends', + widget=SplitSelectDateTimeWidget) + + +class EmailVotersForm(forms.Form): + subject = forms.CharField(max_length=80) + body = forms.CharField(max_length=2000, widget=forms.Textarea) + diff --git a/helios/management/__init__.py b/helios/management/__init__.py new file mode 100644 index 000000000..8c6e88fca --- /dev/null +++ b/helios/management/__init__.py @@ -0,0 +1,5 @@ +""" +Management commands for Helios + +many of these should be run out of cron +""" diff --git a/helios/management/commands/__init__.py b/helios/management/commands/__init__.py new file mode 100644 index 000000000..bf2b23205 --- /dev/null +++ b/helios/management/commands/__init__.py @@ -0,0 +1,3 @@ +""" +commands +""" diff --git a/helios/management/commands/helios_trustee_decrypt.py b/helios/management/commands/helios_trustee_decrypt.py new file mode 100644 index 000000000..3dc75a4af --- /dev/null +++ b/helios/management/commands/helios_trustee_decrypt.py @@ -0,0 +1,37 @@ +""" +decrypt elections where Helios is trustee + +DEPRECATED + +Ben Adida +ben@adida.net +2010-05-22 +""" + +from django.core.management.base import BaseCommand, CommandError +import csv, datetime + +from helios import utils as helios_utils + +from helios.models import * + +class Command(BaseCommand): + args = '' + help = 'decrypt elections where helios is the trustee' + + def handle(self, *args, **options): + # query for elections where decryption is ready to go and Helios is the trustee + active_helios_trustees = Trustee.objects.exclude(secret_key = None).exclude(election__encrypted_tally = None).filter(decryption_factors = None) + + # for each one, do the decryption + for t in active_helios_trustees: + tally = t.election.encrypted_tally + + # FIXME: this should probably be in the encrypted_tally getter + tally.init_election(t.election) + + factors, proof = tally.decryption_factors_and_proofs(t.secret_key) + t.decryption_factors = factors + t.decryption_proofs = proof + t.save() + diff --git a/helios/management/commands/load_voter_files.py b/helios/management/commands/load_voter_files.py new file mode 100644 index 000000000..5b82285a6 --- /dev/null +++ b/helios/management/commands/load_voter_files.py @@ -0,0 +1,90 @@ +""" +parse and set up voters from uploaded voter files + +DEPRECATED + +Ben Adida +ben@adida.net +2010-05-22 +""" + +from django.core.management.base import BaseCommand, CommandError +import csv, datetime + +from helios import utils as helios_utils + +from helios.models import * + +## +## UTF8 craziness for CSV +## + +def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): + # csv.py doesn't do Unicode; encode temporarily as UTF-8: + csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), + dialect=dialect, **kwargs) + for row in csv_reader: + # decode UTF-8 back to Unicode, cell by cell: + yield [unicode(cell, 'utf-8') for cell in row] + +def utf_8_encoder(unicode_csv_data): + for line in unicode_csv_data: + yield line.encode('utf-8') + +def process_csv_file(election, f): + reader = unicode_csv_reader(f) + + num_voters = 0 + for voter in reader: + # bad line + if len(voter) < 1: + continue + + num_voters += 1 + voter_id = voter[0] + name = voter_id + email = voter_id + + if len(voter) > 1: + email = voter[1] + + if len(voter) > 2: + name = voter[2] + + # create the user + user = User.update_or_create(user_type='password', user_id=voter_id, info = {'password': helios_utils.random_string(10), 'email': email, 'name': name}) + user.save() + + # does voter for this user already exist + voter = Voter.get_by_election_and_user(election, user) + + # create the voter + if not voter: + voter_uuid = str(uuid.uuid1()) + voter = Voter(uuid= voter_uuid, voter_type = 'password', voter_id = voter_id, name = name, election = election) + voter.save() + + return num_voters + + +class Command(BaseCommand): + args = '' + help = 'load up voters from unprocessed voter files' + + def handle(self, *args, **options): + # load up the voter files in order of last uploaded + files_to_process = VoterFile.objects.filter(processing_started_at=None).order_by('uploaded_at') + + for file_to_process in files_to_process: + # mark processing begins + file_to_process.processing_started_at = datetime.datetime.utcnow() + file_to_process.save() + + num_voters = process_csv_file(file_to_process.election, file_to_process.voter_file) + + # mark processing done + file_to_process.processing_finished_at = datetime.datetime.utcnow() + file_to_process.num_voters = num_voters + file_to_process.save() + + diff --git a/helios/management/commands/verify_cast_votes.py b/helios/management/commands/verify_cast_votes.py new file mode 100644 index 000000000..5b7f39253 --- /dev/null +++ b/helios/management/commands/verify_cast_votes.py @@ -0,0 +1,38 @@ +""" +verify cast votes that have not yet been verified + +Ben Adida +ben@adida.net +2010-05-22 +""" + +from django.core.management.base import BaseCommand, CommandError +import csv, datetime + +from helios import utils as helios_utils + +from helios.models import * + +def get_cast_vote_to_verify(): + # fixme: add "select for update" functionality here + votes = CastVote.objects.filter(verified_at=None, invalidated_at=None).order_by('-cast_at') + if len(votes) > 0: + return votes[0] + else: + return None + +class Command(BaseCommand): + args = '' + help = 'verify votes that were cast' + + def handle(self, *args, **options): + while True: + cast_vote = get_cast_vote_to_verify() + if not cast_vote: + break + + cast_vote.verify_and_store() + + # once broken out of the while loop, quit and wait for next invocation + # this happens when there are no votes left to verify + diff --git a/helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png new file mode 100644 index 0000000000000000000000000000000000000000..954e22dbd99e8c6dd7091335599abf2d10bf8003 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEr#)R9Ln2z=UU%d=WFXS=@V?HT z#xG*`>Yvsgk=}99w^d^D^d*@m74oMo<%#FcopJf?u00-~YVKV2wzrI*_R6;UORMea zBFVSEnN~eiVA6V&z`E)YLz5Aok^D)In}Yn=OzDpgR5Wv0XfT8pOkmV{sKAJ-PO9#T zZK}IXj&Q-V!U)!LcB_3K0&C*{ literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/helios/media/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png new file mode 100644 index 0000000000000000000000000000000000000000..64ece5707d91a6edf9fad4bfcce0c4dbcafcf58d GIT binary patch literal 251 zcmVbvPcjKS|RKP(6sDcCAB(_QB%0978a<$Ah$!b|E zwn;|HO0i8cQj@~)s!ajF0S002ovPDHLkV1oEp BYH0uf literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png b/helios/media/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..abdc01082bf3534eafecc5819d28c9574d44ea89 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F!3HG1q!d*FsY*{5$B>N1x91EQ4=4yQY-ImG zFPf9b{J;c_6SHRK%WcbN_hZpM=(Ry;4Rxv2@@2Y=$K57eF$X$=!PC{xWt~$(69B)$ BI)4BF literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/helios/media/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..9b383f4d2eab09c0f2a739d6b232c32934bc620b GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjgAK^akKnour1U*q978O6-yYw{%b*}|_(02F z@qbE9)0CJMo;*v*PWv`Vh2h6EmG8IS-Cm{3U~` zFlmZ}YMcJY=eo?o%*@I?2`NblNeMudl#t?{+tN>ySr~=F{k$>;_x^_y?afmf9pRKH0)6?eSP?3s5hEr>mdKI;Vst E0O;M1& literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/helios/media/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png new file mode 100644 index 0000000000000000000000000000000000000000..39d5824d6af5456f1e89fc7847ea3599ea5fd815 GIT binary patch literal 3762 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 literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/helios/media/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..f1273672d253263b7564e9e21d69d7d9d0b337d9 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^i!3HGVb)pi0l%l7LV~E7mxPQ=F85a&M@g_{ d|GeK{$Y5lo%PMu^>wln`44$rjF6*2UngE4^EGqy2 literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-icons_222222_256x240.png b/helios/media/css/ui-lightness/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..b273ff111d219c9b9a8b96d57683d0075fb7871a GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-icons_228ef1_256x240.png b/helios/media/css/ui-lightness/images/ui-icons_228ef1_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..c357355aa7bb9201f3d40b8996449ffa1035dd6a GIT binary patch literal 5355 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 Y^OOM2(oM+v|NSx_Y8qC-Ajq!3AfU8Dx90^_ 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 literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/images/ui-icons_ffd27a_256x240.png b/helios/media/css/ui-lightness/images/ui-icons_ffd27a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..e117effa3dca24e7978cfc5f8b967f661e81044f GIT binary patch literal 4369 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 literal 0 HcmV?d00001 diff --git a/helios/media/css/ui-lightness/jquery-ui-1.8.1.custom.css b/helios/media/css/ui-lightness/jquery-ui-1.8.1.custom.css new file mode 100644 index 000000000..0bf8a2202 --- /dev/null +++ b/helios/media/css/ui-lightness/jquery-ui-1.8.1.custom.css @@ -0,0 +1,486 @@ +/* +* jQuery UI CSS Framework +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* +* jQuery UI CSS Framework +* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px +*/ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } +.ui-widget-content a { color: #333333; } +.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } +.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } +.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* Resizable +----------------------------------*/ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Accordion +----------------------------------*/ +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } +/* IE7-/Win - Fix extra vertical space in lists */ +.ui-accordion a { zoom: 1; } +.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } +.ui-accordion .ui-accordion-content-active { display: block; }/* Autocomplete +----------------------------------*/ +.ui-autocomplete { position: absolute; cursor: default; } +.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; } + +/* workarounds */ +* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ + +/* Menu +----------------------------------*/ +.ui-menu { + list-style:none; + padding: 2px; + margin: 0; + display:block; +} +.ui-menu .ui-menu { + margin-top: -3px; +} +.ui-menu .ui-menu-item { + margin:0; + padding: 0; + zoom: 1; + float: left; + clear: left; + width: 100%; +} +.ui-menu .ui-menu-item a { + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; +} +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} +/* Button +----------------------------------*/ + +.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ +.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ +button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ +.ui-button-icons-only { width: 3.4em; } +button.ui-button-icons-only { width: 3.7em; } + +/*button text element */ +.ui-button .ui-button-text { display: block; line-height: 1.4; } +.ui-button-text-only .ui-button-text { padding: .4em 1em; } +.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } +.ui-button-text-icon .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } +.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } +/* no icon support for input elements, provide padding by default */ +input.ui-button { padding: .4em 1em; } + +/*button icon element(s) */ +.ui-button-icon-only .ui-icon, .ui-button-text-icon .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } +.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } +.ui-button-text-icon .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } +.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } + +/*button sets*/ +.ui-buttonset { margin-right: 7px; } +.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } + +/* workarounds */ +button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ + + + + + +/* Dialog +----------------------------------*/ +.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } +.ui-dialog .ui-dialog-titlebar { padding: .5em 1em .3em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .2em 0; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs +----------------------------------*/ +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* Datepicker +----------------------------------*/ +.ui-datepicker { width: 17em; padding: .2em .2em 0; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* Progressbar +----------------------------------*/ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file diff --git a/helios/media/helios/bigint.class b/helios/media/helios/bigint.class new file mode 100644 index 0000000000000000000000000000000000000000..f8e435bd63fd7fded0b46a12ac80fa843aa0d101 GIT binary patch literal 797 zcmb7?T}uK{5Qg95cdKPtTKU!2jb^oUZ3Gnrf<$BpUaorBPUsrjTJ*2#Y9XN?(2t5{ zc7+xyR7j13yT!`M*+-QP!j zp+VL+xV6<#&bk~`8vn7;TBEVulh=x;knAc`SKz{KI1U9^6pgAtwW!Yod zr!(+f1u`CPJUtDhqhEN7KyL2X=&W*c$3cnB`5m^ghXmaUexeY(ebN+tm1q?lJ#-;H z=!FiCH0A{pq$^01GP+CGiP6Lap^l62dw|v`yNb0by-!yyOoTQ;q*E)phz9u;A~RT@ m#}|%V#DaW=$PHwt`~fu*A^#-%7mCL&;=zI(p*ccA*!%@9I'; + // var applet_html = ' No Java Support. '; + $("#applet_div").html(applet_html); + } + + return use_applet; +}; + +// Set up the pointer to the applet if necessary, and some +// basic Big Ints that everyone needs (0, 1, 2, and 42) +BigInt._setup = function() { + if (BigInt.use_applet) { + BigInt.APPLET = document.applets["bigint"]; + } + + try { + BigInt.ZERO = new BigInt("0",10); + BigInt.ONE = new BigInt("1",10); + BigInt.TWO = new BigInt("2",10); + BigInt.FORTY_TWO = new BigInt("42",10); + + BigInt.ready_p = true; + } catch (e) { + // not ready + // count how many times we've tried + if (this.num_invocations == null) + this.num_invocations = 0; + + this.num_invocations += 1; + + if (this.num_invocations > 5) { + if (BigInt.setup_interval) + window.clearInterval(BigInt.setup_interval); + + if (BigInt.setup_fail) { + BigInt.setup_fail(); + } else { + alert('bigint failed!'); + } + } + return; + } + + if (BigInt.setup_interval) + window.clearInterval(BigInt.setup_interval); + + if (BigInt.setup_callback) + BigInt.setup_callback(); +}; + +BigInt.setup = function(callback, fail_callback) { + if (callback) + BigInt.setup_callback = callback; + + if (fail_callback) + BigInt.setup_fail = fail_callback; + + BigInt.setup_interval = window.setInterval("BigInt._setup()", 1000); +} + +// .onload instead of .ready, as I don't think the applet is ready until onload. +// FIXME: something wrong here in the first load +$(document).ready(function() { + BigInt.use_applet = check_applet(); +}); + diff --git a/helios/media/helios/class.js b/helios/media/helios/class.js new file mode 100644 index 000000000..c1b33a002 --- /dev/null +++ b/helios/media/helios/class.js @@ -0,0 +1,65 @@ + +/* + * John Resig's Class Inheritance + */ + +// Inspired by base2 and Prototype +(function(){ + var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; + + // The base Class implementation (does nothing) + this.Class = function(){}; + + // Create a new Class that inherits from this class + Class.extend = function(prop) { + var _super = this.prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + initializing = true; + var prototype = new this(); + initializing = false; + + // Copy the properties over onto the new prototype + for (var name in prop) { + // Check if we're overwriting an existing function + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]) : + prop[name]; + } + + // The dummy class constructor + function Class() { + // All construction is actually done in the init method + if ( !initializing && this.init ) + this.init.apply(this, arguments); + } + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + Class.constructor = Class; + + // And make this class extendable + Class.extend = arguments.callee; + + return Class; + }; +})(); diff --git a/helios/media/helios/elgamal.js b/helios/media/helios/elgamal.js new file mode 100644 index 000000000..dd022332c --- /dev/null +++ b/helios/media/helios/elgamal.js @@ -0,0 +1,538 @@ + +// +// inspired by George Danezis, rewritten by Ben Adida. +// + +ElGamal = {}; + +ElGamal.Params = Class.extend({ + init: function(p, q, g) { + this.p = p; + this.q = q; + this.g = g; + }, + + generate: function() { + // get the value x + var x = Random.getRandomInteger(this.q); + var y = this.g.modPow(x, this.p); + var pk = new ElGamal.PublicKey(this.p, this.q, this.g, y); + var sk = new ElGamal.SecretKey(x, pk); + return sk; + }, + + toJSONObject: function() { + return {g: this.g.toJSONObject(), p: this.p.toJSONObject(), q: this.q.toJSONObject()}; + } +}); + +ElGamal.Params.fromJSONObject = function(d) { + var params = new ElGamal.Params(); + params.p = BigInt.fromJSONObject(d.p); + params.q = BigInt.fromJSONObject(d.q); + params.g = BigInt.fromJSONObject(d.g); + return params; +}; + +ElGamal.PublicKey = Class.extend({ + init : function(p,q,g,y) { + this.p = p; + this.q = q; + this.g = g; + this.y = y; + }, + + toJSONObject: function() { + return {g : this.g.toJSONObject(), p : this.p.toJSONObject(), q : this.q.toJSONObject(), y : this.y.toJSONObject()}; + }, + + verifyKnowledgeOfSecretKey: function(proof, challenge_generator) { + // if challenge_generator is present, we have to check that the challenge was properly generated. + if (challenge_generator != null) { + if (!proof.challenge.equals(challenge_generator(proof.commitment))) { + return false; + } + } + + // verify that g^response = s * y^challenge + var check = this.g.modPow(proof.response, this.p).equals(this.y.modPow(proof.challenge, this.p).multiply(proof.commitment).mod(this.p)); + + return check; + }, + + // check if the decryption factor is correct for this public key, given the proof + verifyDecryptionFactor: function(ciphertext, decryption_factor, decryption_proof, challenge_generator) { + return decryption_proof.verify(this.g, ciphertext.alpha, this.y, decryption_factor, this.p, this.q, challenge_generator); + }, + + multiply: function(other) { + // base condition + if (other == 0 || other == 1) { + return this; + } + + // check params + if (!this.p.equals(other.p)) + throw "mismatched params"; + if (!this.g.equals(other.g)) + throw "mismatched params"; + + var new_pk = new ElGamal.PublicKey(this.p, this.q, this.g, this.y.multiply(other.y).mod(this.p)); + return new_pk; + }, + + equals: function(other) { + return (this.p.equals(other.p) && this.q.equals(other.q) && this.g.equals(other.g) && this.y.equals(other.y)); + } + +}); + +ElGamal.PublicKey.fromJSONObject = function(d) { + var pk = new ElGamal.PublicKey(); + pk.p = BigInt.fromJSONObject(d.p); + pk.q = BigInt.fromJSONObject(d.q); + pk.g = BigInt.fromJSONObject(d.g); + pk.y = BigInt.fromJSONObject(d.y); + return pk; +}; + +ElGamal.SecretKey = Class.extend({ + init: function(x, pk) { + this.x = x; + this.pk = pk; + }, + + toJSONObject: function() { + return {public_key: this.pk.toJSONObject(), x: this.x.toJSONObject()}; + }, + + // a decryption factor is *not yet* mod-inverted, because it needs to be part of the proof. + decryptionFactor: function(ciphertext) { + var decryption_factor = ciphertext.alpha.modPow(this.x, this.pk.p); + return decryption_factor; + }, + + decrypt: function(ciphertext, decryption_factor) { + if (!decryption_factor) + decryption_factor = this.decryptionFactor(ciphertext); + + // use the ciphertext's built-in decryption given a list of decryption factors. + return ciphertext.decrypt([decryption_factor]); + }, + + decryptAndProve: function(ciphertext, challenge_generator) { + var dec_factor_and_proof = this.decryptionFactorAndProof(ciphertext, challenge_generator); + + // decrypt, but using the already computed decryption factor + var plaintext = this.decrypt(ciphertext, dec_factor_and_proof.decryption_factor); + + return { + 'plaintext': plaintext, + 'proof': dec_factor_and_proof.decryption_proof + }; + }, + + decryptionFactorAndProof: function(ciphertext, challenge_generator) { + var decryption_factor = this.decryptionFactor(ciphertext); + + // the DH tuple we need to prove, given the secret key x, is: + // g, alpha, y, beta/m + var proof = ElGamal.Proof.generate(this.pk.g, ciphertext.alpha, this.x, this.pk.p, this.pk.q, challenge_generator); + + return { + 'decryption_factor' : decryption_factor, + 'decryption_proof' : proof + } + }, + + // generate a proof of knowledge of the secret exponent x + proveKnowledge: function(challenge_generator) { + // generate random w + var w = Random.getRandomInteger(this.pk.q); + + // compute s = g^w for random w. + var s = this.pk.g.modPow(w, this.pk.p); + + // get challenge + var challenge = challenge_generator(s); + + // compute response = w + x * challenge + var response = w.add(this.x.multiply(challenge).mod(this.pk.q)); + + return new ElGamal.DLogProof(s, challenge, response); + } +}); + +ElGamal.SecretKey.fromJSONObject = function(d) { + var sk = new ElGamal.SecretKey(); + sk.pk = ElGamal.PublicKey.fromJSONObject(d.public_key); + sk.x = BigInt.fromJSONObject(d.x); + return sk; +} + +ElGamal.Ciphertext = Class.extend({ + init: function(alpha, beta, pk) { + this.alpha = alpha; + this.beta = beta; + this.pk = pk; + }, + + toString: function() { + return this.alpha.toString() + ',' + this.beta.toString(); + }, + + toJSONObject: function() { + return {alpha: this.alpha.toJSONObject(), beta: this.beta.toJSONObject()} + }, + + multiply: function(other) { + // special case if other is 1 to enable easy aggregate ops + if (other == 1) + return this; + + // homomorphic multiply + return new ElGamal.Ciphertext(this.alpha.multiply(other.alpha).mod(this.pk.p), + this.beta.multiply(other.beta).mod(this.pk.p), + this.pk); + }, + + // a decryption method by decryption factors + decrypt: function(list_of_dec_factors) { + var running_decryption = this.beta; + var self = this; + $(list_of_dec_factors).each(function(i, dec_factor) { + running_decryption = dec_factor.modInverse(self.pk.p).multiply(running_decryption).mod(self.pk.p); + }); + + return new ElGamal.Plaintext(running_decryption, this.pk, false); + }, + + generateProof: function(plaintext, randomness, challenge_generator) { + // DH tuple to prove is + // g, y, alpha, beta/m + // with dlog randomness + var proof = ElGamal.Proof.generate(this.pk.g, this.pk.y, randomness, this.pk.p, this.pk.q, challenge_generator); + + return proof; + }, + + simulateProof: function(plaintext, challenge) { + // compute beta/plaintext, the completion of the DH tuple + var beta_over_plaintext = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p); + + // the DH tuple we are simulating here is + // g, y, alpha, beta/m + return ElGamal.Proof.simulate(this.pk.g, this.pk.y, this.alpha, beta_over_plaintext, this.pk.p, this.pk.q, challenge); + }, + + verifyProof: function(plaintext, proof, challenge_generator) { + // DH tuple to verify is + // g, y, alpha, beta/m + var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p); + + return proof.verify(this.pk.g, this.pk.y, this.alpha, beta_over_m, this.pk.p, this.pk.q, challenge_generator); + }, + + verifyDecryptionProof: function(plaintext, proof, challenge_generator) { + // DH tuple to verify is + // g, alpha, y, beta/m + // since the proven dlog is the secret key x, y=g^x. + var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p); + + return proof.verify(this.pk.g, this.alpha, this.pk.y, beta_over_m, this.pk.p, this.pk.q, challenge_generator); + }, + + generateDisjunctiveProof: function(list_of_plaintexts, real_index, randomness, challenge_generator) { + // go through all plaintexts and simulate the ones that must be simulated. + // note how the interface is as such so that the result does not reveal which is the real proof. + var self = this; + + var proofs = $(list_of_plaintexts).map(function(p_num, plaintext) { + if (p_num == real_index) { + // no real proof yet + return {}; + } else { + // simulate! + return self.simulateProof(plaintext); + } + }); + + // do the real proof + var real_proof = this.generateProof(list_of_plaintexts[real_index], randomness, function(commitment) { + // now we generate the challenge for the real proof by first determining + // the challenge for the whole disjunctive proof. + + // set up the partial real proof so we're ready to get the hash; + proofs[real_index] = {'commitment' : commitment}; + + // get the commitments in a list and generate the whole disjunctive challenge + var commitments = $(proofs).map(function(proof_num, proof) { + return proof.commitment; + }); + + var disjunctive_challenge = challenge_generator(commitments); + + // now we must subtract all of the other challenges from this challenge. + var real_challenge = disjunctive_challenge; + $(proofs).each(function(proof_num, proof) { + if (proof_num != real_index) + real_challenge = real_challenge.add(proof.challenge.negate()); + }); + + // make sure we mod q, the exponent modulus + return real_challenge.mod(self.pk.q); + }); + + // set the real proof + proofs[real_index] = real_proof; + + return new ElGamal.DisjunctiveProof(proofs); + }, + + verifyDisjunctiveProof: function(list_of_plaintexts, disj_proof, challenge_generator) { + var result = true; + var proofs = disj_proof.proofs; + + // for loop because we want to bail out of the inner loop + // if we fail one of the verifications. + for (var i=0; i < list_of_plaintexts.length; i++) { + if (!this.verifyProof(list_of_plaintexts[i], proofs[i])) + return false; + } + + // check the overall challenge + + // first the one expected from the proofs + var commitments = $(proofs).map(function(proof_num, proof) {return proof.commitment;}); + var expected_challenge = challenge_generator(commitments); + + // then the one that is the sum of the previous one. + var sum = new BigInt("0", 10); var self = this; + $(proofs).each(function(p_num, proof) {sum = sum.add(proof.challenge).mod(self.pk.q);}); + + return expected_challenge.equals(sum); + }, + + equals: function(other) { + return (this.alpha.equals(other.alpha) && this.beta.equals(other.beta)); + } +}); + +ElGamal.Ciphertext.fromJSONObject = function(d, pk) { + return new ElGamal.Ciphertext(BigInt.fromJSONObject(d.alpha), BigInt.fromJSONObject(d.beta), pk); +}; + +// we need the public key to figure out how to encode m +ElGamal.Plaintext = Class.extend({ + init: function(m, pk, encode_m) { + if (m == null) { + alert('oy null m'); + return; + } + + this.pk = pk; + + if (encode_m) { + // need to encode the message given that p = 2q+1 + var y = m.add(BigInt.ONE); + var test = y.modPow(pk.q, pk.p); + if (test.equals(BigInt.ONE)) { + this.m = y; + } else { + this.m = y.negate().mod(pk.p); + } + } else { + this.m = m; + } + }, + + getPlaintext: function() { + var y; + + // if m < q + if (this.m.compareTo(this.pk.q) < 0) { + y = this.m; + } else { + y = this.m.negate().mod(this.pk.p); + } + + return y.subtract(BigInt.ONE); + }, + + getM: function() { + return this.m; + } + +}); + +// +// Proof abstraction +// + +ElGamal.Proof = Class.extend({ + init: function(A, B, challenge, response) { + this.commitment = {}; + this.commitment.A = A; + this.commitment.B = B; + this.challenge = challenge; + this.response = response; + }, + + toJSONObject: function() { + return { + challenge : this.challenge.toJSONObject(), + commitment : {A: this.commitment.A.toJSONObject(), B: this.commitment.B.toJSONObject()}, + response : this.response.toJSONObject() + } + }, + + // verify a DH tuple proof + verify: function(little_g, little_h, big_g, big_h, p, q, challenge_generator) { + // check that little_g^response = A * big_g^challenge + var first_check = little_g.modPow(this.response, p).equals(big_g.modPow(this.challenge, p).multiply(this.commitment.A).mod(p)); + + // check that little_h^response = B * big_h^challenge + var second_check = little_h.modPow(this.response, p).equals(big_h.modPow(this.challenge, p).multiply(this.commitment.B).mod(p)); + + var third_check = true; + + if (challenge_generator) { + third_check = this.challenge.equals(challenge_generator(this.commitment)); + } + + return (first_check && second_check && third_check); + } +}); + +ElGamal.Proof.fromJSONObject = function(d) { + return new ElGamal.Proof( + BigInt.fromJSONObject(d.commitment.A), + BigInt.fromJSONObject(d.commitment.B), + BigInt.fromJSONObject(d.challenge), + BigInt.fromJSONObject(d.response)); +}; + +// a generic way to prove that four values are a DH tuple. +// a DH tuple is g,h,G,H where G = g^x and H=h^x +// challenge generator takes a commitment, whose subvalues are A and B +// all modulo p, with group order q, which we provide just in case. +// as it turns out, G and H are not necessary to generate this proof, given that they're implied by x. +ElGamal.Proof.generate = function(little_g, little_h, x, p, q, challenge_generator) { + // generate random w + var w = Random.getRandomInteger(q); + + // create a proof instance + var proof = new ElGamal.Proof(); + + // compute A=little_g^w, B=little_h^w + proof.commitment.A = little_g.modPow(w, p); + proof.commitment.B = little_h.modPow(w, p); + + // Get the challenge from the callback that generates it + proof.challenge = challenge_generator(proof.commitment); + + // Compute response = w + x * challenge + proof.response = w.add(x.multiply(proof.challenge).mod(q)); + + return proof; +}; + +// simulate a a DH-tuple proof, with a potentially assigned challenge (but can be null) +ElGamal.Proof.simulate = function(little_g, little_h, big_g, big_h, p, q, challenge) { + // generate a random challenge if not provided + if (challenge == null) { + challenge = Random.getRandomInteger(q); + } + + // random response, does not even need to depend on the challenge + var response = Random.getRandomInteger(q); + + // now we compute A and B + // A = little_g ^ w, and at verification time, g^response = G^challenge * A, so A = (G^challenge)^-1 * g^response + var A = big_g.modPow(challenge, p).modInverse(p).multiply(little_g.modPow(response, p)).mod(p); + + // B = little_h ^ w, and at verification time, h^response = H^challenge * B, so B = (H^challenge)^-1 * h^response + var B = big_h.modPow(challenge, p).modInverse(p).multiply(little_h.modPow(response, p)).mod(p); + + return new ElGamal.Proof(A, B, challenge, response); +}; + +ElGamal.DisjunctiveProof = Class.extend({ + init: function(list_of_proofs) { + this.proofs = list_of_proofs; + }, + + toJSONObject: function() { + return $(this.proofs).map(function(i, proof) { + return proof.toJSONObject(); + }); + } +}); + +ElGamal.DisjunctiveProof.fromJSONObject = function(d) { + if (d==null) + return null; + + return new ElGamal.DisjunctiveProof( + $(d).map(function(i, p) { + return ElGamal.Proof.fromJSONObject(p); + }) + ); +}; + +ElGamal.encrypt = function(pk, plaintext, r) { + if (plaintext.getM().equals(BigInt.ZERO)) + throw "Can't encrypt 0 with El Gamal" + + if (!r) + r = Random.getRandomInteger(pk.q); + + var alpha = pk.g.modPow(r, pk.p); + var beta = (pk.y.modPow(r, pk.p)).multiply(plaintext.m).mod(pk.p); + + return new ElGamal.Ciphertext(alpha, beta, pk); +}; + +// +// DLog Proof +// +ElGamal.DLogProof = Class.extend({ + init: function(commitment, challenge, response) { + this.commitment = commitment; + this.challenge = challenge; + this.response = response; + }, + + toJSONObject: function() { + return {'challenge' : this.challenge.toJSONObject(), 'commitment': this.commitment.toJSONObject(), 'response': this.response.toJSONObject()}; + } +}); + +ElGamal.DLogProof.fromJSONObject = function(d) { + return new ElGamal.DLogProof(BigInt.fromJSONObject(d.commitment || d.s), BigInt.fromJSONObject(d.challenge), BigInt.fromJSONObject(d.response)); +}; + +// a challenge generator based on a list of commitments of +// proofs of knowledge of plaintext. Just appends A and B with commas. +ElGamal.disjunctive_challenge_generator = function(commitments) { + var strings_to_hash = []; + + // go through all proofs and append the commitments + $(commitments).each(function(commitment_num, commitment) { + // toJSONObject instead of toString because of IE weirdness. + strings_to_hash[strings_to_hash.length] = commitment.A.toJSONObject(); + strings_to_hash[strings_to_hash.length] = commitment.B.toJSONObject(); + }); + + // STRINGS = strings_to_hash; + return new BigInt(hex_sha1(strings_to_hash.join(",")), 16); +}; + +// a challenge generator for Fiat-Shamir +ElGamal.fiatshamir_challenge_generator = function(commitment) { + return ElGamal.disjunctive_challenge_generator([commitment]); +}; + +ElGamal.fiatshamir_dlog_challenge_generator = function(commitment) { + return new BigInt(hex_sha1(commitment.toJSONObject()), 16); +}; \ No newline at end of file diff --git a/helios/media/helios/helios.js b/helios/media/helios/helios.js new file mode 100644 index 000000000..7a71bab1a --- /dev/null +++ b/helios/media/helios/helios.js @@ -0,0 +1,609 @@ + +// +// Helios Protocols +// +// ben@adida.net +// +// FIXME: needs a healthy refactor/cleanup based on Class.extend() +// + +// extend jquery to do object keys +// from http://snipplr.com/view.php?codeview&id=10430 +$.extend({ + keys: function(obj){ + var a = []; + $.each(obj, function(k){ a.push(k) }); + return a.sort(); + } +}); + +var UTILS = {}; + +UTILS.array_remove_value = function(arr, val) { + var new_arr = []; + $(arr).each(function(i, v) { + if (v != val) { + new_arr.push(v); + } + }); + + return new_arr; +}; + +UTILS.select_element_content = function(element) { + var range; + if (window.getSelection) { // FF, Safari, Opera + var sel = window.getSelection(); + range = document.createRange(); + range.selectNodeContents(element); + sel.removeAllRanges(); + sel.addRange(range); + } else { + document.selection.empty(); + range = document.body.createTextRange(); + range.moveToElementText(el); + range.select(); + } +}; + +// a progress tracker +UTILS.PROGRESS = Class.extend({ + init: function() { + this.n_ticks = 0.0; + this.current_tick = 0.0; + }, + + addTicks: function(n_ticks) { + this.n_ticks += n_ticks; + }, + + tick: function() { + this.current_tick += 1.0; + }, + + progress: function() { + return Math.round((this.current_tick / this.n_ticks) * 100); + } +}); + +// produce the same object but with keys sorted +UTILS.object_sort_keys = function(obj) { + var new_obj = {}; + $($.keys(obj)).each(function(i, k) { + new_obj[k] = obj[k]; + }); + return new_obj; +}; + +// +// Helios Stuff +// + +HELIOS = {}; + +// election +HELIOS.Election = Class.extend({ + init: function() { + }, + + toJSONObject: function() { + var json_obj = {uuid : this.uuid, + description : this.description, short_name : this.short_name, name : this.name, + public_key: this.public_key.toJSONObject(), questions : this.questions, + cast_url: this.cast_url, frozen_at: this.frozen_at, + openreg: this.openreg, voters_hash: this.voters_hash, + use_voter_aliases: this.use_voter_aliases, + voting_starts_at: this.voting_starts_at, + voting_ends_at: this.voting_ends_at}; + + return UTILS.object_sort_keys(json_obj); + }, + + get_hash: function() { + if (this.election_hash) + return this.election_hash; + + // otherwise + return b64_sha256(this.toJSON()); + }, + + toJSON: function() { + // FIXME: only way around the backslash thing for now.... how ugly + //return jQuery.toJSON(this.toJSONObject()).replace(/\//g,"\\/"); + return jQuery.toJSON(this.toJSONObject()); + } +}); + +HELIOS.Election.fromJSONString = function(raw_json) { + var json_object = $.secureEvalJSON(raw_json); + + // hash fix for the issue with re-json'ifying unicode chars + var election = HELIOS.Election.fromJSONObject(json_object); + election.election_hash = b64_sha256(election.toJSON()); + + return election; +}; + +HELIOS.Election.fromJSONObject = function(d) { + var el = new HELIOS.Election(); + jQuery.extend(el, d); + + // empty questions + if (!el.questions) + el.questions = []; + + if (el.public_key) + el.public_key = ElGamal.PublicKey.fromJSONObject(el.public_key); + + return el; +}; + +HELIOS.Election.setup = function(election) { + return ELECTION.fromJSONObject(election); +}; + + +// ballot handling +BALLOT = {}; + +BALLOT.pretty_choices = function(election, ballot) { + var questions = election.questions; + var answers = ballot.answers; + + // process the answers + var choices = $(questions).map(function(q_num) { + return $(answers[q_num]).map(function(dummy, ans) { + return questions[q_num].answers[ans]; + }); + }); + + return choices; +}; + + +// open up a new window and do something with it. +UTILS.open_window_with_content = function(content, mime_type) { + if (!mime_type) + mime_type = "text/plain"; + if (BigInt.is_ie) { + w = window.open(""); + w.document.open(mime_type); + w.document.write(content); + w.document.close(); + } else { + w = window.open("data:" + mime_type + "," + encodeURIComponent(content)); + } +}; + +// generate an array of the first few plaintexts +UTILS.generate_plaintexts = function(pk, min, max) { + var last_plaintext = BigInt.ONE; + + // an array of plaintexts + var plaintexts = []; + + if (min == null) + min = 0; + + // questions with more than one possible answer, add to the array. + for (var i=0; i<=max; i++) { + if (i >= min) + plaintexts.push(new ElGamal.Plaintext(last_plaintext, pk, false)); + last_plaintext = last_plaintext.multiply(pk.g).mod(pk.p); + } + + return plaintexts; +} + + +// +// crypto +// + + +HELIOS.EncryptedAnswer = Class.extend({ + init: function(question, answer, pk, progress) { + // if nothing in the constructor + if (question == null) + return; + + // store answer + // CHANGE 2008-08-06: answer is now an *array* of answers, not just a single integer + this.answer = answer; + + // do the encryption + var enc_result = this.doEncryption(question, answer, pk, null, progress); + + this.choices = enc_result.choices; + this.randomness = enc_result.randomness; + this.individual_proofs = enc_result.individual_proofs; + this.overall_proof = enc_result.overall_proof; + }, + + doEncryption: function(question, answer, pk, randomness, progress) { + var choices = []; + var individual_proofs = []; + var overall_proof = null; + + // possible plaintexts [question.min .. , question.max] + var plaintexts = null; + if (question.max != null) + plaintexts = UTILS.generate_plaintexts(pk, question.min, question.max); + + var zero_one_plaintexts = UTILS.generate_plaintexts(pk, 0, 1); + + // keep track of whether we need to generate new randomness + var generate_new_randomness = false; + if (!randomness) { + randomness = []; + generate_new_randomness = true; + } + + // keep track of number of options selected. + var num_selected_answers = 0; + + // go through each possible answer and encrypt either a g^0 or a g^1. + for (var i=0; i -1) { + plaintext_index = 1; + num_selected_answers += 1; + } else { + plaintext_index = 0; + } + + // generate randomness? + if (generate_new_randomness) { + randomness[i] = Random.getRandomInteger(pk.q); + } + + choices[i] = ElGamal.encrypt(pk, zero_one_plaintexts[plaintext_index], randomness[i]); + + // generate proof + if (generate_new_randomness) { + // generate proof that this ciphertext is a 0 or a 1 + individual_proofs[i] = choices[i].generateDisjunctiveProof(zero_one_plaintexts, plaintext_index, randomness[i], ElGamal.disjunctive_challenge_generator); + } + + if (progress) + progress.tick(); + } + + if (generate_new_randomness && question.max != null) { + // we also need proof that the whole thing sums up to the right number + // only if max is non-null, otherwise it's full approval voting + + // compute the homomorphic sum of all the options + var hom_sum = choices[0]; + var rand_sum = randomness[0]; + for (var i=1; i)[^>]*$|^#(\w+)$/;var isSimple=/^.[^:#\[\.]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}else if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem)if(elem.id!=match[3])return jQuery().find(selector);else{this[0]=elem;this.length=1;return this;}else +selector=[];}}else +return new jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return new jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(selector.constructor==Array&&selector||(selector.jquery||selector.length&&selector!=window&&!selector.nodeType&&selector[0]!=undefined&&selector[0].nodeType)&&jQuery.makeArray(selector)||[selector]);},jquery:"1.2.2",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;this.each(function(i){if(this==elem)ret=i;});return ret;},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value==undefined)return this.length&&jQuery[type||"attr"](this[0],name)||undefined;else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div"),container2=document.createElement("div");container.appendChild(clone);container2.innerHTML=container.innerHTML;return container2.firstChild;}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return!selector?this:this.pushStack(jQuery.merge(this.get(),selector.constructor==String?jQuery(selector).get():selector.length!=undefined&&(!selector.nodeName||jQuery.nodeName(selector,"form"))?selector:[selector]));},is:function(selector){return selector?jQuery.multiFilter(selector,this).length>0:false;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=value.constructor==Array?value:[value];jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this.length?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script")){scripts=scripts.add(elem);}else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.prototype.init.prototype=jQuery.prototype;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==1){target=this;i=0;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret;function color(elem){if(!jQuery.browser.safari)return false;var ret=document.defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(elem.style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=elem.style.display;elem.style.display="block";elem.style.display=save;}if(name.match(/float/i))name=styleFloat;if(!force&&elem.style&&elem.style[name])ret=elem.style[name];else if(document.defaultView&&document.defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var getComputedStyle=document.defaultView.getComputedStyle(elem,null);if(getComputedStyle&&!color(elem))ret=getComputedStyle.getPropertyValue(name);else{var swap=[],stack=[];for(var a=elem;a&&color(a);a=a.parentNode)stack.unshift(a);for(var i=0;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var fix=jQuery.isXMLDoc(elem)?{}:jQuery.props;if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(fix[name]){if(value!=undefined)elem[fix[name]]=value;return elem[fix[name]];}else if(jQuery.browser.msie&&name=="style")return jQuery.attr(elem.style,"cssText",value);else if(value==undefined&&jQuery.browser.msie&&jQuery.nodeName(elem,"form")&&(name=="action"||name=="method"))return elem.getAttributeNode(name).nodeValue;else if(elem.tagName){if(value!=undefined){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem.setAttribute(name,""+value);}if(jQuery.browser.msie&&/href|src/.test(name)&&!jQuery.isXMLDoc(elem))return elem.getAttribute(name,2);return elem.getAttribute(name);}else{if(name=="opacity"&&jQuery.browser.msie){if(value!=undefined){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseFloat(value).toString()=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100).toString():"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(value!=undefined)elem[name]=value;return elem[name];}},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(typeof array!="array")for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":"m[2]=='*'||jQuery.nodeName(a,m[2])","#":"a.getAttribute('id')==m[2]",":":{lt:"im[3]-0",nth:"m[3]-0==i",eq:"m[3]-0==i",first:"i==0",last:"i==r.length-1",even:"i%2==0",odd:"i%2","first-child":"a.parentNode.getElementsByTagName('*')[0]==a","last-child":"jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a","only-child":"!jQuery.nth(a.parentNode.lastChild,2,'previousSibling')",parent:"a.firstChild",empty:"!a.firstChild",contains:"(a.textContent||a.innerText||jQuery(a).text()||'').indexOf(m[3])>=0",visible:'"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',hidden:'"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',enabled:"!a.disabled",disabled:"a.disabled",checked:"a.checked",selected:"a.selected||jQuery.attr(a,'selected')",text:"'text'==a.type",radio:"'radio'==a.type",checkbox:"'checkbox'==a.type",file:"'file'==a.type",password:"'password'==a.type",submit:"'submit'==a.type",image:"'image'==a.type",reset:"'reset'==a.type",button:'"button"==a.type||jQuery.nodeName(a,"button")',input:"/input|select|textarea|button/i.test(a.nodeName)",has:"jQuery.find(m[3],a).length",header:"/h\\d/i.test(a.nodeName)",animated:"jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length"}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false;var re=quickChild;var m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var f=jQuery.expr[m[1]];if(typeof f!="string")f=jQuery.expr[m[1]][m[2]];f=eval("false||function(a,i){return "+f+"}");r=jQuery.grep(r,f,not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[];var cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&(!elem||n!=elem))r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval!=undefined)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=function(){return fn.apply(this,arguments);};handler.data=data;handler.guid=fn.guid;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){var val;if(typeof jQuery=="undefined"||jQuery.event.triggered)return val;val=jQuery.event.handle.apply(arguments.callee.elem,arguments);return val;});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined)for(var type in events)this.remove(elem,type);else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data||[]);if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event)data.unshift(this.fix({type:type,target:elem}));data[0].type=type;if(jQuery.isFunction(jQuery.data(elem,"handle")))val=jQuery.data(elem,"handle").apply(elem,data);if(!fn&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val;event=jQuery.event.fix(event||window.event||{});var parts=event.type.split(".");event.type=parts[0];var handlers=jQuery.data(this,"events")&&jQuery.data(this,"events")[event.type],args=Array.prototype.slice.call(arguments,1);args.unshift(event);for(var j in handlers){var handler=handlers[j];args[0].handler=handler;args[0].data=handler.data;if(!parts[1]||handler.type==parts[1]){var ret=handler.apply(this,args);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}if(jQuery.browser.msie)event.target=event.preventDefault=event.stopPropagation=event.handler=event.data=null;return val;},fix:function(event){var originalEvent=event;event=jQuery.extend({},originalEvent);event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=originalEvent.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){return this.each(function(){jQuery.event.add(this,type,function(event){jQuery(this).unbind(event);return(fn||data).apply(this,arguments);},fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){if(this[0])return jQuery.event.trigger(type,data,this[0],false,fn);return undefined;},toggle:function(){var args=arguments;return this.click(function(event){this.lastToggle=0==this.lastToggle?1:0;event.preventDefault();return args[this.lastToggle].apply(this,arguments)||false;});},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else +jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.apply(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=(new Date).getTime();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){var jsonp,jsre=/=\?(&|$)/g,status,data;s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(s.type.toLowerCase()=="get"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&s.type.toLowerCase()=="get"){var ts=(new Date()).getTime();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&s.type.toLowerCase()=="get"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");if((!s.url.indexOf("http")||!s.url.indexOf("//"))&&(s.dataType=="script"||s.dataType=="json")&&s.type.toLowerCase()=="get"){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xml=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();xml.open(s.type,s.url,s.async,s.username,s.password);try{if(s.data)xml.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xml.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xml.setRequestHeader("X-Requested-With","XMLHttpRequest");xml.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend)s.beforeSend(xml);if(s.global)jQuery.event.trigger("ajaxSend",[xml,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xml&&(xml.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xml)&&"error"||s.ifModified&&jQuery.httpNotModified(xml,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xml,s.dataType);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xml.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xml,status);complete();if(s.async)xml=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xml){xml.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xml.send(s.data);}catch(e){jQuery.handleError(s,xml,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xml,s]);}function complete(){if(s.complete)s.complete(xml,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xml,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xml;},handleError:function(s,xml,status,e){if(s.error)s.error(xml,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xml,s,e]);},active:0,httpSuccess:function(r){try{return!r.status&&location.protocol=="file:"||(r.status>=200&&r.status<300)||r.status==304||r.status==1223||jQuery.browser.safari&&r.status==undefined;}catch(e){}return false;},httpNotModified:function(xml,url){try{var xmlRes=xml.getResponseHeader("Last-Modified");return xml.status==304||xmlRes==jQuery.lastModified[url]||jQuery.browser.safari&&xml.status==undefined;}catch(e){}return false;},httpData:function(r,type){var ct=r.getResponseHeader("content-type");var xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0;var data=xml?r.responseXML:r.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle(fn,fn2):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall);var hidden=jQuery(this).is(":hidden"),self=this;for(var p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return jQuery.isFunction(opt.complete)&&opt.complete.apply(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.apply(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(!elem)return undefined;type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",array?jQuery.makeArray(array):[]);return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].apply(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:{slow:600,fast:200}[opt.duration])||400;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.apply(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.apply(this.elem,[this.now,this]);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=(new Date()).getTime();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done&&jQuery.isFunction(this.options.complete))this.options.complete.apply(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.fx.step={scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}};jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522,fixed=jQuery.css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&jQuery.css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(jQuery.css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&jQuery.css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||jQuery.css(offsetChild,"position")=="absolute"))||(mozilla&&jQuery.css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l)||0;top+=parseInt(t)||0;}return results;};})(); \ No newline at end of file diff --git a/helios/media/helios/jquery-jtemplates.js b/helios/media/helios/jquery-jtemplates.js new file mode 100644 index 000000000..651062326 --- /dev/null +++ b/helios/media/helios/jquery-jtemplates.js @@ -0,0 +1,3 @@ +/* jTemplates 0.6.6 (http://jtemplates.tpython.com) Copyright (c) 2008 Tomasz Gloc (http://www.tpython.com) +Please do not remove or modify above line. Thanks. Dual licensed under the MIT and GPL licenses. */ +eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9(2Q.8&&!2Q.8.2V){(7(){5 A=7(s,q,j){4.1t=[];4.1w={};4.2v=C;4.1A={};4.15={};4.j=8.1q({2r:17,2S:3F,2b:17,2O:17},j);4.2K(s,q);9(s){4.1h(4.15[\'1E\'],q)}4.15=C};A.f.1X=\'0.6.6\';A.f.2K=7(s,q){5 2x=/\\{#33 *(\\w*?)\\}/g;5 2u,1y,B;5 1k=C;2n((2u=2x.3J(s))!=C){1k=2x.1k;1y=2u[1];B=s.2i(\'{#/33 \'+1y+\'}\',1k);9(B==-1){13 c 16(\'Z: A "\'+1y+\'" 28 1F 3t.\');}4.15[1y]=s.23(1k,B)}9(1k===C){4.15[\'1E\']=s;a}H(5 i 1D 4.15){9(i!=\'1E\'){4.1A[i]=c A()}}H(5 i 1D 4.15){9(i!=\'1E\'){4.1A[i].1h(4.15[i],8.1q({},q||{},4.1A||{}));4.15[i]=C}}};A.f.1h=7(s,q){9(s==1r){4.1t.z(c 18(\'\',1));a}s=s.L(/[\\n\\r]/g,\'\');s=s.L(/\\{\\*.*?\\*\\}/g,\'\');4.2v=8.1q({},4.1A||{},q||{});5 h=4.1t;5 I=s.1f(/\\{#.*?\\}/g);5 O=0,B=0;5 e;5 19=0;5 1Q=0;H(5 i=0,l=(I)?(I.G):(0);iO){h.z(c 18(s.23(O,B),1))}O=B+11;19=0;i=8.3O(\'{#/1m}\',I);2X}B=s.2i(I[i],O);9(B>O){h.z(c 18(s.23(O,B),19))}5 3M=I[i].1f(/\\{#([\\w\\/]+).*?\\}/);5 2q=J.$1;2U(2q){y\'3I\':++1Q;h.1M();y\'9\':e=c 1i(I[i],h);h.z(e);h=e;N;y\'1b\':h.1M();N;y\'/9\':2n(1Q){h=h.1J();--1Q}y\'/H\':y\'/2h\':h=h.1J();N;y\'2h\':e=c 1l(I[i],h);h.z(e);h=e;N;y\'2g\':h.z(c 2e(I[i],4.2v));N;y\'b\':h.z(c 2d(I[i]));N;y\'2a\':h.z(c 29(I[i]));N;y\'3w\':h.z(c 18(\'{\'));N;y\'3v\':h.z(c 18(\'}\'));N;y\'1m\':19=1;N;y\'/1m\':13 c 16("Z: 31 2N 2Z 1m.");2M:13 c 16(\'Z: 3s 3r \'+2q+\'.\');}O=B+I[i].G}9(s.G>O){h.z(c 18(s.3p(O),19))}};A.f.F=7(d,b,x){5 $T=4.1s(d,{21:4.j.2S,2y:4.j.2r});5 $P=8.1q(4.1w,b);9(4.j.2b){$P=4.1s($P,{21:4.j.2b,2y:17})}5 $Q=x;$Q.1X=4.1X;5 X=\'\';H(5 i=0,l=4.1t.G;i/g,\'&36;\').L(/e)?(e):(12)}9(e>s){5 Y=0;5 2k=3H.3G((e-s)/1o);5 1j,1K;H(;s=e);p.$1H=2k;$T[4.m+\'$1T\']=s;$T[4.m+\'$Y\']=Y;$T[4.m+\'$1I\']=(Y==0);$T[4.m+\'$1N\']=(s+1o>=e);$T[4.m+\'$1H\']=2k;$T[4.m+\'$1x\']=1j;$T[4.m+\'$1z\']=1z 1K;H(i=0,l=4.1c.G;i")}s=8.3o(s);s=s.L(/^<\\!\\[3n\\[([\\s\\S]*)\\]\\]>$/3m,\'$1\');8(4).1h(s,q,j)};8.M.3l=7(){5 K=0;8(4).10(7(){9(8.E(4,\'1d\')){++K}});a K};8.M.3k=7(){8(4).2P();8(4).10(7(){8.2I(4,\'1d\')})};8.M.2p=7(1v,1B){8(4).10(7(){5 t=8.E(4,\'1d\');9(t===1r){13 c 16(\'Z: A 28 1F 2H.\');}t.2p(1v,1B)})};8.M.22=7(d,b){8(4).10(7(){5 t=8.E(4,\'1d\');9(t===1r){13 c 16(\'Z: A 28 1F 2H.\');}8.E(4,\'1u\',8.E(4,\'1u\')+1);8(4).2J(t.F(d,b,4))})};8.M.3j=7(1g,b){5 W=4;5 s=8.1Y({1a:1g,25:17,3i:17,3h:\'3A\',3g:7(d){8(W).22(d,b)}})};5 1L=7(1a,b,1C,1G,U){4.2G=1a;4.1w=b;4.2F=1C;4.2E=1G;4.U=U;4.20=C;5 W=4;8(U).10(7(){8.E(4,\'2j\',W)});4.1Z()};1L.f.1Z=7(){4.2D();9(4.U.G==0){a}5 W=4;8.3f(4.2G,4.2E,7(d){8(W.U).22(d,W.1w)});4.20=3e(7(){W.1Z()},4.2F)};1L.f.2D=7(){4.U=8.2B(4.U,7(o){9(8.3d.3b){5 n=o.2s;2n(n&&n!=3a){n=n.2s}a n!=C}1b{a o.2s!=C}})};8.M.38=7(1a,b,1C,1G){5 u=c 1L(1a,b,1C,1G,4);a u.20};8.M.2P=7(){8(4).10(7(){5 1S=8.E(4,\'2j\');9(1S==C){a}5 W=4;1S.U=8.2B(1S.U,7(o){a o!=W});8.2I(4,\'2j\')})};8.1q({2V:7(s,q,j){a c A(s,q,j)},3W:7(1g,q,j){5 s=8.1Y({1a:1g,25:17}).2L;a c A(s,q,j)}})})(8)}',62,245,'||||this|var||function|jQuery|if|return|param|new|||prototype||node||settings|||_name||||includes|||||||element|case|push|Template|se|null|delete|data|get|length|for|op|RegExp|count|replace|fn|break|ss|||oper|||objs|_option|that|ret|iteration|jTemplates|each||tmp|throw|eval|_templates_code|Error|false|TextNode|literalMode|url|else|_onTrue|jTemplate|_onFalse|match|url_|setTemplate|opIF|ckey|lastIndex|opFOREACH|literal|_currentState|step|filter|extend|undefined|cloneData|_tree|jTemplateSID|name|_param|key|tname|typeof|_templates|value|interval|in|MAIN|not|args|total|first|getParent|cval|Updater|switchToElse|last|_parent|par|elseif_level|_value|updater|index|Object|_index|sid|version|ajax|run|timer|escapeData|processTemplate|substring|elementName|async|_length|_lastSessionID|is|Cycle|cycle|filter_params|_values|UserParam|Include|_template|include|foreach|indexOf|jTemplateUpdater|_total|Number|arr|while|mode|setParam|op_|disallow_functions|parentNode|tab|iter|_includes|val|reg|noFunc|constructor|lt|grep|escapeHTML|detectDeletedNodes|_args|_interval|_url|defined|removeData|html|splitTemplates|responseText|default|begin|runnable_functions|processTemplateStop|window|_root|filter_data|object|switch|createTemplate|txt|continue|_arg|of|_cond|No|__a1|template|_literalMode|String|gt|quot|processTemplateStart||document|msie|amp|browser|setTimeout|getJSON|success|dataType|cache|processTemplateURL|removeTemplate|hasTemplate|im|CDATA|trim|substr|setTemplateElement|tag|unknown|closed|setTemplateURL|rdelim|ldelim|elements|no|has|json|values|find|Cannot|root|true|ceil|Math|elseif|exec|shift|split|ppp|as|inArray|end|__a0|allowed|are|Functions|Function|Array|createTemplateURL'.split('|'),0,{})) \ No newline at end of file diff --git a/helios/media/helios/jquery.json.min.js b/helios/media/helios/jquery.json.min.js new file mode 100644 index 000000000..8335fbf69 --- /dev/null +++ b/helios/media/helios/jquery.json.min.js @@ -0,0 +1,35 @@ +(function($){function toIntegersAtLease(n) +{return n<10?'0'+n:n;} +Date.prototype.toJSON=function(date) +{return date.getUTCFullYear()+'-'+ +toIntegersAtLease(date.getUTCMonth()+1)+'-'+ +toIntegersAtLease(date.getUTCDate());};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'}; +$.quoteString=function(string) +{if(escapeable.test(string)) +{return'"'+string.replace(escapeable,function(a) +{var c=meta[a];if(typeof c==='string'){return c;} +c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';} +return'"'+string+'"';}; +$.toJSON=function(o) +{var type=typeof(o);if(type=="undefined") +return"null";else if(type=="number"||type=="boolean") +return o+"";else if(o===null) +return"null";if(type=="string") +{return $.quoteString(o);} +if(type=="object"&&typeof o.toJSONObject=="function") +return $.toJSON(o.toJSONObject());if(type!="function"&&typeof(o.length)=="number") +{var ret=[];for(var i=0;i= 31 ) + return; + + return day_diff == 0 && ( + diff < 60 && "just now" || + diff < 120 && "1 minute ago" || + diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || + diff < 7200 && "1 hour ago" || + diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || + day_diff == 1 && "Yesterday" || + day_diff < 7 && day_diff + " days ago" || + day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago"; +} + +// If jQuery is included in the page, adds a jQuery plugin to handle it as well +if ( typeof jQuery != "undefined" ) + jQuery.fn.prettyDate = function(){ + return this.each(function(){ + var date = prettyDate(this.title); + if ( date ) + jQuery(this).text( date ); + }); + }; diff --git a/helios/media/helios/random.js b/helios/media/helios/random.js new file mode 100644 index 000000000..27760b03c --- /dev/null +++ b/helios/media/helios/random.js @@ -0,0 +1,48 @@ + +/* + * Random Number generation, now uses the glue to Java + */ + +Random = {}; + +Random.GENERATOR = null; + +Random.setupGenerator = function() { + if (Random.GENERATOR == null) { + if (BigInt.use_applet) { + var foo = BigInt.APPLET.newSecureRandom(); + Random.GENERATOR = BigInt.APPLET.newSecureRandom(); + } else { + // we do it twice because of some weird bug; + var foo = new java.security.SecureRandom(); + Random.GENERATOR = new java.security.SecureRandom(); + } + } +}; + +Random.getRandomInteger = function(max) { + Random.setupGenerator(); + var bit_length = max.bitLength(); + var random; + if (BigInt.use_applet) { + random = BigInt.APPLET.randomBigInteger(bit_length, Random.GENERATOR); + } else { + random = new java.math.BigInteger(bit_length, Random.GENERATOR); + } + + return BigInt._from_java_object(random).mod(max); +}; + +Random.getRandomPrime = function(n_bits) { + Random.setupGenerator(); + var certainty = 80; + var prime; + if (BigInt.use_applet) { + prime = BigInt.APPLET.randomPrimeBigInteger(n_bits, certainty, Random.GENERATOR); + } else { + prime = new java.math.BigInteger(n_bits, certainty, Random.GENERATOR); + } + + return BigInt._from_java_object(prime); +}; + diff --git a/helios/media/helios/sha1.js b/helios/media/helios/sha1.js new file mode 100644 index 000000000..ca6c8523a --- /dev/null +++ b/helios/media/helios/sha1.js @@ -0,0 +1,202 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) + { + if(j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} \ No newline at end of file diff --git a/helios/media/helios/sha2.js b/helios/media/helios/sha2.js new file mode 100644 index 000000000..3199a8c4e --- /dev/null +++ b/helios/media/helios/sha2.js @@ -0,0 +1,144 @@ +/* A JavaScript implementation of the Secure Hash Standard + * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/ + * Distributed under the BSD License + * Some bits taken from Paul Johnston's SHA-1 implementation + */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ +var hexcase = 0;/* hex output format. 0 - lowercase; 1 - uppercase */ + +function safe_add (x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +function S (X, n) {return ( X >>> n ) | (X << (32 - n));} + +function R (X, n) {return ( X >>> n );} + +function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));} + +function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));} + +function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));} + +function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));} + +function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));} + +function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));} + +function Sigma0512(x) {return (S(x, 28) ^ S(x, 34) ^ S(x, 39));} + +function Sigma1512(x) {return (S(x, 14) ^ S(x, 18) ^ S(x, 41));} + +function Gamma0512(x) {return (S(x, 1) ^ S(x, 8) ^ R(x, 7));} + +function Gamma1512(x) {return (S(x, 19) ^ S(x, 61) ^ R(x, 6));} + +function core_sha256 (m, l) { + var K = new Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2); + var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19); + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + + /* append padding */ + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for ( var i = 0; i>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32); + return bin; +} + +function binb2str (bin) { + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; +} + +function binb2hex (binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +function binb2b64 (binarray) { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + +function hex_sha256(s){return binb2hex(core_sha256(str2binb(s),s.length * chrsz));} +function b64_sha256(s){return binb2b64(core_sha256(str2binb(s),s.length * chrsz));} +function str_sha256(s){return binb2str(core_sha256(str2binb(s),s.length * chrsz));} diff --git a/helios/media/js/jqsplitdatetime.js b/helios/media/js/jqsplitdatetime.js new file mode 100644 index 000000000..623a1f2b5 --- /dev/null +++ b/helios/media/js/jqsplitdatetime.js @@ -0,0 +1,3 @@ +$(function() { + $(".datepicker").datepicker({ dateFormat: 'yy-mm-dd' }); +}); diff --git a/helios/media/js/jquery-1.4.2.min.js b/helios/media/js/jquery-1.4.2.min.js new file mode 100644 index 000000000..7c2430802 --- /dev/null +++ b/helios/media/js/jquery-1.4.2.min.js @@ -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)[^>]*$|^#([\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
a"; +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="";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-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=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=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-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;j0){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":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=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 hl[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=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";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=""; +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="

";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="
";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=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},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-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=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_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
","
"];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;b0||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;e0?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]===""&&!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=//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("
").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").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=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.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); diff --git a/helios/media/js/jquery-ui-1.8.1.custom.min.js b/helios/media/js/jquery-ui-1.8.1.custom.min.js new file mode 100644 index 000000000..9ea846d65 --- /dev/null +++ b/helios/media/js/jquery-ui-1.8.1.custom.min.js @@ -0,0 +1,756 @@ +/*! + * jQuery UI 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI + */ +jQuery.ui||function(c){c.ui={version:"1.8.1",plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=0)&&c(a).is(":focusable")}})}(jQuery); +;/*! + * jQuery UI Widget 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Widget + */ +(function(b){var j=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add(this).each(function(){b(this).triggerHandler("remove")});return j.call(b(this),a,c)})};b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend({},c.options);b[e][a].prototype= +b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.substring(0,1)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==undefined){h=i;return false}}):this.each(function(){var g= +b.data(this,a);if(g){d&&g.option(d);g._init()}else b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){this.element=b(c).data(this.widgetName,this);this.options=b.extend(true,{},this.options,b.metadata&&b.metadata.get(c)[this.widgetName],a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create(); +this._init()},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a,e=this;if(arguments.length===0)return b.extend({},e.options);if(typeof a==="string"){if(c===undefined)return this.options[a];d={};d[a]=c}b.each(d,function(f, +h){e._setOption(f,h)});return e},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a= +b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery); +;/*! + * jQuery UI Mouse 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Mouse + * + * Depends: + * jquery.ui.widget.js + */ +(function(c){c.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(b){return a._mouseDown(b)}).bind("click."+this.widgetName,function(b){if(a._preventClickEvent){a._preventClickEvent=false;b.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(a){a.originalEvent=a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&& +this._mouseUp(a);this._mouseDownEvent=a;var b=this,e=a.which==1,f=typeof this.options.cancel=="string"?c(a.target).parents().add(a.target).filter(this.options.cancel).length:false;if(!e||f||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){b.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault(); +return true}}this._mouseMoveDelegate=function(d){return b._mouseMove(d)};this._mouseUpDelegate=function(d){return b._mouseUp(d)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.browser.safari||a.preventDefault();return a.originalEvent.mouseHandled=true}},_mouseMove:function(a){if(c.browser.msie&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&& +this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=a.target==this._mouseDownEvent.target;this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX- +a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); +;/* + * jQuery UI Position 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Position + */ +(function(c){c.ui=c.ui||{};var m=/left|center|right/,n=/top|center|bottom/,p=c.fn.position,q=c.fn.offset;c.fn.position=function(a){if(!a||!a.of)return p.apply(this,arguments);a=c.extend({},a);var b=c(a.of),d=(a.collision||"flip").split(" "),e=a.offset?a.offset.split(" "):[0,0],g,h,i;if(a.of.nodeType===9){g=b.width();h=b.height();i={top:0,left:0}}else if(a.of.scrollTo&&a.of.document){g=b.width();h=b.height();i={top:b.scrollTop(),left:b.scrollLeft()}}else if(a.of.preventDefault){a.at="left top";g=h= +0;i={top:a.of.pageY,left:a.of.pageX}}else{g=b.outerWidth();h=b.outerHeight();i=b.offset()}c.each(["my","at"],function(){var f=(a[this]||"").split(" ");if(f.length===1)f=m.test(f[0])?f.concat(["center"]):n.test(f[0])?["center"].concat(f):["center","center"];f[0]=m.test(f[0])?f[0]:"center";f[1]=n.test(f[1])?f[1]:"center";a[this]=f});if(d.length===1)d[1]=d[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(a.at[0]==="right")i.left+=g;else if(a.at[0]==="center")i.left+= +g/2;if(a.at[1]==="bottom")i.top+=h;else if(a.at[1]==="center")i.top+=h/2;i.left+=e[0];i.top+=e[1];return this.each(function(){var f=c(this),k=f.outerWidth(),l=f.outerHeight(),j=c.extend({},i);if(a.my[0]==="right")j.left-=k;else if(a.my[0]==="center")j.left-=k/2;if(a.my[1]==="bottom")j.top-=l;else if(a.my[1]==="center")j.top-=l/2;j.left=parseInt(j.left);j.top=parseInt(j.top);c.each(["left","top"],function(o,r){c.ui.position[d[o]]&&c.ui.position[d[o]][r](j,{targetWidth:g,targetHeight:h,elemWidth:k, +elemHeight:l,offset:e,my:a.my,at:a.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(j,{using:a.using}))})};c.ui.position={fit:{left:function(a,b){var d=c(window);b=a.left+b.elemWidth-d.width()-d.scrollLeft();a.left=b>0?a.left-b:Math.max(0,a.left)},top:function(a,b){var d=c(window);b=a.top+b.elemHeight-d.height()-d.scrollTop();a.top=b>0?a.top-b:Math.max(0,a.top)}},flip:{left:function(a,b){if(b.at[0]!=="center"){var d=c(window);d=a.left+b.elemWidth-d.width()-d.scrollLeft();var e=b.my[0]==="left"? +-b.elemWidth:b.my[0]==="right"?b.elemWidth:0,g=-2*b.offset[0];a.left+=a.left<0?e+b.targetWidth+g:d>0?e-b.targetWidth+g:0}},top:function(a,b){if(b.at[1]!=="center"){var d=c(window);d=a.top+b.elemHeight-d.height()-d.scrollTop();var e=b.my[1]==="top"?-b.elemHeight:b.my[1]==="bottom"?b.elemHeight:0,g=b.at[1]==="top"?b.targetHeight:-b.targetHeight,h=-2*b.offset[1];a.top+=a.top<0?e+b.targetHeight+h:d>0?e+g+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(a,b){if(/static/.test(c.curCSS(a,"position")))a.style.position= +"relative";var d=c(a),e=d.offset(),g=parseInt(c.curCSS(a,"top",true),10)||0,h=parseInt(c.curCSS(a,"left",true),10)||0;e={top:b.top-e.top+g,left:b.left-e.left+h};"using"in b?b.using.call(a,e):d.css(e)};c.fn.offset=function(a){var b=this[0];if(!b||!b.ownerDocument)return null;if(a)return this.each(function(){c.offset.setOffset(this,a)});return q.call(this)}}})(jQuery); +;/* + * jQuery UI Draggable 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;return true},_mouseStart:function(a){var b=this.options;this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top- +this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions(); +d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis|| +this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if(!this.element[0]||!this.element[0].parentNode)return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element, +b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== +a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone():this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]|| +0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], +this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top- +(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment== +"parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&& +a.containment.constructor!=Array){var b=d(a.containment)[0];if(b){a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"), +10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], +this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft(): +f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.leftthis.containment[2])e=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?e:!(e-this.offset.click.left
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")})},stop:function(){d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options; +if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!= +"HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY=0;h--){var i=c.snapElements[h].left,k=i+c.snapElements[h].width,j=c.snapElements[h].top,l=j+c.snapElements[h].height;if(i-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>=i&& +e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), +top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= +this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!d(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", +nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var e=0;e');/sw|se|ne|nw/.test(g)&&f.css({zIndex:++a.zIndex});"se"==g&&f.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[g]=".ui-resizable-"+g;this.element.append(f)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== +String)this.handles[i]=d(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=d(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}d(this.handles[i])}};this._renderAxis(this.element);this._handles=d(".ui-resizable-handle",this.element).disableSelection(); +this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();d(this.element).addClass("ui-resizable-autohide").hover(function(){d(this).removeClass("ui-resizable-autohide");b._handles.show()},function(){if(!b.resizing){d(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(c){d(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()}; +if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=false;for(var c in this.handles)if(d(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(), +e=this.element;this.resizing=true;this.documentScroll={top:d(document).scrollTop(),left:d(document).scrollLeft()};if(e.is(".ui-draggable")||/absolute/.test(e.css("position")))e.css({position:"absolute",top:c.top,left:c.left});d.browser.opera&&/relative/.test(e.css("position"))&&e.css({position:"relative",top:"auto",left:"auto"});this._renderProxy();c=m(this.helper.css("left"));var g=m(this.helper.css("top"));if(a.containment){c+=d(a.containment).scrollLeft()||0;g+=d(a.containment).scrollTop()||0}this.offset= +this.helper.offset();this.position={left:c,top:g};this.size=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalSize=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalPosition={left:c,top:g};this.sizeDiff={width:e.outerWidth()-e.width(),height:e.outerHeight()-e.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio: +this.originalSize.width/this.originalSize.height||1;a=d(".ui-resizable-"+this.axis).css("cursor");d("body").css("cursor",a=="auto"?this.axis+"-resize":a);e.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,e=this._change[this.axis];if(!e)return false;c=e.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize", +b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var e=this._proportionallyResizeElements,g=e.length&&/textarea/i.test(e[0].nodeName);e=g&&d.ui.hasScroll(e[0],"left")?0:c.sizeDiff.height; +g={width:c.size.width-(g?0:c.sizeDiff.width),height:c.size.height-e};e=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var f=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(d.extend(g,{top:f,left:e}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}d("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop", +b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size,e=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(e=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(e=="nw"){b.top= +a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,e=k(b.width)&&a.maxWidth&&a.maxWidthb.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(f)b.width=a.minWidth;if(h)b.height=a.minHeight;if(e)b.width=a.maxWidth;if(g)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height, +l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(f&&l)b.left=i-a.minWidth;if(e&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(g&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a');var a=d.browser.msie&&d.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+ +a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return d.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return d.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return d.extend(this._change.n.apply(this, +arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return d.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){d.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});d.extend(d.ui.resizable, +{version:"1.8.1"});d.ui.plugin.add("resizable","alsoResize",{start:function(){var b=d(this).data("resizable").options,a=function(c){d(c).each(function(){d(this).data("resizable-alsoresize",{width:parseInt(d(this).width(),10),height:parseInt(d(this).height(),10),left:parseInt(d(this).css("left"),10),top:parseInt(d(this).css("top"),10)})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else d.each(b.alsoResize,function(c){a(c)}); +else a(b.alsoResize)},resize:function(){var b=d(this).data("resizable"),a=b.options,c=b.originalSize,e=b.originalPosition,g={height:b.size.height-c.height||0,width:b.size.width-c.width||0,top:b.position.top-e.top||0,left:b.position.left-e.left||0},f=function(h,i){d(h).each(function(){var j=d(this),l=d(this).data("resizable-alsoresize"),p={};d.each((i&&i.length?i:["width","height","top","left"])||["width","height","top","left"],function(n,o){if((n=(l[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(/relative/.test(j.css("position"))&& +d.browser.opera){b._revertToRelativePosition=true;j.css({position:"absolute",top:"auto",left:"auto"})}j.css(p)})};typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?d.each(a.alsoResize,function(h,i){f(h,i)}):f(a.alsoResize)},stop:function(){var b=d(this).data("resizable");if(b._revertToRelativePosition&&d.browser.opera){b._revertToRelativePosition=false;el.css({position:"relative"})}d(this).removeData("resizable-alsoresize-start")}});d.ui.plugin.add("resizable","animate",{stop:function(b){var a= +d(this).data("resizable"),c=a.options,e=a._proportionallyResizeElements,g=e.length&&/textarea/i.test(e[0].nodeName),f=g&&d.ui.hasScroll(e[0],"left")?0:a.sizeDiff.height;g={width:a.size.width-(g?0:a.sizeDiff.width),height:a.size.height-f};f=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(d.extend(g,h&&f?{top:h,left:f}:{}),{duration:c.animateDuration,easing:c.animateEasing, +step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};e&&e.length&&d(e[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});d.ui.plugin.add("resizable","containment",{start:function(){var b=d(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof d?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement= +d(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:d(document),left:0,top:0,width:d(document).width(),height:d(document).height()||document.body.parentNode.scrollHeight}}else{var e=d(a),g=[];d(["Top","Right","Left","Bottom"]).each(function(i,j){g[i]=m(e.css("padding"+j))});b.containerOffset=e.offset();b.containerPosition=e.position();b.containerSize={height:e.innerHeight()-g[3],width:e.innerWidth()-g[1]};c=b.containerOffset; +var f=b.containerSize.height,h=b.containerSize.width;h=d.ui.hasScroll(a,"left")?a.scrollWidth:h;f=d.ui.hasScroll(a)?a.scrollHeight:f;b.parentData={element:a,left:c.left,top:c.top,width:h,height:f}}}},resize:function(b){var a=d(this).data("resizable"),c=a.options,e=a.containerOffset,g=a.position;b=a._aspectRatio||b.shiftKey;var f={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))f=e;if(g.left<(a._helper?e.left:0)){a.size.width+=a._helper?a.position.left-e.left: +a.position.left-f.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?e.left:0}if(g.top<(a._helper?e.top:0)){a.size.height+=a._helper?a.position.top-e.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?e.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-f.left:a.offset.left-f.left)+a.sizeDiff.width);e=Math.abs((a._helper?a.offset.top-f.top:a.offset.top- +e.top)+a.sizeDiff.height);g=a.containerElement.get(0)==a.element.parent().get(0);f=/relative|absolute/.test(a.containerElement.css("position"));if(g&&f)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(e+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-e;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=d(this).data("resizable"),a=b.options,c=b.containerOffset,e=b.containerPosition, +g=b.containerElement,f=d(b.helper),h=f.offset(),i=f.outerWidth()-b.sizeDiff.width;f=f.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(g.css("position"))&&d(this).css({left:h.left-e.left-c.left,width:i,height:f});b._helper&&!a.animate&&/static/.test(g.css("position"))&&d(this).css({left:h.left-e.left-c.left,width:i,height:f})}});d.ui.plugin.add("resizable","ghost",{start:function(){var b=d(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25, +display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=d(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=d(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});d.ui.plugin.add("resizable","grid",{resize:function(){var b= +d(this).data("resizable"),a=b.options,c=b.size,e=b.originalSize,g=b.originalPosition,f=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-e.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-e.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(f)){b.size.width=e.width+h;b.size.height=e.height+a}else if(/^(ne)$/.test(f)){b.size.width=e.width+h;b.size.height=e.height+a;b.position.top=g.top-a}else{if(/^(sw)$/.test(f)){b.size.width=e.width+h;b.size.height= +e.height+a}else{b.size.width=e.width+h;b.size.height=e.height+a;b.position.top=g.top-a}b.position.left=g.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery); +;/* + * jQuery UI Selectable 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Selectables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var d=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(d.options.filter,d.element[0]);f.each(function(){var c=e(this),b=c.offset();e.data(this,"selectable-item",{element:this,$element:c,left:b.left,top:b.top,right:b.left+c.outerWidth(),bottom:b.top+c.outerHeight(),startselected:false,selected:c.hasClass("ui-selected"), +selecting:c.hasClass("ui-selecting"),unselecting:c.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e(document.createElement("div")).css({border:"1px dotted black"}).addClass("ui-selectable-helper")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this}, +_mouseStart:function(d){var f=this;this.opos=[d.pageX,d.pageY];if(!this.options.disabled){var c=this.options;this.selectees=e(c.filter,this.element[0]);this._trigger("start",d);e(c.appendTo).append(this.helper);this.helper.css({"z-index":100,position:"absolute",left:d.clientX,top:d.clientY,width:0,height:0});c.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!d.metaKey){b.$element.removeClass("ui-selected"); +b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",d,{unselecting:b.element})}});e(d.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){b.$element.removeClass("ui-unselecting").addClass("ui-selecting");b.unselecting=false;b.selecting=true;b.selected=true;f._trigger("selecting",d,{selecting:b.element});return false}})}},_mouseDrag:function(d){var f=this;this.dragged=true;if(!this.options.disabled){var c=this.options, +b=this.opos[0],g=this.opos[1],h=d.pageX,i=d.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(c.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){this.containerCache={};this.element.addClass("ui-sortable"); +this.refresh();this.floating=this.items.length?/left|right/.test(this.items[0].item.css("float")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a==="disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(self, +arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem= +c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset, +{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment(); +if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start", +a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute"); +if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a, +c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]== +document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp();this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate", +null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem): +d(this.domPosition.parent).prepend(this.currentItem);return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});return c.join("&")},toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c}, +_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a= +this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)? +h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"), +b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)? +i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b],e=this.options.toleranceElement?d(this.options.toleranceElement, +c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height= +this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()- +parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0], +this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b= +1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update", +g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity", +this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()==location.href.toLowerCase()}},_create:function(){var a=this.options,b=this;this.running=0;this.element.addClass("ui-accordion ui-widget ui-helper-reset"); +this.element[0].nodeName=="UL"&&this.element.children("li").addClass("ui-accordion-li-fix");this.headers=this.element.find(a.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c(this).removeClass("ui-state-focus")}); +this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(a.navigation){var d=this.element.find("a").filter(a.navigationFilter);if(d.length){var f=d.closest(".ui-accordion-header");this.active=f.length?f:d.closest(".ui-accordion-content").prev()}}this.active=this._findActive(this.active||a.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");this.active.next().addClass("ui-accordion-content-active"); +this._createIcons();this.resize();this.element.attr("role","tablist");this.headers.attr("role","tab").bind("keydown",function(g){return b._keydown(g)}).next().attr("role","tabpanel");this.headers.not(this.active||"").attr("aria-expanded","false").attr("tabIndex","-1").next().hide();this.active.length?this.active.attr("aria-expanded","true").attr("tabIndex","0"):this.headers.eq(0).attr("tabIndex","0");c.browser.safari||this.headers.find("a").attr("tabIndex","-1");a.event&&this.headers.bind(a.event+ +".accordion",function(g){b._clickHandler.call(b,g,this);g.preventDefault()})},_createIcons:function(){var a=this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.find(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role").unbind(".accordion").removeData("accordion"); +this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabIndex");this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");if(a.autoHeight||a.fillHeight)b.css("height", +"");return this},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();b&&this._createIcons()}},_keydown:function(a){var b=c.ui.keyCode;if(!(this.options.disabled||a.altKey||a.ctrlKey)){var d=this.headers.length,f=this.headers.index(a.target),g=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:g=this.headers[(f+1)%d];break;case b.LEFT:case b.UP:g=this.headers[(f-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target}, +a.target);a.preventDefault()}if(g){c(a.target).attr("tabIndex","-1");c(g).attr("tabIndex","0");g.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0, +b-c(this).innerHeight()+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a=="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d= +this.options;if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]==this.active[0];d.active=d.collapsible&&b?false:c(".ui-accordion-header",this.element).index(a);if(!(this.running||!d.collapsible&&b)){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").find(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected); +a.next().addClass("ui-accordion-content-active")}e=a.next();f=this.active.next();g={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):e,oldContent:f};d=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(e,f,g,b,d)}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); +this.active.next().addClass("ui-accordion-content-active");var f=this.active.next(),g={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:f},e=this.active=c([]);this._toggle(e,f,g)}},_toggle:function(a,b,d,f,g){var e=this.options,k=this;this.toShow=a;this.toHide=b;this.data=d;var i=function(){if(k)return k._completed.apply(k,arguments)};this._trigger("changestart",null,this.data);this.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&f?{toShow:c([]), +toHide:b,complete:i,down:g,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:i,down:g,autoHeight:e.autoHeight||e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;f=c.ui.accordion.animations;var h=e.duration,j=e.animated;if(j&&!f[j]&&!c.easing[j])j="slide";f[j]||(f[j]=function(l){this.slide(l,{easing:j, +duration:h||700})});f[j](d)}else{if(e.collapsible&&f)a.toggle();else{b.hide();a.show()}i(true)}b.prev().attr("aria-expanded","false").attr("tabIndex","-1").blur();a.prev().attr("aria-expanded","true").attr("tabIndex","0").focus()},_completed:function(a){var b=this.options;this.running=a?0:--this.running;if(!this.running){b.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion, +{version:"1.8.1",animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),f=0,g={},e={},k;b=a.toShow;k=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(i,h){e[h]="hide";i=(""+c.css(a.toShow[0], +h)).match(/^([\d+-.]+)(.*)$/);g[h]={value:i[1],unit:i[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(e,{step:function(i,h){if(h.prop=="height")f=h.end-h.start===0?0:(h.now-h.start)/(h.end-h.start);a.toShow[0].style[h.prop]=f*g[h.prop].value+g[h.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css("width",k);a.toShow.css({overflow:d});a.complete()}})}else a.toHide.animate({height:"hide"}, +a);else a.toShow.animate({height:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); +;/* + * jQuery UI Autocomplete 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Autocomplete + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.position.js + */ +(function(e){e.widget("ui.autocomplete",{options:{minLength:1,delay:300},_create:function(){var a=this,b=this.element[0].ownerDocument;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){var d=e.ui.keyCode;switch(c.keyCode){case d.PAGE_UP:a._move("previousPage",c);break;case d.PAGE_DOWN:a._move("nextPage",c);break;case d.UP:a._move("previous",c);c.preventDefault(); +break;case d.DOWN:a._move("next",c);c.preventDefault();break;case d.ENTER:a.menu.active&&c.preventDefault();case d.TAB:if(!a.menu.active)return;a.menu.select(c);break;case d.ESCAPE:a.element.val(a.term);a.close(c);break;case d.LEFT:case d.RIGHT:case d.SHIFT:case d.CONTROL:case d.ALT:break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){a.search(null,c)},a.options.delay);break}}).bind("focus.autocomplete",function(){a.selectedItem=null;a.previous=a.element.val()}).bind("blur.autocomplete", +function(c){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)});this._initSource();this.response=function(){return a._response.apply(a,arguments)};this.menu=e("
    ").addClass("ui-autocomplete").appendTo("body",b).menu({focus:function(c,d){d=d.item.data("item.autocomplete");false!==a._trigger("focus",null,{item:d})&&/^key/.test(c.originalEvent.type)&&a.element.val(d.value)},selected:function(c,d){d=d.item.data("item.autocomplete");false!==a._trigger("select", +c,{item:d})&&a.element.val(d.value);a.close(c);c=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=c}a.selectedItem=d},blur:function(){a.menu.element.is(":visible")&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");e.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"); +this.menu.element.remove();e.Widget.prototype.destroy.call(this)},_setOption:function(a){e.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource()},_initSource:function(){var a,b;if(e.isArray(this.options.source)){a=this.options.source;this.source=function(c,d){d(e.ui.autocomplete.filter(a,c.term))}}else if(typeof this.options.source==="string"){b=this.options.source;this.source=function(c,d){e.getJSON(b,c,d)}}else this.source=this.options.source},search:function(a,b){a= +a!=null?a:this.element.val();if(a.length").data("item.autocomplete",b).append(""+b.label+"").appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&& +/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});e.extend(e.ui.autocomplete,{escapeRegex:function(a){return a.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")},filter:function(a,b){var c=new RegExp(e.ui.autocomplete.escapeRegex(b),"i");return e.grep(a,function(d){return c.test(d.label||d.value||d)})}})})(jQuery); +(function(e){e.widget("ui.menu",{_create:function(){var a=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(b){if(e(b.target).closest(".ui-menu-item a").length){b.preventDefault();a.select(b)}});this.refresh()},refresh:function(){var a=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", +-1).mouseenter(function(b){a.activate(b,e(this).parent())}).mouseleave(function(){a.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.attr("scrollTop"),f=this.element.height();if(c<0)this.element.attr("scrollTop",d+c);else c>f&&this.element.attr("scrollTop",d+c-f+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",a,{item:b})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); +this._trigger("blur");this.active=null}},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prev().length},last:function(){return this.active&&!this.active.next().length},move:function(a,b,c){if(this.active){a=this.active[a+"All"](".ui-menu-item").eq(0);a.length?this.activate(c,a):this.activate(c,this.element.children(b))}else this.activate(c,this.element.children(b))},nextPage:function(a){if(this.hasScroll())if(!this.active|| +this.last())this.activate(a,this.element.children(":first"));else{var b=this.active.offset().top,c=this.element.height(),d=this.element.children("li").filter(function(){var f=e(this).offset().top-b-c+e(this).height();return f<10&&f>-10});d.length||(d=this.element.children(":last"));this.activate(a,d)}else this.activate(a,this.element.children(!this.active||this.last()?":first":":last"))},previousPage:function(a){if(this.hasScroll())if(!this.active||this.first())this.activate(a,this.element.children(":last")); +else{var b=this.active.offset().top,c=this.element.height();result=this.element.children("li").filter(function(){var d=e(this).offset().top-b+c-e(this).height();return d<10&&d>-10});result.length||(result=this.element.children(":first"));this.activate(a,result)}else this.activate(a,this.element.children(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()
    ").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(), +d=this.options.icons,e=d.primary&&d.secondary;if(d.primary||d.secondary){b.addClass("ui-button-text-icon"+(e?"s":""));d.primary&&b.prepend("");d.secondary&&b.append("");if(!this.options.text){b.addClass(e?"ui-button-icons-only":"ui-button-icon-only").removeClass("ui-button-text-icons ui-button-text-icon");this.hasTitle||b.attr("title",c)}}else b.addClass("ui-button-text-only")}}}); +a.widget("ui.buttonset",{_create:function(){this.element.addClass("ui-buttonset");this._init()},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(":button, :submit, :reset, :checkbox, :radio, a, :data(button)").filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()}, +destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); +;/* + * jQuery UI Dialog 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Dialog + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.button.js + * jquery.ui.draggable.js + * jquery.ui.mouse.js + * jquery.ui.position.js + * jquery.ui.resizable.js + */ +(function(c){c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:"center",resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");var a=this,b=a.options,d=b.title||a.originalTitle||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ +b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), +h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", +e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); +a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== +b.uiDialog[0])d=Math.max(d,c(this).css("z-index"))});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=1;d.uiDialog.css("z-index", +c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;d.next().length&&d.appendTo("body");a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== +f[0]&&e.shiftKey){g.focus(1);return false}}});c([]).add(d.find(".ui-dialog-content :tabbable:first")).add(d.find(".ui-dialog-buttonpane :tabbable:first")).add(d).filter(":first").focus();a._trigger("open");a._isOpen=true;return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,function(){return!(d=true)});if(d){c.each(a, +function(g,f){g=c('').text(g).click(function(){f.apply(b.element[0],arguments)}).appendTo(e);c.fn.button&&g.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging"); +b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,originalSize:f.originalSize,position:f.position,size:f.size}}a=a===undefined?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position"); +a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop", +f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0];a=a||c.ui.dialog.prototype.options.position;if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(e,g){if(+b[e]===b[e]){d[e]=b[e];b[e]= +g}})}else if(typeof a==="object"){if("left"in a){b[0]="left";d[0]=a.left}else if("right"in a){b[0]="right";d[0]=-a.right}if("top"in a){b[1]="top";d[1]=a.top}else if("bottom"in a){b[1]="bottom";d[1]=-a.bottom}}(a=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position({my:b.join(" "),at:b.join(" "),offset:d.join(" "),of:window,collision:"fit",using:function(e){var g=c(this).css(e).offset().top;g<0&&c(this).css("top",e.top-g)}});a||this.uiDialog.hide()},_setOption:function(a, +b){var d=this,e=d.uiDialog,g=e.is(":data(resizable)"),f=false;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):e.removeClass("ui-dialog-disabled");break;case "draggable":b?d._makeDraggable():e.draggable("destroy");break; +case "height":f=true;break;case "maxHeight":g&&e.resizable("option","maxHeight",b);f=true;break;case "maxWidth":g&&e.resizable("option","maxWidth",b);f=true;break;case "minHeight":g&&e.resizable("option","minHeight",b);f=true;break;case "minWidth":g&&e.resizable("option","minWidth",b);f=true;break;case "position":d._position(b);break;case "resizable":g&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title", +d.uiDialogTitlebar).html(""+(b||" "));break;case "width":f=true;break}c.Widget.prototype._setOption.apply(d,arguments);f&&d._size()},_size:function(){var a=this.options,b;this.element.css({width:"auto",minHeight:0,height:0});b=this.uiDialog.css({height:"auto",width:a.width}).height();this.element.css(a.height==="auto"?{minHeight:Math.max(a.minHeight-b,0),height:"auto"}:{minHeight:0,height:Math.max(a.height-b,0)}).show();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight", +this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.1",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&& +c(document).bind(c.ui.dialog.overlay.events,function(d){return c(d.target).zIndex()>=c.ui.dialog.overlay.maxZ})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||c("
    ").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&& +b.bgiframe();this.instances.push(b);return b},destroy:function(a){this.oldInstances.push(this.instances.splice(c.inArray(a,this.instances),1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var b=0;c.each(this.instances,function(){b=Math.max(b,this.css("z-index"))});this.maxZ=b},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight, +document.body.offsetHeight);return a");if(!a.values)a.values=[this._valueMin(),this._valueMin()];if(a.values.length&&a.values.length!==2)a.values=[a.values[0],a.values[0]]}else this.range=d("
    ");this.range.appendTo(this.element).addClass("ui-slider-range");if(a.range==="min"||a.range==="max")this.range.addClass("ui-slider-range-"+a.range);this.range.addClass("ui-widget-header")}d(".ui-slider-handle",this.element).length===0&&d("").appendTo(this.element).addClass("ui-slider-handle"); +if(a.values&&a.values.length)for(;d(".ui-slider-handle",this.element).length").appendTo(this.element).addClass("ui-slider-handle");this.handles=d(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(c){c.preventDefault()}).hover(function(){a.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(a.disabled)d(this).blur(); +else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(c){d(this).data("index.ui-slider-handle",c)});this.handles.keydown(function(c){var e=true,f=d(this).data("index.ui-slider-handle"),g,h,i;if(!b.options.disabled){switch(c.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:e= +false;if(!b._keySliding){b._keySliding=true;d(this).addClass("ui-state-active");g=b._start(c,f);if(g===false)return}break}i=b.options.step;g=b.options.values&&b.options.values.length?(h=b.values(f)):(h=b.value());switch(c.keyCode){case d.ui.keyCode.HOME:h=b._valueMin();break;case d.ui.keyCode.END:h=b._valueMax();break;case d.ui.keyCode.PAGE_UP:h=g+(b._valueMax()-b._valueMin())/5;break;case d.ui.keyCode.PAGE_DOWN:h=g-(b._valueMax()-b._valueMin())/5;break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(g=== +b._valueMax())return;h=g+i;break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(g===b._valueMin())return;h=g-i;break}b._slide(c,f,h);return e}}).keyup(function(c){var e=d(this).data("index.ui-slider-handle");if(b._keySliding){b._keySliding=false;b._stop(c,e);b._change(c,e);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); +this._mouseDestroy();return this},_mouseCapture:function(b){var a=this.options,c,e,f,g,h,i;if(a.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c={x:b.pageX,y:b.pageY};e=this._normValueFromMouse(c);f=this._valueMax()-this._valueMin()+1;h=this;this.handles.each(function(j){var k=Math.abs(e-h.values(j));if(f>k){f=k;g=d(this);i=j}});if(a.range===true&&this.values(1)===a.min){i+=1;g=d(this.handles[i])}if(this._start(b, +i)===false)return false;this._mouseSliding=true;h._handleIndex=i;g.addClass("ui-state-active").focus();a=g.offset();this._clickOffset=!d(b.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:b.pageX-a.left-g.width()/2,top:b.pageY-a.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)};e=this._normValueFromMouse(c);this._slide(b,i,e);return this._animateOff=true},_mouseStart:function(){return true}, +_mouseDrag:function(b){var a=this._normValueFromMouse({x:b.pageX,y:b.pageY});this._slide(b,this._handleIndex,a);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(b){var a; +if(this.orientation==="horizontal"){a=this.elementSize.width;b=b.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{a=this.elementSize.height;b=b.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}a=b/a;if(a>1)a=1;if(a<0)a=0;if(this.orientation==="vertical")a=1-a;b=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+a*b)},_start:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value= +this.values(a);c.values=this.values()}return this._trigger("start",b,c)},_slide:function(b,a,c){var e;if(this.options.values&&this.options.values.length){e=this.values(a?0:1);if(this.options.values.length===2&&this.options.range===true&&(a===0&&c>e||a===1&&c1){this.options.values[b]=this._trimAlignValue(a);this._refreshValue();this._change(null,b)}if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;e=arguments[0];for(f=0;fthis._valueMax())return this._valueMax();var a=this.options.step,c=b%a;b=b-c;if(c>=a/2)b+=a;return parseFloat(b.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,a=this.options,c=this, +e=!this._animateOff?a.animate:false,f,g={},h,i,j,k;if(this.options.values&&this.options.values.length)this.handles.each(function(l){f=(c.values(l)-c._valueMin())/(c._valueMax()-c._valueMin())*100;g[c.orientation==="horizontal"?"left":"bottom"]=f+"%";d(this).stop(1,1)[e?"animate":"css"](g,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(l===0)c.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},a.animate);if(l===1)c.range[e?"animate":"css"]({width:f-h+"%"},{queue:false,duration:a.animate})}else{if(l=== +0)c.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},a.animate);if(l===1)c.range[e?"animate":"css"]({height:f-h+"%"},{queue:false,duration:a.animate})}h=f});else{i=this.value();j=this._valueMin();k=this._valueMax();f=k!==j?(i-j)/(k-j)*100:0;g[c.orientation==="horizontal"?"left":"bottom"]=f+"%";this.handle.stop(1,1)[e?"animate":"css"](g,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[e? +"animate":"css"]({width:100-f+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[e?"animate":"css"]({height:100-f+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.1"})})(jQuery); +;/* + * jQuery UI Tabs 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Tabs + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(d){var s=0,u=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:'
  • #{label}
  • '},_create:function(){this._tabify(true)},_setOption:function(c,e){if(c=="selected")this.options.collapsible&&e==this.options.selected|| +this.select(e);else{this.options[c]=e;this._tabify()}},_tabId:function(c){return c.title&&c.title.replace(/\s/g,"_").replace(/[^A-Za-z0-9\-_:\.]/g,"")||this.options.idPrefix+ ++s},_sanitizeSelector:function(c){return c.replace(/:/g,"\\:")},_cookie:function(){var c=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+ ++u);return d.cookie.apply(null,[c].concat(d.makeArray(arguments)))},_ui:function(c,e){return{tab:c,panel:e,index:this.anchors.index(c)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var c= +d(this);c.html(c.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function e(g,f){g.css({display:""});!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}this.list=this.element.find("ol,ul").eq(0);this.lis=d("li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);var a=this,b=this.options,h=/^#.+/;this.anchors.each(function(g,f){var j=d(f).attr("href"),l=j.split("#")[0],p;if(l&&(l===location.toString().split("#")[0]|| +(p=d("base")[0])&&l===p.href)){j=f.hash;f.href=j}if(h.test(j))a.panels=a.panels.add(a._sanitizeSelector(j));else if(j!="#"){d.data(f,"href.tabs",j);d.data(f,"load.tabs",j.replace(/#.*$/,""));j=a._tabId(f);f.href="#"+j;f=d("#"+j);if(!f.length){f=d(b.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else b.disabled.push(g)});if(c){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); +this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(b.selected===undefined){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){b.selected=g;return false}});if(typeof b.selected!="number"&&b.cookie)b.selected=parseInt(a._cookie(),10);if(typeof b.selected!="number"&&this.lis.filter(".ui-tabs-selected").length)b.selected= +this.lis.index(this.lis.filter(".ui-tabs-selected"));b.selected=b.selected||(this.lis.length?0:-1)}else if(b.selected===null)b.selected=-1;b.selected=b.selected>=0&&this.anchors[b.selected]||b.selected<0?b.selected:0;b.disabled=d.unique(b.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(b.selected,b.disabled)!=-1&&b.disabled.splice(d.inArray(b.selected,b.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); +if(b.selected>=0&&this.anchors.length){this.panels.eq(b.selected).removeClass("ui-tabs-hide");this.lis.eq(b.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[b.selected],a.panels[b.selected]))});this.load(b.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else b.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));this.element[b.collapsible?"addClass": +"removeClass"]("ui-tabs-collapsible");b.cookie&&this._cookie(b.selected,b.cookie);c=0;for(var i;i=this.lis[c];c++)d(i)[d.inArray(c,b.disabled)!=-1&&!d(i).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");b.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(b.event!="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+g)};this.lis.bind("mouseover.tabs", +function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(b.fx)if(d.isArray(b.fx)){m=b.fx[0];o=b.fx[1]}else m=o=b.fx;var q=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",function(){e(f,o);a._trigger("show", +null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},r=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};this.anchors.bind(b.event+".tabs", +function(){var g=this,f=d(this).closest("li"),j=a.panels.filter(":not(.ui-tabs-hide)"),l=d(a._sanitizeSelector(this.hash));if(f.hasClass("ui-tabs-selected")&&!b.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}b.selected=a.anchors.index(this);a.abort();if(b.collapsible)if(f.hasClass("ui-tabs-selected")){b.selected=-1;b.cookie&&a._cookie(b.selected,b.cookie);a.element.queue("tabs",function(){r(g, +j)}).dequeue("tabs");this.blur();return false}else if(!j.length){b.cookie&&a._cookie(b.selected,b.cookie);a.element.queue("tabs",function(){q(g,l)});a.load(a.anchors.index(this));this.blur();return false}b.cookie&&a._cookie(b.selected,b.cookie);if(l.length){j.length&&a.element.queue("tabs",function(){r(g,j)});a.element.queue("tabs",function(){q(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";d.browser.msie&&this.blur()});this.anchors.bind("click.tabs", +function(){return false})},destroy:function(){var c=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(b,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this, +"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});c.cookie&&this._cookie(null,c.cookie);return this},add:function(c,e,a){if(a===undefined)a=this.anchors.length;var b=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,e));c=!c.indexOf("#")?c.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs", +true);var i=d("#"+c);i.length||(i=d(h.panelTemplate).attr("id",c).data("destroy.tabs",true));i.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);i.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);i.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");i.removeClass("ui-tabs-hide"); +this.element.queue("tabs",function(){b._trigger("show",null,b._ui(b.anchors[0],b.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(c){var e=this.options,a=this.lis.eq(c).remove(),b=this.panels.eq(c).remove();if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(c+(c+1=c?--h:h});this._tabify();this._trigger("remove", +null,this._ui(a.find("a")[0],b[0]));return this},enable:function(c){var e=this.options;if(d.inArray(c,e.disabled)!=-1){this.lis.eq(c).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=c});this._trigger("enable",null,this._ui(this.anchors[c],this.panels[c]));return this}},disable:function(c){var e=this.options;if(c!=e.selected){this.lis.eq(c).addClass("ui-state-disabled");e.disabled.push(c);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[c],this.panels[c]))}return this}, +select:function(c){if(typeof c=="string")c=this.anchors.index(this.anchors.filter("[href$="+c+"]"));else if(c===null)c=-1;if(c==-1&&this.options.collapsible)c=this.options.selected;this.anchors.eq(c).trigger(this.options.event+".tabs");return this},load:function(c){var e=this,a=this.options,b=this.anchors.eq(c)[0],h=d.data(b,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(b,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(c).addClass("ui-state-processing"); +if(a.spinner){var i=d("span",b);i.data("label.tabs",i.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){d(e._sanitizeSelector(b.hash)).html(k);e._cleanup();a.cache&&d.data(b,"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[c],e.panels[c]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[c],e.panels[c]));try{a.ajaxOptions.error(k,n,c,b)}catch(m){}}}));e.element.dequeue("tabs");return this}}, +abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},url:function(c,e){this.anchors.eq(c).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.1"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(c,e){var a=this,b=this.options,h=a._rotate||(a._rotate= +function(i){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=b.selected;a.select(++k')}function E(a,b){d.extend(a, +b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.1"}});var y=(new Date).getTime();d.extend(J.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]= +f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id)a.id="dp"+ ++this.uuid;var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
    ')}}, +_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& +b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== +""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, +c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), +true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){a="dp"+ ++this.uuid;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor==Date? +this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]); +d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}}, +_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b= +d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; +for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target|| +a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a); +d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&& +d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f, +h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover"); +this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover"); +this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); +a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(), +k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"]; +a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val(): +"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&& +!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth; +b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){a=this._getInst(d(a)[0]); +a.input&&a._selectingMonthYear&&!d.browser.msie&&a.input.focus();a._selectingMonthYear=!a._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a, +"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")|| +this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null; +for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1-1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c, +k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c? +c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear|| +a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? +new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a)); +n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m, +g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&& +a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),G=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var K=this._getDefaultDate(a),H="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
    '+(/all|left/.test(t)&&C==0?c? +f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'
    ';var A=k?'":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, +A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var N=0;N";var O=!k?"":'";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,I=B&&!G||!F[0]||j&&qo;O+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=O+""}g++;if(g>11){g=0;m++}x+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(q)+""+(B&&!w?" ":I?''+q.getDate()+ +"":''+q.getDate()+"")+"
    "+(l?""+(i[0]>0&&D==i[1]-1?'
    ':""):"");L+=x}H+=L}H+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': +"");a._keyEvent=false;return H},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='
    ',o="";if(h||!k)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(j+=o+(h||!(k&&l)?" ":""));if(h||!l)j+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b, +i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?" ":"")+o;j+="
    ";return j},_adjustInstDate:function(a,b,c){var e= +a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, +"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); +c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, +"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= +function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); +return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new J;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.1";window["DP_jQuery_"+y]=d})(jQuery); +;/* + * jQuery UI Progressbar 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Progressbar + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function(b){b.widget("ui.progressbar",{options:{value:0},_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); +this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===undefined)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){switch(a){case "value":this.options.value=c;this._refreshValue();this._trigger("change");break}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;if(athis._valueMax())a=this._valueMax();return a}, +_valueMin:function(){return 0},_valueMax:function(){return 100},_refreshValue:function(){var a=this.value();this.valueDiv[a===this._valueMax()?"addClass":"removeClass"]("ui-corner-right").width(a+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.1"})})(jQuery); +;/* + * jQuery UI Effects 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/ + */ +jQuery.effects||function(f){function k(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], +16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return l.transparent;return l[f.trim(c).toLowerCase()]}function q(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return k(b)}function m(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, +a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function n(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in r||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function s(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function j(c,a,b,d){if(typeof c=="object"){d= +a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(f.isFunction(b)){d=b;b=null}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:f.fx.speeds[b]||f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=q(b.elem,a);b.end=k(b.end);b.colorInit= +true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var l={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189, +183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255, +165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},o=["add","remove","toggle"],r={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,d){if(f.isFunction(b)){d=b;b=null}return this.each(function(){var e=f(this),g=e.attr("style")||" ",h=n(m.call(this)),p,t=e.attr("className");f.each(o,function(u, +i){c[i]&&e[i+"Class"](c[i])});p=n(m.call(this));e.attr("className",t);e.animate(s(h,p),a,b,function(){f.each(o,function(u,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments)})})};f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a? +f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===undefined?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.1",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"}); +c.css({position:"relative",top:0,left:0})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=j.apply(this,arguments);a={options:a[1],duration:a[2],callback:a[3]};var b=f.effects[c];return b&&!f.fx.off?b.call(this,a):this},_show:f.fn.show,show:function(c){if(!c|| +typeof c=="number"||f.fx.speeds[c])return this._show.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(!c||typeof c=="number"||f.fx.speeds[c])return this._hide.apply(this,arguments);else{var a=j.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(!c||typeof c=="number"||f.fx.speeds[c]||typeof c=="boolean"||f.isFunction(c))return this.__toggle.apply(this, +arguments);else{var a=j.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c, +a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+ +b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2, +10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)* +a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ +e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); +;/* + * jQuery UI Effects Fold 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Fold + * + * Depends: + * jquery.effects.core.js + */ +(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","left"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],10)/100* +f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); +;/* + * jQuery UI Effects Highlight 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Highlight + * + * Depends: + * jquery.effects.core.js + */ +(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& +this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); +;/* + * jQuery UI Effects Pulsate 1.8.1 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Effects/Pulsate + * + * Depends: + * jquery.effects.core.js + */ +(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); +b.dequeue()})})}})(jQuery); +; \ No newline at end of file diff --git a/helios/media/loading.gif b/helios/media/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c2f7c058836808f7f1f5f9a4bdf37d4e5f9284a GIT binary patch literal 3208 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 +

    +{#if $T.admin_p}[ +{#if $T.question$index > 0}^] [{#/if} +x] [edit] {#/if}{$T.question$index + 1}. {$T.question.question} ({$T.question.choice_type}, select between {$T.question.min} and {#if $T.question.max != null}{$T.question.max}{#else}unlimited{#/if} answers, result type {$T.question.result_type}.)

    +
      +{#foreach $T.question.answers as answer} +
    • {$T.answer}
    • +{#/for} +
    + + +{#else} +no questions yet +{#/for} + +{#if $T.admin_p} +

    Add a Question:

    +
    +

    + + +   Select between    + + +   and    + + + +   +answers. + +   +   +Result Type:  + + +

    + + + + + + + + + + + + +
    Question:
       
    add 5 more answers
    +{#/if} +
    diff --git a/helios/models.py b/helios/models.py new file mode 100644 index 000000000..e0fc2f616 --- /dev/null +++ b/helios/models.py @@ -0,0 +1,706 @@ +# -*- coding: utf-8 -*- +""" +Data Objects for Helios. + +Ben Adida +(ben@adida.net) +""" + +from django.db import models +from django.utils import simplejson +from django.conf import settings + +import datetime, logging, uuid, random + +from crypto import electionalgs, algs, utils +from helios import utils as heliosutils +import helios + +# useful stuff in auth +from auth.models import User, AUTH_SYSTEMS +from auth.jsonfield import JSONField + +import csv, copy + +# global counters +GLOBAL_COUNTER_VOTERS = 'global_counter_voters' +GLOBAL_COUNTER_CAST_VOTES = 'global_counter_cast_votes' +GLOBAL_COUNTER_ELECTIONS = 'global_counter_elections' + +class Election(models.Model, electionalgs.Election): + admin = models.ForeignKey(User) + + uuid = models.CharField(max_length=50, null=False) + + short_name = models.CharField(max_length=100) + name = models.CharField(max_length=250) + + description = models.TextField() + public_key = JSONField(algs.EGPublicKey, null=True) + private_key = JSONField(algs.EGSecretKey, null=True) + questions = JSONField(null=True) + + # eligibility is a JSON field, which lists auth_systems and eligibility details for that auth_system, e.g. + # [{'auth_system': 'cas', 'constraint': [{'year': 'u12'}, {'year':'u13'}]}, {'auth_system' : 'password'}, {'auth_system' : 'openid', 'constraint': [{'host':'http://myopenid.com'}]}] + eligibility = JSONField(null=True) + + # open registration? + # this is now used to indicate the state of registration, + # whether or not the election is frozen + openreg = models.BooleanField(default=False) + + # featured election? + featured_p = models.BooleanField(default=False) + + # voter aliases? + use_voter_aliases = models.BooleanField(default=False) + + # where votes should be cast + cast_url = models.CharField(max_length = 500) + + # dates at which this was touched + created_at = models.DateTimeField(auto_now_add=True) + modified_at = models.DateTimeField(auto_now_add=True) + + # dates at which things happen for the election + frozen_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + archived_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + + # dates for the election steps, as scheduled + # these are always UTC + registration_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + voting_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + voting_ends_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + tallying_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + + # dates when things were forced to be performed + voting_started_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + voting_extended_until = models.DateTimeField(auto_now_add=False, default=None, null=True) + voting_ended_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + tallying_started_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + tallying_finished_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + tallies_combined_at = models.DateTimeField(auto_now_add=False, default=None, null=True) + + # the hash of all voters (stored for large numbers) + voters_hash = models.CharField(max_length=100, null=True) + + # encrypted tally, each a JSON string + # used only for homomorphic tallies + encrypted_tally = JSONField(electionalgs.Tally, null=True) + + # results of the election + result = JSONField(null=True) + + # decryption proof, a JSON object + result_proof = JSONField(null=True) + + @property + def num_cast_votes(self): + return self.voter_set.exclude(vote=None).count() + + @property + def num_voters(self): + return self.voter_set.count() + + @property + def encrypted_tally_hash(self): + if not self.encrypted_tally: + return None + + return utils.hash_b64(self.encrypted_tally.toJSON()) + + @classmethod + def get_featured(cls): + return cls.objects.filter(featured_p = True).order_by('short_name') + + @classmethod + def get_or_create(cls, **kwargs): + return cls.objects.get_or_create(short_name = kwargs['short_name'], defaults=kwargs) + + @classmethod + def get_by_user_as_admin(cls, user, include_archived=False): + query = cls.objects.filter(admin = user) + if include_archived: + query = query.filter('archived_at', None) + query = query.order_by('-created_at') + return query + + @classmethod + def get_by_user_as_voter(cls, user): + return [v.election for v in Voter.get_by_user(user)] + + @classmethod + def get_by_uuid(cls, uuid): + try: + return cls.objects.get(uuid=uuid) + except cls.DoesNotExist: + return None + + @classmethod + def get_by_short_name(cls, short_name): + try: + return cls.objects.get(short_name=short_name) + except cls.DoesNotExist: + return None + + def add_voters_file(self, uploaded_file): + """ + expects a django uploaded_file data structure, which has filename, content, size... + """ + random_filename = str(uuid.uuid1()) + new_voter_file = VoterFile(election = self) + new_voter_file.voter_file.save(random_filename, uploaded_file) + self.append_log(ElectionLog.VOTER_FILE_ADDED) + + return new_voter_file + + def user_eligible_p(self, user): + """ + Checks if a user is eligible for this election. + """ + # registration closed, then eligibility doesn't come into play + if not self.openreg: + return False + + if self.eligibility == None: + return True + + # is the user eligible for one of these cases? + for eligibility_case in self.eligibility: + if user.is_eligible_for(eligibility_case): + return True + + return False + + def voting_has_started(self): + """ + has voting begun? voting begins if the election is frozen, at the prescribed date or at the date that voting was forced to start + """ + return self.frozen_at != None and (self.voting_starts_at == None or (datetime.datetime.utcnow() >= (self.voting_started_at or self.voting_starts_at))) + + def voting_has_stopped(self): + """ + has voting stopped? if tally computed, yes, otherwise if we have passed the date voting was manually stopped at, + or failing that the date voting was extended until, or failing that the date voting is scheduled to end at. + """ + voting_end = self.voting_ended_at or self.voting_extended_until or self.voting_ends_at + return (voting_end != None and datetime.datetime.utcnow() >= voting_end) or self.encrypted_tally + + @property + def issues_before_freeze(self): + issues = [] + if self.questions == None or len(self.questions) == 0: + issues.append("no questions") + + trustees = Trustee.get_by_election(self) + if len(trustees) == 0: + issues.append("no trustees") + + return issues + + + def ready_for_tallying(self): + return datetime.datetime.utcnow() >= self.tallying_starts_at + + def compute_tally(self): + """ + tally the election, assuming votes already verified + """ + tally = self.init_tally() + for voter in self.voter_set.all(): + if voter.vote: + tally.add_vote(voter.vote, verify_p=False) + + self.encrypted_tally = tally + self.save() + + def ready_for_decryption(self): + return self.encrypted_tally != None + + def ready_for_decryption_combination(self): + """ + do we have a tally from all trustees? + """ + for t in Trustee.get_by_election(self): + if not t.decryption_factors: + return False + + return True + + def combine_decryptions(self): + """ + combine all of the decryption results + """ + + # gather the decryption factors + trustees = Trustee.get_by_election(self) + decryption_factors = [t.decryption_factors for t in trustees] + + self.result = self.encrypted_tally.decrypt_from_factors(decryption_factors, self.public_key) + + self.append_log(ElectionLog.DECRYPTIONS_COMBINED) + + self.save() + + def generate_voters_hash(self): + """ + look up the list of voters, make a big file, and hash it + """ + + # FIXME: for now we don't generate this voters hash: + return + + if self.openreg: + self.voters_hash = None + else: + voters = Voter.get_by_election(self) + voters_json = utils.to_json([v.toJSONDict() for v in voters]) + self.voters_hash = utils.hash_b64(voters_json) + + def increment_voters(self): + ## FIXME + return 0 + + def increment_cast_votes(self): + ## FIXME + return 0 + + def set_eligibility(self): + """ + if registration is closed and eligibility has not been + already set, then this call sets the eligibility criteria + based on the actual list of voters who are already there. + + This helps ensure that the login box shows the proper options. + + If registration is open but no voters have been added with password, + then that option is also canceled out to prevent confusion, since + those elections usually just use the existing login systems. + """ + + # don't override existing eligibility + if self.eligibility != None: + return + + auth_systems = copy.copy(settings.AUTH_ENABLED_AUTH_SYSTEMS) + voter_types = [r['voter_type'] for r in self.voter_set.values('voter_type').distinct()] + + if self.openreg: + if not 'password' in voter_types: + auth_systems.remove('password') + else: + auth_systems = [vt for vt in voter_types if vt in auth_systems] + + self.eligibility = [{'auth_system': auth_system} for auth_system in auth_systems] + self.save() + + def freeze(self): + """ + election is frozen when the voter registration, questions, and trustees are finalized + """ + self.frozen_at = datetime.datetime.utcnow() + + # voters hash + self.generate_voters_hash() + + self.set_eligibility() + + # public key for trustees + trustees = Trustee.get_by_election(self) + combined_pk = trustees[0].public_key + for t in trustees[1:]: + combined_pk = combined_pk * t.public_key + + self.public_key = combined_pk + + # log it + self.append_log(ElectionLog.FROZEN) + + self.save() + + def generate_trustee(self, params): + """ + generate a trustee including the secret key, + thus a helios-based trustee + """ + # FIXME: generate the keypair + keypair = params.generate_keypair() + + # create the trustee + trustee = Trustee(election = self) + trustee.uuid = str(uuid.uuid1()) + trustee.name = settings.DEFAULT_FROM_NAME + trustee.email = settings.DEFAULT_FROM_EMAIL + trustee.public_key = keypair.pk + trustee.secret_key = keypair.sk + + # FIXME: compute it + trustee.public_key_hash = utils.hash_b64(utils.to_json(trustee.public_key.toJSONDict())) + trustee.pok = trustee.secret_key.prove_sk(algs.DLog_challenge_generator) + + trustee.save() + + def get_helios_trustee(self): + trustees_with_sk = self.trustee_set.exclude(secret_key = None) + if len(trustees_with_sk) > 0: + return trustees_with_sk[0] + else: + return None + + def has_helios_trustee(self): + return self.get_helios_trustee() != None + + def helios_trustee_decrypt(self): + tally = self.encrypted_tally + tally.init_election(self) + + trustee = self.get_helios_trustee() + factors, proof = tally.decryption_factors_and_proofs(trustee.secret_key) + + trustee.decryption_factors = factors + trustee.decryption_proofs = proof + trustee.save() + + def append_log(self, text): + item = ElectionLog(election = self, log=text, at=datetime.datetime.utcnow()) + item.save() + return item + + def get_log(self): + return self.electionlog_set.order_by('-at') + + @property + def url(self): + return helios.get_election_url(self) + +class ElectionLog(models.Model): + """ + a log of events for an election + """ + + FROZEN = "frozen" + VOTER_FILE_ADDED = "voter file added" + DECRYPTIONS_COMBINED = "decryptions combined" + + election = models.ForeignKey(Election) + log = models.CharField(max_length=500) + at = models.DateTimeField(auto_now_add=True) + +## +## UTF8 craziness for CSV +## + +def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): + # csv.py doesn't do Unicode; encode temporarily as UTF-8: + csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), + dialect=dialect, **kwargs) + for row in csv_reader: + # decode UTF-8 back to Unicode, cell by cell: + yield [unicode(cell, 'utf-8') for cell in row] + +def utf_8_encoder(unicode_csv_data): + for line in unicode_csv_data: + yield line.encode('utf-8') + +class VoterFile(models.Model): + """ + A model to store files that are lists of voters to be processed + """ + # path where we store voter upload + PATH = settings.VOTER_UPLOAD_REL_PATH + + election = models.ForeignKey(Election) + voter_file = models.FileField(upload_to=PATH, max_length=250) + uploaded_at = models.DateTimeField(auto_now_add=True) + processing_started_at = models.DateTimeField(auto_now_add=False, null=True) + processing_finished_at = models.DateTimeField(auto_now_add=False, null=True) + num_voters = models.IntegerField(null=True) + + def process(self): + self.processing_started_at = datetime.datetime.utcnow() + self.save() + + election = self.election + reader = unicode_csv_reader(self.voter_file) + + num_voters_before = election.num_voters + + num_voters = 0 + voter_uuids = [] + for voter in reader: + # bad line + if len(voter) < 1: + continue + + num_voters += 1 + voter_id = voter[0] + name = voter_id + email = voter_id + + if len(voter) > 1: + email = voter[1] + + if len(voter) > 2: + name = voter[2] + + # create the user + user = User.update_or_create(user_type='password', user_id=voter_id, info = {'password': heliosutils.random_string(10), 'email': email, 'name': name}) + user.save() + + # does voter for this user already exist + voter = Voter.get_by_election_and_user(election, user) + + # create the voter + if not voter: + voter_uuid = str(uuid.uuid1()) + voter = Voter(uuid= voter_uuid, voter_type = 'password', voter_id = voter_id, name = name, election = election) + voter_uuids.append(voter_uuid) + voter.save() + + if election.use_voter_aliases: + voter_alias_integers = range(num_voters_before+1, num_voters_before+1+num_voters) + random.shuffle(voter_alias_integers) + for i, voter_uuid in enumerate(voter_uuids): + voter = Voter.get_by_election_and_uuid(election, voter_uuid) + voter.alias = 'V%s' % voter_alias_integers[i] + voter.save() + + self.num_voters = num_voters + self.processing_finished_at = datetime.datetime.utcnow() + self.save() + + return num_voters + + + +class Voter(models.Model, electionalgs.Voter): + election = models.ForeignKey(Election) + + name = models.CharField(max_length = 200, null=True) + voter_type = models.CharField(max_length = 100) + voter_id = models.CharField(max_length = 100) + uuid = models.CharField(max_length = 50) + + # if election uses aliases + alias = models.CharField(max_length = 100, null=True) + + # we keep a copy here for easy tallying + vote = JSONField(electionalgs.EncryptedVote, null=True) + vote_hash = models.CharField(max_length = 100, null=True) + cast_at = models.DateTimeField(auto_now_add=False, null=True) + + @classmethod + def get_by_election(cls, election, cast=None, order_by='voter_id', after=None, limit=None): + query = cls.objects.filter(election = election) + + # the boolean check is not stupid, this is ternary logic + # none means don't care if it's cast or not + if cast == True: + query = query.exclude(cast_at = None) + elif cast == False: + query = query.filter(cast_at = None) + + # little trick to get around GAE limitation + # order by uuid only when no inequality has been added + if cast == None or order_by == 'cast_at' or order_by =='-cast_at': + query = query.order_by(order_by) + + # if we want the list after a certain UUID, add the inequality here + if after: + if order_by[0] == '-': + field_name = "%s__gt" % order_by[1:] + else: + field_name = "%s__gt" % order_by + conditions = {field_name : after} + query = query.filter (**conditions) + + if limit: + query = query[:limit] + + return query + + @classmethod + def get_all_by_election_in_chunks(cls, election, cast=None, chunk=100): + return cls.get_by_election(election) + + @classmethod + def get_by_election_and_voter_id(cls, election, voter_id): + query = cls.objects.filter(election = election, voter_id = voter_id) + + try: + return query[0] + except: + return None + + @classmethod + def get_by_election_and_user(cls, election, user): + query = cls.objects.filter(election = election, voter_id = user.user_id, voter_type= user.user_type) + + try: + return query[0] + except: + return None + + @classmethod + def get_by_election_and_uuid(cls, election, uuid): + query = cls.objects.filter(election = election, uuid = uuid) + + try: + return query[0] + except: + return None + + @classmethod + def get_by_user(cls, user): + return cls.objects.filter(voter_type = user.user_type, voter_id = user.user_id).order_by('-cast_at') + + @property + def user(self): + return User.get_by_type_and_id(self.voter_type, self.voter_id) + + @property + def election_uuid(self): + return self.election.uuid + + def store_vote(self, cast_vote): + # only store the vote if it's cast later than the current one + if self.cast_at and cast_vote.cast_at < self.cast_at: + return + + self.vote = cast_vote.vote + self.vote_hash = cast_vote.vote_hash + self.cast_at = cast_vote.cast_at + self.save() + + def last_cast_vote(self): + return CastVote(vote = self.vote, vote_hash = self.vote_hash, cast_at = self.cast_at, voter=self) + + +class CastVote(models.Model, electionalgs.CastVote): + # the reference to the voter provides the voter_uuid + voter = models.ForeignKey(Voter) + + # a json array, which should contain election_uuid and election_hash + vote = JSONField(electionalgs.EncryptedVote) + + # cache the hash of the vote + vote_hash = models.CharField(max_length=100) + + cast_at = models.DateTimeField(auto_now_add=True) + + # when is the vote verified? + verified_at = models.DateTimeField(null=True) + invalidated_at = models.DateTimeField(null=True) + + @property + def voter_uuid(self): + return self.voter.uuid + + @property + def voter_hash(self): + return self.voter.hash + + @classmethod + def get_by_voter(cls, voter): + return cls.objects.filter(voter = voter).order_by('-cast_at') + + def verify_and_store(self): + result = self.vote.verify(self.voter.election) + + if result: + self.verified_at = datetime.datetime.utcnow() + else: + self.invalidated_at = datetime.datetime.utcnow() + + # save and store the vote as the voter's last cast vote + self.save() + + if result: + self.voter.store_vote(self) + + return result + +class AuditedBallot(models.Model): + """ + ballots for auditing + """ + election = models.ForeignKey(Election) + raw_vote = models.TextField() + vote_hash = models.CharField(max_length=100) + added_at = models.DateTimeField(auto_now_add=True) + + @classmethod + def get(cls, election, vote_hash): + return cls.objects.get(election = election, vote_hash = vote_hash) + + @classmethod + def get_by_election(cls, election, after=None, limit=None): + query = cls.objects.filter(election = election).order_by('vote_hash') + + # if we want the list after a certain UUID, add the inequality here + if after: + query = query.filter(vote_hash__gt = after) + + if limit: + query = query[:limit] + + return query + +class Trustee(models.Model, electionalgs.Trustee): + election = models.ForeignKey(Election) + + uuid = models.CharField(max_length=50) + name = models.CharField(max_length=200) + email = models.EmailField() + secret = models.CharField(max_length=100) + + # public key + public_key = JSONField(algs.EGPublicKey, null=True) + public_key_hash = models.CharField(max_length=100) + + # secret key + # if the secret key is present, this means + # Helios is playing the role of the trustee. + secret_key = JSONField(algs.EGSecretKey, null=True) + + # proof of knowledge of secret key + pok = JSONField(algs.DLogProof, null=True) + + # decryption factors + decryption_factors = JSONField(null=True) + decryption_proofs = JSONField(null=True) + + def save(self, *args, **kwargs): + """ + override this just to get a hook + """ + # not saved yet? + if not self.secret: + self.secret = heliosutils.random_string(12) + self.election.append_log("Trustee %s added" % self.name) + + super(Trustee, self).save(*args, **kwargs) + + @classmethod + def get_by_election(cls, election): + return cls.objects.filter(election = election) + + @classmethod + def get_by_uuid(cls, uuid): + return cls.objects.get(uuid = uuid) + + @classmethod + def get_by_election_and_uuid(cls, election, uuid): + return cls.objects.get(election = election, uuid = uuid) + + @classmethod + def get_by_election_and_email(cls, election, email): + return cls.objects.get(election = election, email = email) + + def verify_decryption_proofs(self): + """ + verify that the decryption proofs match the tally for the election + """ + # verify_decryption_proofs(self, decryption_factors, decryption_proofs, public_key, challenge_generator): + return self.election.encrypted_tally.verify_decryption_proofs(self.decryption_factors, self.decryption_proofs, self.public_key, algs.EG_fiatshamir_challenge_generator) + diff --git a/helios/security.py b/helios/security.py new file mode 100644 index 000000000..a06c2def7 --- /dev/null +++ b/helios/security.py @@ -0,0 +1,145 @@ +""" +Helios Security -- mostly access control + +Ben Adida (ben@adida.net) +""" + +# nicely update the wrapper function +from functools import update_wrapper + +from django.core.exceptions import * +from django.conf import settings + +from models import * +from auth.security import get_user + +from django.http import HttpResponseRedirect + +import helios + +# a function to check if the current user is a trustee +HELIOS_TRUSTEE_UUID = 'helios_trustee_uuid' +def get_logged_in_trustee(request): + if request.session.has_key(HELIOS_TRUSTEE_UUID): + return Trustee.get_by_uuid(request.session[HELIOS_TRUSTEE_UUID]) + else: + return None + +def set_logged_in_trustee(request, trustee): + request.session[HELIOS_TRUSTEE_UUID] = trustee.uuid + +# +# some common election checks +# +def do_election_checks(election, props): + # frozen + if props.has_key('frozen'): + frozen = props['frozen'] + else: + frozen = None + + # newvoters (open for registration) + if props.has_key('newvoters'): + newvoters = props['newvoters'] + else: + newvoters = None + + # frozen check + if frozen != None: + if frozen and not election.frozen_at: + raise PermissionDenied() + if not frozen and election.frozen_at: + raise PermissionDenied() + + # open for new voters check + if newvoters != None: + if election.can_add_voters() != newvoters: + raise PermissionDenied() + + +def get_election_by_uuid(uuid): + if not uuid: + raise Exception("no election ID") + + return Election.get_by_uuid(uuid) + +# decorator for views that pertain to an election +# takes parameters: +# frozen - is the election frozen +# newvoters - does the election accept new voters +def election_view(**checks): + + def election_view_decorator(func): + def election_view_wrapper(request, election_uuid=None, *args, **kw): + election = get_election_by_uuid(election_uuid) + + # do checks + do_election_checks(election, checks) + + return func(request, election, *args, **kw) + + return update_wrapper(election_view_wrapper, func) + + return election_view_decorator + +def user_can_admin_election(user, election): + if not user: + return False + + # election or site administrator + return election.admin == user or user.admin_p + +def api_client_can_admin_election(api_client, election): + return election.api_client == api_client and api_client != None + +# decorator for checking election admin access, and some properties of the election +# frozen - is the election frozen +# newvoters - does the election accept new voters +def election_admin(**checks): + + def election_admin_decorator(func): + def election_admin_wrapper(request, election_uuid=None, *args, **kw): + election = get_election_by_uuid(election_uuid) + + user = get_user(request) + if not user_can_admin_election(user, election): + raise PermissionDenied() + + # do checks + do_election_checks(election, checks) + + return func(request, election, *args, **kw) + + return update_wrapper(election_admin_wrapper, func) + + return election_admin_decorator + +def trustee_check(func): + def trustee_check_wrapper(request, election_uuid, trustee_uuid, *args, **kwargs): + election = get_election_by_uuid(election_uuid) + + trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid) + + if trustee == get_logged_in_trustee(request): + return func(request, election, trustee, *args, **kwargs) + else: + raise PermissionDenied() + + return update_wrapper(trustee_check_wrapper, func) + +def can_create_election(request): + user = get_user(request) + if not user: + return False + + if helios.ADMIN_ONLY: + return user.admin_p + else: + return user != None + +def user_can_feature_election(user, election): + if not user: + return False + + return user.admin_p + diff --git a/helios/signals.py b/helios/signals.py new file mode 100644 index 000000000..4c82d2ed8 --- /dev/null +++ b/helios/signals.py @@ -0,0 +1,16 @@ +""" +Helios Signals + +Effectively callbacks that other apps can wait and be notified about +""" + +import django.dispatch + +# when an election is created +election_created = django.dispatch.Signal(providing_args=["election"]) + +# when a vote is cast +vote_cast = django.dispatch.Signal(providing_args=["user", "voter", "election", "cast_vote"]) + +# when an election is tallied +election_tallied = django.dispatch.Signal(providing_args=["election"]) \ No newline at end of file diff --git a/helios/tasks.py b/helios/tasks.py new file mode 100644 index 000000000..81772feca --- /dev/null +++ b/helios/tasks.py @@ -0,0 +1,124 @@ +""" +Celery queued tasks for Helios + +2010-08-01 +ben@adida.net +""" + +from celery.decorators import task + +from models import * +from view_utils import render_template_raw +import signals + +import copy + + +@task() +def cast_vote_verify_and_store(cast_vote_id, status_update_message=None): + cast_vote = CastVote.objects.get(id = cast_vote_id) + result = cast_vote.verify_and_store() + + voter = cast_vote.voter + election = voter.election + user = voter.user + + if result: + # send the signal + signals.vote_cast.send(sender=election, election=election, user=user, voter=voter, cast_vote=cast_vote) + + if status_update_message and user.can_update_status(): + from views import get_election_url + + user.update_status(status_update_message) + else: + # FIXME: do something about a bad vote + pass + +@task() +def voters_email(election_id, subject_template, body_template, extra_vars={}): + election = Election.objects.get(id = election_id) + for voter in election.voter_set.all(): + single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars) + +@task() +def voters_notify(election_id, notification_template, extra_vars={}): + election = Election.objects.get(id = election_id) + for voter in election.voter_set.all(): + single_voter_notify.delay(voter.uuid, notification_template, extra_vars) + +@task() +def single_voter_email(voter_uuid, subject_template, body_template, extra_vars={}): + voter = Voter.objects.get(uuid = voter_uuid) + + the_vars = copy.copy(extra_vars) + the_vars.update({'voter' : voter}) + + subject = render_template_raw(None, subject_template, the_vars) + body = render_template_raw(None, body_template, the_vars) + + voter.user.send_message(subject, body) + +@task() +def single_voter_notify(voter_uuid, notification_template, extra_vars={}): + voter = Voter.objects.get(uuid = voter_uuid) + + the_vars = copy.copy(extra_vars) + the_vars.update({'voter' : voter}) + + notification = render_template_raw(None, notification_template, the_vars) + + voter.user.send_notification(notification) + +@task() +def election_compute_tally(election_id): + election = Election.objects.get(id = election_id) + election.compute_tally() + + election_notify_admin.delay(election_id = election_id, + subject = "encrypted tally computed", + body = """ +The encrypted tally for election %s has been computed. + +-- +Helios +""" % election.name) + + + if election.has_helios_trustee(): + tally_helios_decrypt.delay(election_id = election.id) + +@task() +def tally_helios_decrypt(election_id): + election = Election.objects.get(id = election_id) + election.helios_trustee_decrypt() + election_notify_admin.delay(election_id = election_id, + subject = 'Helios Decrypt', + body = """ +Helios has decrypted its portion of the tally +for election %s. + +-- +Helios +""" % election.name) + +@task() +def voter_file_process(voter_file_id): + voter_file = VoterFile.objects.get(id = voter_file_id) + voter_file.process() + election_notify_admin.delay(election_id = voter_file.election.id, + subject = 'voter file processed', + body = """ +Your voter file upload for election %s +has been processed. + +%s voters have been created. + +-- +Helios +""" % (voter_file.election.name, voter_file.num_voters)) + +@task() +def election_notify_admin(election_id, subject, body): + election = Election.objects.get(id = election_id) + election.admin.send_message(subject, body) diff --git a/helios/templates/base.html b/helios/templates/base.html new file mode 100644 index 000000000..6c171c063 --- /dev/null +++ b/helios/templates/base.html @@ -0,0 +1,44 @@ + + + + + {% block title %}{% endblock %} - Helios + {% block css %} + + + + + + {% endblock %} + + + + + {% block js %} + {% endblock %} + + {% block extra-head %}{% endblock %} + + + +
    + + {% block content %}{% endblock %} + +
    + + + diff --git a/helios/templates/cast_done.html b/helios/templates/cast_done.html new file mode 100644 index 000000000..6c0faa48c --- /dev/null +++ b/helios/templates/cast_done.html @@ -0,0 +1,26 @@ +{% extends TEMPLATE_BASE %} +{% block title %}Confirm Vote{% endblock %} + +{% block content %} +

    {{election.name}} — Vote Successfully Cast!

    + +

    + Congratulations, your vote has been successfully cast! +

    + +

    + Your smart ballot tracker is:

    + {{last_vote.vote_hash}} +

    + +{% if logout %} +

    For your safety, we have logged you out.

    + +{% endif %} + +

    +[ return to election info ] +

    + +{% endblock %} diff --git a/helios/templates/cryptobase.html b/helios/templates/cryptobase.html new file mode 100644 index 000000000..f472f37d0 --- /dev/null +++ b/helios/templates/cryptobase.html @@ -0,0 +1,15 @@ +{% extends TEMPLATE_BASE %} + +{% block js %} + + + + + + + + + +{% endblock %} + + diff --git a/helios/templates/election_audited_ballots.html b/helios/templates/election_audited_ballots.html new file mode 100644 index 000000000..9b079b4be --- /dev/null +++ b/helios/templates/election_audited_ballots.html @@ -0,0 +1,31 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Audited Ballots for {{election.name}}{% endblock %} + +{% block content %} +

    {{election.name}} — Audited Ballots [back to election]

    + +Audited Ballots {{offset_plus_one}} - {{offset_plus_limit}}    + +

    + To verify an audited ballot, copy its entire content and paste it in the single ballot verifier. +

    + +{% if next_after %} +next {{limit}}    +{% endif %} + +{% ifequal offset 0 %} +{% else %} +back to start    +{% endifequal %} +{% if more_p %} +next {{limit}} +{% endif %} + +{% for b in audited_ballots %} + +{% endfor %} +
    {{b.vote_hash}} [view]
    + +{% endblock %} diff --git a/helios/templates/election_bboard.html b/helios/templates/election_bboard.html new file mode 100644 index 000000000..d170e666c --- /dev/null +++ b/helios/templates/election_bboard.html @@ -0,0 +1,47 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Ballot Tracking Center for {{election.name}}{% endblock %} + +{% block content %} +

    {{election.name}} — Ballot Tracking Center [back to election]

    + +

    + This is the ballot tracking center, which displays the tracking numbers of all cast ballots in this election. +

    + +

    + {{election.num_cast_votes}} cast votes. +

    + +Voters {{offset_plus_one}} - {{offset_plus_limit}}    + +{% if next_after %} +next {{limit}}    +{% endif %} + +{% ifequal offset 0 %} +{% else %} +back to start    +{% endifequal %} +{% if more_p %} +next {{limit}} +{% endif %} + + +{% for voter in voters %} + +{% endfor %} +
    +{% if election.use_voter_aliases %} +Alias +{% else %} +Name +{% endif %} +Smart Ballot Tracker
    +{% if election.use_voter_aliases %} +{{voter.alias}} +{% else %} +{{voter.voter_type}} {% if voter.name %}{{voter.name}}{% else %}{{voter.voter_id}}{% endif %} +{% endif %}{% if voter.vote_hash %}{{voter.vote_hash}} [view]{% else %}—{% endif %}
    + +{% endblock %} diff --git a/helios/templates/election_build.html b/helios/templates/election_build.html new file mode 100644 index 000000000..599fe40f6 --- /dev/null +++ b/helios/templates/election_build.html @@ -0,0 +1,68 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} +

    {{election.name}} — Questions [back to election]

    + + + +
    +
    + + +{% endblock %} diff --git a/helios/templates/election_cast_confirm.html b/helios/templates/election_cast_confirm.html new file mode 100644 index 000000000..9f97c348d --- /dev/null +++ b/helios/templates/election_cast_confirm.html @@ -0,0 +1,119 @@ +{% extends TEMPLATE_BASE %} +{% block title %}Confirm Vote{% endblock %} + +{% block content %} + +

    {{election.name}} — Submit your Vote

    + +

    + We have received, but not yet recorded, your encrypted ballot.
    +Your smart ballot tracker is:

    + {{vote_fingerprint}} +

    + +{% if user %} + +{% if voter %} +{% if past_votes %} + +{% else %} + +{% endif %} + +{% if election.voting_has_started %} +{% if not election.voting_has_stopped %} +
    +
    +
    + + +{% if status_update_label %} +
    + {{status_update_label}}
    +
    +"{{status_update_message}}" +
    + +
    +
    +{% endif %} + + +
    You can cast as many ballots as you want.
    Only the last one counts.
    +
    + +

    +
    + If you cancel now, your ballot will NOT be recorded.
    + You can start the voting process over again, of course.
    +

    + +
    +
    + Verifying and Casting your ballot
    + +
    +{% else %} +

    + voting has stopped, sorry. +

    +{% endif %} +{% else %} +

    + voting has not yet begun, sorry. +

    +{% endif %} +{% else %} +

    +{% if election.openreg %} +Sorry, you are not eligible for this election.
    +{% else %} +Sorry, you are not registered for this election, and registration is closed.
    +{% endif %} +

    +

    + [return to the main election page] +

    +{% endif %} + +{% else %} +

    + Now, we need you to log in, so we can verify your eligibility.

    +{% if election.openreg %} +This election is open to anyone, so log in with your preferred account. +{% else %} +{% if password_only %} +Please log in with the username and password you received by email.
    +{% else %} +This election is only open to registered voters, so log in with +the same account you registered with. +{% endif %} +{% endif %} +

    + +{{login_box|safe}} + +
    +Don't worry, we'll remember your ballot while you log in. +{% endif %} +{% endblock %} diff --git a/helios/templates/election_compute_tally.html b/helios/templates/election_compute_tally.html new file mode 100644 index 000000000..510b9ae33 --- /dev/null +++ b/helios/templates/election_compute_tally.html @@ -0,0 +1,26 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Compute Encryted Tally for {{election.name}}{% endblock %} + +{% block content %} +

    Compute Tally for Election: {{election.name}}

    + +
    +

    + You are about to compute the encrypted tally for election {{election.name}}. +

    + +

    + Once you do this, voters will no longer be able to cast a ballot. +

    + +
    + + + + +
    +
    + +

    +{% endblock %} diff --git a/helios/templates/election_edit.html b/helios/templates/election_edit.html new file mode 100644 index 000000000..b14c4a27a --- /dev/null +++ b/helios/templates/election_edit.html @@ -0,0 +1,22 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} + +

    {{election.name}} — Update [cancel]

    + +{% if error %} +

    + {{error}} +

    +{% endif %} + +
    + + {{election_form.as_table}} +
    +
    + +
    +
    + +{% endblock %} diff --git a/helios/templates/election_freeze.html b/helios/templates/election_freeze.html new file mode 100644 index 000000000..9b770c89f --- /dev/null +++ b/helios/templates/election_freeze.html @@ -0,0 +1,44 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    {{election.name}} — Freeze Ballot

    +

    +Once the ballot is frozen, the questions and available choices can no longer be modified.
    +The list of trustees and their public keys will also be frozen. +

    + +

    +{% if election.openreg %} +Your election currently has open registration. After you freeze the ballot, you will be able to continue to manage the voter list while the election runs. You will not be able to switch back to a closed-registration setting. +{% else %} +Your election currently has closed registration.
    After you freeze the ballot, you also will not be able to modify the voter list, nor will you be able to re-open registration. +{% endif %} +

    + +{% if helios.VOTERS_EMAIL %} +

    +You must freeze the ballot before you can contact voters. +

    +{% endif %} + +{% if issues_p %} +

    + There are problems that prevent you from freezing the election: +

      + {% for issue in issues %} +
    • {{issue}}
    • + {% endfor %} +
    + go back to the election +

    +{% else %} +
    + + + + +
    +{% endif %} + +

    +{% endblock %} \ No newline at end of file diff --git a/helios/templates/election_keygenerator.html b/helios/templates/election_keygenerator.html new file mode 100644 index 000000000..da5ddbe22 --- /dev/null +++ b/helios/templates/election_keygenerator.html @@ -0,0 +1,146 @@ +{% extends "helios/templates/cryptobase.html" %} +{% block content %} + + +

    {{election.name}} — Trustee {{trustee.name}} — Key Setup

    + +

    + As a trustee, it's time to set up your key for this election. +

    + +

    + Please wait for the generator to load... +

    + +

    + + + +

    +If you've already generated a keypair, you can reuse it. +

    + +
    +

    Reusing a Key

    + +

    +Enter your complete secret key below: +

    +
    + +
    + +
    +
    + +
    +

    Your Secret Key

    + +Your key has been generated, but you may choose to
    clear it from memory and start from scratch if you prefer.
    +
    + +

    + +

    + + +
    + +
    +

    Your Public Key

    +

    + It's time to upload the public key to the server. +

    +

    + The fingerprint of your public key is: .
    + You may want to save this to confirm that your public key was properly stored by the server.
    + (Your public key is not currently being displayed because you do not need to save it, the fingerprint is sufficient.) +

    + + +
    + +
    +

    +{% endblock %} diff --git a/helios/templates/election_new.html b/helios/templates/election_new.html new file mode 100644 index 000000000..43a602fba --- /dev/null +++ b/helios/templates/election_new.html @@ -0,0 +1,22 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} + +

    Create a New Election

    + +{% if error %} +

    + {{error}} +

    +{% endif %} + +
    + + {{election_form.as_table}} +
    +
    + +
    +
    + +{% endblock %} diff --git a/helios/templates/election_new_2.html b/helios/templates/election_new_2.html new file mode 100644 index 000000000..a53b1fec2 --- /dev/null +++ b/helios/templates/election_new_2.html @@ -0,0 +1,92 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} + + +

    Create a New Election: {{name}}

    +{% ifequal election_type "helios" %} + An election managed by Helios. +{% else %} +{% ifequal election_type "one" %} + An election managed by you, the single administrator. +{% else %} +An election managed by multiple trustees. +{% endifequal %} +{% endifequal %} +
    + + + +
    + +{% ifnotequal election_type "multiple" %} + +{% else %} +
    +

    Trustees (up to 5)

    +
    +
    +
    +
    +
    +
    +{% endifnotequal %} +

    + +
    +

    +{% ifequal election_type "one" %} +
    + + Your Election's Secret Key:
    + +
    + (You need to copy and paste this key and keep it safe,
    + otherwise you won't be able to tally your election.) +
    +{% endifequal %} +{% endblock %} \ No newline at end of file diff --git a/helios/templates/election_questions.html b/helios/templates/election_questions.html new file mode 100644 index 000000000..cfcae5595 --- /dev/null +++ b/helios/templates/election_questions.html @@ -0,0 +1,132 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block title %}Questions for {{election.name}}{% endblock %} +{% block content %} +

    {{election.name}} — Questions [back to election]

    + + + +
    +
    + + +{% endblock %} diff --git a/helios/templates/election_register.html b/helios/templates/election_register.html new file mode 100644 index 000000000..411f9dd4f --- /dev/null +++ b/helios/templates/election_register.html @@ -0,0 +1,27 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    {{ election.name }} – Register

    + +{% if user %} +

    + You are currently logged in as {{user.user_id}} [{{user.user_type}}] +

    +{% if voter %} +

    + You are currently registered for this election. +

    +{% else %} +

    + You are not registered for this election. +

    + +
    + +
    +{% endif %} + +{% else %} +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/helios/templates/election_tallied.html b/helios/templates/election_tallied.html new file mode 100644 index 000000000..9d1a81818 --- /dev/null +++ b/helios/templates/election_tallied.html @@ -0,0 +1,14 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} + +

    Election {{election.name}} Already Tallied

    + +

    + This election has already been tallied, you can no longer cast a vote. +

    + +

    + view the election tally +

    +{% endblock %} \ No newline at end of file diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html new file mode 100644 index 000000000..59c6d598a --- /dev/null +++ b/helios/templates/election_view.html @@ -0,0 +1,273 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}{{election.name}}{% endblock %} +{% block content %} +{% if election.voting_has_started %} + +{% endif %} +
    +

    {{ election.name }} +{% if admin_p %} +{% if not election.frozen_at %} +edit +{% endif %} +{% endif %}

    +

    +an election created by {{election.admin.display_html_small|safe}}
    +{% if admin_p %} +{% if election.featured_p %} +this election is featured on the front page. +{% if can_feature_p %} +[unfeature it] +{% endif %} +{% else %} +this election is not featured on the front page. +{% if can_feature_p %} +[feature it] +{% endif %} +{% endif %} +{% endif %} +

    + +
    + + +

    +{% if socialbuttons_url %} + +{% endif %} + + +
    + +
    +{{election.description}} +
    + + +

    +questions ({% if election.questions %}{{election.questions|length}}{% else %}0{% endif %}) +  |   +voters +  |   +trustees ({{trustees|length}}) +

    + +{% if admin_p %} +

    + +{% if election.result %} + + + +{% else %} +Next Step: + +{% if not election.frozen_at %} +{% if election.issues_before_freeze %} +add questions, voters, and trustees. +{% else %} +freeze ballot and open election. +
    +{% if election.voting_starts_at %} +once you do this, the election will be ready for voting and will open automatically
    +at {{election.voting_starts_at}}, as per your settings. +{% else %} +once you do this, the election will be immediately open for voting. +{% endif %} +{% endif %} + +{% else %} + +{% if not election.encrypted_tally %} +{% if election.tallying_started_at %} +Tally computation is under way.
    +Reload this page in a couple of minutes. +{% else %} +compute encrypted tally
    +The encrypted votes will be combined into an encrypted tally. Once this is done,
    +trustees will be asked to provide their share of the decryption. +{% endif %} +{% else %} + +{% if election.ready_for_decryption_combination %} +combine trustee decryptions and release results
    +The decryption shares from the trustees are combined and the tally is decrypted.
    +Once you do this, the tally will be immediately available for all to see, and +all voters will be notified that the tally is ready. +{% else %} +trustees (for decryption) +{% endif %} + +{% endif %} + +{% endif %} +
    + +{% endif %} + +

    + +{% endif %} + +

    + +{% if election.result %} + + This election is complete. +
    + +
    +

    Tally

    +{% for question in election.pretty_result %} +{{question.question}}: +
      +{% for answer in question.answers %} +
    • {{answer.answer}}: {{answer.count}} +{% endfor %} +
    +{% endfor %} + +{% else %} + +{% if election.voting_has_stopped %} + + Election closed. Tally will be computed soon. +
    +{% else %} + +{% if election.voting_has_started %} + +Vote in this election +
    +{% if not user %} +
    +

    +For your privacy, you'll be asked to log in only once your ballot is encrypted. +{% endif %} +{% if election.voting_extended_until %} +
    +This election was initially scheduled to end at {{election.voting_ends_at}},
    +but has been extended until {{ election.voting_extended_until }}. +{% else %} +{% if election.voting_ends_at %} +
    +This election is scheduled to end at {{election.voting_ends_at}}. +{% else %} +
    +This election ends at the administrator's discretion. +{% endif %} +
    +{% endif %} + + +
    +{% else %} + + voting is not yet open +

    +{% endif %} + +{% if user %} +{% if voter %} +

    + You are registered to vote in this election. +{% if election.use_voter_aliases %} +Your voter alias is {{voter.alias}}. +{% endif %} +

    + +{% else %} +{% if election.result %} +{% else %} +{% if election.openreg %} +{% if eligible_p %} +{% if election.voting_has_started %} +This election is open to anyone. +{% else %} +You are not registered to vote in this election.
    +
    + + +
    +{% endif %} +{% else %} +Registration for this election is open, but You are not eligible. +{% endif %} +{% else %} +You are not eligible to vote in this election, because registration is closed and you are not registered.
    +{% endif %} +{% endif %} +{% endif %} +{% else %} + +{% endif %} + +{% endif %} + +{% endif %} + + +
    +Audit Info + +
    + + + + +{% endblock %} diff --git a/helios/templates/elections_administered.html b/helios/templates/elections_administered.html new file mode 100644 index 000000000..ac6ded8ba --- /dev/null +++ b/helios/templates/elections_administered.html @@ -0,0 +1,11 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    Elections you Administer [back to home]

    + + +{% endblock %} \ No newline at end of file diff --git a/helios/templates/email/result_body.txt b/helios/templates/email/result_body.txt new file mode 100644 index 000000000..c578bfece --- /dev/null +++ b/helios/templates/email/result_body.txt @@ -0,0 +1,12 @@ +Dear {{voter.name}}, + +The tally has been computed for + + {{voter.election.name}} + +Check out the results at: + + {{election_url}} + +-- +Helios diff --git a/helios/templates/email/result_subject.txt b/helios/templates/email/result_subject.txt new file mode 100644 index 000000000..2e1baa96d --- /dev/null +++ b/helios/templates/email/result_subject.txt @@ -0,0 +1 @@ +Tally computed for {{voter.election.name}} diff --git a/helios/templates/email/vote_body.txt b/helios/templates/email/vote_body.txt new file mode 100644 index 000000000..daa9f74cf --- /dev/null +++ b/helios/templates/email/vote_body.txt @@ -0,0 +1,24 @@ +Dear {{voter.name}}, + +{{custom_message|safe}} + +Election URL: {{election_url}} +Election Fingerprint: {{voter.election.hash}} +{% ifequal voter.voter_type "password" %} +Your username: {{voter.user.user_id}} +Your password: {{voter.user.info.password}} +{% else %} +Log in with your {{voter.voter_type}} account. +{% endifequal %} +{% if election.use_voter_aliases %} +Your voter alias: {{voter.alias}} + +In order to protect your privacy, this election is configured +to never display your username, name, or email address to the public. +Instead, the ballot tracking center will only display your alias. + +IMPORTANTLY, when you are prompted to log in to vote, +please use your *username*, not your alias. +{% endif %} +-- +Helios diff --git a/helios/templates/email/vote_subject.txt b/helios/templates/email/vote_subject.txt new file mode 100644 index 000000000..91486fccd --- /dev/null +++ b/helios/templates/email/vote_subject.txt @@ -0,0 +1 @@ +vote in {{voter.election.name}} diff --git a/helios/templates/list_trustees.html b/helios/templates/list_trustees.html new file mode 100644 index 000000000..15b2e6083 --- /dev/null +++ b/helios/templates/list_trustees.html @@ -0,0 +1,59 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block title %}Trustees for {{election.name}}{% endblock %} + +{% block content %} +

    {{election.name}} — Trustees [back to election]

    + +

    + Trustees are responsible for decrypting the election result. +

    + + +{% if not election.frozen_at %} +

    + new trustee +

    +{% if not election.has_helios_trustee %} +

    + add Helios as a trustee +

    +{% endif %} +{% endif %} + +{% if not trustees|length %} + +{% else %} +{% for t in trustees %} +

    Trustee #{{forloop.counter}}: {{t.name}} +{% if admin_p %} +{% if not t.secret_key %} +({{t.email}}) +{% if not election.frozen_at %}[x]{% endif %} +[send login] +{% endif %} +{% endif %} +

    + +

    +{% if t.public_key_hash %} + Public Key Fingerprint: {{t.public_key_hash}} +{% else %} +No public key uploaded yet. +{% endif %} +

    + +{% if election.encrypted_tally %} +{% if t.decryption_factors %} +tally recorded for this trustee. +{% else %} +waiting for this trustee's tally +{% endif %} +{% endif %} + +{% endfor %} + + +{% endif %} + +{% endblock %} diff --git a/helios/templates/new_trustee.html b/helios/templates/new_trustee.html new file mode 100644 index 000000000..d733d29de --- /dev/null +++ b/helios/templates/new_trustee.html @@ -0,0 +1,13 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} +

    {{election.name}} — New Trustee [cancel]

    + +
    +Name:

    +Email:

    + + +
    + +{% endblock %} \ No newline at end of file diff --git a/helios/templates/notification/result.txt b/helios/templates/notification/result.txt new file mode 100644 index 000000000..2fc3936d7 --- /dev/null +++ b/helios/templates/notification/result.txt @@ -0,0 +1 @@ +Tally computed for {{voter.election.name}}: {{election_url}} diff --git a/helios/templates/socialbuttons.html b/helios/templates/socialbuttons.html new file mode 100644 index 000000000..6942babe3 --- /dev/null +++ b/helios/templates/socialbuttons.html @@ -0,0 +1,5 @@ + +Share + + + diff --git a/helios/templates/stats.html b/helios/templates/stats.html new file mode 100644 index 000000000..c60d93ed8 --- /dev/null +++ b/helios/templates/stats.html @@ -0,0 +1,13 @@ +{% extends TEMPLATE_BASE %} +{% block title %}Statistics{% endblock %} + +{% block content %} +

    Statistics

    + +{% for election in elections %} +

    +{{election.name}} -- {{election.num_voters}} voters +

    +{% endfor %} + +{% endblock %} diff --git a/helios/templates/trustee_check_sk.html b/helios/templates/trustee_check_sk.html new file mode 100644 index 000000000..fe597bd96 --- /dev/null +++ b/helios/templates/trustee_check_sk.html @@ -0,0 +1,78 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} + +

    {{election.name}} — Trustee {{trustee.name}} — Check Secret Key

    + +

    +Your public key fingerprint is: {{trustee.public_key_hash}} +

    + +
    +loading crypto functions... +
    + + + + + +
    +
    + + +
    +{% endblock %} diff --git a/helios/templates/trustee_decrypt_and_prove.html b/helios/templates/trustee_decrypt_and_prove.html new file mode 100644 index 000000000..9e95dc726 --- /dev/null +++ b/helios/templates/trustee_decrypt_and_prove.html @@ -0,0 +1,209 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} + +

    Trustee {{trustee.name}} — Decrypt Result for {{election.name}}

    + +

    + Trustee: {{trustee.email}}
    + Public Key Fingerprint: {{trustee.public_key_hash}}
    + Encrypted Tally Fingerprint: {{election.encrypted_tally_hash}} +

    + +

    + The encrypted tally for your election has been computed.
    + Now it's time to compute and submit your partial decryption. +

    + +

    + This process is performed in two steps.

    + First, your secret key is used to decrypt the tally inside your browser, without connecting to the network.
    + You can choose to take your browser "offline" for this step, if you'd like.

    + Second, once your decryption factors have been computed, your browser will need to be "online" to submit them to the server.
    + If you'd like, you can compute your decryption factors, copy them to your clipboard, restart your browser, and
    + skip to the second step so that your browser is never online when you enter your secret key. +

    + + + +
    + Generating partial decryption factors and proofs...
    + +
    + +
    + Submitting and checking your decryption factors and proofs...
    + +
    + +
    +

    SECOND STEP: upload your partial decryption

    + + Your partial decryption:
    +
    +

    + +
    +
    + reset and restart decryption process +
    +
    + +
    + Done! Back to election +
    + +
    + Error: your secret key was likely incorrect. +
    + +
    +
    + +{% endblock %} diff --git a/helios/templates/trustee_home.html b/helios/templates/trustee_home.html new file mode 100644 index 000000000..f4becb522 --- /dev/null +++ b/helios/templates/trustee_home.html @@ -0,0 +1,32 @@ +{% extends "helios/templates/cryptobase.html" %} + +{% block content %} +

    {{election.name}} — Trustee {{trustee.name}} Home

    + +

    +{% if trustee.public_key_hash %} +You have successfully uploaded your public key.
    +Your public key fingerprint is: {{trustee.public_key_hash}}.
    +You can verify that you have the right secret key. +{% else %} +setup your key +{% endif %} +

    + +

    +{% if election.encrypted_tally %} +{% if trustee.decryption_factors %} +You have successfully uploaded your decryption. +{% else %} + The encrypted tally for this election is ready.
    + decrypt with your key +{% endif %} +{% else %} +Once the tally is computed, come back here to provide your secret key for decryption purposes.
    +You should keep the email with your trustee homepage link, which contains the credentials needed to get back here. +{% endif %} +

    + + + +{% endblock %} diff --git a/helios/templates/voters_email.html b/helios/templates/voters_email.html new file mode 100644 index 000000000..515ce4f91 --- /dev/null +++ b/helios/templates/voters_email.html @@ -0,0 +1,48 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Email Voters for {{election.name}}{% endblock %} +{% block content %} + + +

    {{election.name}} — Email Voters [back to election]

    + +{% if voter %} +

    + You are sending this email to a specific user: {{voter.name}} ({{voter.voter_id}}) +

    +{% endif %} + +

    + The email will automatically include a "Dear Voter" line, as well as a footer including
    + the election URL, the login information, and a simple email signature.
    + No need to include these in the body of your email below. +

    +
    + + + {{email_form.as_table}} +
    +
    + +
    +
    + +
    +
    + + + + + +{% endblock %} diff --git a/helios/templates/voters_list.html b/helios/templates/voters_list.html new file mode 100644 index 000000000..5077b71b9 --- /dev/null +++ b/helios/templates/voters_list.html @@ -0,0 +1,112 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Voters for {{election.name}}{% endblock %} +{% block content %} +

    {{election.name}} — Voters [back to election]

    + +

    +Registration is {% if not election.frozen_at %}currently{% endif %} {{ election.registration_status_pretty }}. +{% if admin_p and not election.frozen_at %} +{% if election.openreg %} +[switch to closed] +{% else %} +[switch to open] +{% endif %} +{% endif %} +

    + + + +{% if email_voters and election.frozen_at and admin_p %} +

    email voters

    +{% endif %} + + +

    +

    search:
    +

    + +{% if admin_p and upload_p %} +

    +{% if not election.frozen_at %} +bulk upload voters +{% endif %} +

    + +{% if voter_files %} +Prior Bulk Uploads: +
      +{% for vf in voter_files %} +
    • {{vf.voter_file.size}} bytes, at {{vf.uploaded_at}}: +{% if vf.processing_finished_at %} +done processing: {{vf.num_voters}} voters loaded +{% else %} + +{% if vf.processing_started_at %} +currently processing +{% else %} +not yet processed +{% endif %} + +{% endif %} +
    • +{% endfor %} +
    +{% endif %} +{% endif %} + +{% if voters %} +Voters {{offset_plus_one}} - {{offset_plus_limit}}    + +{% if next_after %} +next {{limit}}    +{% endif %} + +{% ifequal offset 0 %} +{% else %} +back to start    +{% endifequal %} +{% if more_p %} +next {{limit}} +{% endif %} + + + +{% if admin_p or not election.use_voter_aliases %} + +{% endif %} + +{% if election.use_voter_aliases %} + +{% endif %} + +{% for voter in voters %} + +{% if admin_p or not election.use_voter_aliases %} + +{% endif %} +{% if election.use_voter_aliases %} + +{% endif %} + +{% endfor %} +
    NameAlias
    +{% if admin_p %} +{% if not election.frozen_at %} +[x] +{% endif %} +{% if election.frozen_at and election.openreg and not election.encrypted_tally %} +[x] +{% endif %} +{% endif %} +{{voter.voter_type}} {{voter.name}}{{voter.alias}}
    + +{% else %} +no voters yet +{% endif %} + +{% endblock %} diff --git a/helios/templates/voters_manage.html b/helios/templates/voters_manage.html new file mode 100644 index 000000000..9fb592df2 --- /dev/null +++ b/helios/templates/voters_manage.html @@ -0,0 +1,39 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    {{election.name}} — Manage Voters [back to election]

    + +
    search:
    + +{% if upload_p %} +

    bulk upload voters

    +{% endif %} + +Voters {{offset_plus_one}} - {{offset_plus_limit}}    + +{% if next_after %} +next {{limit}}    +{% endif %} + +{% ifequal offset 0 %} +{% else %} +back to start    +{% endifequal %} +{% if more_p %} +next {{limit}} +{% endif %} + + + +{% for voter in voters %} + +{% endfor %} +
    AliasNameUsername
    {{voter.alias}}{{voter.name}}{{voter.voter_id}} +{% if election.frozen_at %} +{% else %} +[x] +{% endif %} +
    + + +{% endblock %} \ No newline at end of file diff --git a/helios/templates/voters_search.html b/helios/templates/voters_search.html new file mode 100644 index 000000000..77cafcaa5 --- /dev/null +++ b/helios/templates/voters_search.html @@ -0,0 +1,17 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    {{election.name}} — Search Voters for '{{search_term}}' [back to election]

    + +{% if voter %} +Voter Found: {{voter.name}} ({{voter.voter_id}})

    +{% if election.frozen_at %} +email this voter +{% else %} +once this election is frozen, you'll be able to email this voter. +{% endif %} +{% else %} +no such voter found +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/helios/templates/voters_upload.html b/helios/templates/voters_upload.html new file mode 100644 index 000000000..fcd979531 --- /dev/null +++ b/helios/templates/voters_upload.html @@ -0,0 +1,30 @@ +{% extends TEMPLATE_BASE %} + +{% block content %} +

    {{election.name}} — Bulk Upload Voters [back to election]

    + +
    +

    + If your election allows for password-based voters, then you can bulk upload them here.
    + Please enter CSV content for your list of voters as follows: +

    +
    +      benadida,ben@adida.net,Ben Adida
    +      bob,bob@acme.org,Bob Acme
    +      ...
    +  
    + + + + +
    + +
    +
    + + + +{% endblock %} diff --git a/helios/test.py b/helios/test.py new file mode 100644 index 000000000..82eb2ab96 --- /dev/null +++ b/helios/test.py @@ -0,0 +1,19 @@ +""" +Testing Helios Features +""" + +from helios.models import * +from auth.models import * +import uuid + +def generate_voters(election, num_voters = 1000, start_with = 1): + # generate the user + for v_num in range(start_with, start_with + num_voters): + user = User(user_type='password', user_id='testuser%s' % v_num, name='Test User %s' % v_num) + user.put() + voter = Voter(uuid=str(uuid.uuid1()), election = election, voter_type=user.user_type, voter_id = user.user_id) + voter.put() + +def delete_voters(election): + for v in Voter.get_by_election(election): + v.delete() \ No newline at end of file diff --git a/helios/urls.py b/helios/urls.py new file mode 100644 index 000000000..5915f9214 --- /dev/null +++ b/helios/urls.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from django.conf.urls.defaults import * + +from django.conf import settings + +from views import * + +urlpatterns = None + +urlpatterns = patterns('', + (r'^$', home), + (r'^stats$', stats), + (r'^socialbuttons$', socialbuttons), + + # election shortcut by shortname + (r'^e/(?P[^/]+)$', election_shortcut), + + # trustee login + (r'^t/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)$', trustee_login), + + # election + (r'^elections/params$', election_params), + (r'^elections/verifier$', election_verifier), + (r'^elections/single_ballot_verifier$', election_single_ballot_verifier), + (r'^elections/new$', election_new), + (r'^elections/administered$', elections_administered), + + (r'^elections/(?P[^/]+)', include('helios.election_urls')), + +) + + diff --git a/helios/utils.py b/helios/utils.py new file mode 100644 index 000000000..5833a91e7 --- /dev/null +++ b/helios/utils.py @@ -0,0 +1,160 @@ +""" +Utilities. + +Ben Adida - ben@adida.net +2005-04-11 +""" + +import urllib, re, sys, datetime, urlparse, string +import threading + +# utils from auth, too +from auth.utils import * + +from django.conf import settings + +import random, logging +import sha, hmac, base64 + +def do_hmac(k,s): + """ + HMAC a value with a key, hex output + """ + mac = hmac.new(k, s, sha) + return mac.hexdigest() + + +def split_by_length(str, length, rejoin_with=None): + """ + split a string by a given length + """ + str_arr = [] + counter = 0 + while counter') + return new_s + +## +## XSS attack prevention +## + +def xss_strip_all_tags(s): + """ + Strips out all HTML. + """ + return s + def fixup(m): + text = m.group(0) + if text[:1] == "<": + return "" # ignore tags + if text[:2] == "&#": + try: + if text[:3] == "&#x": + return unichr(int(text[3:-1], 16)) + else: + return unichr(int(text[2:-1])) + except ValueError: + pass + elif text[:1] == "&": + import htmlentitydefs + entity = htmlentitydefs.entitydefs.get(text[1:-1]) + if entity: + if entity[:2] == "&#": + try: + return unichr(int(entity[2:-1])) + except ValueError: + pass + else: + return unicode(entity, "iso-8859-1") + return text # leave as is + + return re.sub("(?s)<[^>]*>|&#?\w+;", fixup, s) + + +random.seed() + +def random_string(length=20): + random.seed() + ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' + r_string = '' + for i in range(length): + r_string += random.choice(ALPHABET) + + return r_string + +def get_host(): + return settings.SERVER_HOST + +def get_prefix(): + return settings.SERVER_PREFIX + + +## +## Datetime utilities +## + +def string_to_datetime(str, fmt="%Y-%m-%d %H:%M"): + if str == None: + return None + + return datetime.datetime.strptime(str, fmt) + +## +## email +## + +from django.core import mail as django_mail + +def send_email(sender, recpt_lst, subject, body): + # subject up until the first newline + subject = subject.split("\n")[0] + django_mail.send_mail(subject, body, sender, recpt_lst, fail_silently=True) + + + diff --git a/helios/view_utils.py b/helios/view_utils.py new file mode 100644 index 000000000..1219e9b78 --- /dev/null +++ b/helios/view_utils.py @@ -0,0 +1,87 @@ +""" +Utilities for all views + +Ben Adida (12-30-2008) +""" + +from django.template import Context, Template, loader +from django.http import HttpResponse, Http404 +from django.shortcuts import render_to_response + +import utils + +# nicely update the wrapper function +from functools import update_wrapper + +from auth.security import get_user + +import helios + +from django.conf import settings + +## +## BASICS +## + +SUCCESS = HttpResponse("SUCCESS") + +# FIXME: error code +FAILURE = HttpResponse("FAILURE") + +## +## template abstraction +## +def prepare_vars(request, vars): + vars_with_user = vars.copy() + vars_with_user['user'] = get_user(request) + + # csrf protection + if request.session.has_key('csrf_token'): + vars_with_user['csrf_token'] = request.session['csrf_token'] + + vars_with_user['utils'] = utils + vars_with_user['settings'] = settings + vars_with_user['HELIOS_STATIC'] = '/static/helios/helios' + vars_with_user['TEMPLATE_BASE'] = helios.TEMPLATE_BASE + vars_with_user['CURRENT_URL'] = request.path + vars_with_user['SECURE_URL_HOST'] = settings.SECURE_URL_HOST + + return vars_with_user + +def render_template(request, template_name, vars = {}, include_user=True): + t = loader.get_template(template_name + '.html') + + vars_with_user = prepare_vars(request, vars) + + if not include_user: + del vars_with_user['user'] + + return render_to_response('helios/templates/%s.html' % template_name, vars_with_user) + +def render_template_raw(request, template_name, vars={}): + t = loader.get_template(template_name) + + # if there's a request, prep the vars, otherwise can't do it. + if request: + full_vars = prepare_vars(request, vars) + else: + full_vars = vars + + c = Context(full_vars) + return t.render(c) + + +def render_json(json_txt): + return HttpResponse(json_txt) + +# decorator +def json(func): + """ + A decorator that serializes the output to JSON before returning to the + web client. + """ + def convert_to_json(self, *args, **kwargs): + return render_json(utils.to_json(func(self, *args, **kwargs))) + + return update_wrapper(convert_to_json,func) + diff --git a/helios/views.py b/helios/views.py new file mode 100644 index 000000000..db8d604e1 --- /dev/null +++ b/helios/views.py @@ -0,0 +1,995 @@ +# -*- coding: utf-8 -*- +""" +Helios Django Views + +Ben Adida (ben@adida.net) +""" + +from django.core.urlresolvers import reverse +from django.core.mail import send_mail +from django.http import * + +from mimetypes import guess_type + +import csv, urllib + +from crypto import algs, electionalgs +from crypto import utils as cryptoutils +from helios import utils as helios_utils +from view_utils import * +from auth.security import * +from helios import security +from auth import views as auth_views + +import tasks + +from security import * +from auth.security import get_user, save_in_session_across_logouts + +import uuid, datetime + +from models import * + +import forms, signals + +# Parameters for everything +ELGAMAL_PARAMS = algs.ElGamal() +#ELGAMAL_PARAMS.p = 169989719781940995935039590956086833929670733351333885026079217526937746166790934510618940073906514429409914370072173967782198129423558224854191320917329420870526887804017711055077916007496804049206725568956610515399196848621653907978580213217522397058071043503404700268425750722626265208099856407306527012763L +#ELGAMAL_PARAMS.q = 84994859890970497967519795478043416964835366675666942513039608763468873083395467255309470036953257214704957185036086983891099064711779112427095660458664710435263443902008855527538958003748402024603362784478305257699598424310826953989290106608761198529035521751702350134212875361313132604049928203653263506381L +#ELGAMAL_PARAMS.g = 68111451286792593845145063691659993410221812806874234365854504719057401858372594942893291581957322023471947260828209362467690671421429979048643907159864269436501403220400197614308904460547529574693875218662505553938682573554719632491024304637643868603338114042760529545510633271426088675581644231528918421974L + +# trying new ones from OlivierP +ELGAMAL_PARAMS.p = 16328632084933010002384055033805457329601614771185955389739167309086214800406465799038583634953752941675645562182498120750264980492381375579367675648771293800310370964745767014243638518442553823973482995267304044326777047662957480269391322789378384619428596446446984694306187644767462460965622580087564339212631775817895958409016676398975671266179637898557687317076177218843233150695157881061257053019133078545928983562221396313169622475509818442661047018436264806901023966236718367204710755935899013750306107738002364137917426595737403871114187750804346564731250609196846638183903982387884578266136503697493474682071L +ELGAMAL_PARAMS.q = 61329566248342901292543872769978950870633559608669337131139375508370458778917L +ELGAMAL_PARAMS.g = 14887492224963187634282421537186040801304008017743492304481737382571933937568724473847106029915040150784031882206090286938661464458896494215273989547889201144857352611058572236578734319505128042602372864570426550855201448111746579871811249114781674309062693442442368697449970648232621880001709535143047913661432883287150003429802392229361583608686643243349727791976247247948618930423866180410558458272606627111270040091203073580238905303994472202930783207472394578498507764703191288249547659899997131166130259700604433891232298182348403175947450284433411265966789131024573629546048637848902243503970966798589660808533L + + +# single election server? Load the single electionfrom models import Election +from django.conf import settings + +# a helper function +def get_election_url(election): + return settings.URL_HOST + reverse(election_shortcut, args=[election.short_name]) + +# simple static views +def home(request): + user = get_user(request) + if user: + elections = Election.get_by_user_as_admin(user) + else: + elections = [] + + return render_template(request, "index", {'elections' : elections}) + +def stats(request): + user = get_user(request) + if not user or not user.admin_p: + raise PermissionDenied() + + elections = Election.objects.all().order_by('-created_at')[:25] + + return render_template(request, "stats", {'elections' : elections}) + +## +## General election features +## + +@json +def election_params(request): + return ELGAMAL_PARAMS.toJSONDict() + +def election_verifier(request): + return render_template(request, "tally_verifier") + +def election_single_ballot_verifier(request): + return render_template(request, "ballot_verifier") + +def election_shortcut(request, election_short_name): + election = Election.get_by_short_name(election_short_name) + if election: + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + else: + raise Http404 + +@trustee_check +def trustee_keygenerator(request, election, trustee): + """ + A key generator with the current params, like the trustee home but without a specific election. + """ + eg_params_json = utils.to_json(ELGAMAL_PARAMS.toJSONDict()) + + return render_template(request, "election_keygenerator", {'eg_params_json': eg_params_json, 'election': election, 'trustee': trustee}) + +@login_required +def elections_administered(request): + if not can_create_election(request): + return HttpResponseForbidden('only an administrator has elections to administer') + + user = get_user(request) + elections = Election.get_by_user_as_admin(user) + + return render_template(request, "elections_administered", {'elections': elections}) + + +@login_required +def election_new(request): + if not can_create_election(request): + return HttpResponseForbidden('only an administrator can create an election') + + error = None + + if request.method == "GET": + election_form = forms.ElectionForm() + else: + election_form = forms.ElectionForm(request.POST) + + if election_form.is_valid(): + # create the election obj + election_params = dict(election_form.cleaned_data) + + # is the short name valid + if helios_utils.urlencode(election_params['short_name']) == election_params['short_name']: + election_params['uuid'] = str(uuid.uuid1()) + election_params['cast_url'] = settings.SECURE_URL_HOST + reverse(one_election_cast, args=[election_params['uuid']]) + + # registration starts closed + election_params['openreg'] = False + + user = get_user(request) + election_params['admin'] = user + # election_params['api_client'] = get_api_client(request) + + election, created_p = Election.get_or_create(**election_params) + + if created_p: + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + else: + error = "An election with short name %s already exists" % election_params['short_name'] + else: + error = "No special characters allowed in the short name." + + return render_template(request, "election_new", {'election_form': election_form, 'error': error}) + +@election_admin(frozen=False) +def one_election_edit(request, election): + + error = None + RELEVANT_FIELDS = ['short_name', 'name', 'description', 'use_voter_aliases'] + + if request.method == "GET": + values = {} + for attr_name in RELEVANT_FIELDS: + values[attr_name] = getattr(election, attr_name) + election_form = forms.ElectionForm(values) + else: + election_form = forms.ElectionForm(request.POST) + + if election_form.is_valid(): + clean_data = election_form.cleaned_data + for attr_name in RELEVANT_FIELDS: + setattr(election, attr_name, clean_data[attr_name]) + + election.save() + + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + + return render_template(request, "election_edit", {'election_form' : election_form, 'election' : election, 'error': error}) + +@election_admin(frozen=False) +def one_election_schedule(request, election): + return HttpResponse("foo") + +@election_view() +@json +def one_election(request, election): + return election.toJSONDict() + +@election_view() +def one_election_view(request, election): + user = get_user(request) + admin_p = security.user_can_admin_election(user, election) + can_feature_p = security.user_can_feature_election(user, election) + + notregistered = False + eligible_p = True + + election_url = get_election_url(election) + status_update_message = None + + if user: + voter = Voter.get_by_election_and_user(election, user) + + if voter: + # cast any votes? + votes = CastVote.get_by_voter(voter) + else: + eligible_p = _check_eligibility(election, user) + votes = None + notregistered = True + else: + voter = None + votes = None + + # status update message? + if election.openreg: + if election.voting_has_started: + status_update_message = "Vote in %s" % election.name + else: + status_update_message = "Register to vote in %s" % election.name + + # result! + if election.result: + status_update_message = "Results are in for %s" % election.name + + # a URL for the social buttons + socialbuttons_url = None + if status_update_message: + socialbuttons_url = "%s%s?%s" % (settings.SOCIALBUTTONS_URL_HOST, + reverse(socialbuttons), + urllib.urlencode({ + 'url' : election_url, + 'text': status_update_message + })) + + trustees = Trustee.get_by_election(election) + + return render_template(request, 'election_view', + {'election' : election, 'trustees': trustees, 'admin_p': admin_p, 'user': user, + 'voter': voter, 'votes': votes, 'notregistered': notregistered, 'eligible_p': eligible_p, + 'can_feature_p': can_feature_p, 'election_url' : election_url, + 'socialbuttons_url' : socialbuttons_url}) + +def socialbuttons(request): + """ + just render the social buttons for sharing a URL + expecting "url" and "text" in request.GET + """ + return render_template(request, 'socialbuttons', + {'url': request.GET['url'], 'text':request.GET['text']}) + +## +## Trustees and Public Key +## +## As of July 2009, there are always trustees for a Helios election: one trustee is acceptable, for simple elections. +## +@json +@election_view() +def list_trustees(request, election): + trustees = Trustee.get_by_election(election) + return [t.toJSONDict() for t in trustees] + +@election_view() +def list_trustees_view(request, election): + trustees = Trustee.get_by_election(election) + user = get_user(request) + admin_p = user and (user == election.admin) + + return render_template(request, 'list_trustees', {'election': election, 'trustees': trustees, 'admin_p':admin_p}) + +@election_admin(frozen=False) +def new_trustee(request, election): + if request.method == "GET": + return render_template(request, 'new_trustee', {'election' : election}) + else: + # get the public key and the hash, and add it + name = request.POST['name'] + email = request.POST['email'] + + trustee = Trustee(uuid = str(uuid.uuid1()), election = election, name=name, email=email) + trustee.save() + return HttpResponseRedirect(reverse(list_trustees_view, args=[election.uuid])) + +@election_admin(frozen=False) +def new_trustee_helios(request, election): + """ + Make Helios a trustee of the election + """ + election.generate_trustee(ELGAMAL_PARAMS) + return HttpResponseRedirect(reverse(list_trustees_view, args=[election.uuid])) + +@election_admin() +def delete_trustee(request, election): + trustee = Trustee.get_by_election_and_uuid(election, request.GET['uuid']) + trustee.delete() + return HttpResponseRedirect(reverse(list_trustees_view, args=[election.uuid])) + +def trustee_login(request, election_short_name, trustee_email, trustee_secret): + election = Election.get_by_short_name(election_short_name) + if election: + trustee = Trustee.get_by_election_and_email(election, trustee_email) + + if trustee and trustee.secret == trustee_secret: + set_logged_in_trustee(request, trustee) + return HttpResponseRedirect(reverse(trustee_home, args=[election.uuid, trustee.uuid])) + + return HttpResponseRedirect("/") + +@election_admin() +def trustee_send_url(request, election, trustee_uuid): + trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid) + + url = settings.SECURE_URL_HOST + reverse(trustee_login, args=[election.short_name, trustee.email, trustee.secret]) + + body = """ + +You are a trustee for %s. + +Your trustee dashboard is at + + %s + +-- +Helios +""" % (election.name, url) + + send_mail('your trustee homepage for %s' % election.name, body, settings.SERVER_EMAIL, ["%s <%s>" % (trustee.name, trustee.email)], fail_silently=True) + + logging.info("URL %s " % url) + return HttpResponseRedirect(reverse(list_trustees_view, args = [election.uuid])) + +@trustee_check +def trustee_home(request, election, trustee): + return render_template(request, 'trustee_home', {'election': election, 'trustee':trustee}) + +@trustee_check +def trustee_check_sk(request, election, trustee): + return render_template(request, 'trustee_check_sk', {'election': election, 'trustee':trustee}) + +@trustee_check +def trustee_upload_pk(request, election, trustee): + if request.method == "POST": + # get the public key and the hash, and add it + public_key_and_proof = utils.from_json(request.POST['public_key_json']) + trustee.public_key = algs.EGPublicKey.fromJSONDict(public_key_and_proof['public_key']) + trustee.pok = algs.DLogProof.fromJSONDict(public_key_and_proof['pok']) + + # verify the pok + if not trustee.public_key.verify_sk_proof(trustee.pok, algs.DLog_challenge_generator): + raise Exception("bad pok for this public key") + + trustee.public_key_hash = utils.hash_b64(utils.to_json(trustee.public_key.toJSONDict())) + + trustee.save() + + # send a note to admin + election.admin.send_message("%s - trustee pk upload" % election.name, "trustee %s (%s) uploaded a pk." % (trustee.name, trustee.email)) + + return HttpResponseRedirect(reverse(trustee_home, args=[election.uuid, trustee.uuid])) + +## +## Ballot Management +## + +@json +@election_view(frozen=True) +def encrypt_ballot(request, election): + """ + perform the ballot encryption given answers_json, a JSON'ified list of list of answers + (list of list because each question could have a list of answers if more than one.) + """ + # FIXME: maybe make this just request.POST at some point? + answers = utils.from_json(request.REQUEST['answers_json']) + ev = electionalgs.EncryptedVote.fromElectionAndAnswers(election, answers) + return ev.toJSONDict(with_randomness=True) + +@election_view(frozen=True) +def post_audited_ballot(request, election): + if request.method == "POST": + raw_vote = request.POST['audited_ballot'] + encrypted_vote = electionalgs.EncryptedVote.fromJSONDict(utils.from_json(raw_vote)) + vote_hash = encrypted_vote.get_hash() + audited_ballot = AuditedBallot(raw_vote = raw_vote, vote_hash = vote_hash, election = election) + audited_ballot.save() + + return SUCCESS + + +@election_view(frozen=True) +def one_election_cast(request, election): + """ + on a GET, this is a cancellation, on a POST it's a cast + """ + if request.method == "GET": + return HttpResponseRedirect(reverse(one_election_view, args = [election.uuid])) + + user = get_user(request) + encrypted_vote = request.POST['encrypted_vote'] + + save_in_session_across_logouts(request, 'encrypted_vote', encrypted_vote) + + return HttpResponseRedirect("%s%s" % (settings.URL_HOST, reverse(one_election_cast_confirm, args=[election.uuid]))) + +@election_view(frozen=True) +def one_election_cast_confirm(request, election): + user = get_user(request) + + # if no encrypted vote, the user is reloading this page or otherwise getting here in a bad way + if not request.session.has_key('encrypted_vote'): + return HttpResponseRedirect("/") + + if user: + voter = Voter.get_by_election_and_user(election, user) + else: + voter = None + + # auto-register this person if the election is openreg + if user and not voter and election.openreg: + voter = _register_voter(election, user) + + # tallied election, no vote casting + if election.encrypted_tally or election.result: + return render_template(request, 'election_tallied', {'election': election}) + + encrypted_vote = request.session['encrypted_vote'] + vote_fingerprint = cryptoutils.hash_b64(encrypted_vote) + + # if this user is a voter, prepare some stuff + if voter: + # prepare the vote to cast + cast_vote_params = { + 'vote' : electionalgs.EncryptedVote.fromJSONDict(utils.from_json(encrypted_vote)), + 'voter' : voter, + 'vote_hash': vote_fingerprint, + 'cast_at': datetime.datetime.utcnow() + } + + cast_vote = CastVote(**cast_vote_params) + else: + cast_vote = None + + if request.method == "GET": + if voter: + past_votes = CastVote.get_by_voter(voter) + if len(past_votes) == 0: + past_votes = None + else: + past_votes = None + + if cast_vote: + # check for issues + issues = cast_vote.issues(election) + else: + issues = None + + # status update this vote + if voter and user and user.can_update_status(): + status_update_label = voter.user.update_status_template() % "your smart ballot tracker" + status_update_message = "I voted in %s, my smart tracker is %s.. -- %s" % (election.name, cast_vote.vote_hash[:10], get_election_url(election)) + else: + status_update_label = None + status_update_message = None + + # do we need to constrain the auth_systems? + if election.eligibility: + auth_systems = [e['auth_system'] for e in election.eligibility] + else: + auth_systems = None + + if auth_systems == ['password']: + password_only = True + else: + password_only = False + + return_url = reverse(one_election_cast_confirm, args=[election.uuid]) + login_box = auth_views.login_box_raw(request, return_url=return_url, auth_systems = auth_systems) + + return render_template(request, 'election_cast_confirm', { + 'login_box': login_box, 'election' : election, 'vote_fingerprint': vote_fingerprint, + 'past_votes': past_votes, 'issues': issues, 'voter' : voter, + 'status_update_label': status_update_label, 'status_update_message': status_update_message, + 'password_only': password_only}) + + if request.method == "POST": + check_csrf(request) + + # voting has not started or has ended + if (not election.voting_has_started()) or election.voting_has_stopped(): + return HttpResponseRedirect("/") + + # if user is not logged in + # bring back to the confirmation page to let him know + if not user or not voter: + return HttpResponseRedirect(reverse(one_election_cast_confirm, args=[election.uuid])) + + # don't store the vote in the voter's data structure until verification + cast_vote.save() + + # status update? + if request.POST.get('status_update', False): + status_update_message = request.POST.get('status_update_message') + else: + status_update_message = None + + # launch the verification task + tasks.cast_vote_verify_and_store.delay( + cast_vote_id = cast_vote.id, + status_update_message = status_update_message) + + # remove the vote from the store + del request.session['encrypted_vote'] + + return HttpResponseRedirect(reverse(one_election_cast_done, args=[election.uuid])) + +@election_view() +def one_election_cast_done(request, election): + user = get_user(request) + voter = Voter.get_by_election_and_user(election, user) + votes = CastVote.get_by_voter(voter) + + logout = settings.LOGOUT_ON_CONFIRMATION + + # local logout ensures that there's no more + # user locally + if logout: + auth_views.do_local_logout(request) + + # remote logout is happening asynchronously in an iframe to be modular given the logout mechanism + return render_template(request, 'cast_done', {'election': election, 'last_vote': votes[0], 'logout': logout}) + +@election_view() +@json +def one_election_result(request, election): + return election.result + +@election_view() +@json +def one_election_result_proof(request, election): + return election.result_proof + +@election_view(frozen=True) +def one_election_bboard(request, election): + """ + UI to show election bboard + """ + after = request.GET.get('after', None) + offset= int(request.GET.get('offset', 0)) + limit = int(request.GET.get('limit', 50)) + + order_by = 'voter_id' + + # unless it's by alias, in which case we better go by UUID + if election.use_voter_aliases: + order_by = 'alias' + + # if there's a specific voter + if request.GET.has_key('q'): + # FIXME: figure out the voter by voter_id + voters = [] + else: + # load a bunch of voters + voters = Voter.get_by_election(election, after=after, limit=limit+1, order_by=order_by) + + more_p = len(voters) > limit + if more_p: + voters = voters[0:limit] + next_after = getattr(voters[limit-1], order_by) + else: + next_after = None + + return render_template(request, 'election_bboard', {'election': election, 'voters': voters, 'next_after': next_after, + 'offset': offset, 'limit': limit, 'offset_plus_one': offset+1, 'offset_plus_limit': offset+limit, + 'voter_id': request.GET.get('voter_id', '')}) + +@election_view(frozen=True) +def one_election_audited_ballots(request, election): + """ + UI to show election audited ballots + """ + + if request.GET.has_key('vote_hash'): + b = AuditedBallot.get(election, request.GET['vote_hash']) + return HttpResponse(b.raw_vote, mimetype="text/plain") + + after = request.GET.get('after', None) + offset= int(request.GET.get('offset', 0)) + limit = int(request.GET.get('limit', 50)) + + audited_ballots = AuditedBallot.get_by_election(election, after=after, limit=limit+1) + + more_p = len(audited_ballots) > limit + if more_p: + audited_ballots = audited_ballots[0:limit] + next_after = audited_ballots[limit-1].vote_hash + else: + next_after = None + + return render_template(request, 'election_audited_ballots', {'election': election, 'audited_ballots': audited_ballots, 'next_after': next_after, + 'offset': offset, 'limit': limit, 'offset_plus_one': offset+1, 'offset_plus_limit': offset+limit}) + +@election_admin() +def voter_delete(request, election, voter_uuid): + """ + Two conditions under which a voter can be deleted: + - election is not frozen or + - election is open reg + """ + if not (election.frozen_at or election.openreg): + raise PermissionDenied() + + if election.encrypted_tally: + raise PermissionDenied() + + voter = Voter.get_by_election_and_uuid(election, voter_uuid) + if voter: + voter.delete() + + if election.frozen_at: + # log it + election.append_log("Voter %s/%s removed after election frozen" % (voter.voter_type,voter.voter_id)) + + return HttpResponseRedirect(reverse(voters_list_pretty, args=[election.uuid])) + +@election_admin(frozen=False) +def one_election_set_reg(request, election): + """ + Set whether this is open registration or not + """ + open_p = bool(int(request.GET['open_p'])) + election.openreg = open_p + election.save() + + return HttpResponseRedirect(reverse(voters_list_pretty, args=[election.uuid])) + +@election_admin() +def one_election_set_featured(request, election): + """ + Set whether this is a featured election or not + """ + + user = get_user(request) + if not security.user_can_feature_election(user, election): + raise PermissionDenied() + + featured_p = bool(int(request.GET['featured_p'])) + election.featured_p = featured_p + election.save() + + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + +@election_admin() +def one_election_archive(request, election, admin, api_client): + + archive_p = request.GET.get('archive_p', True) + + if bool(int(archive_p)): + election.archived_at = datetime.datetime.utcnow() + else: + election.archived_at = None + + # FIXME: what is this?? + storage.election_update(election) + + if get_user(request): + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + else: + return SUCCESS + +# changed from admin to view because +# anyone can see the questions, the administration aspect is now +# built into the page +@election_view() +def one_election_questions(request, election): + questions_json = utils.to_json(election.questions) + user = get_user(request) + admin_p = user and (user == election.admin) + + return render_template(request, 'election_questions', {'election': election, 'questions_json' : questions_json, 'admin_p': admin_p}) + +def _check_eligibility(election, user): + return election.user_eligible_p(user) + +def _register_voter(election, user): + if not _check_eligibility(election, user): + return None + + voter_uuid = str(uuid.uuid1()) + voter = Voter(uuid= voter_uuid, voter_type = user.user_type, voter_id = user.user_id, election = election, name = user.name) + + voter.save() + return voter + +@election_view() +def one_election_register(request, election): + if not election.openreg: + return HttpResponseForbidden('registration is closed for this election') + + check_csrf(request) + + user = get_user(request) + voter = Voter.get_by_election_and_user(election, user) + + if not voter: + voter = _register_voter(election, user) + + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + +@election_admin(frozen=False) +def one_election_save_questions(request, election): + check_csrf(request) + + election.questions = utils.from_json(request.POST['questions_json']); + election.save() + + # always a machine API + return SUCCESS + +@election_admin(frozen=False) +def one_election_freeze(request, election): + # figure out the number of questions and trustees + issues = election.issues_before_freeze + + if request.method == "GET": + return render_template(request, 'election_freeze', {'election': election, 'issues' : issues, 'issues_p' : len(issues) > 0}) + else: + check_csrf(request) + + election.freeze() + + if get_user(request): + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + else: + return SUCCESS + +def _check_election_tally_type(election): + for q in election.questions: + if q['tally_type'] != "homomorphic": + return False + return True + +@election_admin(frozen=True) +def one_election_compute_tally(request, election): + """ + tallying is done all at a time now + """ + if not _check_election_tally_type(election): + return HttpResponseRedirect(reverse(one_election_view,args=[election.election_id])) + + if request.method == "GET": + return render_template(request, 'election_compute_tally', {'election': election}) + + check_csrf(request) + + if not election.voting_ended_at: + election.voting_ended_at = datetime.datetime.utcnow() + + election.tallying_started_at = datetime.datetime.utcnow() + election.save() + + tasks.election_compute_tally.delay(election_id = election.id) + + return HttpResponseRedirect(reverse(one_election_view,args=[election.uuid])) + +@trustee_check +def trustee_decrypt_and_prove(request, election, trustee): + if not _check_election_tally_type(election) or election.encrypted_tally == None: + return HttpResponseRedirect(reverse(one_election_view,args=[election.uuid])) + + return render_template(request, 'trustee_decrypt_and_prove', {'election': election, 'trustee': trustee}) + +@election_view(frozen=True) +def trustee_upload_decryption(request, election, trustee_uuid): + if not _check_election_tally_type(election) or election.encrypted_tally == None: + return HttpResponseRedirect(reverse(one_election_view,args=[election.election_id])) + + trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid) + + factors_and_proofs = utils.from_json(request.POST['factors_and_proofs']) + + # verify the decryption factors + trustee.decryption_factors = factors_and_proofs['decryption_factors'] + trustee.decryption_proofs = factors_and_proofs['decryption_proofs'] + + if trustee.verify_decryption_proofs(): + trustee.save() + + # send a note to admin + election.admin.send_message("%s - trustee partial decryption" % election.name, "trustee %s (%s) did their partial decryption." % (trustee.name, trustee.email)) + + return SUCCESS + else: + return FAILURE + +@election_admin(frozen=True) +def combine_decryptions(request, election): + election.combine_decryptions() + election.save() + + # notify voters! + extra_vars = { + 'election_url' : get_election_url(election) + } + + # full-length email + tasks.voters_email.delay(election_id = election.id, + subject_template = 'email/result_subject.txt', + body_template = 'email/result_body.txt', + extra_vars = extra_vars) + + # rapid short-message notification + tasks.voters_notify.delay(election_id = election.id, + notification_template = 'notification/result.txt', + extra_vars = extra_vars) + + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + +@election_admin(frozen=True) +def one_election_set_result_and_proof(request, election): + if election.tally_type != "homomorphic" or election.encrypted_tally == None: + return HttpResponseRedirect(reverse(one_election_view,args=[election.election_id])) + + # FIXME: check csrf + + election.result = utils.from_json(request.POST['result']) + election.result_proof = utils.from_json(request.POST['result_proof']) + election.save() + + if get_user(request): + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + else: + return SUCCESS + + +@election_view() +def voters_list_pretty(request, election): + """ + Show the list of voters + """ + after = request.GET.get('after', None) + offset= int(request.GET.get('offset', 0)) + limit = int(request.GET.get('limit', 50)) + + order_by = 'voter_id' + + user = get_user(request) + admin_p = user and (user == election.admin) + + # files being processed + voter_files = election.voterfile_set.all() + + # load a bunch of voters + voters = Voter.get_by_election(election, after=after, limit=limit+1, order_by=order_by) + + more_p = len(voters) > limit + if more_p: + voters = voters[0:limit] + next_after = getattr(voters[limit-1], order_by) + else: + next_after = None + + return render_template(request, 'voters_list', {'election': election, 'voters': voters, 'admin_p': admin_p, + 'next_after': next_after, 'email_voters': helios.VOTERS_EMAIL, + 'offset': offset, 'limit': limit, 'offset_plus_one': offset+1, + 'offset_plus_limit': offset+min(limit,len(voters)), + 'upload_p': helios.VOTERS_UPLOAD, + 'voter_files': voter_files}) + +@election_admin() +def voters_search(request, election): + """ + Search the voters by voter_id + """ + search_term = request.GET.get('q', None) + if not search_term: + raise Exception("must provide a search term") + + voter = Voter.get_by_election_and_voter_id(election, voter_id=search_term) + return render_template(request, 'voters_search', {'election': election, 'voter': voter, 'search_term': search_term}) + +@election_admin(frozen=False) +def voters_upload(request, election): + """ + Upload a CSV of password-based voters with + voter_id, email, name + + name and email are needed only if voter_type is static + """ + if request.method == "GET": + return render_template(request, 'voters_upload', {'election': election}) + + if request.method == "POST": + # we store the file away for future processing + voters_file = request.FILES['voters_file'] + voter_file_obj = election.add_voters_file(voters_file) + + # launch the background task to parse that file + tasks.voter_file_process.delay(voter_file_id = voter_file_obj.id) + + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + +@election_admin(frozen=True) +def voters_email(request, election): + if not helios.VOTERS_EMAIL: + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + + voter_id = request.REQUEST.get('voter_id', None) + voter = Voter.get_by_election_and_voter_id(election, voter_id) + + if request.method == "GET": + email_form = forms.EmailVotersForm() + else: + email_form = forms.EmailVotersForm(request.POST) + + if email_form.is_valid(): + + # the client knows to submit only once with a specific voter_id + subject_template = 'email/vote_subject.txt' + body_template = 'email/vote_body.txt' + + extra_vars = { + 'custom_message' : email_form.cleaned_data['body'], + 'election_url' : get_election_url(election) + } + + + if voter: + tasks.single_voter_email.delay(voter_uuid = voter.uuid, subject_template = subject_template, body_template = body_template, extra_vars = extra_vars) + else: + tasks.voters_email.delay(election_id = election.id, subject_template = subject_template, body_template = body_template, extra_vars = extra_vars) + + # this batch process is all async, so we can return a nice note + return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid])) + + return render_template(request, "voters_email", {'email_form': email_form, 'election': election, 'voter': voter}) + +# Individual Voters +@election_view() +@json +def voter_list(request, election): + # normalize limit + limit = int(request.GET.get('limit', 500)) + if limit > 500: limit = 500 + + voters = Voter.get_by_election(election, order_by='uuid', after=request.GET.get('after',None), limit= limit) + return [v.toJSONDict() for v in voters] + +@election_view() +@json +def one_voter(request, election, voter_uuid): + """ + View a single voter's info as JSON. + """ + voter = Voter.get_by_election_and_uuid(election, voter_uuid) + return voter.toJSONDict() + +@election_view() +@json +def voter_votes(request, election, voter_uuid): + """ + all cast votes by a voter + """ + voter = Voter.get_by_election_and_uuid(election, voter_uuid) + votes = CastVote.get_by_voter(voter) + return [v.toJSONDict() for v in votes] + +@election_view() +@json +def voter_last_vote(request, election, voter_uuid): + """ + all cast votes by a voter + """ + voter = Voter.get_by_election_and_uuid(election, voter_uuid) + return voter.last_cast_vote().toJSONDict() + +## +## cast ballots +## + +@election_view() +@json +def ballot_list(request, election): + """ + this will order the ballots from most recent to oldest. + and optionally take a after parameter. + """ + limit = after = None + if request.GET.has_key('limit'): + limit = int(request.GET['limit']) + if request.GET.has_key('after'): + after = datetime.datetime.strptime(request.GET['after'], '%Y-%m-%d %H:%M:%S') + + voters = Voter.get_by_election(election, cast=True, order_by='cast_at', limit=limit, after=after) + return [v.last_cast_vote().toJSONDict(include_vote=False) for v in voters] + + + + diff --git a/helios/widgets.py b/helios/widgets.py new file mode 100644 index 000000000..ac28d9dd3 --- /dev/null +++ b/helios/widgets.py @@ -0,0 +1,191 @@ +""" +Widget for datetime split, with calendar for date, and drop-downs for times. +""" + +from django import forms +from django.db import models +from django.template.loader import render_to_string +from django.forms.widgets import Select, MultiWidget, DateInput, TextInput, Widget +from django.forms.extras.widgets import SelectDateWidget +from time import strftime + +import re +from django.utils.safestring import mark_safe + +__all__ = ('SelectTimeWidget', 'SplitSelectDateTimeWidget') + +# Attempt to match many time formats: +# Example: "12:34:56 P.M." matches: +# ('12', '34', ':56', '56', 'P.M.', 'P', '.', 'M', '.') +# ('12', '34', ':56', '56', 'P.M.') +# Note that the colon ":" before seconds is optional, but only if seconds are omitted +time_pattern = r'(\d\d?):(\d\d)(:(\d\d))? *([aApP]\.?[mM]\.?)?$' + +RE_TIME = re.compile(time_pattern) +# The following are just more readable ways to access re.matched groups: +HOURS = 0 +MINUTES = 1 +SECONDS = 3 +MERIDIEM = 4 + +class SelectTimeWidget(Widget): + """ + A Widget that splits time input into