# # # delete "www/viewmtn/.htaccess" # # delete "www/viewmtn/web.py" # # rename "www/viewmtn/grapher" # to "www/viewmtn/experiments/grapher" # # rename "www/viewmtn/templates/revisionfile.html" # to "www/viewmtn/templates/revisionfileview.html" # # rename "www/viewmtn/tests" # to "www/viewmtn/experiments/tests" # # add_dir "www/viewmtn/experiments" # # add_dir "www/viewmtn/web" # # add_file "www/viewmtn/experiments/README" # content [4fead89086ec35aac4a08701363c9540a88f317b] # # add_file "www/viewmtn/templates/branchtags.html" # content [451943bb4f5dc413e1b737dfc03944f5664b8f87] # # add_file "www/viewmtn/templates/revisionfile.html" # content [ba7036a5cecbf905babb29a2f23f1775e1d303ba] # # add_file "www/viewmtn/templates/revisionfilechanges.html" # content [9aa1d3367d46c3f119250f153fa1f4f0d66a1269] # # add_file "www/viewmtn/templates/revisionfilechangesrss.html" # content [efb2657f8b97af62a879ef709af48a1702edb135] # # add_file "www/viewmtn/tests.py" # content [77414646f8157c9ce2e0e4ab5626f4cb4ea66cb6] # # add_file "www/viewmtn/web/__init__.py" # content [48d7afda5b9fd9654898380784d3f031a988b85d] # # add_file "www/viewmtn/web/cheetah.py" # content [06b4764d548093c228c4b27b3c08bc49588fa4a9] # # add_file "www/viewmtn/web/db.py" # content [6a2db17632e0730936276f1ece7faf1c74ef39dd] # # add_file "www/viewmtn/web/debugerror.py" # content [dbd19a03c29519c7f45301222c73fc3b36053362] # # add_file "www/viewmtn/web/form.py" # content [5abbec430ef66a48c76249afe4d41ec5136170a8] # # add_file "www/viewmtn/web/http.py" # content [5be2ec57345dcad9abafa21199cf9e5c252178a9] # # add_file "www/viewmtn/web/httpserver.py" # content [fc34d04446669325863e2151dd9d34c037fa4518] # # add_file "www/viewmtn/web/net.py" # content [82d2ec267f761eb1b45417faf08ffcbf3ddf304d] # # add_file "www/viewmtn/web/request.py" # content [ef22e4a2d55339f64fda752bd6084bf39576c29d] # # add_file "www/viewmtn/web/template.py" # content [a0091de1f9fc61a72f60d89da15f301d931ef36c] # # add_file "www/viewmtn/web/utils.py" # content [2eee2f94d96a42148b310b7eb16eace1409b8718] # # add_file "www/viewmtn/web/webapi.py" # content [304cb537871312a0612dc0389e5f5aaad7a5563e] # # add_file "www/viewmtn/web/wsgi.py" # content [d7214b2a4c8693a87db5d17eacd245d441ad7113] # # patch "www/viewmtn/AUTHORS" # from [efc3dd1070798deb1fc2a2312204cc2f9faf00d4] # to [8d04d002de25e5b2befb3bad088b6da4b24b3470] # # patch "www/viewmtn/ChangeLog" # from [a90102fbf7b5d7be6c5d553f362389c465267832] # to [c4ca52a6b1aa76740f0f94f5ddb69582bd35bc55] # # patch "www/viewmtn/INSTALL" # from [e6b186c3fbe5e1843af38b941f284add5e3ceaa6] # to [96cd4a9bac2f42db1846f3aacff30a58e5706042] # # patch "www/viewmtn/README" # from [46ffe1ce5080c9fd0081574ae8a8da30c3df65dc] # to [51ea9bcf58e2383de5b13e36130946447ed07462] # # patch "www/viewmtn/TODO" # from [d57f9d2d4d2acd8412cb3d9c090f424159eecb6a] # to [eb85131c28211d5b66942a4a499def2ef2866992] # # patch "www/viewmtn/authors.py" # from [0ed0661cf3ddf1b402a73dfc1ff0fccc64ad05f1] # to [011a38a50ea83896fac1db87a8d81860bf0d22e9] # # patch "www/viewmtn/common.py" # from [b5a9eb0d651eb4f1a41e94376d35b31f5c8efe5d] # to [2504e01f69f99347d0e8706fb7c344c6a6471e43] # # patch "www/viewmtn/config.py.example" # from [343449d29b7b0e04c1e86f034c27a48bed094d5e] # to [97fbc51fb12393018862a3073aa718a25bfb6eac] # # patch "www/viewmtn/fdo/__init__.py" # from [da39a3ee5e6b4b0d3255bfef95601890afd80709] # to [14a3ef7b1ed02bfe6219517ba27a3d188d09f78a] # # patch "www/viewmtn/fdo/icontheme.py" # from [9ec270a56c060a3c63b75a4f7d00c370a831ae29] # to [643842c242655847a60538b6a6eeb2495d04bc31] # # patch "www/viewmtn/fdo/sharedmimeinfo.py" # from [dd993b9e526568246adc535ce4d47d360dd4eb3b] # to [4ee53b521b7103c8434435a904cd6692b569548d] # # patch "www/viewmtn/fdo/xdgbasedir.py" # from [864ac96a21a2fe65c401a7beaf2c98513795751d] # to [ea14de106c5d29b333dc8bd1b40bd869a6afbb05] # # patch "www/viewmtn/genproxy.py" # from [408f46a3f5fe0d792eb62e92a8faaf5c28c67a54] # to [4fbf4dd79d7dbe381b8bae3c9b0b63e1864dcfce] # # patch "www/viewmtn/mtn.py" # from [10bae73eaa4b6b891d434bfcffa8ca66968b204b] # to [24bdc6cf0f2935b5768da503135c11c50f86e267] # # patch "www/viewmtn/release.py" # from [9606208d767e8ed4999ca37fa5e20d13f3fa7be5] # to [33b5fb415b50d215b26575154bdcfb6fcd704def] # # patch "www/viewmtn/release.sh" # from [65fe70e241d026a9e09530245362cb09287b6608] # to [524cd8cc20a5c03c1f772cf688a26ec1eceb487d] # # patch "www/viewmtn/static/MochiKit/MochiKit.js" # from [db162319790852ff3e02f6e673b729a3c262f4ec] # to [32da8c6070b029251ef51bc3097460f296bcf7a1] # # patch "www/viewmtn/static/viewmtn.js" # from [b36f4e737e9b054f563c320becbc74d7a4c1df48] # to [001ab5902c148053998d2d6877c82ee3da848d70] # # patch "www/viewmtn/syntax.py" # from [cf93e6e6a6166204daaae6ec50819ba175fa3ed1] # to [10c283ecef173382775ae1924a7bf33c220743d2] # # patch "www/viewmtn/templates/about.html" # from [fea245cb17ab46a7766e03293800361db0b325b2] # to [309dd178a83ac76781efe00745d453d6e8dc0227] # # patch "www/viewmtn/templates/base.html" # from [96ce364d2dfd62b29e6557bebfd3e5f9a6c87b46] # to [1be9c3a8bbe40d46dcd5bc6507b2547233c659dc] # # patch "www/viewmtn/templates/branch.html" # from [9b84a47baffe133624433db124e92d70a206bb6a] # to [7f162c333735ef7f64cd507d524653155972ca9f] # # patch "www/viewmtn/templates/branchchanges.html" # from [84dfe05fe66d988e3cdf426da5e0b339ed5d2df2] # to [26a4a466df7417985215c652b46b476134a11ad9] # # patch "www/viewmtn/templates/branchchangesrss.html" # from [815fa5b5b1d06136d19fbf83b3c9f0c521d7bb05] # to [bfbbfead32d6e0aad40646932be79348f2aba0af] # # patch "www/viewmtn/templates/help.html" # from [f8faec929ff6f5fd1722240f12a8b398c88fb603] # to [3606a77689a20cb26c11679e23fc4779a0df851c] # # patch "www/viewmtn/templates/revisionfilebin.html" # from [ed073e34de5e16ad7ed0d14ce01bff80b0d78970] # to [58a6080633337480f0f620eeece57059fc50a532] # # patch "www/viewmtn/templates/revisionfileimg.html" # from [cd246dd75333b2ced5d8dd9216e0d50a16769dfd] # to [e9c685b6125b311189fb05e0f867326978d5189a] # # patch "www/viewmtn/templates/revisionfileobj.html" # from [aecf0edca364646c23f9fecfb764159a73b4d7d1] # to [01c532aadbb1782abcf1b30bc6c60ceaa06e6c00] # # patch "www/viewmtn/templates/revisionfiletxt.html" # from [c15b907d1bf28ed74a6835cb5dc775241432e958] # to [56c9ac35eb9160347e72c68064d102a9bf02a6f6] # # patch "www/viewmtn/templates/revisionfileview.html" # from [8d316cefa1d01ec52d0e42a923a7736a46efda0d] # to [5a5591e812fdf877cf4c3daa3a14c5eb9b210a0d] # # patch "www/viewmtn/templates/revisioninfo.html" # from [18daa6b4c21b0a208fe275530178c26c0c6cd824] # to [d5a52523b52cf1224512ab312f372e18e7f755d8] # # patch "www/viewmtn/utility.py" # from [78cd2c53af05e63bb76c097dc832b6f75394e40d] # to [fe56ef3c3d8f68a5cc417da48eac4ef38d69576f] # # patch "www/viewmtn/viewmtn.py" # from [1dc5caa196fe72d70fa67baaad7ed79f3a430868] # to [0071746f40c249e34f72d46fecde38f9d8cae948] # # set "www/viewmtn/tests.py" # attr "mtn:execute" # value "true" # ============================================================ --- www/viewmtn/experiments/README 4fead89086ec35aac4a08701363c9540a88f317b +++ www/viewmtn/experiments/README 4fead89086ec35aac4a08701363c9540a88f317b @@ -0,0 +1,5 @@ + +Things in here are old ideas, or new ones, that aren't ready +for prime time yet. They might even be embarassingly bad prototypes +written when half asleep. Hence, beware! + ============================================================ --- www/viewmtn/templates/branchtags.html 451943bb4f5dc413e1b737dfc03944f5664b8f87 +++ www/viewmtn/templates/branchtags.html 451943bb4f5dc413e1b737dfc03944f5664b8f87 @@ -0,0 +1,40 @@ +#extends branch + +#def body +

+A tag marks a particular revision that is in some way significant. +A common use of tags is to mark public release of a piece of software. +To view a particular tag, select it from the list below. +

+ +

+All tags on this branch are listed below. +

+ + + +#for tag in $tags + + + + + + +#end for +
TagSigned byBranchesAge
+ #filter Filter + $link($tag).html() + #end filter + + $tag.author + + #filter Filter + #for branch in $tag.branches + $link($branch).html()
+ #end for + #end filter +
+ $revision_ago($tag.revision) +
+ +#end def ============================================================ --- www/viewmtn/templates/revisionfile.html ba7036a5cecbf905babb29a2f23f1775e1d303ba +++ www/viewmtn/templates/revisionfile.html ba7036a5cecbf905babb29a2f23f1775e1d303ba @@ -0,0 +1,11 @@ +#extends revision + +#def extramenu1 +File $filename.name: +#filter Filter +$link($filename, for_changes=True).html(override_description="Changes") | +$link($filename).html(override_description="View") | +$link($filename, for_download=True).html(override_description="Download") | +$link($filename, for_changes_rss=True).html(override_description="RSS") +#end filter +#end def ============================================================ --- www/viewmtn/templates/revisionfilechanges.html 9aa1d3367d46c3f119250f153fa1f4f0d66a1269 +++ www/viewmtn/templates/revisionfilechanges.html 9aa1d3367d46c3f119250f153fa1f4f0d66a1269 @@ -0,0 +1,51 @@ +#extends revisionfile + +#def body + +

+Revisions $from_change to $to_change in in which '$filename.name' was +changed are listed below. These revisions are ancestors of the +revision +#filter Filter +$link($revision).html(). +#filter WebSafe +The "diff" links listed with each revision will show changes +in '$filename.name' between that revision and +#filter Filter +$link($revision).html(). +#filter WebSafe +

+ + + +#for $revision, $diffs, $ago, $author, $changelog, $shortlog, $when, $verfilename in $display_revs + + + + + + + + + + + + +#end for +
+#filter Filter + $ago ago: $shortlog
+ + $link($verfilename).html(override_description="view file") + $diffs | + $link($revision).html("revision info") | + $link($revision, "browse").html("browse files") + +#end filter +
Author:$author
Changelog: +#filter Filter +$changelog +#end filter +
Date:$when
+ +#end def ============================================================ --- www/viewmtn/templates/revisionfilechangesrss.html efb2657f8b97af62a879ef709af48a1702edb135 +++ www/viewmtn/templates/revisionfilechangesrss.html efb2657f8b97af62a879ef709af48a1702edb135 @@ -0,0 +1,22 @@ + + + en-us + + Changes to file $filename.name (from $revision.abbrev()) + $link($filename, for_changes=True).uri() + Changes to file $filename.name (from $revision.abbrev()) + +#for $revision, $diffs, $ago, $author, $changelog, $shortlog, $when, $verfilename in $display_revs + +#filter Filter + $link($revision).uri() + $shortlog + $changelog +#end filter + $author + $when + +#end for + + + ============================================================ --- www/viewmtn/tests.py 77414646f8157c9ce2e0e4ab5626f4cb4ea66cb6 +++ www/viewmtn/tests.py 77414646f8157c9ce2e0e4ab5626f4cb4ea66cb6 @@ -0,0 +1,149 @@ +#!/usr/bin/env python2.4 + +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + +# +# Unit tests for ViewMTN +# +# Aiming for reasonably full coverage, but the tests are being +# written after the application so it's slow and tedious work :-) +# + +import unittest +import stat +import os + +# modules we're testing.. +import viewmtn +import mtn + +class ConfigTest(unittest.TestCase): + "Test that the configuration file is correct" + + def can_access(self, fname): + self.failIf (not os.access(fname, os.R_OK), "Cannot access file: " + fname) + + def is_uri(self, uri): + self.failIf (not isinstance(uri, str), "URIs must be instances of ") + self.failIf (not uri[-1] == '/', "All URIs in config file must end in '/'") + + def is_file(self, fname): + self.failIf (not stat.S_ISREG(os.stat(fname).st_mode), "Files in config file must be regular files: " + fname) + + def is_command(self, fname): + self.failIf (not os.access(fname, os.X_OK), "Must be able to execute: " + fname) + + def is_directory(self, fname): + self.failIf (not stat.S_ISDIR(os.stat(fname).st_mode), "Must be a directory: " + fname) + + # + # describe the config file with lists of functions that should + # succeed on the value found in the config file. for now, any + # option listed below is required (ViewMTN doesn't really have + # optional configuration directives, anyway..) + # + check_keys = [(lambda self : self.config, + lambda obj, key : hasattr(obj, key), + lambda obj, key : getattr(obj, key), + { 'dynamic_uri_path' : (is_uri,), + 'static_uri_path' : (is_uri,), + 'running_under_apache2' : None, + 'monotone' : (can_access, is_file, is_command), + 'dbfile' : (can_access, is_file,), + 'highlight_command' : (can_access, is_file, is_command), + 'graphopts' : None, + 'icon_theme' : None, + 'icon_size' : None, + }), + (lambda self: self.config.graphopts, + lambda obj, key : obj.has_key(key), + lambda obj, key : obj[key], + { 'directory' : (can_access, is_directory), + 'uri' : (is_uri,), + 'dot' : (can_access, is_file, is_command), + 'nodeopts' : None }), + (lambda self: self.config.graphopts['nodeopts'], + lambda obj, key : obj.has_key(key), + lambda obj, key : obj[key], + { 'fontname' : None, + 'fontsize' : None, + 'shape' : None, + 'height' : None, + 'spline' : None, + 'style' : None, + 'fillcolor' : None }) ] + + def setUp(self): + self.config = __import__('config') + + def testRequiredKeys(self): + "all required configuration options present" + for val_func, has_func, get_func, keys in self.check_keys: + to_check = val_func(self) + for key in keys: + self.failIf(not has_func(to_check, key), "Required configuration option '%s' not found." % key) + funcs = keys[key] + if funcs == None: continue + val = get_func(to_check, key) + map(lambda func : func(self, val), funcs) + +class MTNRegexpTests(unittest.TestCase): + entirely_non_hex = 'NOTX' + starts_with_hex = 'A9NOTX' + ends_with_hex = 'NOTXA9' + valid_revision = 'abcdEf0123456789abcdEf0123456789abcdef12' + too_long_revision = valid_revision + '9' + too_short_revision = valid_revision[:-1] + non_hex_revision = valid_revision.replace('a', 'Q') + non_hex_start_revision = 'QQ' + valid_revision + non_hex_end_revision = valid_revision + 'QQ' + + def testHex(self): + "Test that mtn.py regular expressions do what we think they do" + self.assertEqual(mtn.hex_re_c.match('').groups(), ('',), 'hex_re must match empty string') + self.assertEqual(mtn.hex_re_c.match(self.entirely_non_hex).groups(), ('',), 'hex_re must match entirely non-hex and group empty string') + self.assertEqual(mtn.hex_re_c.match(self.starts_with_hex).groups(), ('A9',), 'hex_re must match hex followed by non-hex and group beginning hex') + self.assertEqual(mtn.hex_re_c.match(self.ends_with_hex).groups(), ('',), 'hex_re must match non-hex followed by hex and group empty string') + + self.assertEqual(mtn.revision_re_c.match(''), None, 'revision_re must not match empty string') + self.assertEqual(mtn.revision_re_c.match(self.valid_revision).groups(), (self.valid_revision,), 'revision_re must match valid revision') + self.failIf(mtn.revision_re_c.match(self.too_short_revision), 'revision_re must not match too-short revision') + self.failIf(mtn.revision_re_c.match(self.non_hex_revision), 'revision_re must not match non-hex revision') + self.failIf(mtn.revision_re_c.match(self.non_hex_start_revision), 'revision_re must not match non-hex followed by valid revision') + self.failIf(mtn.revision_re_c.match(self.non_hex_end_revision), 'revision_re must not match valid revision followed by non-hex') + +class MtnTypeTests(unittest.TestCase): + def testRevision(self): + "Test the mtn.Revision class" + self.assertEqual (mtn.Revision(''), '', 'Revision must work for empty revision, and subclass str()') + self.assertRaises (mtn.MonotoneException, lambda : mtn.Revision(MTNRegexpTests.too_long_revision)) + self.assertRaises (mtn.MonotoneException, lambda : mtn.Revision(MTNRegexpTests.too_short_revision)) + self.assertRaises (mtn.MonotoneException, lambda : mtn.Revision(MTNRegexpTests.non_hex_revision)) + self.assertRaises (mtn.MonotoneException, lambda : mtn.Revision(MTNRegexpTests.non_hex_start_revision)) + self.assertRaises (mtn.MonotoneException, lambda : mtn.Revision(MTNRegexpTests.non_hex_end_revision)) + self.assertEqual (mtn.Revision(MTNRegexpTests.valid_revision), MTNRegexpTests.valid_revision, 'Revision must work for valid revision, and subclass str()') + + def testAuthor(self): + "Test the mtn.Author class" + self.assertEqual (mtn.Author(''), '', 'Author must work for empty string, and subclass str()') + self.assertEqual (mtn.Author('Gumby'), 'Gumby', 'Author must work for non-empty string, and subclass str()') + +class MtnAutomateTests(unittest.TestCase): + + def testPacketHeader(self): + pass + + +if __name__ == '__main__': + unittest.main() + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/web/__init__.py 48d7afda5b9fd9654898380784d3f031a988b85d +++ www/viewmtn/web/__init__.py 48d7afda5b9fd9654898380784d3f031a988b85d @@ -0,0 +1,61 @@ +#!/usr/bin/env python +from __future__ import generators + +"""web.py: makes web apps (http://webpy.org)""" +__version__ = "0.2" +__revision__ = "$Rev: 62 $" +__author__ = "Aaron Swartz " +__license__ = "public domain" +__contributors__ = "see http://webpy.org/changes" + +# todo: +# - some sort of accounts system + +import utils, db, net, wsgi, http, webapi, request, httpserver, debugerror +import template, form + +from utils import * +from db import * +from net import * +from wsgi import * +from http import * +from webapi import * +from request import * +from httpserver import * +from debugerror import * + +try: + import cheetah + from cheetah import * +except ImportError: + pass + +def main(): + import doctest + + doctest.testmod(utils) + doctest.testmod(db) + doctest.testmod(net) + doctest.testmod(wsgi) + doctest.testmod(http) + doctest.testmod(webapi) + doctest.testmod(request) + + try: + doctest.testmod(cheetah) + except NameError: + pass + + template.test() + + import sys + urls = ('/web.py', 'source') + class source: + def GET(self): + header('Content-Type', 'text/python') + print open(sys.argv[0]).read() + + if listget(sys.argv, 1) != 'test': + run(urls, locals()) + +if __name__ == "__main__": main() ============================================================ --- www/viewmtn/web/cheetah.py 06b4764d548093c228c4b27b3c08bc49588fa4a9 +++ www/viewmtn/web/cheetah.py 06b4764d548093c228c4b27b3c08bc49588fa4a9 @@ -0,0 +1,95 @@ +""" +Cheetah API +(from web.py) +""" + +__all__ = ["render"] + +import re, urlparse, pprint, traceback, sys +from Cheetah.Compiler import Compiler +from Cheetah.Filters import Filter +from utils import re_compile, memoize, dictadd +from net import htmlquote, websafe +from webapi import ctx, header, output, input, cookies + +def upvars(level=2): + """Guido van Rossum sez: don't use this function.""" + return dictadd( + sys._getframe(level).f_globals, + sys._getframe(level).f_locals) + +r_include = re_compile(r'(?!\\)#include \"(.*?)\"($|#)', re.M) +def __compiletemplate(template, base=None, isString=False): + if isString: + text = template + else: + text = open('templates/'+template).read() + # implement #include at compile-time + def do_include(match): + text = open('templates/'+match.groups()[0]).read() + return text + while r_include.findall(text): + text = r_include.sub(do_include, text) + + execspace = _compiletemplate.bases.copy() + tmpl_compiler = Compiler(source=text, mainClassName='GenTemplate') + tmpl_compiler.addImportedVarNames(execspace.keys()) + exec str(tmpl_compiler) in execspace + if base: + _compiletemplate.bases[base] = execspace['GenTemplate'] + + return execspace['GenTemplate'] + +_compiletemplate = memoize(__compiletemplate) +_compiletemplate.bases = {} + +def render(template, terms=None, asTemplate=False, base=None, + isString=False): + """ + Renders a template, caching where it can. + + `template` is the name of a file containing the a template in + the `templates/` folder, unless `isString`, in which case it's the + template itself. + + `terms` is a dictionary used to fill the template. If it's None, then + the caller's local variables are used instead, plus context, if it's not + already set, is set to `context`. + + If asTemplate is False, it `output`s the template directly. Otherwise, + it returns the template object. + + If the template is a potential base template (that is, something other templates) + can extend, then base should be a string with the name of the template. The + template will be cached and made available for future calls to `render`. + + Requires [Cheetah](http://cheetahtemplate.org/). + """ + # terms=['var1', 'var2'] means grab those variables + if isinstance(terms, list): + new = {} + old = upvars() + for k in terms: + new[k] = old[k] + terms = new + # default: grab all locals + elif terms is None: + terms = {'context': ctx, 'ctx':ctx} + terms.update(sys._getframe(1).f_locals) + # terms=d means use d as the searchList + if not isinstance(terms, tuple): + terms = (terms,) + + if not isString and template.endswith('.html'): + header('Content-Type','text/html; charset=utf-8', unique=True) + + compiled_tmpl = _compiletemplate(template, base=base, isString=isString) + compiled_tmpl = compiled_tmpl(searchList=terms, filter=WebSafe) + if asTemplate: + return compiled_tmpl + else: + return output(str(compiled_tmpl)) + +class WebSafe(Filter): + def filter(self, val, **keywords): + return websafe(val) ============================================================ --- www/viewmtn/web/db.py 6a2db17632e0730936276f1ece7faf1c74ef39dd +++ www/viewmtn/web/db.py 6a2db17632e0730936276f1ece7faf1c74ef39dd @@ -0,0 +1,628 @@ +""" +Database API +(part of web.py) +""" + +# todo: +# - test with sqlite +# - a store function? + +__all__ = [ + "UnknownParamstyle", "UnknownDB", + "sqllist", "sqlors", "aparam", "reparam", + "SQLQuery", "sqlquote", + "connect", + "transact", "commit", "rollback", + "query", + "select", "insert", "update", "delete" +] + +import time +try: import datetime +except ImportError: datetime = None + +from utils import storage, iters, iterbetter +import webapi as web + +try: + from DBUtils.PooledDB import PooledDB + web.config._hasPooling = True +except ImportError: + web.config._hasPooling = False + +class _ItplError(ValueError): + def __init__(self, text, pos): + ValueError.__init__(self) + self.text = text + self.pos = pos + def __str__(self): + return "unfinished expression in %s at char %d" % ( + repr(self.text), self.pos) + +def _interpolate(format): + """ + Takes a format string and returns a list of 2-tuples of the form + (boolean, string) where boolean says whether string should be evaled + or not. + + from (public domain, Ka-Ping Yee) + """ + from tokenize import tokenprog + + def matchorfail(text, pos): + match = tokenprog.match(text, pos) + if match is None: + raise _ItplError(text, pos) + return match, match.end() + + namechars = "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + chunks = [] + pos = 0 + + while 1: + dollar = format.find("$", pos) + if dollar < 0: + break + nextchar = format[dollar + 1] + + if nextchar == "{": + chunks.append((0, format[pos:dollar])) + pos, level = dollar + 2, 1 + while level: + match, pos = matchorfail(format, pos) + tstart, tend = match.regs[3] + token = format[tstart:tend] + if token == "{": + level = level + 1 + elif token == "}": + level = level - 1 + chunks.append((1, format[dollar + 2:pos - 1])) + + elif nextchar in namechars: + chunks.append((0, format[pos:dollar])) + match, pos = matchorfail(format, dollar + 1) + while pos < len(format): + if format[pos] == "." and \ + pos + 1 < len(format) and format[pos + 1] in namechars: + match, pos = matchorfail(format, pos + 1) + elif format[pos] in "([": + pos, level = pos + 1, 1 + while level: + match, pos = matchorfail(format, pos) + tstart, tend = match.regs[3] + token = format[tstart:tend] + if token[0] in "([": + level = level + 1 + elif token[0] in ")]": + level = level - 1 + else: + break + chunks.append((1, format[dollar + 1:pos])) + + else: + chunks.append((0, format[pos:dollar + 1])) + pos = dollar + 1 + (nextchar == "$") + + if pos < len(format): + chunks.append((0, format[pos:])) + return chunks + +class UnknownParamstyle(Exception): + """ + raised for unsupported db paramstyles + + (currently supported: qmark, numeric, format, pyformat) + """ + pass + +def aparam(): + """ + Returns the appropriate string to be used to interpolate + a value with the current `web.ctx.db_module` or simply %s + if there isn't one. + + >>> aparam() + '%s' + """ + if hasattr(web.ctx, 'db_module'): + style = web.ctx.db_module.paramstyle + else: + style = 'pyformat' + + if style == 'qmark': + return '?' + elif style == 'numeric': + return ':1' + elif style in ['format', 'pyformat']: + return '%s' + raise UnknownParamstyle, style + +def reparam(string_, dictionary): + """ + Takes a string and a dictionary and interpolates the string + using values from the dictionary. Returns an `SQLQuery` for the result. + + >>> reparam("s = $s", dict(s=True)) + + """ + vals = [] + result = [] + for live, chunk in _interpolate(string_): + if live: + result.append(aparam()) + vals.append(eval(chunk, dictionary)) + else: result.append(chunk) + return SQLQuery(''.join(result), vals) + +def sqlify(obj): + """ + converts `obj` to its proper SQL version + + >>> sqlify(None) + 'NULL' + >>> sqlify(True) + "'t'" + >>> sqlify(3) + '3' + """ + + # because `1 == True and hash(1) == hash(True)` + # we have to do this the hard way... + + if obj is None: + return 'NULL' + elif obj is True: + return "'t'" + elif obj is False: + return "'f'" + elif datetime and isinstance(obj, datetime.datetime): + return repr(obj.isoformat()) + else: + return repr(obj) + +class SQLQuery: + """ + You can pass this sort of thing as a clause in any db function. + Otherwise, you can pass a dictionary to the keyword argument `vars` + and the function will call reparam for you. + """ + # tested in sqlquote's docstring + def __init__(self, s='', v=()): + self.s, self.v = str(s), tuple(v) + + def __getitem__(self, key): # for backwards-compatibility + return [self.s, self.v][key] + + def __add__(self, other): + if isinstance(other, str): + self.s += other + elif isinstance(other, SQLQuery): + self.s += other.s + self.v += other.v + return self + + def __radd__(self, other): + if isinstance(other, str): + self.s = other + self.s + return self + else: + return NotImplemented + + def __str__(self): + try: + return self.s % tuple([sqlify(x) for x in self.v]) + except (ValueError, TypeError): + return self.s + + def __repr__(self): + return '' % repr(str(self)) + +def sqlquote(a): + """ + Ensures `a` is quoted properly for use in a SQL query. + + >>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3) + + """ + return SQLQuery(aparam(), (a,)) + +class UnknownDB(Exception): + """raised for unsupported dbms""" + pass + +def connect(dbn, **keywords): + """ + Connects to the specified database. + + `dbn` currently must be "postgres", "mysql", or "sqlite". + + If DBUtils is installed, connection pooling will be used. + """ + if dbn == "postgres": + try: + import psycopg2 as db + except ImportError: + try: + import psycopg as db + except ImportError: + import pgdb as db + if 'pw' in keywords: + keywords['password'] = keywords['pw'] + del keywords['pw'] + keywords['database'] = keywords['db'] + del keywords['db'] + + elif dbn == "mysql": + import MySQLdb as db + if 'pw' in keywords: + keywords['passwd'] = keywords['pw'] + del keywords['pw'] + db.paramstyle = 'pyformat' # it's both, like psycopg + + elif dbn == "sqlite": + try: + import sqlite3 as db + db.paramstyle = 'qmark' + except ImportError: + try: + from pysqlite2 import dbapi2 as db + db.paramstyle = 'qmark' + except ImportError: + import sqlite as db + web.config._hasPooling = False + keywords['database'] = keywords['db'] + del keywords['db'] + + elif dbn == "firebird": + import kinterbasdb as db + if 'pw' in keywords: + keywords['passwd'] = keywords['pw'] + del keywords['pw'] + keywords['database'] = keywords['db'] + del keywords['db'] + + else: + raise UnknownDB, dbn + + web.ctx.db_name = dbn + web.ctx.db_module = db + web.ctx.db_transaction = False + web.ctx.db = keywords + + def db_cursor(): + if isinstance(web.ctx.db, dict): + keywords = web.ctx.db + if web.config._hasPooling: + if 'db' not in globals(): + globals()['db'] = PooledDB(dbapi=db, **keywords) + web.ctx.db = globals()['db'].connection() + else: + web.ctx.db = db.connect(**keywords) + return web.ctx.db.cursor() + web.ctx.db_cursor = db_cursor + + web.ctx.dbq_count = 0 + + def db_execute(cur, sql_query): + """executes an sql query""" + + web.ctx.dbq_count += 1 + + try: + a = time.time() + out = cur.execute(sql_query.s, sql_query.v) + b = time.time() + except: + if web.config.get('db_printing'): + print >> web.debug, 'ERR:', str(sql_query) + rollback() + raise + + if web.config.get('db_printing'): + print >> web.debug, '%s (%s): %s' % (round(b-a, 2), web.ctx.dbq_count, str(sql_query)) + + return out + web.ctx.db_execute = db_execute + return web.ctx.db + +def transact(): + """Start a transaction.""" + # commit everything up to now, so we don't rollback it later + if hasattr(web.ctx.db, 'commit'): web.ctx.db.commit() + web.ctx.db_transaction = True + +def commit(): + """Commits a transaction.""" + if hasattr(web.ctx.db, 'commit'): web.ctx.db.commit() + web.ctx.db_transaction = False + +def rollback(): + """Rolls back a transaction.""" + if hasattr(web.ctx.db, 'rollback'): web.ctx.db.rollback() + web.ctx.db_transaction = False + +def query(sql_query, vars=None, processed=False, _test=False): + """ + Execute SQL query `sql_query` using dictionary `vars` to interpolate it. + If `processed=True`, `vars` is a `reparam`-style list to use + instead of interpolating. + + >>> query("SELECT * FROM foo", _test=True) + + >>> query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True) + + >>> query("SELECT * FROM foo WHERE x = " + sqlquote('f'), _test=True) + + """ + if vars is None: vars = {} + + if not processed and not isinstance(sql_query, SQLQuery): + sql_query = reparam(sql_query, vars) + + if _test: return sql_query + + db_cursor = web.ctx.db_cursor() + web.ctx.db_execute(db_cursor, sql_query) + + if db_cursor.description: + names = [x[0] for x in db_cursor.description] + def iterwrapper(): + row = db_cursor.fetchone() + while row: + yield storage(dict(zip(names, row))) + row = db_cursor.fetchone() + out = iterbetter(iterwrapper()) + if web.ctx.db_name != "sqlite": + out.__len__ = lambda: int(db_cursor.rowcount) + out.list = lambda: [storage(dict(zip(names, x))) \ + for x in db_cursor.fetchall()] + else: + out = db_cursor.rowcount + + if not web.ctx.db_transaction: web.ctx.db.commit() + return out + +def sqllist(lst): + """ + Converts the arguments for use in something like a WHERE clause. + + >>> sqllist(['a', 'b']) + 'a, b' + >>> sqllist('a') + 'a' + + """ + if isinstance(lst, str): + return lst + else: + return ', '.join(lst) + +def sqlors(left, lst): + """ + `left is a SQL clause like `tablename.arg = ` + and `lst` is a list of values. Returns a reparam-style + pair featuring the SQL that ORs together the clause + for each item in the lst. + + >>> sqlors('foo = ', []) + + >>> sqlors('foo = ', [1]) + + >>> sqlors('foo = ', 1) + + >>> sqlors('foo = ', [1,2,3]) + + """ + if isinstance(lst, iters): + lst = list(lst) + ln = len(lst) + if ln == 0: + return SQLQuery("2+2=5", []) + if ln == 1: + lst = lst[0] + + if isinstance(lst, iters): + return SQLQuery('(' + left + + (' OR ' + left).join([aparam() for param in lst]) + ")", lst) + else: + return SQLQuery(left + aparam(), [lst]) + +def sqlwhere(dictionary, grouping=' AND '): + """ + Converts a `dictionary` to an SQL WHERE clause `SQLQuery`. + + >>> sqlwhere({'cust_id': 2, 'order_id':3}) + + >>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ') + + """ + + return SQLQuery(grouping.join([ + '%s = %s' % (k, aparam()) for k in dictionary.keys() + ]), dictionary.values()) + +def select(tables, vars=None, what='*', where=None, order=None, group=None, + limit=None, offset=None, _test=False): + """ + Selects `what` from `tables` with clauses `where`, `order`, + `group`, `limit`, and `offset`. Uses vars to interpolate. + Otherwise, each clause can be a SQLQuery. + + >>> select('foo', _test=True) + + >>> select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True) + + """ + if vars is None: vars = {} + qout = "" + + def gen_clause(sql, value): + if isinstance(val, (int, long)): + if sql == 'WHERE': + nout = 'id = ' + sqlquote(val) + else: + nout = SQLQuery(val) + elif isinstance(val, (list, tuple)) and len(val) == 2: + nout = SQLQuery(val[0], val[1]) # backwards-compatibility + elif isinstance(val, SQLQuery): + nout = val + elif val: + nout = reparam(val, vars) + else: + return "" + + out = "" + if qout: out += " " + out += sql + " " + nout + return out + + if web.ctx.get('db_name') == "firebird": + for (sql, val) in ( + ('FIRST', limit), + ('SKIP', offset) + ): + qout += gen_clause(sql, val) + if qout: + SELECT = 'SELECT ' + qout + else: + SELECT = 'SELECT' + qout = "" + sql_clauses = ( + (SELECT, what), + ('FROM', sqllist(tables)), + ('WHERE', where), + ('GROUP BY', group), + ('ORDER BY', order) + ) + else: + sql_clauses = ( + ('SELECT', what), + ('FROM', sqllist(tables)), + ('WHERE', where), + ('GROUP BY', group), + ('ORDER BY', order), + ('LIMIT', limit), + ('OFFSET', offset) + ) + + for (sql, val) in sql_clauses: + qout += gen_clause(sql, val) + + if _test: return qout + return query(qout, processed=True) + +def insert(tablename, seqname=None, _test=False, **values): + """ + Inserts `values` into `tablename`. Returns current sequence ID. + Set `seqname` to the ID if it's not the default, or to `False` + if there isn't one. + + >>> insert('foo', joe='bob', a=2, _test=True) + + """ + + if values: + sql_query = SQLQuery("INSERT INTO %s (%s) VALUES (%s)" % ( + tablename, + ", ".join(values.keys()), + ', '.join([aparam() for x in values]) + ), values.values()) + else: + sql_query = SQLQuery("INSERT INTO %s DEFAULT VALUES" % tablename) + + if _test: return sql_query + + db_cursor = web.ctx.db_cursor() + if seqname is False: + pass + elif web.ctx.db_name == "postgres": + if seqname is None: + seqname = tablename + "_id_seq" + sql_query += "; SELECT currval('%s')" % seqname + elif web.ctx.db_name == "mysql": + web.ctx.db_execute(db_cursor, sql_query) + sql_query = SQLQuery("SELECT last_insert_id()") + elif web.ctx.db_name == "sqlite": + web.ctx.db_execute(db_cursor, sql_query) + # not really the same... + sql_query = SQLQuery("SELECT last_insert_rowid()") + + web.ctx.db_execute(db_cursor, sql_query) + try: + out = db_cursor.fetchone()[0] + except Exception: + out = None + + if not web.ctx.db_transaction: web.ctx.db.commit() + + return out + +def update(tables, where, vars=None, _test=False, **values): + """ + Update `tables` with clause `where` (interpolated using `vars`) + and setting `values`. + + >>> joe = 'Joseph' + >>> update('foo', where='name = $joe', name='bob', age=5, + ... vars=locals(), _test=True) + + """ + if vars is None: vars = {} + + if isinstance(where, (int, long)): + where = "id = " + sqlquote(where) + elif isinstance(where, (list, tuple)) and len(where) == 2: + where = SQLQuery(where[0], where[1]) + elif isinstance(where, SQLQuery): + pass + else: + where = reparam(where, vars) + + query = ( + "UPDATE " + sqllist(tables) + + " SET " + sqlwhere(values, ', ') + + " WHERE " + where) + + if _test: return query + + db_cursor = web.ctx.db_cursor() + web.ctx.db_execute(db_cursor, query) + + if not web.ctx.db_transaction: web.ctx.db.commit() + return db_cursor.rowcount + +def delete(table, where, using=None, vars=None, _test=False): + """ + Deletes from `table` with clauses `where` and `using`. + + >>> name = 'Joe' + >>> delete('foo', where='name = $name', vars=locals(), _test=True) + + """ + if vars is None: vars = {} + + if isinstance(where, (int, long)): + where = "id = " + sqlquote(where) + elif isinstance(where, (list, tuple)) and len(where) == 2: + where = SQLQuery(where[0], where[1]) + elif isinstance(where, SQLQuery): + pass + else: + where = reparam(where, vars) + + q = 'DELETE FROM ' + table + ' WHERE ' + where + if using and web.ctx.get('db_name') != "firebird": + q += ' USING ' + sqllist(using) + + if _test: return q + + db_cursor = web.ctx.db_cursor() + web.ctx.db_execute(db_cursor, q) + + if not web.ctx.db_transaction: web.ctx.db.commit() + return db_cursor.rowcount + +if __name__ == "__main__": + import doctest + doctest.testmod() ============================================================ --- www/viewmtn/web/debugerror.py dbd19a03c29519c7f45301222c73fc3b36053362 +++ www/viewmtn/web/debugerror.py dbd19a03c29519c7f45301222c73fc3b36053362 @@ -0,0 +1,316 @@ +""" +pretty debug errors +(part of web.py) + +adapted from Django +Copyright (c) 2005, the Lawrence Journal-World +Used under the modified BSD license: +http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 +""" + +__all__ = ["debugerror", "djangoerror"] + +import sys, urlparse, pprint +from net import websafe +from template import Template +import webapi as web + +import os, os.path +whereami = os.path.join(os.getcwd(), __file__) +whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1]) +djangoerror_t = """\ +$def with (exception_type, exception_value, frames) + + + + + + $exception_type at $ctx.path + + + + + +
+

$exception_type at $ctx.path

+

$exception_value

+ + + + + + +
Python$frames[0].filename in $frames[0].function, line $frames[0].lineno
Web$ctx.method $ctx.home$ctx.path
+
+
+

Traceback (innermost first)

+
    +$for frame in frames: +
  • + $frame.filename in $frame.function + $if frame.context_line: +
    + $if frame.pre_context: +
      + $for line in frame.pre_context: +
    1. $line
    2. +
    +
    1. $frame.context_line ...
    + $if frame.post_context: +
      + $for line in frame.post_context: +
    1. $line
    2. +
    +
    + + $if frame.vars: +
    + Local vars + $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame)) +
    + $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id))) +
  • +
+
+ +
+$if ctx.output or ctx.headers: +

Response so far

+

HEADERS

+

+ $for kv in ctx.headers: + $kv[0]: $kv[1]
+ $else: + [no headers] +

+ +

BODY

+

+ $ctx.output +

+ +

Request information

+ +

INPUT

+$:dicttable(web.input()) + + +$:dicttable(web.cookies()) + +

META

+$ newctx = [] +$# ) and (k not in ['env', 'output', 'headers', 'environ', 'status', 'db_execute']): +$for k, v in ctx.iteritems(): + $if not k.startswith('_') and (k in x): + $newctx.append(kv) +$:dicttable(dict(newctx)) + +

ENVIRONMENT

+$:dicttable(ctx.env) +
+ +
+

+ You're seeing this error because you have web.internalerror + set to web.debugerror. Change that if you want a different one. +

+
+ + + +""" + +dicttable_t = r"""$def with (d, kls='req', id=None) +$if d: + + + $ temp = d.items() + $temp.sort() + $for kv in temp: + + +
VariableValue
$kv[0]
$prettify(kv[1])
+$else: +

No data.

+""" + +dicttable_r = Template(dicttable_t, filter=websafe) +djangoerror_r = Template(djangoerror_t, filter=websafe) + +def djangoerror(): + def _get_lines_from_file(filename, lineno, context_lines): + """ + Returns context_lines before and after lineno from file. + Returns (pre_context_lineno, pre_context, context_line, post_context). + """ + try: + source = open(filename).readlines() + lower_bound = max(0, lineno - context_lines) + upper_bound = lineno + context_lines + + pre_context = \ + [line.strip('\n') for line in source[lower_bound:lineno]] + context_line = source[lineno].strip('\n') + post_context = \ + [line.strip('\n') for line in source[lineno + 1:upper_bound]] + + return lower_bound, pre_context, context_line, post_context + except (OSError, IOError): + return None, [], None, [] + + exception_type, exception_value, tback = sys.exc_info() + frames = [] + while tback is not None: + filename = tback.tb_frame.f_code.co_filename + function = tback.tb_frame.f_code.co_name + lineno = tback.tb_lineno - 1 + pre_context_lineno, pre_context, context_line, post_context = \ + _get_lines_from_file(filename, lineno, 7) + frames.append(web.storage({ + 'tback': tback, + 'filename': filename, + 'function': function, + 'lineno': lineno, + 'vars': tback.tb_frame.f_locals, + 'id': id(tback), + 'pre_context': pre_context, + 'context_line': context_line, + 'post_context': post_context, + 'pre_context_lineno': pre_context_lineno, + })) + tback = tback.tb_next + frames.reverse() + urljoin = urlparse.urljoin + def prettify(x): + try: + out = pprint.pformat(x) + except Exception, e: + out = '[could not display: <' + e.__class__.__name__ + \ + ': '+str(e)+'>]' + return out + dt = dicttable_r + dt.globals = {'prettify': prettify} + t = djangoerror_r + t.globals = {'ctx': web.ctx, 'web':web, 'dicttable':dt, 'dict':dict, 'str':str} + return t(exception_type, exception_value, frames) + +def debugerror(): + """ + A replacement for `internalerror` that presents a nice page with lots + of debug information for the programmer. + + (Based on the beautiful 500 page from [Django](http://djangoproject.com/), + designed by [Wilson Miner](http://wilsonminer.com/).) + """ + + web.ctx.headers = [('Content-Type', 'text/html')] + web.ctx.output = djangoerror() + +if __name__ == "__main__": + urls = ( + '/', 'index' + ) + + class index: + def GET(self): + thisdoesnotexist + + web.internalerror = web.debugerror + web.run(urls) ============================================================ --- www/viewmtn/web/form.py 5abbec430ef66a48c76249afe4d41ec5136170a8 +++ www/viewmtn/web/form.py 5abbec430ef66a48c76249afe4d41ec5136170a8 @@ -0,0 +1,183 @@ +""" +HTML forms +(part of web.py) +""" + +import copy, re +import webapi as web +import utils, net + +def attrget(obj, attr, value=None): + if hasattr(obj, 'has_key') and obj.has_key(attr): return obj[attr] + if hasattr(obj, attr): return getattr(obj, attr) + return value + +class Form: + def __init__(self, *inputs): + self.inputs = inputs + self.valid = True + self.note = None + + def __call__(self, x=None): + o = copy.deepcopy(self) + if x: o.validates(x) + return o + + def render(self): + out = '' + out += self.rendernote(self.note) + out += '\n' + for i in self.inputs: + out += ' ' % (i.name, i.description) + out += "" + out += '\n' % (i.name, self.rendernote(i.note)) + out += "
"+i.pre+i.render()+i.post+"%s
" + return out + + def rendernote(self, note): + if note: return '%s' % note + else: return "" + + def validates(self, source=None, _validate=True, **kw): + source = source or kw or web.input() + out = True + for i in self.inputs: + v = attrget(source, i.name) + if _validate: + out = i.validate(v) and out + else: + i.value = v + if _validate: + self.valid = out + return out + + def fill(self, source=None, **kw): + return self.validates(source, _validate=False, **kw) + + def __getitem__(self, i): + for x in self.inputs: + if x.name == i: return x + raise KeyError, i + + def _get_d(self): #@@ should really be form.attr, no? + return utils.storage([(i.name, i.value) for i in self.inputs]) + d = property(_get_d) + +class Input(object): + def __init__(self, name, *validators, **attrs): + self.description = attrs.pop('description', name) + self.value = attrs.pop('value', None) + self.pre = attrs.pop('pre', "") + self.post = attrs.pop('post', "") + if 'class_' in attrs: + attrs['class'] = attrs['class_'] + del attrs['class_'] + self.name, self.validators, self.attrs, self.note = name, validators, attrs, None + + def validate(self, value): + self.value = value + for v in self.validators: + if not v.valid(value): + self.note = v.msg + return False + return True + + def render(self): raise NotImplementedError + + def addatts(self): + str = "" + for (n, v) in self.attrs.items(): + str += ' %s="%s"' % (n, net.websafe(v)) + return str + +#@@ quoting + +class Textbox(Input): + def render(self): + x = '%s\n" % net.websafe(arg) + x += '\n' + return x + +class Radio(Input): + def __init__(self, name, args, *validators, **attrs): + self.args = args + super(Radio, self).__init__(name, *validators, **attrs) + + def render(self): + x = '' + for arg in self.args: + if self.value == arg: select_p = ' checked="checked"' + else: select_p = '' + x += ' %s ' % (net.websafe(self.name), net.websafe(arg), select_p, self.addatts(), net.websafe(arg)) + return x+'' + +class Checkbox(Input): + def render(self): + x = 'moved permanently') + +def found(url): + """A `302 Found` redirect.""" + return redirect(url, '302 Found') + +def seeother(url): + """A `303 See Other` redirect.""" + return redirect(url, '303 See Other') + +def tempredirect(url): + """A `307 Temporary Redirect` redirect.""" + return redirect(url, '307 Temporary Redirect') + +def write(cgi_response): + """ + Converts a standard CGI-style string response into `header` and + `output` calls. + """ + cgi_response = str(cgi_response) + cgi_response.replace('\r\n', '\n') + head, body = cgi_response.split('\n\n', 1) + lines = head.split('\n') + + for line in lines: + if line.isspace(): + continue + hdr, value = line.split(":", 1) + value = value.strip() + if hdr.lower() == "status": + web.ctx.status = value + else: + web.header(hdr, value) + + web.output(body) + +def changequery(**kw): + """ + Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return + `/foo?a=3&b=2` -- the same URL but with the arguments you requested + changed. + """ + query = web.input(_method='get') + for k, v in kw.iteritems(): + if v is None: + query.pop(k, None) + else: + query[k] = v + out = web.ctx.homepath + web.ctx.path + if query: + out += '?' + urllib.urlencode(query) + return out + +def background(func): + """A function decorator to run a long-running function as a background thread.""" + def internal(*a, **kw): + web.data() # cache it + + tmpctx = web._context[threading.currentThread()] + web._context[threading.currentThread()] = utils.storage(web.ctx.copy()) + + def newfunc(): + web._context[threading.currentThread()] = tmpctx + func(*a, **kw) + myctx = web._context[threading.currentThread()] + for k in myctx.keys(): + if k not in ['status', 'headers', 'output']: + try: del myctx[k] + except KeyError: pass + + t = threading.Thread(target=newfunc) + background.threaddb[id(t)] = t + t.start() + web.ctx.headers = [] + return seeother(changequery(_t=id(t))) + return internal +background.threaddb = {} + +def backgrounder(func): + def internal(*a, **kw): + i = web.input(_method='get') + if '_t' in i: + try: + t = background.threaddb[int(i._t)] + except KeyError: + return web.notfound() + web._context[threading.currentThread()] = web._context[t] + return + else: + return func(*a, **kw) + return internal + +class Reloader: + """ + Before every request, checks to see if any loaded modules have changed on + disk and, if so, reloads them. + """ + def __init__(self, func): + self.func = func + self.mtimes = {} + # cheetah: + # b = _compiletemplate.bases + # _compiletemplate = globals()['__compiletemplate'] + # _compiletemplate.bases = b + + web.loadhooks['reloader'] = self.check + # todo: + # - replace relrcheck with a loadhook + #if reloader in middleware: + # relr = reloader(None) + # relrcheck = relr.check + # middleware.remove(reloader) + #else: + # relr = None + # relrcheck = lambda: None + # if relr: + # relr.func = wsgifunc + # return wsgifunc + # + + + def check(self): + for mod in sys.modules.values(): + try: + mtime = os.stat(mod.__file__).st_mtime + except (AttributeError, OSError, IOError): + continue + if mod.__file__.endswith('.pyc') and \ + os.path.exists(mod.__file__[:-1]): + mtime = max(os.stat(mod.__file__[:-1]).st_mtime, mtime) + if mod not in self.mtimes: + self.mtimes[mod] = mtime + elif self.mtimes[mod] < mtime: + try: + reload(mod) + except ImportError: + pass + return True + + def __call__(self, e, o): + self.check() + return self.func(e, o) + +reloader = Reloader + +def profiler(app): + """Outputs basic profiling information at the bottom of each response.""" + from utils import profile + def profile_internal(e, o): + out, result = profile(app)(e, o) + return out + ['
' + net.websafe(result) + '
'] + return profile_internal + +if __name__ == "__main__": + import doctest + doctest.testmod() ============================================================ --- www/viewmtn/web/httpserver.py fc34d04446669325863e2151dd9d34c037fa4518 +++ www/viewmtn/web/httpserver.py fc34d04446669325863e2151dd9d34c037fa4518 @@ -0,0 +1,125 @@ +__all__ = ["runsimple"] + +import sys +import webapi as web + +def runsimple(func, server_address=("0.0.0.0", 8080)): + """ + Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` + is hosted statically. + + Based on [WsgiServer][ws] from [Colin Stewart][cs]. + + [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html + [cs]: http://www.owlfish.com/ + """ + # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/) + # Modified somewhat for simplicity + # Used under the modified BSD license: + # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 + + import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse + import socket, errno + import traceback + + class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def run_wsgi_app(self): + protocol, host, path, parameters, query, fragment = \ + urlparse.urlparse('http://dummyhost%s' % self.path) + # we only use path, query + env = {'wsgi.version': (1, 0) + ,'wsgi.url_scheme': 'http' + ,'wsgi.input': self.rfile + ,'wsgi.errors': sys.stderr + ,'wsgi.multithread': 1 + ,'wsgi.multiprocess': 0 + ,'wsgi.run_once': 0 + ,'REQUEST_METHOD': self.command + ,'REQUEST_URI': self.path + ,'PATH_INFO': path + ,'QUERY_STRING': query + ,'CONTENT_TYPE': self.headers.get('Content-Type', '') + ,'CONTENT_LENGTH': self.headers.get('Content-Length', '') + ,'REMOTE_ADDR': self.client_address[0] + ,'SERVER_NAME': self.server.server_address[0] + ,'SERVER_PORT': str(self.server.server_address[1]) + ,'SERVER_PROTOCOL': self.request_version + } + + for http_header, http_value in self.headers.items(): + env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \ + http_value + + # Setup the state + self.wsgi_sent_headers = 0 + self.wsgi_headers = [] + + try: + # We have there environment, now invoke the application + result = self.server.app(env, self.wsgi_start_response) + try: + try: + for data in result: + if data: + self.wsgi_write_data(data) + finally: + if hasattr(result, 'close'): + result.close() + except socket.error, socket_err: + # Catch common network errors and suppress them + if (socket_err.args[0] in \ + (errno.ECONNABORTED, errno.EPIPE)): + return + except socket.timeout, socket_timeout: + return + except: + print >> web.debug, traceback.format_exc(), + + if (not self.wsgi_sent_headers): + # We must write out something! + self.wsgi_write_data(" ") + return + + do_POST = run_wsgi_app + do_PUT = run_wsgi_app + do_DELETE = run_wsgi_app + + def do_GET(self): + if self.path.startswith('/static/'): + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + else: + self.run_wsgi_app() + + def wsgi_start_response(self, response_status, response_headers, + exc_info=None): + if (self.wsgi_sent_headers): + raise Exception \ + ("Headers already sent and start_response called again!") + # Should really take a copy to avoid changes in the application.... + self.wsgi_headers = (response_status, response_headers) + return self.wsgi_write_data + + def wsgi_write_data(self, data): + if (not self.wsgi_sent_headers): + status, headers = self.wsgi_headers + # Need to send header prior to data + status_code = status[:status.find(' ')] + status_msg = status[status.find(' ') + 1:] + self.send_response(int(status_code), status_msg) + for header, value in headers: + self.send_header(header, value) + self.end_headers() + self.wsgi_sent_headers = 1 + # Send the data + self.wfile.write(data) + + class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): + def __init__(self, func, server_address): + BaseHTTPServer.HTTPServer.__init__(self, + server_address, + WSGIHandler) + self.app = func + self.serverShuttingDown = 0 + + print "http://%s:%d/" % server_address + WSGIServer(func, server_address).serve_forever() ============================================================ --- www/viewmtn/web/net.py 82d2ec267f761eb1b45417faf08ffcbf3ddf304d +++ www/viewmtn/web/net.py 82d2ec267f761eb1b45417faf08ffcbf3ddf304d @@ -0,0 +1,150 @@ +""" +Network Utilities +(from web.py) +""" + +__all__ = [ + "validipaddr", "validipport", "validip", "validaddr", + "urlquote", + "httpdate", "parsehttpdate", + "htmlquote", "websafe", +] + +import urllib, time +try: import datetime +except ImportError: pass + +def validipaddr(address): + """returns True if `address` is a valid IPv4 address""" + try: + octets = address.split('.') + assert len(octets) == 4 + for x in octets: + assert 0 <= int(x) <= 255 + except (AssertionError, ValueError): + return False + return True + +def validipport(port): + """returns True if `port` is a valid IPv4 port""" + try: + assert 0 <= int(port) <= 65535 + except (AssertionError, ValueError): + return False + return True + +def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): + """returns `(ip_address, port)` from string `ip_addr_port`""" + addr = defaultaddr + port = defaultport + + ip = ip.split(":", 1) + if len(ip) == 1: + if not ip[0]: + pass + elif validipaddr(ip[0]): + addr = ip[0] + elif validipport(ip[0]): + port = int(ip[0]) + else: + raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' + elif len(ip) == 2: + addr, port = ip + if not validipaddr(addr) and validipport(port): + raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' + port = int(port) + else: + raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' + return (addr, port) + +def validaddr(string_): + """ + returns either (ip_address, port) or "/path/to/socket" from string_ + + >>> validaddr('/path/to/socket') + '/path/to/socket' + >>> validaddr('8000') + ('0.0.0.0', 8000) + >>> validaddr('127.0.0.1') + ('127.0.0.1', 8080) + >>> validaddr('127.0.0.1:8000') + ('127.0.0.1', 8000) + >>> validaddr('fff') + Traceback (most recent call last): + ... + ValueError: fff is not a valid IP address/port + """ + if '/' in string_: + return string_ + else: + return validip(string_) + +def urlquote(val): + """ + Quotes a string for use in a URL. + + >>> urlquote('://?f=1&j=1') + '%3A//%3Ff%3D1%26j%3D1' + >>> urlquote(None) + '' + >>> urlquote(u'\u203d') + '%E2%80%BD' + """ + if val is None: return '' + if not isinstance(val, unicode): val = str(val) + else: val = val.encode('utf-8') + return urllib.quote(val) + +def httpdate(date_obj): + """ + Formats a datetime object for use in HTTP headers. + + >>> import datetime + >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1)) + 'Thu, 01 Jan 1970 01:01:01 GMT' + """ + return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT") + +def parsehttpdate(string_): + """ + Parses an HTTP date into a datetime object. + + >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT') + datetime.datetime(1970, 1, 1, 1, 1, 1) + """ + try: + t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z") + except ValueError: + return None + return datetime.datetime(*t[:6]) + +def htmlquote(text): + """ + Encodes `text` for raw use in HTML. + + >>> htmlquote("<'&\\">") + '<'&">' + """ + text = text.replace("&", "&") # Must be done first! + text = text.replace("<", "<") + text = text.replace(">", ">") + text = text.replace("'", "'") + text = text.replace('"', """) + return text + +def websafe(val): + """ + Converts `val` so that it's safe for use in HTML. + + >>> websafe("<'&\\">") + '<'&">' + >>> websafe(None) + '' + """ + if val is None: return '' + if not isinstance(val, unicode): val = str(val) + return htmlquote(val) + +if __name__ == "__main__": + import doctest + doctest.testmod() ============================================================ --- www/viewmtn/web/request.py ef22e4a2d55339f64fda752bd6084bf39576c29d +++ www/viewmtn/web/request.py ef22e4a2d55339f64fda752bd6084bf39576c29d @@ -0,0 +1,147 @@ +""" +Request Delegation +(from web.py) +""" + +__all__ = ["handle", "nomethod", "autodelegate", "webpyfunc", "run"] + +import sys, re, types, os.path, urllib + +import http, wsgi, utils, webapi +import webapi as web + +def handle(mapping, fvars=None): + """ + Call the appropriate function based on the url to function mapping in `mapping`. + If no module for the function is specified, look up the function in `fvars`. If + `fvars` is empty, using the caller's context. + + `mapping` should be a tuple of paired regular expressions with function name + substitutions. `handle` will import modules as necessary. + """ + for url, ofno in utils.group(mapping, 2): + if isinstance(ofno, tuple): + ofn, fna = ofno[0], list(ofno[1:]) + else: + ofn, fna = ofno, [] + fn, result = utils.re_subm('^' + url + '$', ofn, web.ctx.path) + if result: # it's a match + if fn.split(' ', 1)[0] == "redirect": + url = fn.split(' ', 1)[1] + if web.ctx.method == "GET": + x = web.ctx.env.get('QUERY_STRING', '') + if x: + url += '?' + x + return http.redirect(url) + elif '.' in fn: + x = fn.split('.') + mod, cls = '.'.join(x[:-1]), x[-1] + mod = __import__(mod, globals(), locals(), [""]) + cls = getattr(mod, cls) + else: + cls = fn + mod = fvars + if isinstance(mod, types.ModuleType): + mod = vars(mod) + try: + cls = mod[cls] + except KeyError: + return web.notfound() + + meth = web.ctx.method + if meth == "HEAD": + if not hasattr(cls, meth): + meth = "GET" + if not hasattr(cls, meth): + return nomethod(cls) + tocall = getattr(cls(), meth) + args = list(result.groups()) + for d in re.findall(r'\\(\d+)', ofn): + args.pop(int(d) - 1) + return tocall(*([urllib.unquote(x) for x in args] + fna)) + + return web.notfound() + +def nomethod(cls): + """Returns a `405 Method Not Allowed` error for `cls`.""" + web.ctx.status = '405 Method Not Allowed' + web.header('Content-Type', 'text/html') + web.header('Allow', \ + ', '.join([method for method in \ + ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] \ + if hasattr(cls, method)])) + + # commented out for the same reason redirect is + # return output('method not allowed') + +def autodelegate(prefix=''): + """ + Returns a method that takes one argument and calls the method named prefix+arg, + calling `notfound()` if there isn't one. Example: + + urls = ('/prefs/(.*)', 'prefs') + + class prefs: + GET = autodelegate('GET_') + def GET_password(self): pass + def GET_privacy(self): pass + + `GET_password` would get called for `/prefs/password` while `GET_privacy` for + `GET_privacy` gets called for `/prefs/privacy`. + + If a user visits `/prefs/password/change` then `GET_password(self, '/change')` + is called. + """ + def internal(self, arg): + if '/' in arg: + first, rest = arg.split('/', 1) + func = prefix + first + args = ['/' + rest] + else: + func = prefix + arg + args = [] + + if hasattr(self, func): + try: + return getattr(self, func)(*args) + except TypeError: + return web.notfound() + else: + return web.notfound() + return internal + +def webpyfunc(inp, fvars, autoreload=False): + """If `inp` is a url mapping, returns a function that calls handle.""" + if not hasattr(inp, '__call__'): + if autoreload: + # black magic to make autoreload work: + mod = \ + __import__( + fvars['__file__'].split(os.path.sep).pop().split('.')[0]) + #@@probably should replace this with some inspect magic + name = utils.dictfind(fvars, inp) + func = lambda: handle(getattr(mod, name), mod) + else: + func = lambda: handle(inp, fvars) + else: + func = inp + return func + +def run(inp, fvars, *middleware): + """ + Starts handling requests. If called in a CGI or FastCGI context, it will follow + that protocol. If called from the command line, it will start an HTTP + server on the port named in the first command line argument, or, if there + is no argument, on port 8080. + + `input` is a callable, then it's called with no arguments. + Otherwise, it's a `mapping` object to be passed to `handle(...)`. + + **Caveat:** So that `reloader` will work correctly, input has to be a variable, + it can't be a tuple passed in directly. + + `middleware` is a list of WSGI middleware which is applied to the resulting WSGI + function. + """ + autoreload = http.reloader in middleware + return wsgi.runwsgi(webapi.wsgifunc(webpyfunc(inp, fvars, autoreload), *middleware)) ============================================================ --- www/viewmtn/web/template.py a0091de1f9fc61a72f60d89da15f301d931ef36c +++ www/viewmtn/web/template.py a0091de1f9fc61a72f60d89da15f301d931ef36c @@ -0,0 +1,838 @@ +""" +simple, elegant templating +(part of web.py) +""" + +import re, glob, os, os.path +from types import FunctionType as function +from utils import storage, group +from net import websafe + +# differences from python: +# - for: has an optional else: that gets called if the loop never runs +# differences to add: +# - you can use the expression inside if, while blocks +# - special for loop attributes, like django? +# - you can check to see if a variable is defined (perhaps w/ get func?) +# all these are probably good ideas for python... + +# todo: +# inline tuple +# relax constraints on spacing +# continue, break, etc. +# tracebacks + +global_globals = {'None':None, 'False':False, 'True': True} +MAX_ITERS = 100000 + +WHAT = 0 +ARGS = 4 +KWARGS = 6 +NAME = 2 +BODY = 4 +CLAUSE = 2 +ELIF = 6 +ELSE = 8 +IN = 6 +NAME = 2 +EXPR = 4 +FILTER = 4 +THING = 2 +ATTR = 4 +ITEM = 4 +NEGATE = 4 +X = 2 +OP = 4 +Y = 6 +LINENO = -1 + +# http://docs.python.org/ref/identifiers.html +r_var = '[a-zA-Z_][a-zA-Z0-9_]*' + +class ParseError(Exception): pass +class Parser: + def __init__(self, text): + self.t = text + self.p = 0 + self._lock = [False] + + def lock(self): + self._lock[-1] = True + + def curline(self): + return self.t[:self.p].count('\n')+1 + + def csome(self): + return repr(self.t[self.p:self.p+5]+'...') + + def Error(self, x, y=None): + if y is None: y = self.csome() + raise ParseError, "expected %s, got %s (line %s)" % (x, y, self.curline()) + + def q(self, f): + def internal(*a, **kw): + checkp = self.p + self._lock.append(False) + try: + q = f(*a, **kw) + except ParseError: + if self._lock[-1]: + raise + self.p = checkp + self._lock.pop() + return False + self._lock.pop() + return q or True + return internal + + def tokr(self, t): + text = self.c(len(t)) + if text != t: + self.Error(repr(t), repr(text)) + return t + + def ltokr(self, *l): + for x in l: + o = self.tokq(x) + if o: return o + self.Error('one of '+repr(l)) + + def rer(self, r): + x = re.match(r, self.t[self.p:]) #@@re_compile + if not x: + self.Error('r'+repr(r)) + return self.tokr(x.group()) + + def endr(self): + if self.p != len(self.t): + self.Error('EOF') + + def c(self, n=1): + out = self.t[self.p:self.p+n] + if out == '' and n != 0: + self.Error('character', 'EOF') + self.p += n + return out + + def lookbehind(self, t): + return self.t[self.p-len(t):self.p] == t + + def __getattr__(self, a): + if a.endswith('q'): + return self.q(getattr(self, a[:-1]+'r')) + raise AttributeError, a + +class TemplateParser(Parser): + def __init__(self, *a, **kw): + Parser.__init__(self, *a, **kw) + self.curws = '' + self.curind = '' + + def o(self, *a): + return a+('lineno', self.curline()) + + def go(self): + # maybe try to do some traceback parsing/hacking + return self.gor() + + def gor(self): + header = self.defwithq() + results = self.lines(start=True) + self.endr() + return header, results + + def ws(self): + n = 0 + while self.tokq(" "): n += 1 + return " " * n + + def defwithr(self): + self.tokr('$def with ') + self.lock() + self.tokr('(') + args = [] + kw = [] + x = self.req(r_var) + while x: + if self.tokq('='): + v = self.exprr() + kw.append((x, v)) + else: + args.append(x) + x = self.tokq(', ') and self.req(r_var) + self.tokr(')\n') + return self.o('defwith', 'null', None, 'args', args, 'kwargs', kw) + + def literalr(self): + o = ( + self.req('"[^"]*"') or #@@ no support for escapes + self.req("'[^']*'") + ) + if o is False: + o = self.req('\-?[0-9]+(\.[0-9]*)?') + if o is not False: + if '.' in o: o = float(o) + else: o = int(o) + + if o is False: self.Error('literal') + return self.o('literal', 'thing', o) + + def listr(self): + self.tokr('[') + self.lock() + x = [] + if not self.tokq(']'): + while True: + t = self.exprr() + x.append(t) + if not self.tokq(', '): break + self.tokr(']') + return self.o('list', 'thing', x) + + def dictr(self): + self.tokr('{') + self.lock() + x = {} + if not self.tokq('}'): + while True: + k = self.exprr() + self.tokr(': ') + v = self.exprr() + x[k] = v + if not self.tokq(', '): break + self.tokr('}') + return self.o('dict', 'thing', x) + + def parenr(self): + self.tokr('(') + self.lock() + o = self.exprr() # todo: allow list + self.tokr(')') + return self.o('paren', 'thing', o) + + def atomr(self): + """returns var, literal, paren, dict, or list""" + o = ( + self.varq() or + self.parenq() or + self.dictq() or + self.listq() or + self.literalq() + ) + if o is False: self.Error('atom') + return o + + def primaryr(self): + """returns getattr, call, or getitem""" + n = self.atomr() + while 1: + if self.tokq('.'): + v = self.req(r_var) + if not v: + self.p -= 1 # get rid of the '.' + break + else: + n = self.o('getattr', 'thing', n, 'attr', v) + elif self.tokq('('): + args = [] + kw = [] + + while 1: + # need to see if we're doing a keyword argument + checkp = self.p + k = self.req(r_var) + if k and self.tokq('='): # yup + v = self.exprr() + kw.append((k, v)) + else: + self.p = checkp + x = self.exprq() + if x: # at least it's something + args.append(x) + else: + break + + if not self.tokq(', '): break + self.tokr(')') + n = self.o('call', 'thing', n, 'args', args, 'kwargs', kw) + elif self.tokq('['): + v = self.exprr() + self.tokr(']') + n = self.o('getitem', 'thing', n, 'item', v) + else: + break + + return n + + def exprr(self): + negate = self.tokq('not ') + x = self.primaryr() + if self.tokq(' '): + operator = self.ltokr('not in', 'in', 'is not', 'is', '==', '!=', '>=', '<=', '<', '>', 'and', 'or', '*', '+', '-', '/', '%') + self.tokr(' ') + y = self.exprr() + x = self.o('test', 'x', x, 'op', operator, 'y', y) + + return self.o('expr', 'thing', x, 'negate', negate) + + def varr(self): + return self.o('var', 'name', self.rer(r_var)) + + def liner(self): + out = [] + o = self.curws + while 1: + c = self.c() + self.lock() + if c == '\n': + self.p -= 1 + break + if c == '$': + if self.lookbehind('\\$'): + o = o[:-1] + c + else: + filter = not bool(self.tokq(':')) + + if self.tokq('{'): + out.append(o) + out.append(self.o('itpl', 'name', self.exprr(), 'filter', filter)) + self.tokr('}') + o = '' + else: + g = self.primaryq() + if g: + out.append(o) + out.append(self.o('itpl', 'name', g, 'filter', filter)) + o = '' + else: + o += c + else: + o += c + self.tokr('\n') + if not self.lookbehind('\\\n'): + o += '\n' + else: + o = o[:-1] + out.append(o) + return self.o('line', 'thing', out) + + def varsetr(self): + self.tokr('$var ') + self.lock() + what = self.rer(r_var) + self.tokr(':') + body = self.lines() + return self.o('varset', 'name', what, 'body', body) + + def ifr(self): + self.tokr("$if ") + self.lock() + expr = self.exprr() + self.tokr(":") + ifc = self.lines() + + elifs = [] + while self.tokq(self.curws + self.curind + '$elif '): + v = self.exprr() + self.tokr(':') + c = self.lines() + elifs.append(self.o('elif', 'clause', v, 'body', c)) + + if self.tokq(self.curws + self.curind + "$else:"): + elsec = self.lines() + else: + elsec = None + + return self.o('if', 'clause', expr, 'then', ifc, 'elif', elifs, 'else', elsec) + + def forr(self): + self.tokr("$for ") + self.lock() + v = self.setabler() + self.tokr(" in ") + g = self.exprr() + self.tokr(":") + l = self.lines() + + if self.tokq(self.curws + self.curind + '$else:'): + elsec = self.lines() + else: + elsec = None + + return self.o('for', 'name', v, 'body', l, 'in', g, 'else', elsec) + + def whiler(self): + self.tokr('$while ') + self.lock() + v = self.exprr() + self.tokr(":") + l = self.lines() + + if self.tokq(self.curws + self.curind + '$else:'): + elsec = self.lines() + else: + elsec = None + + return self.o('while', 'clause', v, 'body', l, 'null', None, 'else', elsec) + + def assignr(self): + self.tokr('$ ') + assign = self.rer(r_var) # NOTE: setable + self.tokr(' = ') + expr = self.exprr() + self.tokr('\n') + + return self.o('assign', 'name', assign, 'expr', expr) + + def commentr(self): + self.tokr('$#') + self.lock() + while self.c() != '\n': pass + return self.o('comment') + + def setabler(self): + out = [self.varr()] #@@ not quite right + while self.tokq(', '): + out.append(self.varr()) + return out + + def lines(self, start=False): + """ + This function gets called from two places: + 1. at the start, where it's matching the document itself + 2. after any command, where it matches one line or an indented block + """ + o = [] + if not start: # try to match just one line + singleline = self.tokq(' ') and self.lineq() + if singleline: + return [singleline] + else: + self.rer(' *') #@@slurp space? + self.tokr('\n') + oldind = self.curind + self.curind += ' ' + while 1: + oldws = self.curws + t = self.tokq(oldws + self.curind) + if not t: break + + self.curws += self.ws() + x = t and ( + self.varsetq() or + self.ifq() or + self.forq() or + self.whileq() or + self.assignq() or + self.commentq() or + self.lineq()) + self.curws = oldws + if not x: + break + elif x[WHAT] == 'comment': + pass + else: + o.append(x) + + if not start: self.curind = oldind + return o + +class Stowage(storage): + def __str__(self): return self.get('_str') + #@@ edits in place + def __add__(self, other): + if isinstance(other, (unicode, str)): + self._str += other + return self + else: + raise TypeError, 'cannot add' + def __radd__(self, other): + if isinstance(other, (unicode, str)): + self._str = other + self._str + return self + else: + raise TypeError, 'cannot add' + +class WTF(AssertionError): pass +class SecurityError(Exception): + """The template seems to be trying to do something naughty.""" + pass + +Required = object() +class Template: + globals = {} + def __init__(self, text, filter=None): + self.filter = filter + # universal newlines: + text = text.replace('\r\n', '\n').replace('\r', '\n') + if not text.endswith('\n'): text += '\n' + header, tree = TemplateParser(text).go() + self.tree = tree + if header: + self.h_defwith(header) + else: + self.args, self.kwargs = (), {} + + def __call__(self, *a, **kw): + d = self.globals.copy() + d.update(self._parseargs(a, kw)) + f = Fill(self.tree, d=d) + if self.filter: f.filter = self.filter + return f.go() + + def _parseargs(self, inargs, inkwargs): + # difference from Python: + # no error on setting a keyword arg twice + d = {} + for arg in self.args: + d[arg] = Required + for kw, val in self.kwargs: + d[kw] = val + + for n, val in enumerate(inargs): + if n < len(self.args): + d[self.args[n]] = val + elif n < len(self.args)+len(self.kwargs): + kw = self.kwargs[n - len(self.args)][0] + d[kw] = val + + for kw, val in inkwargs.iteritems(): + d[kw] = val + + unset = [] + for k, v in d.iteritems(): + if v is Required: + unset.append(k) + if unset: + raise TypeError, 'values for %s are required' % unset + + return d + + def h_defwith(self, header): + assert header[WHAT] == 'defwith' + f = Fill(self.tree, d={}) + + self.args = header[ARGS] + self.kwargs = [] + for var, valexpr in header[KWARGS]: + self.kwargs.append((var, f.h(valexpr))) + +class Handle: + def __init__(self, parsetree, **kw): + self._funccache = {} + self.parsetree = parsetree + for (k, v) in kw.iteritems(): setattr(self, k, v) + + def h(self, item): + return getattr(self, 'h_' + item[WHAT])(item) + +class Fill(Handle): + builtins = global_globals + def filter(self, text): + if text is None: return '' + else: return str(text) + # later: can do stuff like WebSafe + + def h_literal(self, i): + item = i[THING] + if isinstance(item, str) and item[0] in ['"', "'"]: + item = item[1:-1] + elif isinstance(item, (float, int)): + pass + return item + + def h_list(self, i): + x = i[THING] + out = [] + for item in x: + out.append(self.h(item)) + return out + + def h_dict(self, i): + x = i[THING] + out = {} + for k, v in x.iteritems(): + out[self.h(k)] = self.h(v) + return out + + def h_paren(self, i): + item = i[THING] + if isinstance(item, list): + raise NotImplementedError, 'tuples' + return self.h(item) + + def h_getattr(self, i): + thing, attr = i[THING], i[ATTR] + thing = self.h(thing) + if attr.startswith('_') or attr.startswith('func_') or attr.startswith('im_'): + raise SecurityError, 'tried to get ' + attr + try: + if thing in self.builtins: + raise SecurityError, 'tried to getattr on ' + repr(thing) + except TypeError: + pass # raised when testing an unhashable object + try: + return getattr(thing, attr) + except AttributeError: + if isinstance(thing, list) and attr == 'join': + return lambda s: s.join(thing) + else: + raise + + def h_call(self, i): + call = self.h(i[THING]) + args = [self.h(x) for x in i[ARGS]] + kw = dict([(x, self.h(y)) for (x, y) in i[KWARGS]]) + return call(*args, **kw) + + def h_getitem(self, i): + thing, item = i[THING], i[ITEM] + thing = self.h(thing) + item = self.h(item) + return thing[item] + + def h_expr(self, i): + item = self.h(i[THING]) + if i[NEGATE]: + item = not item + return item + + def h_test(self, item): + ox, op, oy = item[X], item[OP], item[Y] + # for short-circuiting to work, we can't eval these here + e = self.h + if op == 'is': + return e(ox) is e(oy) + elif op == 'is not': + return e(ox) is not e(oy) + elif op == 'in': + return e(ox) in e(oy) + elif op == 'not in': + return e(ox) not in e(oy) + elif op == '==': + return e(ox) == e(oy) + elif op == '!=': + return e(ox) != e(oy) + elif op == '>': + return e(ox) > e(oy) + elif op == '<': + return e(ox) < e(oy) + elif op == '<=': + return e(ox) <= e(oy) + elif op == '>=': + return e(ox) >= e(oy) + elif op == 'and': + return e(ox) and e(oy) + elif op == 'or': + return e(ox) or e(oy) + elif op == '+': + return e(ox) + e(oy) + elif op == '-': + return e(ox) - e(oy) + elif op == '*': + return e(ox) * e(oy) + elif op == '/': + return e(ox) / e(oy) + elif op == '%': + return e(ox) % e(oy) + else: + raise WTF, 'op ' + op + + def h_var(self, i): + v = i[NAME] + if v in self.d: + return self.d[v] + elif v in self.builtins: + return self.builtins[v] + elif v == 'self': + return self.output + else: + raise NameError, 'could not find %s (line %s)' % (repr(i[NAME]), i[LINENO]) + + def h_line(self, i): + out = [] + for x in i[THING]: + if isinstance(x, str): + out.append(x) + elif x[WHAT] == 'itpl': + o = self.h(x[NAME]) + if x[FILTER]: + o = self.filter(o) + else: + if isinstance(o, Stowage): + o = o._str + out.append(o) + else: + raise WTF, x + return ''.join(out) + + def h_varset(self, i): + self.output[i[NAME]] = ''.join(self.h_lines(i[BODY])) + return '' + + def h_if(self, i): + expr = self.h(i[CLAUSE]) + if expr: + do = i[BODY] + else: + for e in i[ELIF]: + expr = self.h(e[CLAUSE]) + if expr: + do = e[BODY] + break + else: + do = i[ELSE] + return ''.join(self.h_lines(do)) + + def h_for(self, i): + out = [] + assert i[IN][WHAT] == 'expr' + invar = self.h(i[IN]) + forvar = i[NAME] + if invar: + for nv in invar: + if len(forvar) == 1: + fv = forvar[0] + assert fv[WHAT] == 'var' + self.d[fv[NAME]] = nv # same (lack of) scoping as Python + else: + for x, y in zip(forvar, nv): + assert x[WHAT] == 'var' + self.d[x[NAME]] = y + + out.extend(self.h_lines(i[BODY])) + else: + if i[ELSE]: + out.extend(self.h_lines(i[ELSE])) + return ''.join(out) + + def h_while(self, i): + out = [] + expr = self.h(i[CLAUSE]) + if not expr: + return ''.join(self.h_lines(i[ELSE])) + c = 0 + while expr: + c += 1 + if c >= MAX_ITERS: + raise RuntimeError, 'too many while-loop iterations (line %s)' % i[LINENO] + out.extend(self.h_lines(i[BODY])) + expr = self.h(i[CLAUSE]) + return ''.join(out) + + def h_assign(self, i): + self.d[i[NAME]] = self.h(i[EXPR]) + return '' + + def h_comment(self, i): pass + + def h_lines(self, lines): + if lines is None: return [] + return map(self.h, lines) + + def go(self): + self.output = Stowage() + self.output._str = ''.join(map(self.h, self.parsetree)) + if self.output.keys() == ['_str']: + self.output = self.output['_str'] + return self.output + +class render: + def __init__(self, loc='templates/', cache=True): + self.loc = loc + if cache: + self.cache = {} + else: + self.cache = False + + def _do(self, name, filter=None): + if self.cache is False or name not in self.cache: + p = glob.glob(self.loc + name + '.*') + if not p and os.path.isdir(self.loc + name): + return render(self.loc + name + '/', cache=self.cache) + elif not p: + raise AttributeError, 'no template named ' + name + p = p[0] + c = Template(open(p).read()) + if self.cache is not False: self.cache[name] = (p, c) + + if self.cache is not False: p, c = self.cache[name] + + if p.endswith('.html'): + import webapi as web + if 'headers' in web.ctx: + web.header('Content-Type', 'text/html; charset=utf-8', unique=True) + if not filter: c.filter = websafe + elif p.endswith('.xml'): + if not filter: c.filter = websafe + + return c + + def __getattr__(self, p): + return self._do(p) + +def frender(fn, *a, **kw): + return Template(open(fn).read(), *a, **kw) + +def test(): + import sys + verbose = '-v' in sys.argv + def assertEqual(a, b): + if a == b: + if verbose: + sys.stderr.write('.') + sys.stderr.flush() + else: + assert a == b, "\nexpected: %s\ngot: %s" % (repr(b), repr(a)) + + from utils import storage, group + t = Template + + tests = [ + lambda: t('1')(), '1\n', + lambda: t('$def with ()\n1')(), '1\n', + lambda: t('$def with (a)\n$a')(1), '1\n', + lambda: t('$def with (a=0)\n$a')(1), '1\n', + lambda: t('$def with (a=0)\n$a')(a=1), '1\n', + lambda: t('$if 1: 1')(), '1\n', + lambda: t('$if 1:\n 1')(), '1\n', + lambda: t('$if 0: 0\n$elif 1: 1')(), '1\n', + lambda: t('$if 0: 0\n$elif None: 0\n$else: 1')(), '1\n', + lambda: t('$if (0 < 1) and (1 < 2): 1')(), '1\n', + lambda: t('$for x in [1, 2, 3]: $x')(), '1\n2\n3\n', + lambda: t('$for x in []: 0\n$else: 1')(), '1\n', + lambda: t('$def with (a)\n$while a and a.pop(): 1')([1, 2, 3]), '1\n1\n1\n', + lambda: t('$while 0: 0\n$else: 1')(), '1\n', + lambda: t('$ a = 1\n$a')(), '1\n', + lambda: t('$# 0')(), '', + lambda: t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1}), '1\n', + lambda: t('$def with (a)\n$(a)')(1), '1\n', + lambda: t('$def with (a)\n$a')(1), '1\n', + lambda: t('$def with (a)\n$a.b')(storage(b=1)), '1\n', + lambda: t('$def with (a)\n$a[0]')([1]), '1\n', + lambda: t('${0 or 1}')(), '1\n', + lambda: t('$ a = [1]\n$a[0]')(), '1\n', + lambda: t('$ a = {1: 1}\n$a.keys()[0]')(), '1\n', + lambda: t('$ a = []\n$if not a: 1')(), '1\n', + lambda: t('$ a = {}\n$if not a: 1')(), '1\n', + lambda: t('$ a = -1\n$a')(), '-1\n', + lambda: t('$ a = "1"\n$a')(), '1\n', + lambda: t('$if 1 is 1: 1')(), '1\n', + lambda: t('$if not 0: 1')(), '1\n', + lambda: t('$if 1:\n $if 1: 1')(), '1\n', + lambda: t('$ a = 1\n$a')(), '1\n', + lambda: t('$ a = 1.\n$a')(), '1.0\n', + lambda: t('$({1: 1}.keys()[0])')(), '1\n', + ] + + for func, value in group(tests, 2): + assertEqual(func(), value) + + j = Template("$var foo: bar")() + assertEqual(str(j), '') + assertEqual(j.foo, 'bar\n') + if verbose: sys.stderr.write('\n') + + +if __name__ == "__main__": + test() ============================================================ --- www/viewmtn/web/utils.py 2eee2f94d96a42148b310b7eb16eace1409b8718 +++ www/viewmtn/web/utils.py 2eee2f94d96a42148b310b7eb16eace1409b8718 @@ -0,0 +1,763 @@ +""" +General Utilities +(part of web.py) +""" + +__all__ = [ + "Storage", "storage", "storify", + "iters", + "rstrips", "lstrips", "strips", + "TimeoutError", "timelimit", + "Memoize", "memoize", + "re_compile", "re_subm", + "group", + "IterBetter", "iterbetter", + "dictreverse", "dictfind", "dictfindall", "dictincr", "dictadd", + "listget", "intget", "datestr", + "numify", "denumify", "dateify", + "CaptureStdout", "capturestdout", "Profile", "profile", + "tryall", + "ThreadedDict", + "autoassign", + "to36", + "safemarkdown" +] + +import re, sys, time, threading +try: import datetime +except ImportError: pass + +class Storage(dict): + """ + A Storage object is like a dictionary except `obj.foo` can be used + in addition to `obj['foo']`. + + >>> o = storage(a=1) + >>> o.a + 1 + >>> o['a'] + 1 + >>> o.a = 2 + >>> o['a'] + 2 + >>> del o.a + >>> o.a + Traceback (most recent call last): + ... + AttributeError: 'a' + + """ + def __getattr__(self, key): + try: + return self[key] + except KeyError, k: + raise AttributeError, k + + def __setattr__(self, key, value): + self[key] = value + + def __delattr__(self, key): + try: + del self[key] + except KeyError, k: + raise AttributeError, k + + def __repr__(self): + return '' + +storage = Storage + +def storify(mapping, *requireds, **defaults): + """ + Creates a `storage` object from dictionary `mapping`, raising `KeyError` if + d doesn't have all of the keys in `requireds` and using the default + values for keys found in `defaults`. + + For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of + `storage({'a':1, 'b':2, 'c':3})`. + + If a `storify` value is a list (e.g. multiple values in a form submission), + `storify` returns the last element of the list, unless the key appears in + `defaults` as a list. Thus: + + >>> storify({'a':[1, 2]}).a + 2 + >>> storify({'a':[1, 2]}, a=[]).a + [1, 2] + >>> storify({'a':1}, a=[]).a + [1] + >>> storify({}, a=[]).a + [] + + Similarly, if the value has a `value` attribute, `storify will return _its_ + value, unless the key appears in `defaults` as a dictionary. + + >>> storify({'a':storage(value=1)}).a + 1 + >>> storify({'a':storage(value=1)}, a={}).a + + >>> storify({}, a={}).a + {} + + """ + def getvalue(x): + if hasattr(x, 'value'): + return x.value + else: + return x + + stor = Storage() + for key in requireds + tuple(mapping.keys()): + value = mapping[key] + if isinstance(value, list): + if isinstance(defaults.get(key), list): + value = [getvalue(x) for x in value] + else: + value = value[-1] + if not isinstance(defaults.get(key), dict): + value = getvalue(value) + if isinstance(defaults.get(key), list) and not isinstance(value, list): + value = [value] + setattr(stor, key, value) + + for (key, value) in defaults.iteritems(): + result = value + if hasattr(stor, key): + result = stor[key] + if value == () and not isinstance(result, tuple): + result = (result,) + setattr(stor, key, result) + + return stor + +iters = [list, tuple] +import __builtin__ +if hasattr(__builtin__, 'set'): + iters.append(set) +try: + from sets import Set + iters.append(Set) +except ImportError: + pass + +class _hack(tuple): pass +iters = _hack(iters) +iters.__doc__ = """ +A list of iterable items (like lists, but not strings). Includes whichever +of lists, tuples, sets, and Sets are available in this version of Python. +""" + +def _strips(direction, text, remove): + if direction == 'l': + if text.startswith(remove): + return text[len(remove):] + elif direction == 'r': + if text.endswith(remove): + return text[:-len(remove)] + else: + raise ValueError, "Direction needs to be r or l." + return text + +def rstrips(text, remove): + """ + removes the string `remove` from the right of `text` + + >>> rstrips("foobar", "bar") + 'foo' + + """ + return _strips('r', text, remove) + +def lstrips(text, remove): + """ + removes the string `remove` from the left of `text` + + >>> lstrips("foobar", "foo") + 'bar' + + """ + return _strips('l', text, remove) + +def strips(text, remove): + """removes the string `remove` from the both sides of `text` + + >>> strips("foobarfoo", "foo") + 'bar' + + """ + return rstrips(lstrips(text, remove), remove) + +class TimeoutError(Exception): pass +def timelimit(timeout): + """ + A decorator to limit a function to `timeout` seconds, raising `TimeoutError` + if it takes longer. + + >>> import time + >>> def meaningoflife(): + ... time.sleep(.2) + ... return 42 + >>> + >>> timelimit(.1)(meaningoflife)() + Traceback (most recent call last): + ... + TimeoutError: took too long + >>> timelimit(1)(meaningoflife)() + 42 + + _Caveat:_ The function isn't stopped after `timeout` seconds but continues + executing in a separate thread. (There seems to be no way to kill a thread.) + + inspired by + """ + def _1(function): + def _2(*args, **kw): + class Dispatch(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.result = None + self.error = None + + self.setDaemon(True) + self.start() + + def run(self): + try: + self.result = function(*args, **kw) + except: + self.error = sys.exc_info() + + c = Dispatch() + c.join(timeout) + if c.isAlive(): + raise TimeoutError, 'took too long' + if c.error: + raise c.error[0], c.error[1] + return c.result + return _2 + return _1 + +class Memoize: + """ + 'Memoizes' a function, caching its return values for each input. + + >>> import time + >>> def meaningoflife(): + ... time.sleep(.2) + ... return 42 + >>> fastlife = memoize(meaningoflife) + >>> meaningoflife() + 42 + >>> timelimit(.1)(meaningoflife)() + Traceback (most recent call last): + ... + TimeoutError: took too long + >>> fastlife() + 42 + >>> timelimit(.1)(fastlife)() + 42 + + """ + def __init__(self, func): + self.func = func + self.cache = {} + def __call__(self, *args, **keywords): + key = (args, tuple(keywords.items())) + if key not in self.cache: + self.cache[key] = self.func(*args, **keywords) + return self.cache[key] + +memoize = Memoize + +re_compile = memoize(re.compile) #@@ threadsafe? +re_compile.__doc__ = """ +A memoized version of re.compile. +""" + +class _re_subm_proxy: + def __init__(self): + self.match = None + def __call__(self, match): + self.match = match + return '' + +def re_subm(pat, repl, string): + """ + Like re.sub, but returns the replacement _and_ the match object. + + >>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball') + >>> t + 'foooooolish' + >>> m.groups() + ('oooooo',) + """ + compiled_pat = re_compile(pat) + proxy = _re_subm_proxy() + compiled_pat.sub(proxy.__call__, string) + return compiled_pat.sub(repl, string), proxy.match + +def group(seq, size): + """ + Returns an iterator over a series of lists of length size from iterable. + + >>> list(group([1,2,3,4], 2)) + [[1, 2], [3, 4]] + """ + if not hasattr(seq, 'next'): + seq = iter(seq) + while True: + yield [seq.next() for i in xrange(size)] + +class IterBetter: + """ + Returns an object that can be used as an iterator + but can also be used via __getitem__ (although it + cannot go backwards -- that is, you cannot request + `iterbetter[0]` after requesting `iterbetter[1]`). + + >>> import itertools + >>> c = iterbetter(itertools.count()) + >>> c[1] + 1 + >>> c[5] + 5 + >>> c[3] + Traceback (most recent call last): + ... + IndexError: already passed 3 + """ + def __init__(self, iterator): + self.i, self.c = iterator, 0 + def __iter__(self): + while 1: + yield self.i.next() + self.c += 1 + def __getitem__(self, i): + #todo: slices + if i < self.c: + raise IndexError, "already passed "+str(i) + try: + while i > self.c: + self.i.next() + self.c += 1 + # now self.c == i + self.c += 1 + return self.i.next() + except StopIteration: + raise IndexError, str(i) +iterbetter = IterBetter + +def dictreverse(mapping): + """ + >>> dictreverse({1: 2, 3: 4}) + {2: 1, 4: 3} + """ + return dict([(value, key) for (key, value) in mapping.iteritems()]) + +def dictfind(dictionary, element): + """ + Returns a key whose value in `dictionary` is `element` + or, if none exists, None. + + >>> d = {1:2, 3:4} + >>> dictfind(d, 4) + 3 + >>> dictfind(d, 5) + """ + for (key, value) in dictionary.iteritems(): + if element is value: + return key + +def dictfindall(dictionary, element): + """ + Returns the keys whose values in `dictionary` are `element` + or, if none exists, []. + + >>> d = {1:4, 3:4} + >>> dictfindall(d, 4) + [1, 3] + >>> dictfindall(d, 5) + [] + """ + res = [] + for (key, value) in dictionary.iteritems(): + if element is value: + res.append(key) + return res + +def dictincr(dictionary, element): + """ + Increments `element` in `dictionary`, + setting it to one if it doesn't exist. + + >>> d = {1:2, 3:4} + >>> dictincr(d, 1) + 3 + >>> d[1] + 3 + >>> dictincr(d, 5) + 1 + >>> d[5] + 1 + """ + dictionary.setdefault(element, 0) + dictionary[element] += 1 + return dictionary[element] + +def dictadd(dict_a, dict_b): + """ + Returns a dictionary consisting of the keys in `a` and `b`. + If they share a key, the value from b is used. + + >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1}) + {1: 0, 2: 1, 3: 1} + """ + result = {} + result.update(dict_a) + result.update(dict_b) + return result + +def listget(lst, ind, default=None): + """ + Returns `lst[ind]` if it exists, `default` otherwise. + + >>> listget(['a'], 0) + 'a' + >>> listget(['a'], 1) + >>> listget(['a'], 1, 'b') + 'b' + """ + if len(lst)-1 < ind: + return default + return lst[ind] + +def intget(integer, default=None): + """ + Returns `integer` as an int or `default` if it can't. + + >>> intget('3') + 3 + >>> intget('3a') + >>> intget('3a', 0) + 0 + """ + try: + return int(integer) + except (TypeError, ValueError): + return default + +def datestr(then, now=None): + """ + Converts a (UTC) datetime object to a nice string representation. + + >>> from datetime import datetime, timedelta + >>> d = datetime(1970, 5, 1) + >>> datestr(d, now=d) + '0 microseconds ago' + >>> for t, v in { + ... timedelta(microseconds=1): '1 microsecond ago', + ... timedelta(microseconds=2): '2 microseconds ago', + ... -timedelta(microseconds=1): '1 microsecond from now', + ... -timedelta(microseconds=2): '2 microseconds from now', + ... timedelta(microseconds=2000): '2 milliseconds ago', + ... timedelta(seconds=2): '2 seconds ago', + ... timedelta(seconds=2*60): '2 minutes ago', + ... timedelta(seconds=2*60*60): '2 hours ago', + ... timedelta(days=2): '2 days ago', + ... }.iteritems(): + ... assert datestr(d, now=d+t) == v + >>> datestr(datetime(1970, 1, 1), now=d) + 'January 1' + >>> datestr(datetime(1969, 1, 1), now=d) + 'January 1, 1969' + >>> datestr(datetime(1970, 6, 1), now=d) + 'June 1, 1970' + """ + def agohence(n, what, divisor=None): + if divisor: n = n // divisor + + out = str(abs(n)) + ' ' + what # '2 day' + if abs(n) != 1: out += 's' # '2 days' + out += ' ' # '2 days ' + if n < 0: + out += 'from now' + else: + out += 'ago' + return out # '2 days ago' + + oneday = 24 * 60 * 60 + + if not now: now = datetime.datetime.utcnow() + delta = now - then + deltaseconds = int(delta.days * oneday + delta.seconds + delta.microseconds * 1e-06) + deltadays = abs(deltaseconds) // oneday + if deltaseconds < 0: deltadays *= -1 # fix for oddity of floor + + if deltadays: + if abs(deltadays) < 4: + return agohence(deltadays, 'day') + + out = then.strftime('%B %e') # e.g. 'June 13' + if then.year != now.year or deltadays < 0: + out += ', %s' % then.year + return out + + if int(deltaseconds): + if abs(deltaseconds) > (60 * 60): + return agohence(deltaseconds, 'hour', 60 * 60) + elif abs(deltaseconds) > 60: + return agohence(deltaseconds, 'minute', 60) + else: + return agohence(deltaseconds, 'second') + + deltamicroseconds = delta.microseconds + if delta.days: deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity + if abs(deltamicroseconds) > 1000: + return agohence(deltamicroseconds, 'millisecond', 1000) + + return agohence(deltamicroseconds, 'microsecond') + +def numify(string): + """ + Removes all non-digit characters from `string`. + + >>> numify('800-555-1212') + '8005551212' + + """ + return ''.join(c for c in str(string).split('.')[0] if c.isdigit()) + +def denumify(string, pattern): + """ + Formats `string` according to `pattern`, where the letter X gets replaced + by characters from `string`. + + >>> denumify("8005551212", "(XXX) XXX-XXXX") + '(800) 555-1212' + + """ + out = [] + for c in pattern: + if c == "X": + out.append(string[0]) + string = string[1:] + else: + out.append(c) + return ''.join(out) + +def dateify(datestring): + """ + Formats a numified `datestring` properly. + """ + return denumify(datestring, "XXXX-XX-XX XX:XX:XX") + +class CaptureStdout: + """ + Captures everything `func` prints to stdout and returns it instead. + + >>> def idiot(): + ... print "foo" + >>> capturestdout(idiot)() + 'foo\\n' + + **WARNING:** Not threadsafe! + """ + def __init__(self, func): + self.func = func + def __call__(self, *args, **keywords): + from cStringIO import StringIO + # Not threadsafe! + out = StringIO() + oldstdout = sys.stdout + sys.stdout = out + try: + self.func(*args, **keywords) + finally: + sys.stdout = oldstdout + return out.getvalue() + +capturestdout = CaptureStdout + +class Profile: + """ + Profiles `func` and returns a tuple containing its output + and a string with human-readable profiling information. + + >>> import time + >>> out, inf = profile(time.sleep)(.001) + >>> out + >>> inf[:10].strip() + 'took 0.0' + """ + def __init__(self, func): + self.func = func + def __call__(self, *args): ##, **kw): kw unused + import hotshot, hotshot.stats, tempfile ##, time already imported + temp = tempfile.NamedTemporaryFile() + prof = hotshot.Profile(temp.name) + + stime = time.time() + result = prof.runcall(self.func, *args) + stime = time.time() - stime + + prof.close() + stats = hotshot.stats.load(temp.name) + stats.strip_dirs() + stats.sort_stats('time', 'calls') + x = '\n\ntook '+ str(stime) + ' seconds\n' + x += capturestdout(stats.print_stats)(40) + x += capturestdout(stats.print_callers)() + return result, x + +profile = Profile + + +import traceback +# hack for compatibility with Python 2.3: +if not hasattr(traceback, 'format_exc'): + from cStringIO import StringIO + def format_exc(limit=None): + strbuf = StringIO() + traceback.print_exc(limit, strbuf) + return strbuf.getvalue() + traceback.format_exc = format_exc + +def tryall(context, prefix=None): + """ + Tries a series of functions and prints their results. + `context` is a dictionary mapping names to values; + the value will only be tried if it's callable. + + >>> tryall(dict(j=lambda: True)) + j: True + ---------------------------------------- + results: + True: 1 + + For example, you might have a file `test/stuff.py` + with a series of functions testing various things in it. + At the bottom, have a line: + + if __name__ == "__main__": tryall(globals()) + + Then you can run `python test/stuff.py` and get the results of + all the tests. + """ + context = context.copy() # vars() would update + results = {} + for (key, value) in context.iteritems(): + if not hasattr(value, '__call__'): + continue + if prefix and not key.startswith(prefix): + continue + print key + ':', + try: + r = value() + dictincr(results, r) + print r + except: + print 'ERROR' + dictincr(results, 'ERROR') + print ' ' + '\n '.join(traceback.format_exc().split('\n')) + + print '-'*40 + print 'results:' + for (key, value) in results.iteritems(): + print ' '*2, str(key)+':', value + +class ThreadedDict: + """ + Takes a dictionary that maps threads to objects. + When a thread tries to get or set an attribute or item + of the threadeddict, it passes it on to the object + for that thread in dictionary. + """ + def __init__(self, dictionary): + self.__dict__['_ThreadedDict__d'] = dictionary + + def __getattr__(self, attr): + return getattr(self.__d[threading.currentThread()], attr) + + def __getitem__(self, item): + return self.__d[threading.currentThread()][item] + + def __setattr__(self, attr, value): + if attr == '__doc__': + self.__dict__[attr] = value + else: + return setattr(self.__d[threading.currentThread()], attr, value) + + def __setitem__(self, item, value): + self.__d[threading.currentThread()][item] = value + + def __hash__(self): + return hash(self.__d[threading.currentThread()]) + +threadeddict = ThreadedDict + +def autoassign(self, locals): + """ + Automatically assigns local variables to `self`. + + >>> self = storage() + >>> autoassign(self, dict(a=1, b=2)) + >>> self + + + Generally used in `__init__` methods, as in: + + def __init__(self, foo, bar, baz=1): autoassign(self, locals()) + """ + for (key, value) in locals.iteritems(): + if key == 'self': + continue + setattr(self, key, value) + +def to36(q): + """ + Converts an integer to base 36 (a useful scheme for human-sayable IDs). + + >>> to36(35) + 'z' + >>> to36(119292) + '2k1o' + >>> int(to36(939387374), 36) + 939387374 + >>> to36(0) + '0' + >>> to36(-393) + Traceback (most recent call last): + ... + ValueError: must supply a positive integer + + """ + if q < 0: raise ValueError, "must supply a positive integer" + letters = "0123456789abcdefghijklmnopqrstuvwxyz" + converted = [] + while q != 0: + q, r = divmod(q, 36) + converted.insert(0, letters[r]) + return "".join(converted) or '0' + + +r_url = re_compile('(?', text) + text = markdown(text) + return text + + +if __name__ == "__main__": + import doctest + doctest.testmod() ============================================================ --- www/viewmtn/web/webapi.py 304cb537871312a0612dc0389e5f5aaad7a5563e +++ www/viewmtn/web/webapi.py 304cb537871312a0612dc0389e5f5aaad7a5563e @@ -0,0 +1,368 @@ +""" +Web API (wrapper around WSGI) +(from web.py) +""" + +__all__ = [ + "config", + "badrequest", "notfound", "gone", "internalerror", + "header", "output", "flush", "debug", + "input", "data", + "setcookie", "cookies", + "ctx", + "loadhooks", "load", "unloadhooks", "unload", "_loadhooks", + "wsgifunc" +] + +import sys, os, cgi, threading, Cookie, pprint, traceback +try: import itertools +except ImportError: pass +from utils import storage, storify, threadeddict, dictadd, intget, lstrips + +config = storage() +config.__doc__ = """ +A configuration object for various aspects of web.py. + +`db_parameters` + : A dictionary containing the parameters to be passed to `connect` + when `load()` is called. +`db_printing` + : Set to `True` if you would like SQL queries and timings to be + printed to the debug output. + +""" + +def badrequest(): + """Return a `400 Bad Request` error.""" + ctx.status = '400 Bad Request' + header('Content-Type', 'text/html') + return output('bad request') + +def notfound(): + """Returns a `404 Not Found` error.""" + ctx.status = '404 Not Found' + header('Content-Type', 'text/html') + return output('not found') + +def gone(): + """Returns a `410 Gone` error.""" + ctx.status = '410 Gone' + header('Content-Type', 'text/html') + return output("gone") + +def internalerror(): + """Returns a `500 Internal Server` error.""" + ctx.status = "500 Internal Server Error" + ctx.headers = [('Content-Type', 'text/html')] + ctx.output = "internal server error" + +def header(hdr, value, unique=False): + """ + Adds the header `hdr: value` with the response. + + If `unique` is True and a header with that name already exists, + it doesn't add a new one. If `unique` is None and a header with + that name already exists, it replaces it with this one. + """ + if unique is True: + for h, v in ctx.headers: + if h == hdr: return + elif unique is False: + ctx.headers = [h for h in ctx.headers if h[0] != hdr] + + ctx.headers.append((hdr, value)) + +def output(string_): + """Appends `string_` to the response.""" + if isinstance(string_, unicode): string_ = string_.encode('utf8') + if ctx.get('flush'): + ctx._write(string_) + else: + ctx.output += str(string_) + +def flush(): + ctx.flush = True + return flush + +def input(*requireds, **defaults): + """ + Returns a `storage` object with the GET and POST arguments. + See `storify` for how `requireds` and `defaults` work. + """ + from cStringIO import StringIO + def dictify(fs): return dict([(k, fs[k]) for k in fs.keys()]) + + _method = defaults.pop('_method', 'both') + + e = ctx.env.copy() + out = {} + if _method.lower() in ['both', 'post']: + a = {} + if e['REQUEST_METHOD'] == 'POST': + a = cgi.FieldStorage(fp = StringIO(data()), environ=e, + keep_blank_values=1) + a = dictify(a) + out = dictadd(out, a) + + if _method.lower() in ['both', 'get']: + e['REQUEST_METHOD'] = 'GET' + a = dictify(cgi.FieldStorage(environ=e, keep_blank_values=1)) + out = dictadd(out, a) + + try: + return storify(out, *requireds, **defaults) + except KeyError: + badrequest() + raise StopIteration + +def data(): + """Returns the data sent with the request.""" + if 'data' not in ctx: + cl = intget(ctx.env.get('CONTENT_LENGTH'), 0) + ctx.data = ctx.env['wsgi.input'].read(cl) + return ctx.data + +def setcookie(name, value, expires="", domain=None): + """Sets a cookie.""" + if expires < 0: + expires = -1000000000 + kargs = {'expires': expires, 'path':'/'} + if domain: + kargs['domain'] = domain + # @@ should we limit cookies to a different path? + cookie = Cookie.SimpleCookie() + cookie[name] = value + for key, val in kargs.iteritems(): + cookie[name][key] = val + header('Set-Cookie', cookie.items()[0][1].OutputString()) + +def cookies(*requireds, **defaults): + """ + Returns a `storage` object with all the cookies in it. + See `storify` for how `requireds` and `defaults` work. + """ + cookie = Cookie.SimpleCookie() + cookie.load(ctx.env.get('HTTP_COOKIE', '')) + try: + return storify(cookie, *requireds, **defaults) + except KeyError: + badrequest() + raise StopIteration + +def debug(*args): + """ + Prints a prettyprinted version of `args` to stderr. + """ + try: + out = ctx.environ['wsgi.errors'] + except: + out = sys.stderr + for arg in args: + print >> out, pprint.pformat(arg) + return '' + +def _debugwrite(x): + try: + out = ctx.environ['wsgi.errors'] + except: + out = sys.stderr + out.write(x) +debug.write = _debugwrite + +class _outputter: + """Wraps `sys.stdout` so that print statements go into the response.""" + def __init__(self, file): self.file = file + def write(self, string_): + if hasattr(ctx, 'output'): + return output(string_) + else: + self.file.write(string_) + def __getattr__(self, attr): return getattr(self.file, attr) + def __getitem__(self, item): return self.file[item] + +def _capturedstdout(): + sysstd = sys.stdout + while hasattr(sysstd, 'file'): + if isinstance(sys.stdout, _outputter): return True + sysstd = sysstd.file + if isinstance(sys.stdout, _outputter): return True + return False + +if not _capturedstdout(): + sys.stdout = _outputter(sys.stdout) + +_context = {threading.currentThread(): storage()} +ctx = context = threadeddict(_context) + +ctx.__doc__ = """ +A `storage` object containing various information about the request: + +`environ` (aka `env`) + : A dictionary containing the standard WSGI environment variables. + +`host` + : The domain (`Host` header) requested by the user. + +`home` + : The base path for the application. + +`ip` + : The IP address of the requester. + +`method` + : The HTTP method used. + +`path` + : The path request. + +`query` + : If there are no query arguments, the empty string. Otherwise, a `?` followed + by the query string. + +`fullpath` + : The full path requested, including query arguments (`== path + query`). + +### Response Data + +`status` (default: "200 OK") + : The status code to be used in the response. + +`headers` + : A list of 2-tuples to be used in the response. + +`output` + : A string to be used as the response. +""" + +loadhooks = {} +_loadhooks = {} + +def load(): + """ + Loads a new context for the thread. + + You can ask for a function to be run at loadtime by + adding it to the dictionary `loadhooks`. + """ + _context[threading.currentThread()] = storage() + ctx.status = '200 OK' + ctx.headers = [] + if config.get('db_parameters'): + import db + db.connect(**config.db_parameters) + + for x in loadhooks.values(): x() + +def _load(env): + load() + ctx.output = '' + ctx.environ = ctx.env = env + ctx.host = env.get('HTTP_HOST') + ctx.homedomain = 'http://' + env.get('HTTP_HOST', '[unknown]') + ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', '')) + ctx.home = ctx.homedomain + ctx.homepath + ctx.ip = env.get('REMOTE_ADDR') + ctx.method = env.get('REQUEST_METHOD') + ctx.path = env.get('PATH_INFO') + # http://trac.lighttpd.net/trac/ticket/406 requires: + if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'): + ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0], + os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))) + + if env.get('QUERY_STRING'): + ctx.query = '?' + env.get('QUERY_STRING', '') + else: + ctx.query = '' + + ctx.fullpath = ctx.path + ctx.query + for x in _loadhooks.values(): x() + +unloadhooks = {} + +def unload(): + """ + Unloads the context for the thread. + + You can ask for a function to be run at loadtime by + adding it ot the dictionary `unloadhooks`. + """ + for x in unloadhooks.values(): x() + # ensures db cursors and such are GCed promptly + del _context[threading.currentThread()] + +def _unload(): + unload() + +def wsgifunc(func, *middleware): + """Returns a WSGI-compatible function from a webpy-function.""" + middleware = list(middleware) + + def wsgifunc(env, start_resp): + _load(env) + try: + result = func() + except StopIteration: + result = None + except: + print >> debug, traceback.format_exc() + result = internalerror() + + is_generator = result and hasattr(result, 'next') + if is_generator: + # wsgi requires the headers first + # so we need to do an iteration + # and save the result for later + try: + firstchunk = result.next() + except StopIteration: + firstchunk = '' + + status, headers, output = ctx.status, ctx.headers, ctx.output + ctx._write = start_resp(status, headers) + + # and now, the fun: + + def cleanup(): + # we insert this little generator + # at the end of our itertools.chain + # so that it unloads the request + # when everything else is done + + yield '' # force it to be a generator + _unload() + + # result is the output of calling the webpy function + # it could be a generator... + + if is_generator: + if firstchunk is flush: + # oh, it's just our special flush mode + # ctx._write is set up, so just continue execution + try: + result.next() + except StopIteration: + pass + + _unload() + return [] + else: + return itertools.chain([firstchunk], result, cleanup()) + + # ... but it's usually just None + # + # output is the stuff in ctx.output + # it's usually a string... + if isinstance(output, str): #@@ other stringlikes? + _unload() + return [output] + # it could be a generator... + elif hasattr(output, 'next'): + return itertools.chain(output, cleanup()) + else: + _unload() + raise Exception, "Invalid ctx.output" + + for mw_func in middleware: + wsgifunc = mw_func(wsgifunc) + + return wsgifunc ============================================================ --- www/viewmtn/web/wsgi.py d7214b2a4c8693a87db5d17eacd245d441ad7113 +++ www/viewmtn/web/wsgi.py d7214b2a4c8693a87db5d17eacd245d441ad7113 @@ -0,0 +1,54 @@ +""" +WSGI Utilities +(from web.py) +""" + +import os, sys + +import http +import webapi as web +from utils import listget +from net import validaddr, validip +import httpserver + +def runfcgi(func, addr=('localhost', 8000)): + """Runs a WSGI function as a FastCGI server.""" + import flup.server.fcgi as flups + return flups.WSGIServer(func, multiplexed=True, bindAddress=addr).run() + +def runscgi(func, addr=('localhost', 4000)): + """Runs a WSGI function as an SCGI server.""" + import flup.server.scgi as flups + return flups.WSGIServer(func, bindAddress=addr).run() + +def runwsgi(func): + """ + Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server, + as appropriate based on context and `sys.argv`. + """ + + if os.environ.has_key('SERVER_SOFTWARE'): # cgi + os.environ['FCGI_FORCE_CGI'] = 'Y' + + if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi + or os.environ.has_key('SERVER_SOFTWARE')): + return runfcgi(func, None) + + if 'fcgi' in sys.argv or 'fastcgi' in sys.argv: + args = sys.argv[1:] + if 'fastcgi' in args: args.remove('fastcgi') + elif 'fcgi' in args: args.remove('fcgi') + if args: + return runfcgi(func, validaddr(args[0])) + else: + return runfcgi(func, None) + + if 'scgi' in sys.argv: + args = sys.argv[1:] + args.remove('scgi') + if args: + return runscgi(func, validaddr(args[0])) + else: + return runscgi(func) + + return httpserver.runsimple(func, validip(listget(sys.argv, 1, ''))) ============================================================ --- www/viewmtn/AUTHORS efc3dd1070798deb1fc2a2312204cc2f9faf00d4 +++ www/viewmtn/AUTHORS 8d04d002de25e5b2befb3bad088b6da4b24b3470 @@ -1,3 +1,4 @@ + Authors: Grahame Bowland @@ -7,4 +8,6 @@ David Reiss Bruce Stephens Lapo Luchini David Reiss +Matthias Radestock +Matthew Nicholson ============================================================ --- www/viewmtn/ChangeLog a90102fbf7b5d7be6c5d553f362389c465267832 +++ www/viewmtn/ChangeLog c4ca52a6b1aa76740f0f94f5ddb69582bd35bc55 @@ -1,3 +1,128 @@ +2007-05-01 Grahame Bowland + + * patch from Rob Schoening fixing a hardcoded + URI to the help page + +2007-04-13 Grahame Bowland + + * this is the 0.07 release + +2007-04-11 Grahame Bowland + + * beta of file-based changelogs + * NB: RSS feeds are misleading, as the information + in the RSS feed is immutable. A TODO here is to + make the RSS feed URI bounce off the to-head proxy + method. + +2007-04-09 Grahame Bowland + + * put an explanation of the revision graph on + the revision/info/ page. + * update the robots.txt request handler + +2007-04-02 Grahame Bowland + + * more tests; check regexps (and found a bug + or two with them, that might have done untoward + things!) and and start checking the string + subclasses in mtn.py + +2007-04-02 Grahame Bowland + + * start of a test suite using PyUnit; first off, + check that config.py is sane. This is actually + useful for people that accidentally specify wrong + things in here. + * move some experimental / old things into + experimental/ so that people know they're not + something you should actually try running. + +2007-04-01 Grahame Bowland + + * fix a number of places where dynamic_join() + was called with the first argument beginning with '/' + resulting in an absolute link to / + +2007-03-31 Grahame Bowland + + * remove accidental hardcoded absolute URI in + viewmtn.js; we now get the URI from a JS variable + output into the document by a template. + +2007-03-31 Grahame Bowland + + * don't fail entirely if xdg_* can't be found; + add a new module option to fdo/xdgbasedir + determining whether exceptions should be raised, + or just silently ignored. + * work under Apache2! + * update INSTALL, README + * rename LICENSE to COPYING + * add AUTHORS credit to Matthias Radestock and + Matthew Nicholson + * add GNU GPL2 boilerplate to the top of all the + Python files. + * credit json.py in the about.html template + * remove cruft from config.py.example, update the + paths to be more sensible on a stock Debian/Redhat + machine. + +2007-03-30 Grahame Bowland + + * upgrade to MochiKit development version (today's + subversion trunk) to resolve issues with Opera and + the elementPosition method. + * rework the javascript / JSON RPC popup code; + introduce one-second delay to dampen requests, + mouse must hover for one second over something + before a JSON request is actually made. + * fix problem where branchse with no heads got a + silly "branch changed by undefined" message + * clean up the logic of viewmtn.js, cancel + outstanding requests and correctly track + boundTo. + +2007-03-30 Grahame Bowland + + * upgrade to web.py 0.2 + +2007-03-30 Grahame Bowland + + * in Automate, check per request with stat to see if the + database has changed. If so, stop the automate process - + it'll be restarted when the next request comes along anyway. + +2007-03-30 Grahame Bowland + + * add new branch tag view, showing all tags + on a given branch + +2007-03-29 Grahame Bowland + + * fix bug in revised get_last_changes; don't + loop forever when we run out of revisions to + look at + * format dates in recent list so as to follow + the RSS spec. + +2007-03-29 Grahame Bowland + + * rewrite BranchChanges.get_last_changes, much more + efficient algorithm suggested by Matthias Radestock. + Use python's heapq class to get an efficient, sorted + list of revisions of interest. + * to my horror, discover that README has totally out + of date, incorrect installation instructions. Delete + and replace with something useful. This explains the + emails I've been getting asking for help setting + up mod_python and wrapper.py.. + +2006-12-11 Matthew Nicholson + + * templates/branch.html: Support branch names with '/' in them. + * viewmtn.py: Support branch names with '/' in them. + 2006-10-26 Grahame Bowland * release 0.06 ============================================================ --- www/viewmtn/INSTALL e6b186c3fbe5e1843af38b941f284add5e3ceaa6 +++ www/viewmtn/INSTALL 96cd4a9bac2f42db1846f3aacff30a58e5706042 @@ -1,9 +1,11 @@ This document briefly describes what is Installing ViewMTN ------------------ This document briefly describes what is necessary to install ViewMTN -and configure a working installation. +and configure a working installation. If something goes awry and you +can't get ViewMTN to work, please feel free to contact me. See the +contact details in "README" Dependencies ------------ @@ -14,7 +16,7 @@ A version which is descended from [62961 Monotone: http://www.venge.net/monotone/ A version which is descended from [62961c1dc..] is required. -This is post-0.30 +This is post-0.30. Python: http://www.python.org/ A version >= 2.4 is required. @@ -22,6 +24,16 @@ Version 0.9.16-1 from Debian is known to Cheetah templates: http://www.cheetahtemplate.org/ Version 0.9.16-1 from Debian is known to work. +Flup: http://www.saddi.com/software/flup/dist/ +Version: flup-r2311.tar.gz is know to work. + +On a Debian machine, the following should give you what you need: + apt-get install python-flup python-cheetah gnome-icon-theme highlight shared-mime-info +You might also want: + apt-get install lighttpd +Unless you plan to run Apache, in which case: + apt-get install libapache2-mod-fastcgi + Optional -------- @@ -30,12 +42,11 @@ Shared Mime Info: http://freedesktop.org Highlight is required if source code is to be shown highlighted. Shared Mime Info: http://freedesktop.org/wiki/Software/shared-mime-info -Version 0.19 is known to work, although there is a specification -so older versions should be fine. Most distributions provide this -info. Note that if you install this into a non-standard path, -please export XDG_DATA_DIRS correctly (eg. XDG_DATA_DIRS=/opt/local/share) -Without this package, ViewMTN will only perform extremely basic -MIME type auto-detection. +Version 0.19 is known to work, although there is a specification so older +versions should be fine. Most distributions provide this info. Note that +if you install this into a non-standard path, please export XDG_DATA_DIRS +correctly (eg. XDG_DATA_DIRS=/opt/local/share) Without this package, +ViewMTN will only perform extremely basic MIME type auto-detection. Icon Theme: http://www.freedesktop.org/software/icon-theme/ Any version should be fine. If possible, use a distributor version @@ -56,9 +67,10 @@ You're then ready to run ViewMTN; too hard. You're then ready to run ViewMTN; - ./viewmtn.py -If you leave off the argument, ViewMTN will bind to port 8080. -You can access ViewMTN by visiting: + ./viewmtn.py + +If you omit the argument, ViewMTN will bind to port +127.0.0.1:8080. You can access ViewMTN by visiting: http://localhost:8080/ If everything has gone well, you should get the normal ViewMTN front @@ -66,6 +78,11 @@ cannot read your monotone database, the If not, look at the output of viewmtn.py on the console; perhaps it cannot read your monotone database, the path to 'mtn' is wrong, etc. +Running standalone in this mode is quite useful when using ViewMTN +as an application for a single user. However, if you want to run a +ViewMTN server for multiple users I suggest you have set up ViewMTN +to run under a web server. + Running ViewMTN in a web server ------------------------------- @@ -74,6 +91,12 @@ here: here: http://webpy.infogami.com/install +In any of the examples, substitute "code.py" (or whatever) for +web.py. + +Lighttpd +-------- + The following snippet of configuration is used to configure ViewMTN on http://viewmtn.angrygoats.net/ (running lighttpd) and is therefore known to work. You should be able to use it (with adjustment to @@ -99,3 +122,41 @@ suit your site). "^/(.*)$" => "/viewmtn.py/$1", ) } + +Apache2 +------- + +I've only tried ViewMTN under Apache with mod_fastcgi. Below is an +example htaccess file for ViewMTN: + # ExecCGI privilege is required to run the + # ViewMTN process + Options +ExecCGI + + # Make sure you update config.py with this URL. + + SetHandler fastcgi-script + + +Copy this file to .htaccess in the viewmtn directory. This directory +must have Options ExecCGI set. You might not be able to do this via +directives in .htaccess, depending on site configuration; you may need +to change the global server settings. + +Unfortunately mod_fastcgi does not provide information in the environment +so that web.py can automatically determine to run as fastcgi. Edit +config.py, and set running_under_apache2 to True. + running_under_apache2 = True + +Note that if you ever want to run ViewMTN in a different mode, you +will need reset this value to False. + +After all of this you should be able to access ViewMTN at: + http://hostname/path/to/viewmtn/viewmtn.py/ + +The trailing slash is important, otherwise you'll get a 404 error +trying to access the page. Note that the URL for static content +is: + http://hostname/path/to/viewmtn/static/ + +Be sure to set both of these in config.py. + ============================================================ --- www/viewmtn/README 46ffe1ce5080c9fd0081574ae8a8da30c3df65dc +++ www/viewmtn/README 51ea9bcf58e2383de5b13e36130946447ed07462 @@ -1,62 +1,34 @@ - ViewMTN ------- -A web interface to monotone. See "LICENSE" for distribution terms. +A web interface to monotone. See "COPYING" for distribution terms. ViewMTN is Copyright 2005 Grahame Bowland. -The minimum version of monotone required is: 0.24 +The minimum version of monotone required is: 0.32 -For the graphs to work you'll need dotty installed - it is a -part of GraphViz. - http://www.research.att.com/sw/tools/graphviz/ +See "INSTALL" for installation information. -ViewMTN requires mod_python. - http://www.modpython.org/ - -When installing be sure to copy config.py.example to config.py, and -then edit config.py (paying attention to the comments!) - -Common issues -------------- - -If you are getting a 404 error for "getfile.py", make sure that you have -updated ".htacess" to include the following line. - PythonHandler wrapper -"wrapper.py" contains several Python functions which are called -for various pages that do not need to go through mod_python's PSP -framework. The above line causes all page requests to go through -wrapper.py, which decides which function (or PSP file) should be called -to handle the request. - -(Note: recent versions of ViewMTN will not work at all if you are missing - the line above.) - -MacOS ------ - -I run ViewMTN on MacOS by using mod_python and apache2 compiled from source -using Darwin Ports: - http://darwinports.opendarwin.org/ -You should be able to install graphviz from Darwin Ports as well. - -The graphs seem to look terrible with the default fault from config.py.example; -I set the font to "Monaco" instead and it looks pretty good. - Bugs, suggestions, feedback --------------------------- -Send any bugs, suggestions or feedback to: - Grahame Bowland - PO BOX 3105, Broadway, Nedlands WA 6009 Australia +If you need help with ViewMTN or there is any other issue, feel +free to get in touch with me: + Grahame Bowland + PO BOX 3105, Broadway, Nedlands WA 6009 Australia -In particular, please look at the TODO file. If you're interested in -fixing any of the issues listed (or just adding extra TODO entries) -please go ahead - perhaps let me know so I can keep track and let you -know if the item is already done but not committed. +I have put up a web page: + http://grahame.angrygoats.net/moinmoin/ViewMTN/Suggestions +Feel free to edit this page and list bugs or things you +would like to have implemented. As monotone is a distributed version control system, feel free to grab a copy of viewmtn, commit to your local DB, etc. If you want to send me your commits, email me (or catch me in #monotone) and we'll work something out. +I generally sync all my changes to the following public monotone +repositories: + venge.net + monotone.ucc.gu.uwa.edu.au +You should be able to grab the latest viewmtn from any of them. + ============================================================ --- www/viewmtn/TODO d57f9d2d4d2acd8412cb3d9c090f424159eecb6a +++ www/viewmtn/TODO eb85131c28211d5b66942a4a499def2ef2866992 @@ -1,76 +1,5 @@ -NEW VERSION +The contents of this file have been moved to: +http://grahame.angrygoats.net/moinmoin/ViewMTN/Suggestions +Please go to that site, and look there! - * Fix the RSS date fields - * Highlight -> content_type mapping (works at the moment, somehow) - * JSON - * Unit tests - * Read-only WebDAV support - * Put copyright notices on the files, mostly so I can keep track - of where they end up. - * remove the leading path from subdirectories in file browser - * page showing the tags for a given branch - * client side reorderable tables using JS - -BUGS: - - * HEAD just does GET; we should really do HEAD properly. Also, we - should start issuing eTags - -TODO: - - * support for translation, and see if we can get some people to help with that - モノトネ - - * some sort of contest for a logo (with some sort of cool prize, perhaps - con someone into putting one up on offer or pay up myself.) - - * a revision selector interface - make sure it prints out the revision - selector to the person, so that they can use it on the command line later - ** make this AJAX; show the results *as the person types* ** - ** this feature would blow people out of the water ** - - * optionally pop-up a window (or some sort of AJAX box lurking someplace) - which updates with the commands that have been run - useful for debugging, - also useful for beginners to see how to do stuff. - - * Show information when mousing over long hex strings. - - * Magically make http:// ftp:// etc links inside files clickable - - * In the file diff view for multiple files, show the same change set - information that is available in the revision view - this will help - explain what's happened. For bonus points, index into the diff so you - can click on a file that changed and jump to that section in the diff. - - * When viewing a file, give a list of revisions in which that file - was recently changed. Include in the list a link to diff with each - revision. - - * from [mrb] - support for multiple databases (perhaps some sort of dropdown to say which - database you want to look in) - perhaps also make the branches page show the - branches in each of the DBs, for ease. - -AJAX ideas: - - * When hovering over nodes in the ancestry graph, display the ChangeLog. - - * When hovering over branch links, show heads and other information - - * When hovering over revisions, show author / date - - * When hovering over diff links - - * turn line numbering in file / diff view (etc) on and off - - * file viewer; treat merges as a special case and show the side which - actually has the resultant file (no change on that edge) - - * file viewer / diff viewer / anywhere showing info related to a rev - magic box that downloads and inserts - revision stuff. - - * we need some concept of selecting two points revisions for diffs or other - comparison. This is the main strength ViewCVS seems to have over us. - ============================================================ --- www/viewmtn/authors.py 0ed0661cf3ddf1b402a73dfc1ff0fccc64ad05f1 +++ www/viewmtn/authors.py 011a38a50ea83896fac1db87a8d81860bf0d22e9 @@ -1,3 +1,12 @@ +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + authors='''Authors: Grahame Bowland @@ -9,3 +18,7 @@ David Reiss David Reiss ''' + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/common.py b5a9eb0d651eb4f1a41e94376d35b31f5c8efe5d +++ www/viewmtn/common.py 2504e01f69f99347d0e8706fb7c344c6a6471e43 @@ -1,4 +1,13 @@ +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + import datetime import time import fcntl @@ -46,3 +55,7 @@ def ago(event): seconds = (ago.seconds - (minutes * 60)) rv = "%s" % (plural(minutes, "minute", "minutes")) return rv + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/config.py.example 343449d29b7b0e04c1e86f034c27a48bed094d5e +++ www/viewmtn/config.py.example 97fbc51fb12393018862a3073aa718a25bfb6eac @@ -1,4 +1,13 @@ +# Copyright (C) 2005 Grahame Bowland # +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + +# # config.py # # This python script is imported by ViewMTN @@ -16,7 +25,16 @@ conffile = confdir + '/hostconfig' confdir = 'CONFDIR' conffile = confdir + '/hostconfig' +# default addresses should work without modification +# if running viewmtn standalone. You must change these +# if you run viewmtn with a web server, see INSTALL. +dynamic_uri_path = 'http://localhost:8080/' +static_uri_path = 'http://localhost:8080/static/' +# if you are running under Apache2, set this. +# don't set it otherwise, it breaks any other configuration +# including running standalone. +running_under_apache2 = False def file_tokens(fn): f = open(fn, 'r') data = f.read() @@ -47,24 +65,17 @@ monotone = config_data['monotone'][0] monotone = config_data['monotone'][0] # the monotone database to be shared out -# obviously, everything in this database might -# become public if something goes wrong; probably -# a good idea not to leave your private key in it +# everything in this database should be considered subject +# to disclosure. So don't store your private key in +# it! def dbfile(uri): d = os.path.dirname(os.path.dirname(uri)) return (config_data['project_dir'][0] + '/%s/database.viewmtn') % os.path.basename(d) -# where to find GNOME icons (used in manifest listing) -gnome_mimetype_icon_path = config_data['www_dir'][0] + '/viewmtn/mimetypes/' #'/usr/share/icons/gnome/' -gnome_mimetype_icon_path = '/Users/grahame/mtn/viewmtn/mimetypes/' - -# and where they are on the web -gnome_mimetype_uri = config_data['base_url'][0] + '/viewmtn/mimetypes/' - # highlight from http://andre-simon.de/ # if you don't have this available, just comment # the "highlight_command" line out -highlight_command = '/opt/local/bin/highlight' +highlight_command = '/usr/bin/highlight' graphopts = { # a directory (must be writable by the web user) @@ -79,7 +90,7 @@ graphopts = { 'uri' : 'graph/', # the path to the 'dot' program - 'dot' : '/opt/local/bin/dot', + 'dot' : '/usr/bin/dot', # options to use for nodes in the dot input file # we generate. @@ -98,10 +109,4 @@ icon_size = '16' icon_theme = 'gnome' icon_size = '16' -# for tests/ -# don't worry about these unless you are going to run -# the tests -test_branch = "net.angrygoats.viewmtn" -#test_revision = "53b7a6866f0f7268a8eb721e8d74688de8567fb8" -test_revision = "ea14ea3aadb3a02ffe5041e0a98db15306cbcd81" ============================================================ --- www/viewmtn/fdo/__init__.py da39a3ee5e6b4b0d3255bfef95601890afd80709 +++ www/viewmtn/fdo/__init__.py 14a3ef7b1ed02bfe6219517ba27a3d188d09f78a @@ -0,0 +1,4 @@ + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/fdo/icontheme.py 9ec270a56c060a3c63b75a4f7d00c370a831ae29 +++ www/viewmtn/fdo/icontheme.py 643842c242655847a60538b6a6eeb2495d04bc31 @@ -119,7 +119,7 @@ class MimeIcon: # otherwise, otherwise, one of these should work\n really_fallbacks = [(['Applications'], 'gnome-unknown'), (['MimeTypes'], 'unknown')] for contexts, icon_name in really_fallbacks: - rv = self.icon_name.lookup(icon_name, contexts=contexts, size=self.size) + rv = self.icon_theme.lookup(icon_name, contexts=contexts, size=self.size) if rv: return rv if not self.cache.has_key(mime_type): self.cache[mime_type] = __lookup() @@ -129,3 +129,7 @@ if __name__ == '__main__': it = IconTheme('gnome') mi = MimeIcon(it, size="16") print mi.lookup('inode/directory') + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/fdo/sharedmimeinfo.py dd993b9e526568246adc535ce4d47d360dd4eb3b +++ www/viewmtn/fdo/sharedmimeinfo.py 4ee53b521b7103c8434435a904cd6692b569548d @@ -293,3 +293,7 @@ if __name__ == '__main__': if __name__ == '__main__': c = LookupHelper() print c.lookup('test.tar', '') + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/fdo/xdgbasedir.py 864ac96a21a2fe65c401a7beaf2c98513795751d +++ www/viewmtn/fdo/xdgbasedir.py ea14de106c5d29b333dc8bd1b40bd869a6afbb05 @@ -7,44 +7,53 @@ import os import os +strict = False + +def with_home (partial, exc_text): + home = os.getenv('HOME') + if home: + return partial(home) + elif strict: + raise Exception(exc_text) + else: + return None + def xdg_data_home(): rv = os.getenv('XDG_DATA_HOME') if rv: return rv - home = os.getenv('HOME') - if home: - return os.path.join(home, ".local", "share") - else: - raise Exception("Unable to determine xdg_data_home") + return with_home (lambda home: os.path.join(home, ".local", "share"), "Unable to determine xdg_data_home") def xdg_config_home(): rv = os.getenv('XDG_CONFIG_HOME') if rv: return rv - home = os.getenv('HOME') - if home: - return os.path.join(home, ".config") - else: - raise Exception("Unable to determine xdg_config_home") + return with_home (lambda home: os.path.join(home, ".config"), "Unable to determine xdg_config_home") def xdg_data_dirs(): - dirs = [xdg_data_home()] - for dir in os.getenv('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':'): - dirs.append(dir) + dirs = [] + dirname = xdg_data_home() + if dirname: + dirs.append(dirname) + for dirname in os.getenv('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':'): + dirs.append(dirname) return dirs def xdg_config_dirs(): - dirs = [xdg_config_home()] - for dir in os.getenv('XDG_DATA_DIRS', '/etc/xdg').split(':'): - dirs.append(dir) + dirs = [] + dirname = xdg_config_home() + if dirname: + dirs.append(dirname) + for dirname in os.getenv('XDG_DATA_DIRS', '/etc/xdg').split(':'): + dirs.append(dirname) return dirs def xdg_cache_home(): rv = os.getenv('XDG_CACHE_HOME') if rv: return rv + return with_home (lambda home: os.path.join(home, ".cache"), "Unable to determine xdg_cache_home") + +### +### vi:expandtab:sw=4:ts=4 +### - home = os.getenv('HOME') - if home: - return os.path.join(home, ".cache") - else: - raise Exception("Unable to determine xdg_cache_home") ============================================================ --- www/viewmtn/genproxy.py 408f46a3f5fe0d792eb62e92a8faaf5c28c67a54 +++ www/viewmtn/genproxy.py 4fbf4dd79d7dbe381b8bae3c9b0b63e1864dcfce @@ -1,5 +1,14 @@ #!/usr/bin/env python +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + class GeneratorProxy(object): def __init__(self, generator): self.generator = generator @@ -23,3 +32,7 @@ if __name__ == '__main__': for i in b: print i + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/mtn.py 10bae73eaa4b6b891d434bfcffa8ca66968b204b +++ www/viewmtn/mtn.py 24bdc6cf0f2935b5768da503135c11c50f86e267 @@ -1,4 +1,13 @@ +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + import os import re import fcntl @@ -18,9 +27,10 @@ def group_compile(r): def group_compile(r): return re.compile('('+r+')') -hex_re = r'[A-Fa-f0-9]*' +sha1_len = 40 +hex_re = r'^[A-Fa-f0-9]*' hex_re_c = group_compile(hex_re) -revision_re = r'[A-Fa-f0-9]{40}' +revision_re = r'\b[A-Fa-f0-9]{%d}\b' % sha1_len revision_re_c = group_compile(revision_re) name_re = r'^[\S]+' name_re_c = group_compile(name_re) @@ -33,8 +43,8 @@ class Revision(str): # special case that must be handled: empty (initial) revision ID '' str.__init__(v) self.obj_type = "revision" - if v != '' and not revision_re_c.match(self): - raise MonotoneException("Not a valid revision ID: %s" % (v)) + if v != '' and not (revision_re_c.match(self) and len(self) == sha1_len): + raise MonotoneException("Not a valid revision ID: %s" % (repr(v))) def abbrev(self): return '[' + self[:8] + '..]' @@ -43,8 +53,9 @@ class Author(str): str.__init__(v) self.obj_type = "author" -class Runner: +class Runner(object): def __init__(self, monotone, database): + self.database = database self.base_command = [monotone, "--db=%s" % pipes.quote(database)] packet_header_re = re.compile(r'^(\d+):(\d+):([lm]):(\d+):') @@ -63,24 +74,34 @@ class Automate(Runner): Runner.__init__(*[self] + list(args), **kwargs) self.lock = threading.Lock() self.process = None + self.running_mtime = None def stop(self): if not self.process: return terminate_popen3(self.process) self.process = None + + def database_mtime(self): + return os.stat(self.database).st_mtime + + def check_current(self): + if self.process != None and self.database_mtime() > self.running_mtime: + debug("stopped process, database has changed.") + self.stop() def __process_required(self): if self.process != None: return to_run = self.base_command + ['automate', 'stdio'] + self.running_mtime = self.database_mtime() self.process = popen2.Popen3(to_run, capturestderr=True) map (set_nonblocking, [ self.process.fromchild, self.process.tochild, self.process.childerr ]) def run(self, *args, **kwargs): -# debug(("automate is running:", args, kwargs)) + #debug(("automate is running:", args, kwargs)) lock = self.lock stop = self.stop @@ -335,7 +356,13 @@ class Operations: def __init__(self, runner_args): self.standalone = apply(Standalone, runner_args) self.automate = apply(Automate, runner_args) - + + def per_request(self): + """"Call this method every distinct request, to allow Operations to do any + cleanup operations. + """ + self.automate.check_current() + def tags(self): for stanza in basic_io_from_stream(self.automate.run('tags', [])): if stanza[0] == 'tag': @@ -380,6 +407,10 @@ class Operations: continue yield apply(Revision, (line,)) + def get_corresponding_path(self, revision1, path, revision2): + for stanza in basic_io_from_stream(self.automate.run('get_corresponding_path', [revision1, path, revision2])): + yield stanza + def get_content_changed(self, revision, path): for stanza in basic_io_from_stream(self.automate.run('get_content_changed', [revision, path])): yield stanza @@ -405,3 +436,6 @@ class Operations: for line in self.standalone.run('diff', args): yield line +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/release.py 9606208d767e8ed4999ca37fa5e20d13f3fa7be5 +++ www/viewmtn/release.py 33b5fb415b50d215b26575154bdcfb6fcd704def @@ -1,5 +1,6 @@ -version='0.06beta' -authors='''Authors: +version='0.07' +authors=''' +Authors: Grahame Bowland Contributors: @@ -8,5 +9,7 @@ David Reiss Bruce Stephens Lapo Luchini David Reiss +Matthias Radestock +Matthew Nicholson ''' ============================================================ --- www/viewmtn/release.sh 65fe70e241d026a9e09530245362cb09287b6608 +++ www/viewmtn/release.sh 524cd8cc20a5c03c1f772cf688a26ec1eceb487d @@ -1,8 +1,17 @@ #!/bin/sh +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + # generate the help file data OUT="release.py" -RELEASE="0.06beta" +RELEASE="0.07" echo -n > "$OUT" echo "version='$RELEASE'" > "$OUT" ============================================================ --- www/viewmtn/static/MochiKit/MochiKit.js db162319790852ff3e02f6e673b729a3c262f4ec +++ www/viewmtn/static/MochiKit/MochiKit.js 32da8c6070b029251ef51bc3097460f296bcf7a1 @@ -1,6 +1,6 @@ /*** - MochiKit.MochiKit 1.2 : PACKED VERSION + MochiKit.MochiKit 1.4 : PACKED VERSION THIS FILE IS AUTOMATICALLY GENERATED. If creating patches, please diff against the source tree, not this file. @@ -20,15 +20,18 @@ MochiKit.Base={}; if(typeof (MochiKit.Base)=="undefined"){ MochiKit.Base={}; } -MochiKit.Base.VERSION="1.2"; +if(typeof (MochiKit.__export__)=="undefined"){ +MochiKit.__export__=(MochiKit.__compat__||(typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")); +} +MochiKit.Base.VERSION="1.4"; MochiKit.Base.NAME="MochiKit.Base"; MochiKit.Base.update=function(_1,_2){ -if(_1==null){ +if(_1===null){ _1={}; } for(var i=1;i=0;i--){ +_15.unshift(o[i]); +} +}else{ +res.push(o); +} +} +return res; +},extend:function(_18,obj,_1a){ +if(!_1a){ +_1a=0; +} if(obj){ var l=obj.length; if(typeof (l)!="number"){ @@ -67,68 +104,74 @@ throw new TypeError("Argument not an arr throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); } } -if(!_9){ -_9=[]; +if(!_18){ +_18=[]; } -for(var i=_11;il){ -_37=l; +if(_90===null||_90>l){ +_90=l; } } -_36=[]; -for(i=0;i<_37;i++){ -var _38=[]; +_8e=[]; +for(i=0;i<_90;i++){ +var _92=[]; for(var j=1;j=0;i--){ +_af=[_ab[i].apply(this,_af)]; +} +return _af[0]; +}; +},bind:function(_b1,_b2){ +if(typeof (_b1)=="string"){ +_b1=_b2[_b1]; +} +var _b3=_b1.im_func; +var _b4=_b1.im_preargs; +var _b5=_b1.im_self; +var m=MochiKit.Base; +if(typeof (_b1)=="function"&&typeof (_b1.apply)=="undefined"){ +_b1=m._wrapDumbFunction(_b1); +} +if(typeof (_b3)!="function"){ +_b3=_b1; +} +if(typeof (_b2)!="undefined"){ +_b5=_b2; +} +if(typeof (_b4)=="undefined"){ +_b4=[]; }else{ -_48=_48.slice(); +_b4=_b4.slice(); } -m.extend(_48,arguments,2); -var _50=function(){ -var _51=arguments; +m.extend(_b4,arguments,2); +var _b7=function(){ +var _b8=arguments; var me=arguments.callee; if(me.im_preargs.length>0){ -_51=m.concat(me.im_preargs,_51); +_b8=m.concat(me.im_preargs,_b8); } -var _46=me.im_self; -if(!_46){ -_46=this; +var _ba=me.im_self; +if(!_ba){ +_ba=this; } -return me.im_func.apply(_46,_51); +return me.im_func.apply(_ba,_b8); }; -_50.im_self=_49; -_50.im_func=_47; -_50.im_preargs=_48; -return _50; -},bindMethods:function(_52){ -var _53=MochiKit.Base.bind; -for(var k in _52){ -var _54=_52[k]; -if(typeof (_54)=="function"){ -_52[k]=_53(_54,_52); +_b7.im_self=_b5; +_b7.im_func=_b3; +_b7.im_preargs=_b4; +return _b7; +},bindMethods:function(_bb){ +var _bc=MochiKit.Base.bind; +for(var k in _bb){ +var _be=_bb[k]; +if(typeof (_be)=="function"){ +_bb[k]=_bc(_be,_bb); } } -},registerComparator:function(_55,_56,_57,_58){ -MochiKit.Base.comparatorRegistry.register(_55,_56,_57,_58); -},_primitives:{"bool":true,"string":true,"number":true},compare:function(a,b){ +},registerComparator:function(_bf,_c0,_c1,_c2){ +MochiKit.Base.comparatorRegistry.register(_bf,_c0,_c1,_c2); +},_primitives:{"boolean":true,"string":true,"number":true},compare:function(a,b){ if(a==b){ return 0; } -var _59=(typeof (a)=="undefined"||a==null); -var _60=(typeof (b)=="undefined"||b==null); -if(_59&&_60){ +var _c5=(typeof (a)=="undefined"||a===null); +var _c6=(typeof (b)=="undefined"||b===null); +if(_c5&&_c6){ return 0; }else{ -if(_59){ +if(_c5){ return -1; }else{ -if(_60){ +if(_c6){ return 1; } } } var m=MochiKit.Base; -var _61=m._primitives; -if(!(typeof (a) in _61&&typeof (b) in _61)){ +var _c8=m._primitives; +if(!(typeof (a) in _c8&&typeof (b) in _c8)){ try{ return m.comparatorRegistry.match(a,b); } @@ -455,31 +544,31 @@ return 1; return 1; } } -var _62=m.repr; -throw new TypeError(_62(a)+" and "+_62(b)+" can not be compared"); +var _c9=m.repr; +throw new TypeError(_c9(a)+" and "+_c9(b)+" can not be compared"); },compareDateLike:function(a,b){ return MochiKit.Base.compare(a.getTime(),b.getTime()); },compareArrayLike:function(a,b){ -var _63=MochiKit.Base.compare; -var _64=a.length; -var _65=0; -if(_64>b.length){ -_65=1; -_64=b.length; +var _ce=MochiKit.Base.compare; +var _cf=a.length; +var _d0=0; +if(_cf>b.length){ +_d0=1; +_cf=b.length; }else{ -if(_64=0;i--){ +sum+=o[i]; +} +}else{ +sum+=o; +} +} +if(_113<=0){ +throw new TypeError("mean() requires at least one argument"); +} +return sum/_113; +},median:function(){ +var data=MochiKit.Base.flattenArguments(arguments); +if(data.length===0){ +throw new TypeError("median() requires at least one argument"); +} +data.sort(compare); +if(data.length%2==0){ +var _117=data.length/2; +return (data[_117]+data[_117-1])/2; +}else{ +return data[(data.length-1)/2]; +} +},findValue:function(lst,_119,_11a,end){ +if(typeof (end)=="undefined"||end===null){ +end=lst.length; +} +if(typeof (_11a)=="undefined"||_11a===null){ +_11a=0; +} +var cmp=MochiKit.Base.compare; +for(var i=_11a;i0))){ -var kv=MochiKit.DOM.formContents(_103); -_103=kv[0]; -_104=kv[1]; +},queryString:function(_127,_128){ +if(typeof (MochiKit.DOM)!="undefined"&&arguments.length==1&&(typeof (_127)=="string"||(typeof (_127.nodeType)!="undefined"&&_127.nodeType>0))){ +var kv=MochiKit.DOM.formContents(_127); +_127=kv[0]; +_128=kv[1]; }else{ if(arguments.length==1){ -var o=_103; -_103=[]; -_104=[]; +if(typeof (_127.length)=="number"&&_127.length==2){ +return arguments.callee(_127[0],_127[1]); +} +var o=_127; +_127=[]; +_128=[]; for(var k in o){ var v=o[k]; -if(typeof (v)!="function"){ -_103.push(k); -_104.push(v); +if(typeof (v)=="function"){ +continue; +}else{ +if(typeof (v)!="string"&&typeof (v.length)=="number"){ +for(var i=0;i=stop){ +if(_174>=stop){ throw self.StopIteration; } -_137+=step; +_174+=step; return rval; }}; },imap:function(fun,p,q){ var m=MochiKit.Base; var self=MochiKit.Iter; -var _141=m.map(self.iter,m.extend(null,arguments,1)); +var _17e=m.map(self.iter,m.extend(null,arguments,1)); var map=m.map; var next=self.next; return {repr:function(){ return "imap(...)"; -},toString:m.forward("repr"),next:function(){ -return fun.apply(this,map(next,_141)); +},toString:m.forwardCall("repr"),next:function(){ +return fun.apply(this,map(next,_17e)); }}; },applymap:function(fun,seq,self){ seq=MochiKit.Iter.iter(seq); var m=MochiKit.Base; return {repr:function(){ return "applymap(...)"; -},toString:m.forward("repr"),next:function(){ +},toString:m.forwardCall("repr"),next:function(){ return fun.apply(self,seq.next()); }}; },chain:function(p,q){ @@ -1034,24 +1213,24 @@ return self.iter(arguments[0]); if(arguments.length==1){ return self.iter(arguments[0]); } -var _143=m.map(self.iter,arguments); +var _189=m.map(self.iter,arguments); return {repr:function(){ return "chain(...)"; -},toString:m.forward("repr"),next:function(){ -while(_143.length>1){ +},toString:m.forwardCall("repr"),next:function(){ +while(_189.length>1){ try{ -return _143[0].next(); +return _189[0].next(); } catch(e){ if(e!=self.StopIteration){ throw e; } -_143.shift(); +_189.shift(); } } -if(_143.length==1){ -var arg=_143.shift(); -this.next=m.bind(arg.next,arg); +if(_189.length==1){ +var arg=_189.shift(); +this.next=m.bind("next",arg); return this.next(); } throw self.StopIteration; @@ -1061,7 +1240,7 @@ return "takewhile(...)"; seq=self.iter(seq); return {repr:function(){ return "takewhile(...)"; -},toString:MochiKit.Base.forward("repr"),next:function(){ +},toString:MochiKit.Base.forwardCall("repr"),next:function(){ var rval=seq.next(); if(!pred(rval)){ this.next=function(){ @@ -1077,68 +1256,71 @@ return "dropwhile(...)"; var bind=m.bind; return {"repr":function(){ return "dropwhile(...)"; -},"toString":m.forward("repr"),"next":function(){ +},"toString":m.forwardCall("repr"),"next":function(){ while(true){ var rval=seq.next(); if(!pred(rval)){ break; } } -this.next=bind(seq.next,seq); +this.next=bind("next",seq); return rval; }}; -},_tee:function(_145,sync,_147){ -sync.pos[_145]=-1; +},_tee:function(_194,sync,_196){ +sync.pos[_194]=-1; var m=MochiKit.Base; -var _148=m.listMin; +var _198=m.listMin; return {repr:function(){ -return "tee("+_145+", ...)"; -},toString:m.forward("repr"),next:function(){ +return "tee("+_194+", ...)"; +},toString:m.forwardCall("repr"),next:function(){ var rval; -var i=sync.pos[_145]; +var i=sync.pos[_194]; if(i==sync.max){ -rval=_147.next(); +rval=_196.next(); sync.deque.push(rval); sync.max+=1; -sync.pos[_145]+=1; +sync.pos[_194]+=1; }else{ rval=sync.deque[i-sync.min]; -sync.pos[_145]+=1; -if(i==sync.min&&_148(sync.pos)!=sync.min){ +sync.pos[_194]+=1; +if(i==sync.min&&_198(sync.pos)!=sync.min){ sync.min+=1; sync.deque.shift(); } } return rval; }}; -},tee:function(_149,n){ +},tee:function(_19b,n){ var rval=[]; var sync={"pos":[],"deque":[],"max":-1,"min":-1}; -if(arguments.length==1){ +if(arguments.length==1||typeof (n)=="undefined"||n===null){ n=2; } var self=MochiKit.Iter; -_149=self.iter(_149); +_19b=self.iter(_19b); var _tee=self._tee; for(var i=0;i0&&_155>=stop)||(step<0&&_155<=stop)){ +if((step>0&&_1ac>=stop)||(step<0&&_1ac<=stop)){ throw MochiKit.Iter.StopIteration; } -var rval=_155; -_155+=step; +var rval=_1ac; +_1ac+=step; return rval; },repr:function(){ -return "range("+[_155,stop,step].join(", ")+")"; -},toString:MochiKit.Base.forward("repr")}; -},sum:function(_156,_157){ -var x=_157||0; +return "range("+[_1ac,stop,step].join(", ")+")"; +},toString:MochiKit.Base.forwardCall("repr")}; +},sum:function(_1b0,_1b1){ +if(typeof (_1b1)=="undefined"||_1b1===null){ +_1b1=0; +} +var x=_1b1; var self=MochiKit.Iter; -_156=self.iter(_156); +_1b0=self.iter(_1b0); try{ while(true){ -x+=_156.next(); +x+=_1b0.next(); } } catch(e){ @@ -1224,12 +1409,12 @@ return x; } } return x; -},exhaust:function(_158){ +},exhaust:function(_1b4){ var self=MochiKit.Iter; -_158=self.iter(_158); +_1b4=self.iter(_1b4); try{ while(true){ -_158.next(); +_1b4.next(); } } catch(e){ @@ -1237,23 +1422,30 @@ throw e; throw e; } } -},forEach:function(_159,func,self){ +},forEach:function(_1b6,func,self){ var m=MochiKit.Base; if(arguments.length>2){ func=m.bind(func,self); } -if(m.isArrayLike(_159)){ -for(var i=0;i<_159.length;i++){ -func(_159[i]); +if(m.isArrayLike(_1b6)){ +try{ +for(var i=0;i<_1b6.length;i++){ +func(_1b6[i]); } +} +catch(e){ +if(e!=MochiKit.Iter.StopIteration){ +throw e; +} +} }else{ self=MochiKit.Iter; -self.exhaust(self.imap(func,_159)); +self.exhaust(self.imap(func,_1b6)); } -},every:function(_161,func){ +},every:function(_1bb,func){ var self=MochiKit.Iter; try{ -self.ifilterfalse(func,_161).next(); +self.ifilterfalse(func,_1bb).next(); return false; } catch(e){ @@ -1262,21 +1454,21 @@ return true; } return true; } -},sorted:function(_162,cmp){ -var rval=MochiKit.Iter.list(_162); +},sorted:function(_1be,cmp){ +var rval=MochiKit.Iter.list(_1be); if(arguments.length==1){ cmp=MochiKit.Base.compare; } rval.sort(cmp); return rval; -},reversed:function(_163){ -var rval=MochiKit.Iter.list(_163); +},reversed:function(_1c1){ +var rval=MochiKit.Iter.list(_1c1); rval.reverse(); return rval; -},some:function(_164,func){ +},some:function(_1c3,func){ var self=MochiKit.Iter; try{ -self.ifilter(func,_164).next(); +self.ifilter(func,_1c3).next(); return true; } catch(e){ @@ -1285,17 +1477,17 @@ return false; } return false; } -},iextend:function(lst,_165){ -if(MochiKit.Base.isArrayLike(_165)){ -for(var i=0;i<_165.length;i++){ -lst.push(_165[i]); +},iextend:function(lst,_1c7){ +if(MochiKit.Base.isArrayLike(_1c7)){ +for(var i=0;i<_1c7.length;i++){ +lst.push(_1c7[i]); } }else{ var self=MochiKit.Iter; -_165=self.iter(_165); +_1c7=self.iter(_1c7); try{ while(true){ -lst.push(_165.next()); +lst.push(_1c7.next()); } } catch(e){ @@ -1305,33 +1497,34 @@ return lst; } } return lst; -},groupby:function(_166,_167){ +},groupby:function(_1ca,_1cb){ var m=MochiKit.Base; var self=MochiKit.Iter; if(arguments.length<2){ -_167=m.operator.identity; +_1cb=m.operator.identity; } -_166=self.iter(_166); +_1ca=self.iter(_1ca); var pk=undefined; var k=undefined; var v; function fetch(){ -v=_166.next(); -k=_167(v); +v=_1ca.next(); +k=_1cb(v); } function eat(){ var ret=v; v=undefined; return ret; } -var _170=true; +var _1d2=true; +var _1d3=m.compare; return {repr:function(){ return "groupby(...)"; },next:function(){ -while(k==pk){ +while(_1d3(k,pk)===0){ fetch(); -if(_170){ -_170=false; +if(_1d2){ +_1d2=false; break; } } @@ -1340,26 +1533,27 @@ fetch(); if(v==undefined){ fetch(); } -if(k!=pk){ +if(_1d3(k,pk)!==0){ throw self.StopIteration; } return eat(); }}]; }}; -},groupby_as_array:function(_171,_172){ +},groupby_as_array:function(_1d4,_1d5){ var m=MochiKit.Base; var self=MochiKit.Iter; if(arguments.length<2){ -_172=m.operator.identity; +_1d5=m.operator.identity; } -_171=self.iter(_171); -var _173=[]; -var _174=true; -var _175; +_1d4=self.iter(_1d4); +var _1d8=[]; +var _1d9=true; +var _1da; +var _1db=m.compare; while(true){ try{ -var _176=_171.next(); -var key=_172(_176); +var _1dc=_1d4.next(); +var key=_1d5(_1dc); } catch(e){ if(e==self.StopIteration){ @@ -1367,32 +1561,32 @@ throw e; } throw e; } -if(_174||key!=_175){ -var _177=[]; -_173.push([key,_177]); +if(_1d9||_1db(key,_1da)!==0){ +var _1de=[]; +_1d8.push([key,_1de]); } -_177.push(_176); -_174=false; -_175=key; +_1de.push(_1dc); +_1d9=false; +_1da=key; } -return _173; -},arrayLikeIter:function(_178){ +return _1d8; +},arrayLikeIter:function(_1df){ var i=0; return {repr:function(){ return "arrayLikeIter(...)"; -},toString:MochiKit.Base.forward("repr"),next:function(){ -if(i>=_178.length){ +},toString:MochiKit.Base.forwardCall("repr"),next:function(){ +if(i>=_1df.length){ throw MochiKit.Iter.StopIteration; } -return _178[i++]; +return _1df[i++]; }}; -},hasIterateNext:function(_179){ -return (_179&&typeof (_179.iterateNext)=="function"); -},iterateNextIter:function(_180){ +},hasIterateNext:function(_1e1){ +return (_1e1&&typeof (_1e1.iterateNext)=="function"); +},iterateNextIter:function(_1e2){ return {repr:function(){ return "iterateNextIter(...)"; -},toString:MochiKit.Base.forward("repr"),next:function(){ -var rval=_180.iterateNext(); +},toString:MochiKit.Base.forwardCall("repr"),next:function(){ +var rval=_1e2.iterateNext(); if(rval===null||rval===undefined){ throw MochiKit.Iter.StopIteration; } @@ -1403,7 +1597,11 @@ var m=MochiKit.Base; MochiKit.Iter.EXPORT=["StopIteration","registerIteratorFactory","iter","count","cycle","repeat","next","izip","ifilter","ifilterfalse","islice","imap","applymap","chain","takewhile","dropwhile","tee","list","reduce","range","sum","exhaust","forEach","every","sorted","reversed","some","iextend","groupby","groupby_as_array"]; MochiKit.Iter.__new__=function(){ var m=MochiKit.Base; +if(typeof (StopIteration)!="undefined"){ +this.StopIteration=StopIteration; +}else{ this.StopIteration=new m.NamedError("StopIteration"); +} this.iteratorRegistry=new m.AdapterRegistry(); this.registerIteratorFactory("arrayLike",m.isArrayLike,this.arrayLikeIter); this.registerIteratorFactory("iterateNext",this.hasIterateNext,this.iterateNextIter); @@ -1411,7 +1609,9 @@ MochiKit.Iter.__new__(); m.nameFunctions(this); }; MochiKit.Iter.__new__(); +if(MochiKit.__export__){ reduce=MochiKit.Iter.reduce; +} MochiKit.Base._exportSymbols(this,MochiKit.Iter); if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.Logging"); @@ -1432,7 +1632,7 @@ MochiKit.Logging.NAME="MochiKit.Logging" MochiKit.Logging={}; } MochiKit.Logging.NAME="MochiKit.Logging"; -MochiKit.Logging.VERSION="1.2"; +MochiKit.Logging.VERSION="1.4"; MochiKit.Logging.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; @@ -1441,32 +1641,32 @@ MochiKit.Logging.EXPORT_OK=["logLevelAtL }; MochiKit.Logging.EXPORT=["LogLevel","LogMessage","Logger","alertListener","logger","log","logError","logDebug","logFatal","logWarning"]; MochiKit.Logging.EXPORT_OK=["logLevelAtLeast","isLogMessage","compareLogMessage"]; -MochiKit.Logging.LogMessage=function(num,_182,info){ +MochiKit.Logging.LogMessage=function(num,_1e6,info){ this.num=num; -this.level=_182; +this.level=_1e6; this.info=info; this.timestamp=new Date(); }; MochiKit.Logging.LogMessage.prototype={repr:function(){ var m=MochiKit.Base; return "LogMessage("+m.map(m.repr,[this.num,this.level,this.info]).join(", ")+")"; -},toString:MochiKit.Base.forward("repr")}; -MochiKit.Base.update(MochiKit.Logging,{logLevelAtLeast:function(_184){ +},toString:MochiKit.Base.forwardCall("repr")}; +MochiKit.Base.update(MochiKit.Logging,{logLevelAtLeast:function(_1e9){ var self=MochiKit.Logging; -if(typeof (_184)=="string"){ -_184=self.LogLevel[_184]; +if(typeof (_1e9)=="string"){ +_1e9=self.LogLevel[_1e9]; } return function(msg){ -var _186=msg.level; -if(typeof (_186)=="string"){ -_186=self.LogLevel[_186]; +var _1ec=msg.level; +if(typeof (_1ec)=="string"){ +_1ec=self.LogLevel[_1ec]; } -return _186>=_184; +return _1ec>=_1e9; }; },isLogMessage:function(){ -var _187=MochiKit.Logging.LogMessage; +var _1ed=MochiKit.Logging.LogMessage; for(var i=0;i=0&&this._messages.length>this.maxSize){ -this._messges.shift(); +this._messages.shift(); } -},getMessages:function(_196){ -var _197=0; -if(!(typeof (_196)=="undefined"||_196==null)){ -_197=Math.max(0,this._messages.length-_196); +},getMessages:function(_1ff){ +var _200=0; +if(!(typeof (_1ff)=="undefined"||_1ff===null)){ +_200=Math.max(0,this._messages.length-_1ff); } -return this._messages.slice(_197); -},getMessageText:function(_198){ -if(typeof (_198)=="undefined"||_198==null){ -_198=30; +return this._messages.slice(_200); +},getMessageText:function(_201){ +if(typeof (_201)=="undefined"||_201===null){ +_201=30; } -var _199=this.getMessages(_198); -if(_199.length){ +var _202=this.getMessages(_201); +if(_202.length){ var lst=map(function(m){ return "\n ["+m.num+"] "+m.level+": "+m.info.join(" "); -},_199); -lst.unshift("LAST "+_199.length+" MESSAGES:"); +},_202); +lst.unshift("LAST "+_202.length+" MESSAGES:"); return lst.join(""); } return ""; -},debuggingBookmarklet:function(_200){ +},debuggingBookmarklet:function(_205){ if(typeof (MochiKit.LoggingPane)=="undefined"){ alert(this.getMessageText()); }else{ -MochiKit.LoggingPane.createLoggingPane(_200||false); +MochiKit.LoggingPane.createLoggingPane(_205||false); } }}; MochiKit.Logging.__new__=function(){ this.LogLevel={ERROR:40,FATAL:50,WARNING:30,INFO:20,DEBUG:10}; var m=MochiKit.Base; m.registerComparator("LogMessage",this.isLogMessage,this.compareLogMessage); -var _201=m.partial; -var _202=this.Logger; -var _203=_202.prototype.baseLog; -m.update(this.Logger.prototype,{debug:_201(_203,"DEBUG"),log:_201(_203,"INFO"),error:_201(_203,"ERROR"),fatal:_201(_203,"FATAL"),warning:_201(_203,"WARNING")}); +var _207=m.partial; +var _208=this.Logger; +var _209=_208.prototype.baseLog; +m.update(this.Logger.prototype,{debug:_207(_209,"DEBUG"),log:_207(_209,"INFO"),error:_207(_209,"ERROR"),fatal:_207(_209,"FATAL"),warning:_207(_209,"WARNING")}); var self=this; -var _204=function(name){ +var _20b=function(name){ return function(){ self.logger[name].apply(self.logger,arguments); }; }; -this.log=_204("log"); -this.logError=_204("error"); -this.logDebug=_204("debug"); -this.logFatal=_204("fatal"); -this.logWarning=_204("warning"); -this.logger=new _202(); +this.log=_20b("log"); +this.logError=_20b("error"); +this.logDebug=_20b("debug"); +this.logFatal=_20b("fatal"); +this.logWarning=_20b("warning"); +this.logger=new _208(); +this.logger.useNativeConsole=true; this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }; +if(typeof (printfire)=="undefined"&&typeof (document)!="undefined"&&document.createEvent&&typeof (dispatchEvent)!="undefined"){ +printfire=function(){ +printfire.args=arguments; +var ev=document.createEvent("Events"); +ev.initEvent("printfire",false,true); +dispatchEvent(ev); +}; +} MochiKit.Logging.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Logging); if(typeof (dojo)!="undefined"){ @@ -1573,7 +1806,7 @@ MochiKit.DateTime.NAME="MochiKit.DateTim MochiKit.DateTime={}; } MochiKit.DateTime.NAME="MochiKit.DateTime"; -MochiKit.DateTime.VERSION="1.2"; +MochiKit.DateTime.VERSION="1.4"; MochiKit.DateTime.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; @@ -1582,11 +1815,11 @@ str=str+""; }; MochiKit.DateTime.isoDate=function(str){ str=str+""; -if(typeof (str)!="string"||str.length==0){ +if(typeof (str)!="string"||str.length===0){ return null; } var iso=str.split("-"); -if(iso.length==0){ +if(iso.length===0){ return null; } return new Date(iso[0],iso[1]-1,iso[2]); @@ -1594,38 +1827,38 @@ str=str+""; MochiKit.DateTime._isoRegexp=/(\d{4,})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d+))?)?(?:(Z)|([+-])(\d{1,2})(?::(\d{1,2}))?)?)?)?)?/; MochiKit.DateTime.isoTimestamp=function(str){ str=str+""; -if(typeof (str)!="string"||str.length==0){ +if(typeof (str)!="string"||str.length===0){ return null; } var res=str.match(MochiKit.DateTime._isoRegexp); -if(typeof (res)=="undefined"||res==null){ +if(typeof (res)=="undefined"||res===null){ return null; } -var year,month,day,hour,min,sec,msec; +var year,_213,day,hour,min,sec,msec; year=parseInt(res[1],10); -if(typeof (res[2])=="undefined"||res[2]==""){ +if(typeof (res[2])=="undefined"||res[2]===""){ return new Date(year); } -month=parseInt(res[2],10)-1; +_213=parseInt(res[2],10)-1; day=parseInt(res[3],10); -if(typeof (res[4])=="undefined"||res[4]==""){ -return new Date(year,month,day); +if(typeof (res[4])=="undefined"||res[4]===""){ +return new Date(year,_213,day); } hour=parseInt(res[4],10); min=parseInt(res[5],10); -sec=(typeof (res[6])!="undefined"&&res[6]!="")?parseInt(res[6],10):0; -if(typeof (res[7])!="undefined"&&res[7]!=""){ +sec=(typeof (res[6])!="undefined"&&res[6]!=="")?parseInt(res[6],10):0; +if(typeof (res[7])!="undefined"&&res[7]!==""){ msec=Math.round(1000*parseFloat("0."+res[7])); }else{ msec=0; } -if((typeof (res[8])=="undefined"||res[8]=="")&&(typeof (res[9])=="undefined"||res[9]=="")){ -return new Date(year,month,day,hour,min,sec,msec); +if((typeof (res[8])=="undefined"||res[8]==="")&&(typeof (res[9])=="undefined"||res[9]==="")){ +return new Date(year,_213,day,hour,min,sec,msec); } var ofs; -if(typeof (res[9])!="undefined"&&res[9]!=""){ +if(typeof (res[9])!="undefined"&&res[9]!==""){ ofs=parseInt(res[10],10)*3600000; -if(typeof (res[11])!="undefined"&&res[11]!=""){ +if(typeof (res[11])!="undefined"&&res[11]!==""){ ofs+=parseInt(res[11],10)*60000; } if(res[9]=="-"){ @@ -1634,39 +1867,39 @@ ofs=0; }else{ ofs=0; } -return new Date(Date.UTC(year,month,day,hour,min,sec,msec)-ofs); +return new Date(Date.UTC(year,_213,day,hour,min,sec,msec)-ofs); }; -MochiKit.DateTime.toISOTime=function(date,_210){ -if(typeof (date)=="undefined"||date==null){ +MochiKit.DateTime.toISOTime=function(date,_21b){ +if(typeof (date)=="undefined"||date===null){ return null; } var hh=date.getHours(); var mm=date.getMinutes(); var ss=date.getSeconds(); -var lst=[((_210&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)]; +var lst=[((_21b&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)]; return lst.join(":"); }; -MochiKit.DateTime.toISOTimestamp=function(date,_214){ -if(typeof (date)=="undefined"||date==null){ +MochiKit.DateTime.toISOTimestamp=function(date,_221){ +if(typeof (date)=="undefined"||date===null){ return null; } -var sep=_214?"T":" "; -var foot=_214?"Z":""; -if(_214){ +var sep=_221?"T":" "; +var foot=_221?"Z":""; +if(_221){ date=new Date(date.getTime()+(date.getTimezoneOffset()*60000)); } -return MochiKit.DateTime.toISODate(date)+sep+MochiKit.DateTime.toISOTime(date,_214)+foot; +return MochiKit.DateTime.toISODate(date)+sep+MochiKit.DateTime.toISOTime(date,_221)+foot; }; MochiKit.DateTime.toISODate=function(date){ -if(typeof (date)=="undefined"||date==null){ +if(typeof (date)=="undefined"||date===null){ return null; } -var _217=MochiKit.DateTime._padTwo; -return [date.getFullYear(),_217(date.getMonth()+1),_217(date.getDate())].join("-"); +var _225=MochiKit.DateTime._padTwo; +return [date.getFullYear(),_225(date.getMonth()+1),_225(date.getDate())].join("-"); }; MochiKit.DateTime.americanDate=function(d){ d=d+""; -if(typeof (d)!="string"||d.length==0){ +if(typeof (d)!="string"||d.length===0){ return null; } var a=d.split("/"); @@ -1676,14 +1909,14 @@ MochiKit.DateTime.toPaddedAmericanDate=f return (n>9)?n:"0"+n; }; MochiKit.DateTime.toPaddedAmericanDate=function(d){ -if(typeof (d)=="undefined"||d==null){ +if(typeof (d)=="undefined"||d===null){ return null; } -var _219=MochiKit.DateTime._padTwo; -return [_219(d.getMonth()+1),_219(d.getDate()),d.getFullYear()].join("/"); +var _22a=MochiKit.DateTime._padTwo; +return [_22a(d.getMonth()+1),_22a(d.getDate()),d.getFullYear()].join("/"); }; MochiKit.DateTime.toAmericanDate=function(d){ -if(typeof (d)=="undefined"||d==null){ +if(typeof (d)=="undefined"||d===null){ return null; } return [d.getMonth()+1,d.getDate(),d.getFullYear()].join("/"); @@ -1705,14 +1938,18 @@ MochiKit.DateTime.__new__(); } }; MochiKit.DateTime.__new__(); -(function(_220,_221){ -if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(typeof (MochiKit.__compat__)=="boolean"&&MochiKit.__compat__)){ -var all=_221.EXPORT_TAGS[":all"]; +if(typeof (MochiKit.Base)!="undefined"){ +MochiKit.Base._exportSymbols(this,MochiKit.DateTime); +}else{ +(function(_22f,_230){ +if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ +var all=_230.EXPORT_TAGS[":all"]; for(var i=0;i_229){ -var i=_235.length-_229; -res=fmt.separator+_235.substring(i,_235.length)+res; -_235=_235.substring(0,i); +if(_23a){ +while(_242.length>_23a){ +var i=_242.length-_23a; +res=fmt.separator+_242.substring(i,_242.length)+res; +_242=_242.substring(0,i); } } -res=_235+res; -if(_227>0){ -while(frac.length<_230){ +res=_242+res; +if(_238>0){ +while(frac.length<_23b){ frac=frac+"0"; } res=res+fmt.decimal+frac; } -return _231+res+_232; +return _23d+res+_23e; }; }; -MochiKit.Format.numberFormatter=function(_237,_238,_239){ -if(typeof (_238)=="undefined"){ -_238=""; +MochiKit.Format.numberFormatter=function(_246,_247,_248){ +if(typeof (_247)=="undefined"){ +_247=""; } -var _240=_237.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/); -if(!_240){ +var _249=_246.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/); +if(!_249){ throw TypeError("Invalid pattern"); } -var _241=_237.substr(0,_240.index); -var _242=_237.substr(_240.index+_240[0].length); -if(_241.search(/-/)==-1){ -_241=_241+"-"; +var _24a=_246.substr(0,_249.index); +var _24b=_246.substr(_249.index+_249[0].length); +if(_24a.search(/-/)==-1){ +_24a=_24a+"-"; } -var _243=_240[1]; -var frac=(typeof (_240[2])=="string"&&_240[2]!="")?_240[2]:""; -var _244=(typeof (_240[3])=="string"&&_240[3]!=""); -var tmp=_243.split(/,/); -var _246; -if(typeof (_239)=="undefined"){ -_239="default"; +var _24c=_249[1]; +var frac=(typeof (_249[2])=="string"&&_249[2]!="")?_249[2]:""; +var _24e=(typeof (_249[3])=="string"&&_249[3]!=""); +var tmp=_24c.split(/,/); +var _250; +if(typeof (_248)=="undefined"){ +_248="default"; } if(tmp.length==1){ -_246=null; +_250=null; }else{ -_246=tmp[1].length; +_250=tmp[1].length; } -var _247=_243.length-_243.replace(/0/g,"").length; -var _248=frac.length-frac.replace(/0/g,"").length; -var _249=frac.length; -var rval=MochiKit.Format._numberFormatter(_238,_241,_242,_239,_244,_249,_247,_246,_248); +var _251=_24c.length-_24c.replace(/0/g,"").length; +var _252=frac.length-frac.replace(/0/g,"").length; +var _253=frac.length; +var rval=MochiKit.Format._numberFormatter(_247,_24a,_24b,_248,_24e,_253,_251,_250,_252); var m=MochiKit.Base; if(m){ var fn=arguments.callee; @@ -1814,33 +2051,33 @@ return rval; } return rval; }; -MochiKit.Format.formatLocale=function(_251){ -if(typeof (_251)=="undefined"||_251==null){ -_251="default"; +MochiKit.Format.formatLocale=function(_258){ +if(typeof (_258)=="undefined"||_258===null){ +_258="default"; } -if(typeof (_251)=="string"){ -var rval=MochiKit.Format.LOCALE[_251]; +if(typeof (_258)=="string"){ +var rval=MochiKit.Format.LOCALE[_258]; if(typeof (rval)=="string"){ rval=arguments.callee(rval); -MochiKit.Format.LOCALE[_251]=rval; +MochiKit.Format.LOCALE[_258]=rval; } return rval; }else{ -return _251; +return _258; } }; -MochiKit.Format.twoDigitAverage=function(_252,_253){ -if(_253){ -var res=_252/_253; +MochiKit.Format.twoDigitAverage=function(_25a,_25b){ +if(_25b){ +var res=_25a/_25b; if(!isNaN(res)){ -return MochiKit.Format.twoDigitFloat(_252/_253); +return MochiKit.Format.twoDigitFloat(_25a/_25b); } } return "0"; }; -MochiKit.Format.twoDigitFloat=function(_254){ -var sign=(_254<0?"-":""); -var s=Math.floor(Math.abs(_254)*100).toString(); +MochiKit.Format.twoDigitFloat=function(_25d){ +var sign=(_25d<0?"-":""); +var s=Math.floor(Math.abs(_25d)*100).toString(); if(s=="0"){ return s; } @@ -1862,45 +2099,45 @@ return head+"."+tail; } } }; -MochiKit.Format.lstrip=function(str,_259){ +MochiKit.Format.lstrip=function(str,_263){ str=str+""; if(typeof (str)!="string"){ return null; } -if(!_259){ +if(!_263){ return str.replace(/^\s+/,""); }else{ -return str.replace(new RegExp("^["+_259+"]+"),""); +return str.replace(new RegExp("^["+_263+"]+"),""); } }; -MochiKit.Format.rstrip=function(str,_260){ +MochiKit.Format.rstrip=function(str,_265){ str=str+""; if(typeof (str)!="string"){ return null; } -if(!_260){ +if(!_265){ return str.replace(/\s+$/,""); }else{ -return str.replace(new RegExp("["+_260+"]+$"),""); +return str.replace(new RegExp("["+_265+"]+$"),""); } }; -MochiKit.Format.strip=function(str,_261){ +MochiKit.Format.strip=function(str,_267){ var self=MochiKit.Format; -return self.rstrip(self.lstrip(str,_261),_261); +return self.rstrip(self.lstrip(str,_267),_267); }; -MochiKit.Format.truncToFixed=function(_262,_263){ -_262=Math.floor(_262*Math.pow(10,_263)); -var res=(_262*Math.pow(10,-_263)).toFixed(_263); +MochiKit.Format.truncToFixed=function(_269,_26a){ +_269=Math.floor(_269*Math.pow(10,_26a)); +var res=(_269*Math.pow(10,-_26a)).toFixed(_26a); if(res.charAt(0)=="."){ res="0"+res; } return res; }; -MochiKit.Format.roundToFixed=function(_264,_265){ -return MochiKit.Format.truncToFixed(_264+0.5*Math.pow(10,-_265),_265); +MochiKit.Format.roundToFixed=function(_26c,_26d){ +return MochiKit.Format.truncToFixed(_26c+0.5*Math.pow(10,-_26d),_26d); }; -MochiKit.Format.percentFormat=function(_266){ -return MochiKit.Format.twoDigitFloat(100*_266)+"%"; +MochiKit.Format.percentFormat=function(_26e){ +return MochiKit.Format.twoDigitFloat(100*_26e)+"%"; }; MochiKit.Format.EXPORT=["truncToFixed","roundToFixed","numberFormatter","formatLocale","twoDigitAverage","twoDigitFloat","percentFormat","lstrip","rstrip","strip"]; MochiKit.Format.LOCALE={en_US:{separator:",",decimal:".",percent:"%"},de_DE:{separator:".",decimal:",",percent:"%"},fr_FR:{separator:" ",decimal:",",percent:"%"},"default":"en_US"}; @@ -1930,14 +2167,18 @@ MochiKit.Format.__new__(); } }; MochiKit.Format.__new__(); -(function(_267,_268){ -if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(typeof (MochiKit.__compat__)=="boolean"&&MochiKit.__compat__)){ -var all=_268.EXPORT_TAGS[":all"]; +if(typeof (MochiKit.Base)!="undefined"){ +MochiKit.Base._exportSymbols(this,MochiKit.Format); +}else{ +(function(_273,_274){ +if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ +var all=_274.EXPORT_TAGS[":all"]; for(var i=0;i=0)){ -this._fire(); -} -},_continue:function(res){ -this._resback(res); -this._unpause(); },_resback:function(res){ this.fired=((res instanceof Error)?1:0); this.results[this.fired]=res; @@ -2025,11 +2257,18 @@ this._check(); } },callback:function(res){ this._check(); +if(res instanceof MochiKit.Async.Deferred){ +throw new Error("Deferred instances can only be chained if they are the result of a callback"); +} this._resback(res); },errback:function(res){ this._check(); +var self=MochiKit.Async; +if(res instanceof self.Deferred){ +throw new Error("Deferred instances can only be chained if they are the result of a callback"); +} if(!(res instanceof Error)){ -res=new MochiKit.Async.GenericError(res); +res=new self.GenericError(res); } this._resback(res); },addBoth:function(fn){ @@ -2048,58 +2287,69 @@ return this.addCallbacks(null,fn); } return this.addCallbacks(null,fn); },addCallbacks:function(cb,eb){ +if(this.chained){ +throw new Error("Chained Deferreds can not be re-used"); +} this.chain.push([cb,eb]); if(this.fired>=0){ this._fire(); } return this; },_fire:function(){ -var _273=this.chain; -var _274=this.fired; -var res=this.results[_274]; +var _283=this.chain; +var _284=this.fired; +var res=this.results[_284]; var self=this; var cb=null; -while(_273.length>0&&this.paused==0){ -var pair=_273.shift(); -var f=pair[_274]; -if(f==null){ +while(_283.length>0&&this.paused===0){ +var pair=_283.shift(); +var f=pair[_284]; +if(f===null){ continue; } try{ res=f(res); -_274=((res instanceof Error)?1:0); +_284=((res instanceof Error)?1:0); if(res instanceof MochiKit.Async.Deferred){ cb=function(res){ -self._continue(res); +self._resback(res); +self.paused--; +if((self.paused===0)&&(self.fired>=0)){ +self._fire(); +} }; -this._pause(); +this.paused++; } } catch(err){ -_274=1; +_284=1; +if(!(err instanceof Error)){ +err=new MochiKit.Async.GenericError(err); +} res=err; } } -this.fired=_274; -this.results[_274]=res; +this.fired=_284; +this.results[_284]=res; if(cb&&this.paused){ res.addBoth(cb); +res.chained=true; } }}; MochiKit.Base.update(MochiKit.Async,{evalJSONRequest:function(){ return eval("("+arguments[0].responseText+")"); -},succeed:function(_276){ +},succeed:function(_28b){ var d=new MochiKit.Async.Deferred(); d.callback.apply(d,arguments); return d; -},fail:function(_277){ +},fail:function(_28d){ var d=new MochiKit.Async.Deferred(); d.errback.apply(d,arguments); return d; },getXMLHttpRequest:function(){ var self=arguments.callee; if(!self.XMLHttpRequest){ -var _278=[function(){ +var _290=[function(){ return new XMLHttpRequest(); },function(){ return new ActiveXObject("Msxml2.XMLHTTP"); @@ -2110,8 +2360,8 @@ throw new MochiKit.Async.BrowserComplian },function(){ throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest"); }]; -for(var i=0;i<_278.length;i++){ -var func=_278[i]; +for(var i=0;i<_290.length;i++){ +var func=_290[i]; try{ self.XMLHttpRequest=func; return func(); @@ -2121,63 +2371,61 @@ return self.XMLHttpRequest(); } } return self.XMLHttpRequest(); -},sendXMLHttpRequest:function(req,_280){ -if(typeof (_280)=="undefined"){ -_280=null; -} -var _281=function(){ +},_xhr_onreadystatechange:function(d){ +var m=MochiKit.Base; +if(this.readyState==4){ try{ -req.onreadystatechange=null; +this.onreadystatechange=null; } catch(e){ try{ -req.onreadystatechange=function(){ -}; +this.onreadystatechange=m.noop; } catch(e){ } } -req.abort(); -}; -var self=MochiKit.Async; -var d=new self.Deferred(_281); -var _282=function(){ -if(req.readyState==4){ +var _295=null; try{ -req.onreadystatechange=null; +_295=this.status; +if(!_295&&m.isNotEmpty(this.responseText)){ +_295=304; } +} catch(e){ -try{ -req.onreadystatechange=function(){ -}; } -catch(e){ +if(_295==200||_295==201||_295==204||_295==304||_295==1223){ +d.callback(this); +}else{ +var err=new MochiKit.Async.XMLHttpRequestError(this,"Request failed"); +if(err.number){ +d.errback(err); +}else{ +d.errback(err); } } -var _283=null; +} +},_xhr_canceller:function(req){ try{ -_283=req.status; -if(!_283&&MochiKit.Base.isNotEmpty(req.responseText)){ -_283=304; +req.onreadystatechange=null; } -} catch(e){ +try{ +req.onreadystatechange=MochiKit.Base.noop; } -if(_283==200||_283==304){ -d.callback(req); -}else{ -var err=new self.XMLHttpRequestError(req,"Request failed"); -if(err.number){ -d.errback(err); -}else{ -d.errback(err); +catch(e){ } } +req.abort(); +},sendXMLHttpRequest:function(req,_299){ +if(typeof (_299)=="undefined"||_299===null){ +_299=""; } -}; +var m=MochiKit.Base; +var self=MochiKit.Async; +var d=new self.Deferred(m.partial(self._xhr_canceller,req)); try{ -req.onreadystatechange=_282; -req.send(_280); +req.onreadystatechange=m.bind(self._xhr_onreadystatechange,req,d); +req.send(_299); } catch(e){ try{ @@ -2188,45 +2436,82 @@ return d; d.errback(e); } return d; -},doSimpleXMLHttpRequest:function(url){ +},doXHR:function(url,opts){ var self=MochiKit.Async; +return self.callLater(0,self._doXHR,url,opts); +},_doXHR:function(url,opts){ +var m=MochiKit.Base; +opts=m.update({method:"GET",sendContent:""},opts); +var self=MochiKit.Async; var req=self.getXMLHttpRequest(); +if(opts.queryString){ +var qs=m.queryString(opts.queryString); +if(qs){ +url+="?"+qs; +} +} +if("username" in opts){ +req.open(opts.method,url,true,opts.username,opts.password); +}else{ +req.open(opts.method,url,true); +} +if(req.overrideMimeType&&opts.mimeType){ +req.overrideMimeType(opts.mimeType); +} +if(opts.headers){ +var _2a6=opts.headers; +if(!m.isArrayLike(_2a6)){ +_2a6=m.items(_2a6); +} +for(var i=0;i<_2a6.length;i++){ +var _2a8=_2a6[i]; +var name=_2a8[0]; +var _2aa=_2a8[1]; +req.setRequestHeader(name,_2aa); +} +} +return self.sendXMLHttpRequest(req,opts.sendContent); +},_buildURL:function(url){ if(arguments.length>1){ var m=MochiKit.Base; var qs=m.queryString.apply(null,m.extend(null,arguments,1)); if(qs){ -url+="?"+qs; +return url+"?"+qs; } } -req.open("GET",url,true); -return self.sendXMLHttpRequest(req); +return url; +},doSimpleXMLHttpRequest:function(url){ +var self=MochiKit.Async; +url=self._buildURL.apply(self,arguments); +return self.doXHR(url); },loadJSONDoc:function(url){ var self=MochiKit.Async; -var d=self.doSimpleXMLHttpRequest.apply(self,arguments); +url=self._buildURL.apply(self,arguments); +var d=self.doXHR(url,{"mimeType":"text/plain","headers":[["Accept","application/json"]]}); d=d.addCallback(self.evalJSONRequest); return d; -},wait:function(_287,_288){ +},wait:function(_2b3,_2b4){ var d=new MochiKit.Async.Deferred(); var m=MochiKit.Base; -if(typeof (_288)!="undefined"){ +if(typeof (_2b4)!="undefined"){ d.addCallback(function(){ -return _288; +return _2b4; }); } -var _289=setTimeout(m.bind(d.callback,d),Math.floor(_287*1000)); +var _2b7=setTimeout(m.bind("callback",d),Math.floor(_2b3*1000)); d.canceller=function(){ try{ -clearTimeout(_289); +clearTimeout(_2b7); } catch(e){ } }; return d; -},callLater:function(_290,func){ +},callLater:function(_2b8,func){ var m=MochiKit.Base; -var _291=m.partial.apply(m,m.extend(null,arguments,1)); -return MochiKit.Async.wait(_290).addCallback(function(res){ -return _291(); +var _2bb=m.partial.apply(m,m.extend(null,arguments,1)); +return MochiKit.Async.wait(_2b8).addCallback(function(res){ +return _2bb(); }); }}); MochiKit.Async.DeferredLock=function(){ @@ -2235,7 +2520,7 @@ MochiKit.Async.DeferredLock.prototype={_ this.id=this._nextId(); }; MochiKit.Async.DeferredLock.prototype={__class__:MochiKit.Async.DeferredLock,acquire:function(){ -d=new MochiKit.Async.Deferred(); +var d=new MochiKit.Async.Deferred(); if(this.locked){ this.waiting.push(d); }else{ @@ -2253,24 +2538,97 @@ this.waiting.shift().callback(this); this.waiting.shift().callback(this); } },_nextId:MochiKit.Base.counter(),repr:function(){ -var _292; +var _2be; if(this.locked){ -_292="locked, "+this.waiting.length+" waiting"; +_2be="locked, "+this.waiting.length+" waiting"; }else{ -_292="unlocked"; +_2be="unlocked"; } -return "DeferredLock("+this.id+", "+_292+")"; -},toString:MochiKit.Base.forward("repr")}; -MochiKit.Async.EXPORT=["AlreadyCalledError","CancelledError","BrowserComplianceError","GenericError","XMLHttpRequestError","Deferred","succeed","fail","getXMLHttpRequest","doSimpleXMLHttpRequest","loadJSONDoc","wait","callLater","sendXMLHttpRequest","DeferredLock"]; +return "DeferredLock("+this.id+", "+_2be+")"; +},toString:MochiKit.Base.forwardCall("repr")}; +MochiKit.Async.DeferredList=function(list,_2c0,_2c1,_2c2,_2c3){ +MochiKit.Async.Deferred.apply(this,[_2c3]); +this.list=list; +var _2c4=[]; +this.resultList=_2c4; +this.finishedCount=0; +this.fireOnOneCallback=_2c0; +this.fireOnOneErrback=_2c1; +this.consumeErrors=_2c2; +var cb=MochiKit.Base.bind(this._cbDeferred,this); +for(var i=0;i=0){ +var opt=elem.options[elem.selectedIndex]; +var v=opt.value; +if(!v){ +var h=opt.outerHTML; +if(h&&!h.match(/^[^>]+\svalue\s*=/i)){ +v=opt.text; +} +} +_2e3.push(name); +_2e4.push(v); return null; } +_2e3.push(name); +_2e4.push(""); +return null; +}else{ +var opts=elem.options; +if(!opts.length){ +_2e3.push(name); +_2e4.push(""); +return null; +} +for(var i=0;i]+\svalue\s*=/i)){ +v=opt.text; +} +} +_2e3.push(name); +_2e4.push(v); +} +return null; +} +} +if(_2e9==="FORM"||_2e9==="P"||_2e9==="SPAN"||_2e9==="DIV"){ return elem.childNodes; +} +_2e3.push(name); +_2e4.push(elem.value||""); +return null; +} +return elem.childNodes; }); -return [_302,_303]; -}; -MochiKit.DOM.withDocument=function(doc,func){ +return [_2e3,_2e4]; +},withDocument:function(doc,func){ var self=MochiKit.DOM; -var _306=self._document; +var _2f2=self._document; var rval; try{ self._document=doc; rval=func(); } catch(e){ -self._document=_306; +self._document=_2f2; throw e; } -self._document=_306; +self._document=_2f2; return rval; -}; -MochiKit.DOM.registerDOMConverter=function(name,_307,wrap,_308){ -MochiKit.DOM.domConverters.register(name,_307,wrap,_308); -}; -MochiKit.DOM.coerceToDOM=function(node,ctx){ +},registerDOMConverter:function(name,_2f5,wrap,_2f7){ +MochiKit.DOM.domConverters.register(name,_2f5,wrap,_2f7); +},coerceToDOM:function(node,ctx){ +var m=MochiKit.Base; var im=MochiKit.Iter; var self=MochiKit.DOM; +if(im){ var iter=im.iter; -var _313=im.repeat; -var imap=im.imap; -var _315=self.domConverters; -var _316=self.coerceToDOM; -var _317=MochiKit.Base.NotFound; +var _2fe=im.repeat; +var map=m.map; +} +var _300=self.domConverters; +var _301=arguments.callee; +var _302=m.NotFound; while(true){ -if(typeof (node)=="undefined"||node==null){ +if(typeof (node)=="undefined"||node===null){ return null; } +if(typeof (node)=="function"&&typeof (node.length)=="number"&&!(node instanceof Function)){ +node=im.list(node); +} if(typeof (node.nodeType)!="undefined"&&node.nodeType>0){ return node; } -if(typeof (node)=="number"||typeof (node)=="bool"){ +if(typeof (node)=="number"||typeof (node)=="boolean"){ node=node.toString(); } if(typeof (node)=="string"){ return self._document.createTextNode(node); } -if(typeof (node.toDOM)=="function"){ -node=node.toDOM(ctx); +if(typeof (node.__dom__)=="function"){ +node=node.__dom__(ctx); continue; } +if(typeof (node.dom)=="function"){ +node=node.dom(ctx); +continue; +} if(typeof (node)=="function"){ -node=node(ctx); +node=node.apply(ctx,[ctx]); continue; } -var _318=null; +if(im){ +var _303=null; try{ -_318=iter(node); +_303=iter(node); } catch(e){ } -if(_318){ -return imap(_316,_318,_313(ctx)); +if(_303){ +return map(_301,_303,_2fe(ctx)); } +} try{ -node=_315.match(node,ctx); +node=_300.match(node,ctx); continue; } catch(e){ -if(e!=_317){ +if(e!=_302){ throw e; } } return self._document.createTextNode(node.toString()); } return undefined; -}; -MochiKit.DOM.setNodeAttribute=function(node,attr,_320){ +},isChildNode:function(node,_305){ +var self=MochiKit.DOM; +if(typeof (node)=="string"){ +node=self.getElement(node); +} +if(typeof (_305)=="string"){ +_305=self.getElement(_305); +} +if(node===_305){ +return true; +} +while(node&&node.tagName.toUpperCase()!="BODY"){ +node=node.parentNode; +if(node===_305){ +return true; +} +} +return false; +},setNodeAttribute:function(node,attr,_309){ var o={}; -o[attr]=_320; +o[attr]=_309; try{ return MochiKit.DOM.updateNodeAttributes(node,o); } catch(e){ } return null; -}; -MochiKit.DOM.getNodeAttribute=function(node,attr){ +},getNodeAttribute:function(node,attr){ var self=MochiKit.DOM; -var _321=self.attributeArray.renames[attr]; +var _30e=self.attributeArray.renames[attr]; node=self.getElement(node); try{ -if(_321){ -return node[_321]; +if(_30e){ +return node[_30e]; } return node.getAttribute(attr); } catch(e){ } return null; -}; -MochiKit.DOM.updateNodeAttributes=function(node,_322){ +},removeNodeAttribute:function(node,attr){ +var self=MochiKit.DOM; +var _312=self.attributeArray.renames[attr]; +node=self.getElement(node); +try{ +if(_312){ +return node[_312]; +} +return node.removeAttribute(attr); +} +catch(e){ +} +return null; +},updateNodeAttributes:function(node,_314){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } -if(_322){ -var _323=MochiKit.Base.updatetree; +if(_314){ +var _317=MochiKit.Base.updatetree; if(self.attributeArray.compliant){ -for(var k in _322){ -var v=_322[k]; +for(var k in _314){ +var v=_314[k]; if(typeof (v)=="object"&&typeof (elem[k])=="object"){ -_323(elem[k],v); +if(k=="style"&&MochiKit.Style){ +MochiKit.Style.setStyle(elem,v); }else{ +_317(elem[k],v); +} +}else{ if(k.substring(0,2)=="on"){ if(typeof (v)=="string"){ v=new Function(v); @@ -2524,19 +2933,23 @@ elem.setAttribute(k,v); } } }else{ -var _324=self.attributeArray.renames; -for(k in _322){ -v=_322[k]; -var _325=_324[k]; +var _31a=self.attributeArray.renames; +for(var k in _314){ +v=_314[k]; +var _31b=_31a[k]; if(k=="style"&&typeof (v)=="string"){ elem.style.cssText=v; }else{ -if(typeof (_325)=="string"){ -elem[_325]=v; +if(typeof (_31b)=="string"){ +elem[_31b]=v; }else{ if(typeof (elem[k])=="object"&&typeof (v)=="object"){ -_323(elem[k],v); +if(k=="style"&&MochiKit.Style){ +MochiKit.Style.setStyle(elem,v); }else{ +_317(elem[k],v); +} +}else{ if(k.substring(0,2)=="on"){ if(typeof (v)=="string"){ v=new Function(v); @@ -2552,146 +2965,175 @@ return elem; } } return elem; -}; -MochiKit.DOM.appendChildNodes=function(node){ +},appendChildNodes:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } -var _326=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; -var _327=MochiKit.Iter.iextend; -while(_326.length){ -var n=_326.shift(); -if(typeof (n)=="undefined"||n==null){ +var _31f=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; +var _320=MochiKit.Base.concat; +while(_31f.length){ +var n=_31f.shift(); +if(typeof (n)=="undefined"||n===null){ }else{ if(typeof (n.nodeType)=="number"){ elem.appendChild(n); }else{ -_327(_326,n); +_31f=_320(n,_31f); } } } return elem; -}; -MochiKit.DOM.replaceChildNodes=function(node){ +},insertSiblingNodesBefore:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); +} +var _325=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; +var _326=elem.parentNode; +var _327=MochiKit.Base.concat; +while(_325.length){ +var n=_325.shift(); +if(typeof (n)=="undefined"||n===null){ +}else{ +if(typeof (n.nodeType)=="number"){ +_326.insertBefore(n,elem); +}else{ +_325=_327(n,_325); +} +} +} +return _326; +},insertSiblingNodesAfter:function(node){ +var elem=node; +var self=MochiKit.DOM; +if(typeof (node)=="string"){ +elem=self.getElement(node); +} +var _32c=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; +if(elem.nextSibling){ +return self.insertSiblingNodesBefore(elem.nextSibling,_32c); +}else{ +return self.appendChildNodes(elem.parentNode,_32c); +} +},replaceChildNodes:function(node){ +var elem=node; +var self=MochiKit.DOM; +if(typeof (node)=="string"){ +elem=self.getElement(node); arguments[0]=elem; } -var _328; -while((_328=elem.firstChild)){ -elem.removeChild(_328); +var _330; +while((_330=elem.firstChild)){ +elem.removeChild(_330); } if(arguments.length<2){ return elem; }else{ return self.appendChildNodes.apply(this,arguments); } -}; -MochiKit.DOM.createDOM=function(name,_329){ +},createDOM:function(name,_332){ var elem; var self=MochiKit.DOM; +var m=MochiKit.Base; +if(typeof (_332)=="string"||typeof (_332)=="number"){ +var args=m.extend([name,null],arguments,1); +return arguments.callee.apply(this,args); +} if(typeof (name)=="string"){ -if(_329&&"name" in _329&&!self.attributeArray.compliant){ -name="<"+name+" name=\""+self.escapeHTML(_329.name)+"\">"; +var _337=self._xhtml; +if(_332&&!self.attributeArray.compliant){ +var _338=""; +if("name" in _332){ +_338+=" name=\""+self.escapeHTML(_332.name)+"\""; } -elem=self._document.createElement(name); +if(name=="input"&&"type" in _332){ +_338+=" type=\""+self.escapeHTML(_332.type)+"\""; +} +if(_338){ +name="<"+name+_338+">"; +_337=false; +} +} +var d=self._document; +if(_337&&d===document){ +elem=d.createElementNS("http://www.w3.org/1999/xhtml",name); }else{ +elem=d.createElement(name); +} +}else{ elem=name; } -if(_329){ -self.updateNodeAttributes(elem,_329); +if(_332){ +self.updateNodeAttributes(elem,_332); } if(arguments.length<=2){ return elem; }else{ -var args=MochiKit.Base.extend([elem],arguments,2); +var args=m.extend([elem],arguments,2); return self.appendChildNodes.apply(this,args); } -}; -MochiKit.DOM.createDOMFunc=function(){ +},createDOMFunc:function(){ var m=MochiKit.Base; return m.partial.apply(this,m.extend([MochiKit.DOM.createDOM],arguments)); -}; -MochiKit.DOM.swapDOM=function(dest,src){ +},removeElement:function(elem){ +var e=MochiKit.DOM.getElement(elem); +e.parentNode.removeChild(e); +return e; +},swapDOM:function(dest,src){ var self=MochiKit.DOM; dest=self.getElement(dest); -var _332=dest.parentNode; +var _340=dest.parentNode; if(src){ src=self.getElement(src); -_332.replaceChild(src,dest); +_340.replaceChild(src,dest); }else{ -_332.removeChild(dest); +_340.removeChild(dest); } return src; -}; -MochiKit.DOM.getElement=function(id){ +},getElement:function(id){ var self=MochiKit.DOM; if(arguments.length==1){ return ((typeof (id)=="string")?self._document.getElementById(id):id); }else{ return MochiKit.Base.map(self.getElement,arguments); } -}; -MochiKit.DOM.computedStyle=function(_334,_335,_336){ -if(arguments.length==2){ -_336=_335; -} +},getElementsByTagAndClassName:function(_343,_344,_345){ var self=MochiKit.DOM; -var el=self.getElement(_334); -var _338=self._document; -if(!el||el==_338){ -return undefined; +if(typeof (_343)=="undefined"||_343===null){ +_343="*"; } -if(el.currentStyle){ -return el.currentStyle[_335]; +if(typeof (_345)=="undefined"||_345===null){ +_345=self._document; } -if(typeof (_338.defaultView)=="undefined"){ -return undefined; +_345=self.getElement(_345); +var _347=(_345.getElementsByTagName(_343)||self._document.all); +if(typeof (_344)=="undefined"||_344===null){ +return MochiKit.Base.extend(null,_347); } -if(_338.defaultView==null){ -return undefined; +var _348=[]; +for(var i=0;i<_347.length;i++){ +var _34a=_347[i]; +var cls=_34a.className; +if(!cls){ +continue; } -var _339=_338.defaultView.getComputedStyle(el,null); -if(typeof (_339)=="undefined"||_339==null){ -return undefined; -} -return _339.getPropertyValue(_336); -}; -MochiKit.DOM.getElementsByTagAndClassName=function(_340,_341,_342){ -var self=MochiKit.DOM; -if(typeof (_340)=="undefined"||_340==null){ -_340="*"; -} -if(typeof (_342)=="undefined"||_342==null){ -_342=self._document; -} -_342=self.getElement(_342); -var _343=_342.getElementsByTagName(_340)||self._document.all; -if(typeof (_341)=="undefined"||_341==null){ -return MochiKit.Base.extend(null,_343); -} -var _344=[]; -for(var i=0;i<_343.length;i++){ -var _345=_343[i]; -var _346=_345.className.split(" "); -for(var j=0;j<_346.length;j++){ -if(_346[j]==_341){ -_344.push(_345); +var _34c=cls.split(" "); +for(var j=0;j<_34c.length;j++){ +if(_34c[j]==_344){ +_348.push(_34a); break; } } } -return _344; -}; -MochiKit.DOM._newCallStack=function(path,once){ +return _348; +},_newCallStack:function(path,once){ var rval=function(){ -var _349=arguments.callee.callStack; -for(var i=0;i<_349.length;i++){ -if(_349[i].apply(this,arguments)===false){ +var _351=arguments.callee.callStack; +for(var i=0;i<_351.length;i++){ +if(_351[i].apply(this,arguments)===false){ break; } } @@ -2705,107 +3147,102 @@ return rval; }; rval.callStack=[]; return rval; -}; -MochiKit.DOM.addToCallStack=function(_350,path,func,once){ +},addToCallStack:function(_353,path,func,once){ var self=MochiKit.DOM; -var _351=_350[path]; -var _352=_351; -if(!(typeof (_351)=="function"&&typeof (_351.callStack)=="object"&&_351.callStack!=null)){ -_352=self._newCallStack(path,once); -if(typeof (_351)=="function"){ -_352.callStack.push(_351); +var _358=_353[path]; +var _359=_358; +if(!(typeof (_358)=="function"&&typeof (_358.callStack)=="object"&&_358.callStack!==null)){ +_359=self._newCallStack(path,once); +if(typeof (_358)=="function"){ +_359.callStack.push(_358); } -_350[path]=_352; +_353[path]=_359; } -_352.callStack.push(func); -}; -MochiKit.DOM.addLoadEvent=function(func){ +_359.callStack.push(func); +},addLoadEvent:function(func){ var self=MochiKit.DOM; self.addToCallStack(self._window,"onload",func,true); -}; -MochiKit.DOM.focusOnLoad=function(_353){ +},focusOnLoad:function(_35c){ var self=MochiKit.DOM; self.addLoadEvent(function(){ -_353=self.getElement(_353); -if(_353){ -_353.focus(); +_35c=self.getElement(_35c); +if(_35c){ +_35c.focus(); } }); -}; -MochiKit.DOM.setElementClass=function(_354,_355){ +},setElementClass:function(_35e,_35f){ var self=MochiKit.DOM; -var obj=self.getElement(_354); +var obj=self.getElement(_35e); if(self.attributeArray.compliant){ -obj.setAttribute("class",_355); +obj.setAttribute("class",_35f); }else{ -obj.setAttribute("className",_355); +obj.setAttribute("className",_35f); } -}; -MochiKit.DOM.toggleElementClass=function(_356){ +},toggleElementClass:function(_362){ var self=MochiKit.DOM; for(var i=1;i/g,">"); -}; -MochiKit.DOM.toHTML=function(dom){ +},toHTML:function(dom){ return MochiKit.DOM.emitHTML(dom).join(""); -}; -MochiKit.DOM.emitHTML=function(dom,lst){ -if(typeof (lst)=="undefined"||lst==null){ +},emitHTML:function(dom,lst){ +if(typeof (lst)=="undefined"||lst===null){ lst=[]; } -var _372=[dom]; +var _385=[dom]; var self=MochiKit.DOM; -var _373=self.escapeHTML; -var _374=self.attributeArray; -while(_372.length){ -dom=_372.pop(); +var _387=self.escapeHTML; +var _388=self.attributeArray; +while(_385.length){ +dom=_385.pop(); if(typeof (dom)=="string"){ lst.push(dom); }else{ if(dom.nodeType==1){ -lst.push("<"+dom.nodeName.toLowerCase()); -var _375=[]; -var _376=_374(dom); -for(var i=0;i<_376.length;i++){ -var a=_376[i]; -_375.push([" ",a.name,"=\"",_373(a.value),"\""]); +lst.push("<"+dom.tagName.toLowerCase()); +var _389=[]; +var _38a=_388(dom); +for(var i=0;i<_38a.length;i++){ +var a=_38a[i]; +_389.push([" ",a.name,"=\"",_387(a.value),"\""]); } -_375.sort(); -for(i=0;i<_375.length;i++){ -var _377=_375[i]; -for(var j=0;j<_377.length;j++){ -lst.push(_377[j]); +_389.sort(); +for(i=0;i<_389.length;i++){ +var _38d=_389[i]; +for(var j=0;j<_38d.length;j++){ +lst.push(_38d[j]); } } if(dom.hasChildNodes()){ lst.push(">"); -_372.push(""); -var _378=dom.childNodes; -for(i=_378.length-1;i>=0;i--){ -_372.push(_378[i]); +_385.push(""); +var _38f=dom.childNodes; +for(i=_38f.length-1;i>=0;i--){ +_385.push(_38f[i]); } }else{ lst.push("/>"); } }else{ if(dom.nodeType==3){ -lst.push(_373(dom.nodeValue)); +lst.push(_387(dom.nodeValue)); } } } } return lst; -}; -MochiKit.DOM.setDisplayForElement=function(_379,_380){ -var m=MochiKit.Base; -var _381=m.extend(null,arguments,1); -MochiKit.Iter.forEach(m.filter(null,m.map(MochiKit.DOM.getElement,_381)),function(_380){ -_380.style.display=_379; -}); -}; -MochiKit.DOM.scrapeText=function(node,_382){ +},scrapeText:function(node,_391){ var rval=[]; (function(node){ var cn=node.childNodes; @@ -2885,90 +3311,866 @@ arguments.callee.call(this,cn[i]); arguments.callee.call(this,cn[i]); } } -var _384=node.nodeValue; -if(typeof (_384)=="string"){ -rval.push(_384); +var _396=node.nodeValue; +if(typeof (_396)=="string"){ +rval.push(_396); } })(MochiKit.DOM.getElement(node)); -if(_382){ +if(_391){ return rval; }else{ return rval.join(""); } -}; -MochiKit.DOM.__new__=function(win){ +},removeEmptyTextNodes:function(_397){ +_397=MochiKit.DOM.getElement(_397); +for(var i=0;i<_397.childNodes.length;i++){ +var node=_397.childNodes[i]; +if(node.nodeType==3&&!/\S/.test(node.nodeValue)){ +node.parentNode.removeChild(node); +} +} +},makeClipping:function(_39a){ +_39a=MochiKit.DOM.getElement(_39a); +var _39b=_39a.style.overflow; +if((MochiKit.Style.getStyle(_39a,"overflow")||"visible")!="hidden"){ +_39a.style.overflow="hidden"; +} +return _39b; +},undoClipping:function(_39c,_39d){ +_39c=MochiKit.DOM.getElement(_39c); +if(!_39d){ +return; +} +_39c.style.overflow=_39d; +},makePositioned:function(_39e){ +_39e=MochiKit.DOM.getElement(_39e); +var pos=MochiKit.Style.getStyle(_39e,"position"); +if(pos=="static"||!pos){ +_39e.style.position="relative"; +if(/Opera/.test(navigator.userAgent)){ +_39e.style.top=0; +_39e.style.left=0; +} +} +},undoPositioned:function(_3a0){ +_3a0=MochiKit.DOM.getElement(_3a0); +if(_3a0.style.position=="relative"){ +_3a0.style.position=_3a0.style.top=_3a0.style.left=_3a0.style.bottom=_3a0.style.right=""; +} +},getFirstElementByTagAndClassName:function(_3a1,_3a2,_3a3){ +var self=MochiKit.DOM; +if(typeof (_3a1)=="undefined"||_3a1===null){ +_3a1="*"; +} +if(typeof (_3a3)=="undefined"||_3a3===null){ +_3a3=self._document; +} +_3a3=self.getElement(_3a3); +var _3a5=(_3a3.getElementsByTagName(_3a1)||self._document.all); +if(typeof (_3a2)=="undefined"||_3a2===null){ +return _3a5[0]; +} +for(var i=0;i<_3a5.length;i++){ +var _3a7=_3a5[i]; +var _3a8=_3a7.className.split(" "); +for(var j=0;j<_3a8.length;j++){ +if(_3a8[j]==_3a2){ +return _3a7; +} +} +} +},getFirstParentByTagAndClassName:function(elem,_3ab,_3ac){ +var self=MochiKit.DOM; +elem=self.getElement(elem); +if(typeof (_3ab)=="undefined"||_3ab===null){ +_3ab="*"; +}else{ +_3ab=_3ab.toUpperCase(); +} +if(typeof (_3ac)=="undefined"||_3ac===null){ +_3ac=null; +} +var _3ae=""; +var _3af=""; +while(elem&&elem.tagName){ +elem=elem.parentNode; +if(_3ab=="*"&&_3ac===null){ +return elem; +} +_3ae=elem.className.split(" "); +_3af=elem.tagName.toUpperCase(); +if(_3ac===null&&_3ab==_3af){ +return elem; +}else{ +if(_3ac!==null){ +for(var i=0;i<_3ae.length;i++){ +if(_3ab=="*"&&_3ae[i]==_3ac){ +return elem; +}else{ +if(_3ab==_3af&&_3ae[i]==_3ac){ +return elem; +} +} +} +} +} +} +return elem; +},isParent:function(_3b1,_3b2){ +if(!_3b1.parentNode||_3b1==_3b2){ +return false; +} +if(_3b1.parentNode==_3b2){ +return true; +} +return MochiKit.DOM.isParent(_3b1.parentNode,_3b2); +},__new__:function(win){ var m=MochiKit.Base; +if(typeof (document)!="undefined"){ this._document=document; +var _3b5="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +this._xhtml=(document.documentElement&&document.createElementNS&&document.documentElement.namespaceURI===_3b5); +}else{ +if(MochiKit.MockDOM){ +this._document=MochiKit.MockDOM.document; +} +} this._window=win; this.domConverters=new m.AdapterRegistry(); -var _385=this._document.createElement("span"); -var _386; -if(_385.attributes.length>0){ -var _387=m.filter; -_386=function(node){ -return _387(_386.ignoreAttrFilter,node.attributes); +var _3b6=this._document.createElement("span"); +var _3b7; +if(_3b6&&_3b6.attributes&&_3b6.attributes.length>0){ +var _3b8=m.filter; +_3b7=function(node){ +return _3b8(_3b7.ignoreAttrFilter,node.attributes); }; -_386.ignoreAttr={}; -MochiKit.Iter.forEach(_385.attributes,function(a){ -_386.ignoreAttr[a.name]=a.value; -}); -_386.ignoreAttrFilter=function(a){ -return (_386.ignoreAttr[a.name]!=a.value); +_3b7.ignoreAttr={}; +var _3ba=_3b6.attributes; +var _3bb=_3b7.ignoreAttr; +for(var i=0;i<_3ba.length;i++){ +var a=_3ba[i]; +_3bb[a.name]=a.value; +} +_3b7.ignoreAttrFilter=function(a){ +return (_3b7.ignoreAttr[a.name]!=a.value); }; -_386.compliant=false; -_386.renames={"class":"className","checked":"defaultChecked","usemap":"useMap","for":"htmlFor"}; +_3b7.compliant=false; +_3b7.renames={"class":"className","checked":"defaultChecked","usemap":"useMap","for":"htmlFor","readonly":"readOnly","colspan":"colSpan","bgcolor":"bgColor","cellspacing":"cellSpacing","cellpadding":"cellPadding"}; }else{ -_386=function(node){ +_3b7=function(node){ return node.attributes; }; -_386.compliant=true; -_386.renames={}; +_3b7.compliant=true; +_3b7.renames={}; } -this.attributeArray=_386; -var _388=this.createDOMFunc; -this.UL=_388("ul"); -this.OL=_388("ol"); -this.LI=_388("li"); -this.TD=_388("td"); -this.TR=_388("tr"); -this.TBODY=_388("tbody"); -this.THEAD=_388("thead"); -this.TFOOT=_388("tfoot"); -this.TABLE=_388("table"); -this.TH=_388("th"); -this.INPUT=_388("input"); -this.SPAN=_388("span"); -this.A=_388("a"); -this.DIV=_388("div"); -this.IMG=_388("img"); -this.BUTTON=_388("button"); -this.TT=_388("tt"); -this.PRE=_388("pre"); -this.H1=_388("h1"); -this.H2=_388("h2"); -this.H3=_388("h3"); -this.BR=_388("br"); -this.HR=_388("hr"); -this.LABEL=_388("label"); -this.TEXTAREA=_388("textarea"); -this.FORM=_388("form"); -this.P=_388("p"); -this.SELECT=_388("select"); -this.OPTION=_388("option"); -this.OPTGROUP=_388("optgroup"); -this.LEGEND=_388("legend"); -this.FIELDSET=_388("fieldset"); -this.STRONG=_388("strong"); -this.CANVAS=_388("canvas"); +this.attributeArray=_3b7; +var _3c0=function(_3c1,arr){ +var _3c3=arr[1].split("."); +var str=""; +var obj={}; +str+="if (!MochiKit."+_3c3[1]+") { throw new Error(\""; +str+="This function has been deprecated and depends on MochiKit."; +str+=_3c3[1]+".\");}"; +str+="return MochiKit."+_3c3[1]+"."+arr[0]; +str+=".apply(this, arguments);"; +obj[_3c3[2]]=new Function(str); +MochiKit.Base.update(MochiKit[_3c1],obj); +}; +for(var i;i0){ +abort(repr(expr)); +} +},buildMatchExpression:function(){ +var repr=MochiKit.Base.repr; +var _3d1=this.params; +var _3d2=[]; +var _3d3,i; +function childElements(_3d5){ +return "MochiKit.Base.filter(function (node) { return node.nodeType == 1; }, "+_3d5+".childNodes)"; +} +if(_3d1.wildcard){ +_3d2.push("true"); +} +if(_3d3=_3d1.id){ +_3d2.push("element.id == "+repr(_3d3)); +} +if(_3d3=_3d1.tagName){ +_3d2.push("element.tagName.toUpperCase() == "+repr(_3d3)); +} +if((_3d3=_3d1.classNames).length>0){ +for(i=0;i<_3d3.length;i++){ +_3d2.push("MochiKit.DOM.hasElementClass(element, "+repr(_3d3[i])+")"); +} +} +if((_3d3=_3d1.pseudoClassNames).length>0){ +for(i=0;i<_3d3.length;i++){ +var _3d6=_3d3[i].match(/^([^(]+)(?:\((.*)\))?$/); +var _3d7=_3d6[1]; +var _3d8=_3d6[2]; +switch(_3d7){ +case "root": +_3d2.push("element.nodeType == 9 || element === element.ownerDocument.documentElement"); +break; +case "nth-child": +case "nth-last-child": +case "nth-of-type": +case "nth-last-of-type": +_3d6=_3d8.match(/^((?:(\d+)n\+)?(\d+)|odd|even)$/); +if(!_3d6){ +throw "Invalid argument to pseudo element nth-child: "+_3d8; +} +var a,b; +if(_3d6[0]=="odd"){ +a=2; +b=1; +}else{ +if(_3d6[0]=="even"){ +a=2; +b=0; +}else{ +a=_3d6[2]&&parseInt(_3d6)||null; +b=parseInt(_3d6[3]); +} +} +_3d2.push("this.nthChild(element,"+a+","+b+","+!!_3d7.match("^nth-last")+","+!!_3d7.match("of-type$")+")"); +break; +case "first-child": +_3d2.push("this.nthChild(element, null, 1)"); +break; +case "last-child": +_3d2.push("this.nthChild(element, null, 1, true)"); +break; +case "first-of-type": +_3d2.push("this.nthChild(element, null, 1, false, true)"); +break; +case "last-of-type": +_3d2.push("this.nthChild(element, null, 1, true, true)"); +break; +case "only-child": +_3d2.push(childElements("element.parentNode")+".length == 1"); +break; +case "only-of-type": +_3d2.push("MochiKit.Base.filter(function (node) { return node.tagName == element.tagName; }, "+childElements("element.parentNode")+").length == 1"); +break; +case "empty": +_3d2.push("element.childNodes.length == 0"); +break; +case "enabled": +_3d2.push("(this.isUIElement(element) && element.disabled === false)"); +break; +case "disabled": +_3d2.push("(this.isUIElement(element) && element.disabled === true)"); +break; +case "checked": +_3d2.push("(this.isUIElement(element) && element.checked === true)"); +break; +case "not": +var _3db=new MochiKit.Selector.Selector(_3d8); +_3d2.push("!( "+_3db.buildMatchExpression()+")"); +break; +} +} +} +if(_3d3=_3d1.attributes){ +MochiKit.Base.map(function(_3dc){ +var _3dd="MochiKit.DOM.getNodeAttribute(element, "+repr(_3dc.name)+")"; +var _3de=function(_3df){ +return _3dd+".split("+repr(_3df)+")"; +}; +switch(_3dc.operator){ +case "=": +_3d2.push(_3dd+" == "+repr(_3dc.value)); +break; +case "~=": +_3d2.push(_3dd+" && MochiKit.Base.findValue("+_3de(" ")+", "+repr(_3dc.value)+") > -1"); +break; +case "^=": +_3d2.push(_3dd+".substring(0, "+_3dc.value.length+") == "+repr(_3dc.value)); +break; +case "$=": +_3d2.push(_3dd+".substring("+_3dd+".length - "+_3dc.value.length+") == "+repr(_3dc.value)); +break; +case "*=": +_3d2.push(_3dd+".match("+repr(_3dc.value)+")"); +break; +case "|=": +_3d2.push(_3dd+" && "+_3de("-")+"[0].toUpperCase() == "+repr(_3dc.value.toUpperCase())); +break; +case "!=": +_3d2.push(_3dd+" != "+repr(_3dc.value)); +break; +case "": +case undefined: +_3d2.push(_3dd+" != null"); +break; +default: +throw "Unknown operator "+_3dc.operator+" in selector"; +} +},_3d3); +} +return _3d2.join(" && "); +},compileMatcher:function(){ +this.match=new Function("element","if (!element.tagName) return false; return "+this.buildMatchExpression()); +},nthChild:function(_3e0,a,b,_3e3,_3e4){ +var _3e5=MochiKit.Base.filter(function(node){ +return node.nodeType==1; +},_3e0.parentNode.childNodes); +if(_3e4){ +_3e5=MochiKit.Base.filter(function(node){ +return node.tagName==_3e0.tagName; +},_3e5); +} +if(_3e3){ +_3e5=MochiKit.Iter.reversed(_3e5); +} +if(a){ +var _3e8=MochiKit.Base.findIdentical(_3e5,_3e0); +return ((_3e8+1-b)/a)%1==0; +}else{ +return b==MochiKit.Base.findIdentical(_3e5,_3e0)+1; +} +},isUIElement:function(_3e9){ +return MochiKit.Base.findValue(["input","button","select","option","textarea","object"],_3e9.tagName.toLowerCase())>-1; +},findElements:function(_3ea,axis){ +var _3ec; +if(axis==undefined){ +axis=""; +} +function inScope(_3ed,_3ee){ +if(axis==""){ +return MochiKit.DOM.isChildNode(_3ed,_3ee); +}else{ +if(axis==">"){ +return _3ed.parentNode==_3ee; +}else{ +if(axis=="+"){ +return _3ed==nextSiblingElement(_3ee); +}else{ +if(axis=="~"){ +var _3ef=_3ee; +while(_3ef=nextSiblingElement(_3ef)){ +if(_3ed==_3ef){ +return true; +} +} +return false; +}else{ +throw "Invalid axis: "+axis; +} +} +} +} +} +if(_3ec=MochiKit.DOM.getElement(this.params.id)){ +if(this.match(_3ec)){ +if(!_3ea||inScope(_3ec,_3ea)){ +return [_3ec]; +} +} +} +function nextSiblingElement(node){ +node=node.nextSibling; +while(node&&node.nodeType!=1){ +node=node.nextSibling; +} +return node; +} +if(axis==""){ +_3ea=(_3ea||MochiKit.DOM.currentDocument()).getElementsByTagName(this.params.tagName||"*"); +}else{ +if(axis==">"){ +if(!_3ea){ +throw "> combinator not allowed without preceeding expression"; +} +_3ea=MochiKit.Base.filter(function(node){ +return node.nodeType==1; +},_3ea.childNodes); +}else{ +if(axis=="+"){ +if(!_3ea){ +throw "+ combinator not allowed without preceeding expression"; +} +_3ea=nextSiblingElement(_3ea)&&[nextSiblingElement(_3ea)]; +}else{ +if(axis=="~"){ +if(!_3ea){ +throw "~ combinator not allowed without preceeding expression"; +} +var _3f2=[]; +while(nextSiblingElement(_3ea)){ +_3ea=nextSiblingElement(_3ea); +_3f2.push(_3ea); +} +_3ea=_3f2; +} +} +} +} +if(!_3ea){ +return []; +} +var _3f3=MochiKit.Base.filter(MochiKit.Base.bind(function(_3f4){ +return this.match(_3f4); +},this),_3ea); +return _3f3; +},repr:function(){ +return "Selector("+this.expression+")"; +},toString:MochiKit.Base.forwardCall("repr")}; +MochiKit.Base.update(MochiKit.Selector,{findChildElements:function(_3f5,_3f6){ +return MochiKit.Base.flattenArray(MochiKit.Base.map(function(_3f7){ +var _3f8=""; +return MochiKit.Iter.reduce(function(_3f9,expr){ +if(match=expr.match(/^[>+~]$/)){ +_3f8=match[0]; +return _3f9; +}else{ +var _3fb=new MochiKit.Selector.Selector(expr); +var _3fc=MochiKit.Iter.reduce(function(_3fd,_3fe){ +return MochiKit.Base.extend(_3fd,_3fb.findElements(_3fe||_3f5,_3f8)); +},_3f9,[]); +_3f8=""; +return _3fc; +} +},_3f7.replace(/(^\s+|\s+$)/g,"").split(/\s+/),[null]); +},_3f6)); +},findDocElements:function(){ +return MochiKit.Selector.findChildElements(MochiKit.DOM.currentDocument(),arguments); +},__new__:function(){ +var m=MochiKit.Base; +this.$$=this.findDocElements; +this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; +m.nameFunctions(this); +}}); +MochiKit.Selector.__new__(); +MochiKit.Base._exportSymbols(this,MochiKit.Selector); +if(typeof (dojo)!="undefined"){ +dojo.provide("MochiKit.Style"); +dojo.require("MochiKit.Base"); +dojo.require("MochiKit.DOM"); +} +if(typeof (JSAN)!="undefined"){ +JSAN.use("MochiKit.Base",[]); +JSAN.use("MochiKit.DOM",[]); +} +try{ +if(typeof (MochiKit.Base)=="undefined"){ +throw ""; +} +} +catch(e){ +throw "MochiKit.Style depends on MochiKit.Base!"; +} +try{ +if(typeof (MochiKit.DOM)=="undefined"){ +throw ""; +} +} +catch(e){ +throw "MochiKit.Style depends on MochiKit.DOM!"; +} +if(typeof (MochiKit.Style)=="undefined"){ +MochiKit.Style={}; +} +MochiKit.Style.NAME="MochiKit.Style"; +MochiKit.Style.VERSION="1.4"; +MochiKit.Style.__repr__=function(){ +return "["+this.NAME+" "+this.VERSION+"]"; +}; +MochiKit.Style.toString=function(){ +return this.__repr__(); +}; +MochiKit.Style.EXPORT_OK=[]; +MochiKit.Style.EXPORT=["setStyle","setOpacity","getStyle","getElementDimensions","elementDimensions","setElementDimensions","getElementPosition","elementPosition","setElementPosition","setDisplayForElement","hideElement","showElement","getViewportDimensions","getViewportPosition","Dimensions","Coordinates"]; +MochiKit.Style.Dimensions=function(w,h){ +this.w=w; +this.h=h; +}; +MochiKit.Style.Dimensions.prototype.__repr__=function(){ +var repr=MochiKit.Base.repr; +return "{w: "+repr(this.w)+", h: "+repr(this.h)+"}"; +}; +MochiKit.Style.Dimensions.prototype.toString=function(){ +return this.__repr__(); +}; +MochiKit.Style.Coordinates=function(x,y){ +this.x=x; +this.y=y; +}; +MochiKit.Style.Coordinates.prototype.__repr__=function(){ +var repr=MochiKit.Base.repr; +return "{x: "+repr(this.x)+", y: "+repr(this.y)+"}"; +}; +MochiKit.Style.Coordinates.prototype.toString=function(){ +return this.__repr__(); +}; +MochiKit.Base.update(MochiKit.Style,{getStyle:function(elem,_407){ +var dom=MochiKit.DOM; +var d=dom._document; +elem=dom.getElement(elem); +_407=MochiKit.Base.camelize(_407); +if(!elem||elem==d){ +return undefined; +} +if(_407=="opacity"&&elem.filters){ +var _40a=(MochiKit.Style.getStyle(elem,"filter")||"").match(/alpha\(opacity=(.*)\)/); +if(_40a&&_40a[1]){ +return parseFloat(_40a[1])/100; +} +return 1; +} +var _40b=elem.style?elem.style[_407]:null; +if(!_40b){ +if(d.defaultView&&d.defaultView.getComputedStyle){ +var css=d.defaultView.getComputedStyle(elem,null); +_407=_407.replace(/([A-Z])/g,"-$1").toLowerCase(); +_40b=css?css.getPropertyValue(_407):null; +}else{ +if(elem.currentStyle){ +_40b=elem.currentStyle[_407]; +} +} +} +if(_407=="opacity"){ +_40b=parseFloat(_40b); +} +if(/Opera/.test(navigator.userAgent)&&(MochiKit.Base.find(["left","top","right","bottom"],_407)!=-1)){ +if(MochiKit.Style.getStyle(elem,"position")=="static"){ +_40b="auto"; +} +} +return _40b=="auto"?null:_40b; +},setStyle:function(elem,_40e){ +elem=MochiKit.DOM.getElement(elem); +for(var name in _40e){ +if(name=="opacity"){ +MochiKit.Style.setOpacity(elem,_40e[name]); +}else{ +elem.style[MochiKit.Base.camelize(name)]=_40e[name]; +} +} +},setOpacity:function(elem,o){ +elem=MochiKit.DOM.getElement(elem); +var self=MochiKit.Style; +if(o==1){ +var _413=/Gecko/.test(navigator.userAgent)&&!(/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent)); +elem.style["opacity"]=_413?0.999999:1; +if(/MSIE/.test(navigator.userAgent)){ +elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,""); +} +}else{ +if(o<0.00001){ +o=0; +} +elem.style["opacity"]=o; +if(/MSIE/.test(navigator.userAgent)){ +elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,"")+"alpha(opacity="+o*100+")"; +} +} +},getElementPosition:function(elem,_415){ +var self=MochiKit.Style; +var dom=MochiKit.DOM; +elem=dom.getElement(elem); +if(!elem||(!(elem.x&&elem.y)&&(!elem.parentNode===null||self.getStyle(elem,"display")=="none"))){ +return undefined; +} +var c=new self.Coordinates(0,0); +var box=null; +var _41a=null; +var d=MochiKit.DOM._document; +var de=d.documentElement; +var b=d.body; +if(!elem.parentNode&&elem.x&&elem.y){ +c.x+=elem.x||0; +c.y+=elem.y||0; +}else{ +if(elem.getBoundingClientRect){ +box=elem.getBoundingClientRect(); +c.x+=box.left+(de.scrollLeft||b.scrollLeft)-(de.clientLeft||0); +c.y+=box.top+(de.scrollTop||b.scrollTop)-(de.clientTop||0); +}else{ +if(elem.offsetParent){ +c.x+=elem.offsetLeft; +c.y+=elem.offsetTop; +_41a=elem.offsetParent; +if(_41a!=elem){ +while(_41a){ +c.x+=_41a.offsetLeft; +c.y+=_41a.offsetTop; +_41a=_41a.offsetParent; +} +} +var ua=navigator.userAgent.toLowerCase(); +if((typeof (opera)!="undefined"&&parseFloat(opera.version())<9)||(ua.indexOf("AppleWebKit")!=-1&&self.getStyle(elem,"position")=="absolute")){ +c.x-=b.offsetLeft; +c.y-=b.offsetTop; +} +} +} +} +if(typeof (_415)!="undefined"){ +_415=arguments.callee(_415); +if(_415){ +c.x-=(_415.x||0); +c.y-=(_415.y||0); +} +} +if(elem.parentNode){ +_41a=elem.parentNode; +}else{ +_41a=null; +} +while(_41a){ +var _41f=_41a.tagName.toUpperCase(); +if(_41f==="BODY"||_41f==="HTML"){ +break; +} +var disp=self.getStyle(_41a,"display"); +if(disp!="inline"&&disp!="table-row"){ +c.x-=_41a.scrollLeft; +c.y-=_41a.scrollTop; +} +if(_41a.parentNode){ +_41a=_41a.parentNode; +}else{ +_41a=null; +} +} +return c; +},setElementPosition:function(elem,_422,_423){ +elem=MochiKit.DOM.getElement(elem); +if(typeof (_423)=="undefined"){ +_423="px"; +} +var _424={}; +var _425=MochiKit.Base.isUndefinedOrNull; +if(!_425(_422.x)){ +_424["left"]=_422.x+_423; +} +if(!_425(_422.y)){ +_424["top"]=_422.y+_423; +} +MochiKit.DOM.updateNodeAttributes(elem,{"style":_424}); +},getElementDimensions:function(elem){ +var self=MochiKit.Style; +var dom=MochiKit.DOM; +if(typeof (elem.w)=="number"||typeof (elem.h)=="number"){ +return new self.Dimensions(elem.w||0,elem.h||0); +} +elem=dom.getElement(elem); +if(!elem){ +return undefined; +} +var disp=self.getStyle(elem,"display"); +if(disp!="none"&&disp!==""&&typeof (disp)!="undefined"){ +return new self.Dimensions(elem.offsetWidth||0,elem.offsetHeight||0); +} +var s=elem.style; +var _42b=s.visibility; +var _42c=s.position; +s.visibility="hidden"; +s.position="absolute"; +s.display=""; +var _42d=elem.offsetWidth; +var _42e=elem.offsetHeight; +s.display="none"; +s.position=_42c; +s.visibility=_42b; +return new self.Dimensions(_42d,_42e); +},setElementDimensions:function(elem,_430,_431){ +elem=MochiKit.DOM.getElement(elem); +if(typeof (_431)=="undefined"){ +_431="px"; +} +var _432={}; +var _433=MochiKit.Base.isUndefinedOrNull; +if(!_433(_430.w)){ +_432["width"]=_430.w+_431; +} +if(!_433(_430.h)){ +_432["height"]=_430.h+_431; +} +MochiKit.DOM.updateNodeAttributes(elem,{"style":_432}); +},setDisplayForElement:function(_434,_435){ +var _436=MochiKit.Base.extend(null,arguments,1); +var _437=MochiKit.DOM.getElement; +for(var i=0;i<_436.length;i++){ +_435=_437(_436[i]); +if(_435){ +_435.style.display=_434; +} +} +},getViewportDimensions:function(){ +var d=new MochiKit.Style.Dimensions(); +var w=MochiKit.DOM._window; +var b=MochiKit.DOM._document.body; +if(w.innerWidth){ +d.w=w.innerWidth; +d.h=w.innerHeight; +}else{ +if(b.parentElement.clientWidth){ +d.w=b.parentElement.clientWidth; +d.h=b.parentElement.clientHeight; +}else{ +if(b&&b.clientWidth){ +d.w=b.clientWidth; +d.h=b.clientHeight; +} +} +} +return d; +},getViewportPosition:function(){ +var c=new MochiKit.Style.Coordinates(0,0); +var d=MochiKit.DOM._document; +var de=d.documentElement; +var db=d.body; +if(de&&(de.scrollTop||de.scrollLeft)){ +c.x=de.scrollLeft; +c.y=de.scrollTop; +}else{ +if(db){ +c.x=db.scrollLeft; +c.y=db.scrollTop; +} +} +return c; +},__new__:function(){ +var m=MochiKit.Base; +this.elementPosition=this.getElementPosition; +this.elementDimensions=this.getElementDimensions; this.hideElement=m.partial(this.setDisplayForElement,"none"); this.showElement=m.partial(this.setDisplayForElement,"block"); -this.removeElement=this.swapDOM; -this.$=this.getElement; this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); -}; -MochiKit.DOM.__new__(this); -MochiKit.Base._exportSymbols(this,MochiKit.DOM); +}}); +MochiKit.Style.__new__(); +MochiKit.Base._exportSymbols(this,MochiKit.Style); if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.LoggingPane"); dojo.require("MochiKit.Logging"); @@ -2990,41 +4192,42 @@ MochiKit.LoggingPane.NAME="MochiKit.Logg MochiKit.LoggingPane={}; } MochiKit.LoggingPane.NAME="MochiKit.LoggingPane"; -MochiKit.LoggingPane.VERSION="1.2"; +MochiKit.LoggingPane.VERSION="1.4"; MochiKit.LoggingPane.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.LoggingPane.toString=function(){ return this.__repr__(); }; -MochiKit.LoggingPane.createLoggingPane=function(_389){ +MochiKit.LoggingPane.createLoggingPane=function(_441){ var m=MochiKit.LoggingPane; -_389=!(!_389); -if(m._loggingPane&&m._loggingPane.inline!=_389){ +_441=!(!_441); +if(m._loggingPane&&m._loggingPane.inline!=_441){ m._loggingPane.closePane(); m._loggingPane=null; } if(!m._loggingPane||m._loggingPane.closed){ -m._loggingPane=new m.LoggingPane(_389,MochiKit.Logging.logger); +m._loggingPane=new m.LoggingPane(_441,MochiKit.Logging.logger); } return m._loggingPane; }; -MochiKit.LoggingPane.LoggingPane=function(_390,_391){ -if(typeof (_391)=="undefined"||_391==null){ -_391=MochiKit.Logging.logger; +MochiKit.LoggingPane.LoggingPane=function(_443,_444){ +if(typeof (_444)=="undefined"||_444===null){ +_444=MochiKit.Logging.logger; } -this.logger=_391; -var _392=MochiKit.Base.update; -var _393=MochiKit.Base.updatetree; +this.logger=_444; +var _445=MochiKit.Base.update; +var _446=MochiKit.Base.updatetree; var bind=MochiKit.Base.bind; -var _394=MochiKit.Base.clone; +var _448=MochiKit.Base.clone; var win=window; +var uid="_MochiKit_LoggingPane"; if(typeof (MochiKit.DOM)!="undefined"){ win=MochiKit.DOM.currentWindow(); } -if(!_390){ -var url=win.location.href.split("?")[0].replace(/[:\/.><&]/g,"_"); -var name="MochiKit_LoggingPane_"+url; +if(!_443){ +var url=win.location.href.split("?")[0].replace(/[#:\/.><&-]/g,"_"); +var name=uid+"_"+url; var nwin=win.open("",name,"dependent,resizable,height=200"); if(!nwin){ alert("Not able to open debugging window due to pop-up blocking."); @@ -3037,81 +4240,89 @@ this.doc=doc; } var doc=win.document; this.doc=doc; -var _396=doc.getElementById("_debugPane"); -if(_396&&typeof (_396.loggingPane)!="undefined"){ -_396.loggingPane.logger=this.logger; -_396.loggingPane.buildAndApplyFilter(); -return _396.loggingPane; +var _44f=doc.getElementById(uid); +var _450=!!_44f; +if(_44f&&typeof (_44f.loggingPane)!="undefined"){ +_44f.loggingPane.logger=this.logger; +_44f.loggingPane.buildAndApplyFilter(); +return _44f.loggingPane; } -_396=doc.createElement("div"); -_396.id="_debugPane"; -_396.loggingPane=this; -var _397=doc.createElement("input"); -var _398=doc.createElement("input"); -var _399=doc.createElement("button"); -var _400=doc.createElement("button"); -var _401=doc.createElement("button"); -var _402=doc.createElement("button"); -var _403=doc.createElement("div"); -var _404=doc.createElement("div"); -var _405="_debugPaneListener"; -this.colorTable=_394(this.colorTable); -var _406=[]; -var _407=null; -var _408=function(msg){ -var _409=msg.level; -if(typeof (_409)=="number"){ -_409=MochiKit.Logging.LogLevel[_409]; +if(_450){ +var _451; +while((_451=_44f.firstChild)){ +_44f.removeChild(_451); } -return _409; +}else{ +_44f=doc.createElement("div"); +_44f.id=uid; +} +_44f.loggingPane=this; +var _452=doc.createElement("input"); +var _453=doc.createElement("input"); +var _454=doc.createElement("button"); +var _455=doc.createElement("button"); +var _456=doc.createElement("button"); +var _457=doc.createElement("button"); +var _458=doc.createElement("div"); +var _459=doc.createElement("div"); +var _45a=uid+"_Listener"; +this.colorTable=_448(this.colorTable); +var _45b=[]; +var _45c=null; +var _45d=function(msg){ +var _45f=msg.level; +if(typeof (_45f)=="number"){ +_45f=MochiKit.Logging.LogLevel[_45f]; +} +return _45f; }; -var _410=function(msg){ +var _460=function(msg){ return msg.info.join(" "); }; -var _411=bind(function(msg){ -var _412=_408(msg); -var text=_410(msg); -var c=this.colorTable[_412]; +var _462=bind(function(msg){ +var _464=_45d(msg); +var text=_460(msg); +var c=this.colorTable[_464]; var p=doc.createElement("span"); -p.className="MochiKit-LogMessage MochiKit-LogLevel-"+_412; +p.className="MochiKit-LogMessage MochiKit-LogLevel-"+_464; p.style.cssText="margin: 0px; white-space: -moz-pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; wrap-option: emergency; color: "+c; -p.appendChild(doc.createTextNode(_412+": "+text)); -_404.appendChild(p); -_404.appendChild(doc.createElement("br")); -if(_403.offsetHeight>_403.scrollHeight){ -_403.scrollTop=0; +p.appendChild(doc.createTextNode(_464+": "+text)); +_459.appendChild(p); +_459.appendChild(doc.createElement("br")); +if(_458.offsetHeight>_458.scrollHeight){ +_458.scrollTop=0; }else{ -_403.scrollTop=_403.scrollHeight; +_458.scrollTop=_458.scrollHeight; } },this); -var _415=function(msg){ -_406[_406.length]=msg; -_411(msg); +var _468=function(msg){ +_45b[_45b.length]=msg; +_462(msg); }; -var _416=function(){ -var _417,infore; +var _46a=function(){ +var _46b,_46c; try{ -_417=new RegExp(_397.value); -infore=new RegExp(_398.value); +_46b=new RegExp(_452.value); +_46c=new RegExp(_453.value); } catch(e){ logDebug("Error in filter regex: "+e.message); return null; } return function(msg){ -return (_417.test(_408(msg))&&infore.test(_410(msg))); +return (_46b.test(_45d(msg))&&_46c.test(_460(msg))); }; }; -var _418=function(){ -while(_404.firstChild){ -_404.removeChild(_404.firstChild); +var _46e=function(){ +while(_459.firstChild){ +_459.removeChild(_459.firstChild); } }; -var _419=function(){ -_406=[]; -_418(); +var _46f=function(){ +_45b=[]; +_46e(); }; -var _420=bind(function(){ +var _470=bind(function(){ if(this.closed){ return; } @@ -3119,83 +4330,94 @@ MochiKit.LoggingPane._loggingPane=null; if(MochiKit.LoggingPane._loggingPane==this){ MochiKit.LoggingPane._loggingPane=null; } -this.logger.removeListener(_405); -_396.loggingPane=null; -if(_390){ -_396.parentNode.removeChild(_396); +this.logger.removeListener(_45a); +try{ +try{ +_44f.loggingPane=null; +} +catch(e){ +logFatal("Bookmarklet was closed incorrectly."); +} +if(_443){ +_44f.parentNode.removeChild(_44f); }else{ this.win.close(); } +} +catch(e){ +} },this); -var _421=function(){ -_418(); -for(var i=0;i<_406.length;i++){ -var msg=_406[i]; -if(_407==null||_407(msg)){ -_411(msg); +var _471=function(){ +_46e(); +for(var i=0;i<_45b.length;i++){ +var msg=_45b[i]; +if(_45c===null||_45c(msg)){ +_462(msg); } } }; this.buildAndApplyFilter=function(){ -_407=_416(); -_421(); -this.logger.removeListener(_405); -this.logger.addListener(_405,_407,_415); +_45c=_46a(); +_471(); +this.logger.removeListener(_45a); +this.logger.addListener(_45a,_45c,_468); }; -var _422=bind(function(){ -_406=this.logger.getMessages(); -_421(); +var _474=bind(function(){ +_45b=this.logger.getMessages(); +_471(); },this); -var _423=bind(function(_424){ -_424=_424||window.event; -key=_424.which||_424.keyCode; +var _475=bind(function(_476){ +_476=_476||window.event; +key=_476.which||_476.keyCode; if(key==13){ this.buildAndApplyFilter(); } },this); -var _425="display: block; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: "+this.logFont; -if(_390){ -_425+="; height: 10em; border-top: 2px solid black"; +var _477="display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: "+this.logFont; +if(_443){ +_477+="; height: 10em; border-top: 2px solid black"; }else{ -_425+="; height: 100%;"; +_477+="; height: 100%;"; } -_396.style.cssText=_425; -doc.body.appendChild(_396); -_425={"cssText":"width: 33%; display: inline; font: "+this.logFont}; -_393(_397,{"value":"FATAL|ERROR|WARNING|INFO|DEBUG","onkeypress":_423,"style":_425}); -_396.appendChild(_397); -_393(_398,{"value":".*","onkeypress":_423,"style":_425}); -_396.appendChild(_398); -_425="width: 8%; display:inline; font: "+this.logFont; -_399.appendChild(doc.createTextNode("Filter")); -_399.onclick=bind(this.buildAndApplyFilter,this); -_399.style.cssText=_425; -_396.appendChild(_399); -_400.appendChild(doc.createTextNode("Load")); -_400.onclick=_422; -_400.style.cssText=_425; -_396.appendChild(_400); -_401.appendChild(doc.createTextNode("Clear")); -_401.onclick=_419; -_401.style.cssText=_425; -_396.appendChild(_401); -_402.appendChild(doc.createTextNode("Close")); -_402.onclick=_420; -_402.style.cssText=_425; -_396.appendChild(_402); -_403.style.cssText="overflow: auto; width: 100%"; -_404.style.cssText="width: 100%; height: "+(_390?"8em":"100%"); -_403.appendChild(_404); -_396.appendChild(_403); +_44f.style.cssText=_477; +if(!_450){ +doc.body.appendChild(_44f); +} +_477={"cssText":"width: 33%; display: inline; font: "+this.logFont}; +_446(_452,{"value":"FATAL|ERROR|WARNING|INFO|DEBUG","onkeypress":_475,"style":_477}); +_44f.appendChild(_452); +_446(_453,{"value":".*","onkeypress":_475,"style":_477}); +_44f.appendChild(_453); +_477="width: 8%; display:inline; font: "+this.logFont; +_454.appendChild(doc.createTextNode("Filter")); +_454.onclick=bind("buildAndApplyFilter",this); +_454.style.cssText=_477; +_44f.appendChild(_454); +_455.appendChild(doc.createTextNode("Load")); +_455.onclick=_474; +_455.style.cssText=_477; +_44f.appendChild(_455); +_456.appendChild(doc.createTextNode("Clear")); +_456.onclick=_46f; +_456.style.cssText=_477; +_44f.appendChild(_456); +_457.appendChild(doc.createTextNode("Close")); +_457.onclick=_470; +_457.style.cssText=_477; +_44f.appendChild(_457); +_458.style.cssText="overflow: auto; width: 100%"; +_459.style.cssText="width: 100%; height: "+(_443?"8em":"100%"); +_458.appendChild(_459); +_44f.appendChild(_458); this.buildAndApplyFilter(); -_422(); -if(_390){ +_474(); +if(_443){ this.win=undefined; }else{ this.win=win; } -this.inline=_390; -this.closePane=_420; +this.inline=_443; +this.closePane=_470; this.closed=false; return this; }; @@ -3212,9 +4434,13 @@ dojo.require("MochiKit.Base"); if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.Color"); dojo.require("MochiKit.Base"); +dojo.require("MochiKit.DOM"); +dojo.require("MochiKit.Style"); } if(typeof (JSAN)!="undefined"){ JSAN.use("MochiKit.Base",[]); +JSAN.use("MochiKit.DOM",[]); +JSAN.use("MochiKit.Style",[]); } try{ if(typeof (MochiKit.Base)=="undefined"){ @@ -3224,64 +4450,80 @@ throw "MochiKit.Color depends on MochiKi catch(e){ throw "MochiKit.Color depends on MochiKit.Base"; } +try{ +if(typeof (MochiKit.DOM)=="undefined"){ +throw ""; +} +} +catch(e){ +throw "MochiKit.Color depends on MochiKit.DOM"; +} +try{ +if(typeof (MochiKit.Style)=="undefined"){ +throw ""; +} +} +catch(e){ +throw "MochiKit.Color depends on MochiKit.Style"; +} if(typeof (MochiKit.Color)=="undefined"){ MochiKit.Color={}; } MochiKit.Color.NAME="MochiKit.Color"; -MochiKit.Color.VERSION="1.2"; +MochiKit.Color.VERSION="1.4"; MochiKit.Color.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Color.toString=function(){ return this.__repr__(); }; -MochiKit.Color.Color=function(red,_427,blue,_429){ -if(typeof (_429)=="undefined"||_429==null){ -_429=1; +MochiKit.Color.Color=function(red,_479,blue,_47b){ +if(typeof (_47b)=="undefined"||_47b===null){ +_47b=1; } -this.rgb={r:red,g:_427,b:blue,a:_429}; +this.rgb={r:red,g:_479,b:blue,a:_47b}; }; -MochiKit.Color.Color.prototype={__class__:MochiKit.Color.Color,colorWithAlpha:function(_430){ +MochiKit.Color.Color.prototype={__class__:MochiKit.Color.Color,colorWithAlpha:function(_47c){ var rgb=this.rgb; var m=MochiKit.Color; -return m.Color.fromRGB(rgb.r,rgb.g,rgb.b,_430); +return m.Color.fromRGB(rgb.r,rgb.g,rgb.b,_47c); },colorWithHue:function(hue){ var hsl=this.asHSL(); hsl.h=hue; var m=MochiKit.Color; return m.Color.fromHSL(hsl); -},colorWithSaturation:function(_434){ +},colorWithSaturation:function(_482){ var hsl=this.asHSL(); -hsl.s=_434; +hsl.s=_482; var m=MochiKit.Color; return m.Color.fromHSL(hsl); -},colorWithLightness:function(_435){ +},colorWithLightness:function(_485){ var hsl=this.asHSL(); -hsl.l=_435; +hsl.l=_485; var m=MochiKit.Color; return m.Color.fromHSL(hsl); -},darkerColorWithLevel:function(_436){ +},darkerColorWithLevel:function(_488){ var hsl=this.asHSL(); -hsl.l=Math.max(hsl.l-_436,0); +hsl.l=Math.max(hsl.l-_488,0); var m=MochiKit.Color; return m.Color.fromHSL(hsl); -},lighterColorWithLevel:function(_437){ +},lighterColorWithLevel:function(_48b){ var hsl=this.asHSL(); -Math.min(hsl.l+_437,1); +hsl.l=Math.min(hsl.l+_48b,1); var m=MochiKit.Color; return m.Color.fromHSL(hsl); -},blendedColor:function(_438,_439){ -if(typeof (_439)=="undefined"||_439==null){ -_439=0.5; +},blendedColor:function(_48e,_48f){ +if(typeof (_48f)=="undefined"||_48f===null){ +_48f=0.5; } -var sf=1-_439; +var sf=1-_48f; var s=this.rgb; -var d=_438.rgb; -var df=_439; +var d=_48e.rgb; +var df=_48f; return MochiKit.Color.Color.fromRGB((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df)); -},compareRGB:function(_442){ +},compareRGB:function(_494){ var a=this.asRGB(); -var b=_442.asRGB(); +var b=_494.asRGB(); return MochiKit.Base.compare([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a]); },isLight:function(){ return this.asHSL().b>0.5; @@ -3335,7 +4577,7 @@ var c=this.rgb; },asHSV:function(){ var hsv=this.hsv; var c=this.rgb; -if(typeof (hsv)=="undefined"||hsv==null){ +if(typeof (hsv)=="undefined"||hsv===null){ hsv=MochiKit.Color.rgbToHSV(this.rgb); this.hsv=hsv; } @@ -3343,7 +4585,7 @@ var c=this.rgb; },asHSL:function(){ var hsl=this.hsl; var c=this.rgb; -if(typeof (hsl)=="undefined"||hsl==null){ +if(typeof (hsl)=="undefined"||hsl===null){ hsl=MochiKit.Color.rgbToHSL(this.rgb); this.hsl=hsl; } @@ -3355,111 +4597,114 @@ return this.__class__.NAME+"("+col.join( var col=[c.r,c.g,c.b,c.a]; return this.__class__.NAME+"("+col.join(", ")+")"; }}; -MochiKit.Base.update(MochiKit.Color.Color,{fromRGB:function(red,_447,blue,_448){ -var _449=MochiKit.Color.Color; +MochiKit.Base.update(MochiKit.Color.Color,{fromRGB:function(red,_4ab,blue,_4ad){ +var _4ae=MochiKit.Color.Color; if(arguments.length==1){ var rgb=red; red=rgb.r; -_447=rgb.g; +_4ab=rgb.g; blue=rgb.b; if(typeof (rgb.a)=="undefined"){ -_448=undefined; +_4ad=undefined; }else{ -_448=rgb.a; +_4ad=rgb.a; } } -return new _449(red,_447,blue,_448); -},fromHSL:function(hue,_450,_451,_452){ +return new _4ae(red,_4ab,blue,_4ad); +},fromHSL:function(hue,_4b1,_4b2,_4b3){ var m=MochiKit.Color; return m.Color.fromRGB(m.hslToRGB.apply(m,arguments)); -},fromHSV:function(hue,_453,_454,_455){ +},fromHSV:function(hue,_4b6,_4b7,_4b8){ var m=MochiKit.Color; return m.Color.fromRGB(m.hsvToRGB.apply(m,arguments)); },fromName:function(name){ -var _456=MochiKit.Color.Color; -var _457=_456._namedColors[name.toLowerCase()]; -if(typeof (_457)=="string"){ -return _456.fromHexString(_457); +var _4bb=MochiKit.Color.Color; +if(name.charAt(0)=="\""){ +name=name.substr(1,name.length-2); +} +var _4bc=_4bb._namedColors[name.toLowerCase()]; +if(typeof (_4bc)=="string"){ +return _4bb.fromHexString(_4bc); }else{ if(name=="transparent"){ -return _456.transparentColor(); +return _4bb.transparentColor(); } } return null; -},fromString:function(_458){ +},fromString:function(_4bd){ var self=MochiKit.Color.Color; -var _459=_458.substr(0,3); -if(_459=="rgb"){ -return self.fromRGBString(_458); +var _4bf=_4bd.substr(0,3); +if(_4bf=="rgb"){ +return self.fromRGBString(_4bd); }else{ -if(_459=="hsl"){ -return self.fromHSLString(_458); +if(_4bf=="hsl"){ +return self.fromHSLString(_4bd); }else{ -if(_458.charAt(0)=="#"){ -return self.fromHexString(_458); +if(_4bd.charAt(0)=="#"){ +return self.fromHexString(_4bd); } } } -return self.fromName(_458); -},fromHexString:function(_460){ -if(_460.charAt(0)=="#"){ -_460=_460.substring(1); +return self.fromName(_4bd); +},fromHexString:function(_4c0){ +if(_4c0.charAt(0)=="#"){ +_4c0=_4c0.substring(1); } -var _461=[]; +var _4c1=[]; var i,hex; -if(_460.length==3){ +if(_4c0.length==3){ for(i=0;i<3;i++){ -hex=_460.substr(i,1); -_461.push(parseInt(hex+hex,16)/255); +hex=_4c0.substr(i,1); +_4c1.push(parseInt(hex+hex,16)/255); } }else{ for(i=0;i<6;i+=2){ -hex=_460.substr(i,2); -_461.push(parseInt(hex,16)/255); +hex=_4c0.substr(i,2); +_4c1.push(parseInt(hex,16)/255); } } -var _462=MochiKit.Color.Color; -return _462.fromRGB.apply(_462,_461); -},_fromColorString:function(pre,_464,_465,_466){ -if(_466.indexOf(pre)==0){ -_466=_466.substring(_466.indexOf("(",3)+1,_466.length-1); +var _4c4=MochiKit.Color.Color; +return _4c4.fromRGB.apply(_4c4,_4c1); +},_fromColorString:function(pre,_4c6,_4c7,_4c8){ +if(_4c8.indexOf(pre)===0){ +_4c8=_4c8.substring(_4c8.indexOf("(",3)+1,_4c8.length-1); } -var _467=_466.split(/\s*,\s*/); -var _468=[]; -for(var i=0;i<_467.length;i++){ -var c=_467[i]; +var _4c9=_4c8.split(/\s*,\s*/); +var _4ca=[]; +for(var i=0;i<_4c9.length;i++){ +var c=_4c9[i]; var val; -var _469=c.substring(c.length-3); +var _4ce=c.substring(c.length-3); if(c.charAt(c.length-1)=="%"){ val=0.01*parseFloat(c.substring(0,c.length-1)); }else{ -if(_469=="deg"){ +if(_4ce=="deg"){ val=parseFloat(c)/360; }else{ -if(_469=="rad"){ +if(_4ce=="rad"){ val=parseFloat(c)/(Math.PI*2); }else{ -val=_465[i]*parseFloat(c); +val=_4c7[i]*parseFloat(c); } } } -_468.push(val); +_4ca.push(val); } -return this[_464].apply(this,_468); -},fromComputedStyle:function(elem,_470,_471){ +return this[_4c6].apply(this,_4ca); +},fromComputedStyle:function(elem,_4d0){ var d=MochiKit.DOM; var cls=MochiKit.Color.Color; for(elem=d.getElement(elem);elem;elem=elem.parentNode){ -var _472=d.computedStyle.apply(d,arguments); -if(!_472){ +var _4d3=MochiKit.Style.getStyle.apply(d,arguments); +if(!_4d3){ continue; } -var _473=cls.fromString(_472); -if(!_473){ +var _4d4=cls.fromString(_4d3); +if(!_4d4){ break; } -if(_473.asRGB().a>0){ -return _473; +if(_4d4.asRGB().a>0){ +return _4d4; } } return null; @@ -3472,13 +4717,13 @@ return MochiKit.Base.clone(MochiKit.Colo },namedColors:function(){ return MochiKit.Base.clone(MochiKit.Color.Color._namedColors); }}); -MochiKit.Base.update(MochiKit.Color,{clampColorComponent:function(v,_474){ -v*=_474; +MochiKit.Base.update(MochiKit.Color,{clampColorComponent:function(v,_4da){ +v*=_4da; if(v<0){ return 0; }else{ -if(v>_474){ -return _474; +if(v>_4da){ +return _4da; }else{ return v; } @@ -3506,118 +4751,118 @@ return val; } } return val; -},hsvToRGB:function(hue,_477,_478,_479){ +},hsvToRGB:function(hue,_4e0,_4e1,_4e2){ if(arguments.length==1){ var hsv=hue; hue=hsv.h; -_477=hsv.s; -_478=hsv.v; -_479=hsv.a; +_4e0=hsv.s; +_4e1=hsv.v; +_4e2=hsv.a; } var red; -var _480; +var _4e5; var blue; -if(_477==0){ -red=0; -_480=0; -blue=0; +if(_4e0===0){ +red=_4e1; +_4e5=_4e1; +blue=_4e1; }else{ var i=Math.floor(hue*6); var f=(hue*6)-i; -var p=_478*(1-_477); -var q=_478*(1-(_477*f)); -var t=_478*(1-(_477*(1-f))); +var p=_4e1*(1-_4e0); +var q=_4e1*(1-(_4e0*f)); +var t=_4e1*(1-(_4e0*(1-f))); switch(i){ case 1: red=q; -_480=_478; +_4e5=_4e1; blue=p; break; case 2: red=p; -_480=_478; +_4e5=_4e1; blue=t; break; case 3: red=p; -_480=q; -blue=_478; +_4e5=q; +blue=_4e1; break; case 4: red=t; -_480=p; -blue=_478; +_4e5=p; +blue=_4e1; break; case 5: -red=_478; -_480=p; +red=_4e1; +_4e5=p; blue=q; break; case 6: case 0: -red=_478; -_480=t; +red=_4e1; +_4e5=t; blue=p; break; } } -return {r:red,g:_480,b:blue,a:_479}; -},hslToRGB:function(hue,_482,_483,_484){ +return {r:red,g:_4e5,b:blue,a:_4e2}; +},hslToRGB:function(hue,_4ed,_4ee,_4ef){ if(arguments.length==1){ var hsl=hue; hue=hsl.h; -_482=hsl.s; -_483=hsl.l; -_484=hsl.a; +_4ed=hsl.s; +_4ee=hsl.l; +_4ef=hsl.a; } var red; -var _485; +var _4f2; var blue; -if(_482==0){ -red=_483; -_485=_483; -blue=_483; +if(_4ed===0){ +red=_4ee; +_4f2=_4ee; +blue=_4ee; }else{ var m2; -if(_483<=0.5){ -m2=_483*(1+_482); +if(_4ee<=0.5){ +m2=_4ee*(1+_4ed); }else{ -m2=_483+_482-(_483*_482); +m2=_4ee+_4ed-(_4ee*_4ed); } -var m1=(2*_483)-m2; +var m1=(2*_4ee)-m2; var f=MochiKit.Color._hslValue; var h6=hue*6; red=f(m1,m2,h6+2); -_485=f(m1,m2,h6); +_4f2=f(m1,m2,h6); blue=f(m1,m2,h6-2); } -return {r:red,g:_485,b:blue,a:_484}; -},rgbToHSV:function(red,_489,blue,_490){ +return {r:red,g:_4f2,b:blue,a:_4ef}; +},rgbToHSV:function(red,_4f9,blue,_4fb){ if(arguments.length==1){ var rgb=red; red=rgb.r; -_489=rgb.g; +_4f9=rgb.g; blue=rgb.b; -_490=rgb.a; +_4fb=rgb.a; } -var max=Math.max(Math.max(red,_489),blue); -var min=Math.min(Math.min(red,_489),blue); +var max=Math.max(Math.max(red,_4f9),blue); +var min=Math.min(Math.min(red,_4f9),blue); var hue; -var _493; -var _494=max; +var _500; +var _501=max; if(min==max){ hue=0; -_493=0; +_500=0; }else{ -var _495=(max-min); -_493=_495/max; +var _502=(max-min); +_500=_502/max; if(red==max){ -hue=(_489-blue)/_495; +hue=(_4f9-blue)/_502; }else{ -if(_489==max){ -hue=2+((blue-red)/_495); +if(_4f9==max){ +hue=2+((blue-red)/_502); }else{ -hue=4+((red-_489)/_495); +hue=4+((red-_4f9)/_502); } } hue/=6; @@ -3628,37 +4873,37 @@ hue-=1; hue-=1; } } -return {h:hue,s:_493,v:_494,a:_490}; -},rgbToHSL:function(red,_496,blue,_497){ +return {h:hue,s:_500,v:_501,a:_4fb}; +},rgbToHSL:function(red,_504,blue,_506){ if(arguments.length==1){ var rgb=red; red=rgb.r; -_496=rgb.g; +_504=rgb.g; blue=rgb.b; -_497=rgb.a; +_506=rgb.a; } -var max=Math.max(red,Math.max(_496,blue)); -var min=Math.min(red,Math.min(_496,blue)); +var max=Math.max(red,Math.max(_504,blue)); +var min=Math.min(red,Math.min(_504,blue)); var hue; -var _498; -var _499=(max+min)/2; -var _500=max-min; -if(_500==0){ +var _50b; +var _50c=(max+min)/2; +var _50d=max-min; +if(_50d===0){ hue=0; -_498=0; +_50b=0; }else{ -if(_499<=0.5){ -_498=_500/(max+min); +if(_50c<=0.5){ +_50b=_50d/(max+min); }else{ -_498=_500/(2-max-min); +_50b=_50d/(2-max-min); } if(red==max){ -hue=(_496-blue)/_500; +hue=(_504-blue)/_50d; }else{ -if(_496==max){ -hue=2+((blue-red)/_500); +if(_504==max){ +hue=2+((blue-red)/_50d); }else{ -hue=4+((red-_496)/_500); +hue=4+((red-_504)/_50d); } } hue/=6; @@ -3669,32 +4914,33 @@ hue-=1; hue-=1; } } -return {h:hue,s:_498,l:_499,a:_497}; +return {h:hue,s:_50b,l:_50c,a:_506}; },toColorPart:function(num){ -var _501=Math.round(num).toString(16); +num=Math.round(num); +var _50f=num.toString(16); if(num<16){ -return "0"+_501; +return "0"+_50f; } -return _501; +return _50f; },__new__:function(){ var m=MochiKit.Base; this.Color.fromRGBString=m.bind(this.Color._fromColorString,this.Color,"rgb","fromRGB",[1/255,1/255,1/255,1]); this.Color.fromHSLString=m.bind(this.Color._fromColorString,this.Color,"hsl","fromHSL",[1/360,0.01,0.01,1]); -var _502=1/3; -var _503={black:[0,0,0],blue:[0,0,1],brown:[0.6,0.4,0.2],cyan:[0,1,1],darkGray:[_502,_502,_502],gray:[0.5,0.5,0.5],green:[0,1,0],lightGray:[2*_502,2*_502,2*_502],magenta:[1,0,1],orange:[1,0.5,0],purple:[0.5,0,0.5],red:[1,0,0],transparent:[0,0,0,0],white:[1,1,1],yellow:[1,1,0]}; -var _504=function(name,r,g,b,a){ +var _511=1/3; +var _512={black:[0,0,0],blue:[0,0,1],brown:[0.6,0.4,0.2],cyan:[0,1,1],darkGray:[_511,_511,_511],gray:[0.5,0.5,0.5],green:[0,1,0],lightGray:[2*_511,2*_511,2*_511],magenta:[1,0,1],orange:[1,0.5,0],purple:[0.5,0,0.5],red:[1,0,0],transparent:[0,0,0,0],white:[1,1,1],yellow:[1,1,0]}; +var _513=function(name,r,g,b,a){ var rval=this.fromRGB(r,g,b,a); this[name]=function(){ return rval; }; return rval; }; -for(var k in _503){ +for(var k in _512){ var name=k+"Color"; -var _507=m.concat([_504,this.Color,name],_503[k]); -this.Color[name]=m.bind.apply(null,_507); +var _51c=m.concat([_513,this.Color,name],_512[k]); +this.Color[name]=m.bind.apply(null,_51c); } -var _508=function(){ +var _51d=function(){ for(var i=0;i1){ +var src=MochiKit.DOM.getElement(arguments[0]); +var sig=arguments[1]; +var obj=arguments[2]; +var func=arguments[3]; +for(var i=_55f.length-1;i>=0;i--){ +var o=_55f[i]; +if(o.source===src&&o.signal===sig&&o.objOrFunc===obj&&o.funcOrStr===func){ +self._disconnect(o); +if(!self._lock){ +_55f.splice(i,1); +}else{ +self._dirty=true; +} +return true; +} +} +}else{ +var idx=m.findIdentical(_55f,_55d); +if(idx>=0){ +self._disconnect(_55d); +if(!self._lock){ +_55f.splice(idx,1); +}else{ +self._dirty=true; +} +return true; +} +} +return false; +},disconnectAllTo:function(_568,_569){ +var self=MochiKit.Signal; +var _56b=self._observers; +var _56c=self._disconnect; +var _56d=self._lock; +var _56e=self._dirty; +if(typeof (_569)==="undefined"){ +_569=null; +} +for(var i=_56b.length-1;i>=0;i--){ +var _570=_56b[i]; +if(_570.objOrFunc===_568&&(_569===null||_570.funcOrStr===_569)){ +_56c(_570); +if(_56d){ +_56e=true; +}else{ +_56b.splice(i,1); +} +} +} +self._dirty=_56e; +},disconnectAll:function(src,sig){ +src=MochiKit.DOM.getElement(src); +var m=MochiKit.Base; +var _574=m.flattenArguments(m.extend(null,arguments,1)); +var self=MochiKit.Signal; +var _576=self._disconnect; +var _577=self._observers; +var i,_579; +var _57a=self._lock; +var _57b=self._dirty; +if(_574.length===0){ +for(i=_577.length-1;i>=0;i--){ +_579=_577[i]; +if(_579.source===src){ +_576(_579); +if(!_57a){ +_577.splice(i,1); +}else{ +_57b=true; +} +} +} +}else{ +var sigs={}; +for(i=0;i<_574.length;i++){ +sigs[_574[i]]=true; +} +for(i=_577.length-1;i>=0;i--){ +_579=_577[i]; +if(_579.source===src&&_579.signal in sigs){ +_576(_579); +if(!_57a){ +_577.splice(i,1); +}else{ +_57b=true; +} +} +} +} +self._dirty=_57b; +},signal:function(src,sig){ +var self=MochiKit.Signal; +var _580=self._observers; +src=MochiKit.DOM.getElement(src); +var args=MochiKit.Base.extend(null,arguments,2); +var _582=[]; +self._lock=true; +for(var i=0;i<_580.length;i++){ +var _584=_580[i]; +if(_584.source===src&&_584.signal===sig){ +try{ +_584.listener.apply(src,args); +} +catch(e){ +_582.push(e); +} +} +} +self._lock=false; +if(self._dirty){ +self._dirty=false; +for(var i=_580.length-1;i>=0;i--){ +if(!_580[i].connected){ +_580.splice(i,1); +} +} +} +if(_582.length==1){ +throw _582[0]; +}else{ +if(_582.length>1){ +var e=new Error("Multiple errors thrown in handling 'sig', see errors property"); +e.errors=_582; +throw e; +} +} +}}); +MochiKit.Signal.EXPORT_OK=[]; +MochiKit.Signal.EXPORT=["connect","disconnect","signal","disconnectAll","disconnectAllTo"]; +MochiKit.Signal.__new__=function(win){ +var m=MochiKit.Base; +this._document=document; +this._window=win; +this._lock=false; +this._dirty=false; +try{ +this.connect(window,"onunload",this._unloadCache); +} +catch(e){ +} +this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; +m.nameFunctions(this); +}; +MochiKit.Signal.__new__(this); +if(MochiKit.__export__){ +connect=MochiKit.Signal.connect; +disconnect=MochiKit.Signal.disconnect; +disconnectAll=MochiKit.Signal.disconnectAll; +signal=MochiKit.Signal.signal; +} +MochiKit.Base._exportSymbols(this,MochiKit.Signal); +if(typeof (dojo)!="undefined"){ +dojo.provide("MochiKit.Position"); +dojo.require("MochiKit.Base"); +dojo.require("MochiKit.DOM"); +dojo.require("MochiKit.Style"); +} +if(typeof (JSAN)!="undefined"){ +JSAN.use("MochiKit.Base",[]); +JSAN.use("MochiKit.DOM",[]); +JSAN.use("MochiKit.Style",[]); +} +try{ +if(typeof (MochiKit.Base)=="undefined"||typeof (MochiKit.Style)=="undefined"||typeof (MochiKit.DOM)=="undefined"){ +throw ""; +} +} +catch(e){ +throw "MochiKit.Style depends on MochiKit.Base, MochiKit.DOM, and MochiKit.Style!"; +} +if(typeof (MochiKit.Position)=="undefined"){ +MochiKit.Position={}; +} +MochiKit.Position.NAME="MochiKit.Position"; +MochiKit.Position.VERSION="1.4"; +MochiKit.Position.__repr__=function(){ +return "["+this.NAME+" "+this.VERSION+"]"; +}; +MochiKit.Position.toString=function(){ +return this.__repr__(); +}; +MochiKit.Position.EXPORT_OK=[]; +MochiKit.Position.EXPORT=[]; +MochiKit.Base.update(MochiKit.Position,{includeScrollOffsets:false,prepare:function(){ +var _588=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0; +var _589=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0; +this.windowOffset=new MochiKit.Style.Coordinates(_588,_589); +},cumulativeOffset:function(_58a){ +var _58b=0; +var _58c=0; +do{ +_58b+=_58a.offsetTop||0; +_58c+=_58a.offsetLeft||0; +_58a=_58a.offsetParent; +}while(_58a); +return new MochiKit.Style.Coordinates(_58c,_58b); +},realOffset:function(_58d){ +var _58e=0; +var _58f=0; +do{ +_58e+=_58d.scrollTop||0; +_58f+=_58d.scrollLeft||0; +_58d=_58d.parentNode; +}while(_58d); +return new MochiKit.Style.Coordinates(_58f,_58e); +},within:function(_590,x,y){ +if(this.includeScrollOffsets){ +return this.withinIncludingScrolloffsets(_590,x,y); +} +this.xcomp=x; +this.ycomp=y; +this.offset=this.cumulativeOffset(_590); +if(_590.style.position=="fixed"){ +this.offset.x+=this.windowOffset.x; +this.offset.y+=this.windowOffset.y; +} +return (y>=this.offset.y&&y=this.offset.x&&x=this.offset.y&&this.ycomp=this.offset.x&&this.xcomp"+el.innerHTML+""; -},_roundTopCorners:function(el,_524,_525){ -var _526=this._createCorner(_525); +},_renderBorder:function(el,_5bc){ +var _5bd="1px solid "+this._borderColor(_5bc); +var _5be="border-left: "+_5bd; +var _5bf="border-right: "+_5bd; +var _5c0="style='"+_5be+";"+_5bf+"'"; +el.innerHTML="
"+el.innerHTML+"
"; +},_roundTopCorners:function(el,_5c2,_5c3){ +var _5c4=this._createCorner(_5c3); for(var i=0;i=0;i--){ -_529.appendChild(this._createCornerSlice(_527,_528,i,"bottom")); +_5c9.appendChild(this._createCornerSlice(_5c7,_5c8,i,"bottom")); } el.style.paddingBottom=0; -el.appendChild(_529); -},_createCorner:function(_530){ +el.appendChild(_5c9); +},_createCorner:function(_5cb){ var dom=MochiKit.DOM; -return dom.DIV({style:{backgroundColor:_530.toString()}}); -},_createCornerSlice:function(_531,_532,n,_533){ -var _534=MochiKit.DOM.SPAN(); -var _535=_534.style; -_535.backgroundColor=_531.toString(); -_535.display="block"; -_535.height="1px"; -_535.overflow="hidden"; -_535.fontSize="1px"; -var _536=this._borderColor(_531,_532); -if(this.options.border&&n==0){ -_535.borderTopStyle="solid"; -_535.borderTopWidth="1px"; -_535.borderLeftWidth="0px"; -_535.borderRightWidth="0px"; -_535.borderBottomWidth="0px"; -_535.height="0px"; -_535.borderColor=_536.toString(); +return dom.DIV({style:{backgroundColor:_5cb.toString()}}); +},_createCornerSlice:function(_5cd,_5ce,n,_5d0){ +var _5d1=MochiKit.DOM.SPAN(); +var _5d2=_5d1.style; +_5d2.backgroundColor=_5cd.toString(); +_5d2.display="block"; +_5d2.height="1px"; +_5d2.overflow="hidden"; +_5d2.fontSize="1px"; +var _5d3=this._borderColor(_5cd,_5ce); +if(this.options.border&&n===0){ +_5d2.borderTopStyle="solid"; +_5d2.borderTopWidth="1px"; +_5d2.borderLeftWidth="0px"; +_5d2.borderRightWidth="0px"; +_5d2.borderBottomWidth="0px"; +_5d2.height="0px"; +_5d2.borderColor=_5d3.toString(); }else{ -if(_536){ -_535.borderColor=_536.toString(); -_535.borderStyle="solid"; -_535.borderWidth="0px 1px"; +if(_5d3){ +_5d2.borderColor=_5d3.toString(); +_5d2.borderStyle="solid"; +_5d2.borderWidth="0px 1px"; } } if(!this.options.compact&&(n==(this.options.numSlices-1))){ -_535.height="2px"; +_5d2.height="2px"; } -this._setMargin(_534,n,_533); -this._setBorder(_534,n,_533); -return _534; -},_setOptions:function(_537){ +this._setMargin(_5d1,n,_5d0); +this._setBorder(_5d1,n,_5d0); +return _5d1; +},_setOptions:function(_5d4){ this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false,__unstable__wrapElement:false}; -MochiKit.Base.update(this.options,_537); +MochiKit.Base.update(this.options,_5d4); this.options.numSlices=(this.options.compact?2:4); },_whichSideTop:function(){ -var _538=this.options.corners; -if(this._hasString(_538,"all","top")){ +var _5d5=this.options.corners; +if(this._hasString(_5d5,"all","top")){ return ""; } -var _539=(_538.indexOf("tl")!=-1); -var _540=(_538.indexOf("tr")!=-1); -if(_539&&_540){ +var _5d6=(_5d5.indexOf("tl")!=-1); +var _5d7=(_5d5.indexOf("tr")!=-1); +if(_5d6&&_5d7){ return ""; } -if(_539){ +if(_5d6){ return "left"; } -if(_540){ +if(_5d7){ return "right"; } return ""; },_whichSideBottom:function(){ -var _541=this.options.corners; -if(this._hasString(_541,"all","bottom")){ +var _5d8=this.options.corners; +if(this._hasString(_5d8,"all","bottom")){ return ""; } -var _542=(_541.indexOf("bl")!=-1); -var _543=(_541.indexOf("br")!=-1); -if(_542&&_543){ +var _5d9=(_5d8.indexOf("bl")!=-1); +var _5da=(_5d8.indexOf("br")!=-1); +if(_5d9&&_5da){ return ""; } -if(_542){ +if(_5d9){ return "left"; } -if(_543){ +if(_5da){ return "right"; } return ""; -},_borderColor:function(_544,_545){ -if(_544=="transparent"){ -return _545; +},_borderColor:function(_5db,_5dc){ +if(_5db=="transparent"){ +return _5dc; }else{ if(this.options.border){ return this.options.border; }else{ if(this.options.blend){ -return _545.blendedColor(_544); +return _5dc.blendedColor(_5db); } } } return ""; -},_setMargin:function(el,n,_546){ -var _547=this._marginSize(n)+"px"; -var _548=(_546=="top"?this._whichSideTop():this._whichSideBottom()); -var _549=el.style; -if(_548=="left"){ -_549.marginLeft=_547; -_549.marginRight="0px"; +},_setMargin:function(el,n,_5df){ +var _5e0=this._marginSize(n)+"px"; +var _5e1=(_5df=="top"?this._whichSideTop():this._whichSideBottom()); +var _5e2=el.style; +if(_5e1=="left"){ +_5e2.marginLeft=_5e0; +_5e2.marginRight="0px"; }else{ -if(_548=="right"){ -_549.marginRight=_547; -_549.marginLeft="0px"; +if(_5e1=="right"){ +_5e2.marginRight=_5e0; +_5e2.marginLeft="0px"; }else{ -_549.marginLeft=_547; -_549.marginRight=_547; +_5e2.marginLeft=_5e0; +_5e2.marginRight=_5e0; } } -},_setBorder:function(el,n,_550){ -var _551=this._borderSize(n)+"px"; -var _552=(_550=="top"?this._whichSideTop():this._whichSideBottom()); -var _553=el.style; -if(_552=="left"){ -_553.borderLeftWidth=_551; -_553.borderRightWidth="0px"; +},_setBorder:function(el,n,_5e5){ +var _5e6=this._borderSize(n)+"px"; +var _5e7=(_5e5=="top"?this._whichSideTop():this._whichSideBottom()); +var _5e8=el.style; +if(_5e7=="left"){ +_5e8.borderLeftWidth=_5e6; +_5e8.borderRightWidth="0px"; }else{ -if(_552=="right"){ -_553.borderRightWidth=_551; -_553.borderLeftWidth="0px"; +if(_5e7=="right"){ +_5e8.borderRightWidth=_5e6; +_5e8.borderLeftWidth="0px"; }else{ -_553.borderLeftWidth=_551; -_553.borderRightWidth=_551; +_5e8.borderLeftWidth=_5e6; +_5e8.borderRightWidth=_5e6; } } },_marginSize:function(n){ @@ -3937,39 +5884,39 @@ if(o.compact&&o.blend){ } var o=this.options; if(o.compact&&o.blend){ -var _554=[1,0]; -return _554[n]; +var _5eb=[1,0]; +return _5eb[n]; }else{ if(o.compact){ -var _555=[2,1]; -return _555[n]; +var _5ec=[2,1]; +return _5ec[n]; }else{ if(o.blend){ -var _556=[3,2,1,0]; -return _556[n]; +var _5ed=[3,2,1,0]; +return _5ed[n]; }else{ -var _557=[5,3,2,1]; -return _557[n]; +var _5ee=[5,3,2,1]; +return _5ee[n]; } } } },_borderSize:function(n){ var o=this.options; -var _558; +var _5f1; if(o.compact&&(o.blend||this.isTransparent)){ return 1; }else{ if(o.compact){ -_558=[1,0]; +_5f1=[1,0]; }else{ if(o.blend){ -_558=[2,1,1,1]; +_5f1=[2,1,1,1]; }else{ if(o.border){ -_558=[0,2,0,0]; +_5f1=[0,2,0,0]; }else{ if(this.isTransparent){ -_558=[5,3,2,1]; +_5f1=[5,3,2,1]; }else{ return 0; } @@ -3977,7 +5924,7 @@ return 0; } } } -return _558[n]; +return _5f1[n]; },_hasString:function(str){ for(var i=1;i=(_61c||i)){ +_61c=i; +} +},this.effects); +_618=_61c||_618; +break; +case "break": +ma(function(e){ +e.finalize(); +},this.effects); +break; +} +_617.startOn+=_618; +_617.finishOn+=_618; +if(!_617.options.queue.limit||this.effects.length<_617.options.queue.limit){ +this.effects.push(_617); +} +if(!this.interval){ +this.interval=this.startLoop(MochiKit.Base.bind(this.loop,this),40); +} +},startLoop:function(func,_621){ +return setInterval(func,_621); +},remove:function(_622){ +this.effects=MochiKit.Base.filter(function(e){ +return e!=_622; +},this.effects); +if(!this.effects.length){ +this.stopLoop(this.interval); +this.interval=null; +} +},stopLoop:function(_624){ +clearInterval(_624); +},loop:function(){ +var _625=new Date().getTime(); +MochiKit.Base.map(function(_626){ +_626.loop(_625); +},this.effects); +}}); +MochiKit.Visual.Queues={instances:{},get:function(_627){ +if(typeof (_627)!="string"){ +return _627; +} +if(!this.instances[_627]){ +this.instances[_627]=new MochiKit.Visual.ScopedQueue(); +} +return this.instances[_627]; +}}; +MochiKit.Visual.Queue=MochiKit.Visual.Queues.get("global"); +MochiKit.Visual.DefaultOptions={transition:MochiKit.Visual.Transitions.sinoidal,duration:1,fps:25,sync:false,from:0,to:1,delay:0,queue:"parallel"}; +MochiKit.Visual.Base=function(){ +}; +MochiKit.Visual.Base.prototype={__class__:MochiKit.Visual.Base,start:function(_628){ +var v=MochiKit.Visual; +this.options=MochiKit.Base.setdefault(_628||{},v.DefaultOptions); +this.currentFrame=0; +this.state="idle"; +this.startOn=this.options.delay*1000; +this.finishOn=this.startOn+(this.options.duration*1000); +this.event("beforeStart"); +if(!this.options.sync){ +v.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).add(this); +} +},loop:function(_62a){ +if(_62a>=this.startOn){ +if(_62a>=this.finishOn){ +return this.finalize(); +} +var pos=(_62a-this.startOn)/(this.finishOn-this.startOn); +var _62c=Math.round(pos*this.options.fps*this.options.duration); +if(_62c>this.currentFrame){ +this.render(pos); +this.currentFrame=_62c; +} +} +},render:function(pos){ +if(this.state=="idle"){ +this.state="running"; +this.event("beforeSetup"); +this.setup(); +this.event("afterSetup"); +} +if(this.state=="running"){ +if(this.options.transition){ +pos=this.options.transition(pos); +} +pos*=(this.options.to-this.options.from); +pos+=this.options.from; +this.event("beforeUpdate"); +this.update(pos); +this.event("afterUpdate"); +} +},cancel:function(){ +if(!this.options.sync){ +MochiKit.Visual.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).remove(this); +} +this.state="finished"; +},finalize:function(){ +this.render(1); +this.cancel(); +this.event("beforeFinish"); +this.finish(); +this.event("afterFinish"); +},setup:function(){ +},finish:function(){ +},update:function(_62e){ +},event:function(_62f){ +if(this.options[_62f+"Internal"]){ +this.options[_62f+"Internal"](this); +} +if(this.options[_62f]){ +this.options[_62f](this); +} +},repr:function(){ +return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; +}}; +MochiKit.Visual.Parallel=function(_630,_631){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_630,_631); +} +this.__init__(_630,_631); +}; +MochiKit.Visual.Parallel.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Parallel.prototype,{__class__:MochiKit.Visual.Parallel,__init__:function(_633,_634){ +this.effects=_633||[]; +this.start(_634); +},update:function(_635){ +MochiKit.Base.map(function(_636){ +_636.render(_635); +},this.effects); +},finish:function(){ +MochiKit.Base.map(function(_637){ +_637.finalize(); +},this.effects); +}}); +MochiKit.Visual.Opacity=function(_638,_639){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_638,_639); +} +this.__init__(_638,_639); +}; +MochiKit.Visual.Opacity.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Opacity.prototype,{__class__:MochiKit.Visual.Opacity,__init__:function(_63b,_63c){ +var b=MochiKit.Base; +var s=MochiKit.Style; +this.element=MochiKit.DOM.getElement(_63b); +if(this.element.currentStyle&&(!this.element.currentStyle.hasLayout)){ +s.setStyle(this.element,{zoom:1}); +} +_63c=b.update({from:s.getStyle(this.element,"opacity")||0,to:1},_63c||{}); +this.start(_63c); +},update:function(_63f){ +MochiKit.Style.setStyle(this.element,{"opacity":_63f}); +}}); +MochiKit.Visual.Move=function(_640,_641){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_640,_641); +} +this.__init__(_640,_641); +}; +MochiKit.Visual.Move.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Move.prototype,{__class__:MochiKit.Visual.Move,__init__:function(_643,_644){ +this.element=MochiKit.DOM.getElement(_643); +_644=MochiKit.Base.update({x:0,y:0,mode:"relative"},_644||{}); +this.start(_644); +},setup:function(){ +MochiKit.DOM.makePositioned(this.element); +var s=this.element.style; +var _646=s.visibility; +var _647=s.display; +if(_647=="none"){ +s.visibility="hidden"; +s.display=""; +} +this.originalLeft=parseFloat(MochiKit.Style.getStyle(this.element,"left")||"0"); +this.originalTop=parseFloat(MochiKit.Style.getStyle(this.element,"top")||"0"); +if(this.options.mode=="absolute"){ +this.options.x-=this.originalLeft; +this.options.y-=this.originalTop; +} +if(_647=="none"){ +s.visibility=_646; +s.display=_647; +} +},update:function(_648){ +MochiKit.Style.setStyle(this.element,{left:Math.round(this.options.x*_648+this.originalLeft)+"px",top:Math.round(this.options.y*_648+this.originalTop)+"px"}); +}}); +MochiKit.Visual.Scale=function(_649,_64a,_64b){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_649,_64a,_64b); +} +this.__init__(_649,_64a,_64b); +}; +MochiKit.Visual.Scale.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Scale.prototype,{__class__:MochiKit.Visual.Scale,__init__:function(_64d,_64e,_64f){ +this.element=MochiKit.DOM.getElement(_64d); +_64f=MochiKit.Base.update({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:"box",scaleFrom:100,scaleTo:_64e},_64f||{}); +this.start(_64f); +},setup:function(){ +this.restoreAfterFinish=this.options.restoreAfterFinish||false; +this.elementPositioning=MochiKit.Style.getStyle(this.element,"position"); +var ma=MochiKit.Base.map; +var b=MochiKit.Base.bind; +this.originalStyle={}; +ma(b(function(k){ +this.originalStyle[k]=this.element.style[k]; +},this),["top","left","width","height","fontSize"]); +this.originalTop=this.element.offsetTop; +this.originalLeft=this.element.offsetLeft; +var _653=MochiKit.Style.getStyle(this.element,"font-size")||"100%"; +ma(b(function(_654){ +if(_653.indexOf(_654)>0){ +this.fontSize=parseFloat(_653); +this.fontSizeType=_654; +} +},this),["em","px","%"]); +this.factor=(this.options.scaleTo-this.options.scaleFrom)/100; +if(/^content/.test(this.options.scaleMode)){ +this.dims=[this.element.scrollHeight,this.element.scrollWidth]; +}else{ +if(this.options.scaleMode=="box"){ +this.dims=[this.element.offsetHeight,this.element.offsetWidth]; +}else{ +this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth]; +} +} +},update:function(_655){ +var _656=(this.options.scaleFrom/100)+(this.factor*_655); +if(this.options.scaleContent&&this.fontSize){ +MochiKit.Style.setStyle(this.element,{fontSize:this.fontSize*_656+this.fontSizeType}); +} +this.setDimensions(this.dims[0]*_656,this.dims[1]*_656); +},finish:function(){ +if(this.restoreAfterFinish){ +MochiKit.Style.setStyle(this.element,this.originalStyle); +} +},setDimensions:function(_657,_658){ +var d={}; +var r=Math.round; +if(/MSIE/.test(navigator.userAgent)){ +r=Math.ceil; +} +if(this.options.scaleX){ +d.width=r(_658)+"px"; +} +if(this.options.scaleY){ +d.height=r(_657)+"px"; +} +if(this.options.scaleFromCenter){ +var topd=(_657-this.dims[0])/2; +var _65c=(_658-this.dims[1])/2; +if(this.elementPositioning=="absolute"){ +if(this.options.scaleY){ +d.top=this.originalTop-topd+"px"; +} +if(this.options.scaleX){ +d.left=this.originalLeft-_65c+"px"; +} +}else{ +if(this.options.scaleY){ +d.top=-topd+"px"; +} +if(this.options.scaleX){ +d.left=-_65c+"px"; +} +} +} +MochiKit.Style.setStyle(this.element,d); +}}); +MochiKit.Visual.Highlight=function(_65d,_65e){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_65d,_65e); +} +this.__init__(_65d,_65e); +}; +MochiKit.Visual.Highlight.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Highlight.prototype,{__class__:MochiKit.Visual.Highlight,__init__:function(_660,_661){ +this.element=MochiKit.DOM.getElement(_660); +_661=MochiKit.Base.update({startcolor:"#ffff99"},_661||{}); +this.start(_661); +},setup:function(){ +var b=MochiKit.Base; +var s=MochiKit.Style; +if(s.getStyle(this.element,"display")=="none"){ +this.cancel(); +return; +} +this.oldStyle={backgroundImage:s.getStyle(this.element,"background-image")}; +s.setStyle(this.element,{backgroundImage:"none"}); +if(!this.options.endcolor){ +this.options.endcolor=MochiKit.Color.Color.fromBackground(this.element).toHexString(); +} +if(b.isUndefinedOrNull(this.options.restorecolor)){ +this.options.restorecolor=s.getStyle(this.element,"background-color"); +} +this._base=b.map(b.bind(function(i){ +return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16); +},this),[0,1,2]); +this._delta=b.map(b.bind(function(i){ +return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]; +},this),[0,1,2]); +},update:function(_666){ +var m="#"; +MochiKit.Base.map(MochiKit.Base.bind(function(i){ +m+=MochiKit.Color.toColorPart(Math.round(this._base[i]+this._delta[i]*_666)); +},this),[0,1,2]); +MochiKit.Style.setStyle(this.element,{backgroundColor:m}); +},finish:function(){ +MochiKit.Style.setStyle(this.element,MochiKit.Base.update(this.oldStyle,{backgroundColor:this.options.restorecolor})); +}}); +MochiKit.Visual.ScrollTo=function(_669,_66a){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_669,_66a); +} +this.__init__(_669,_66a); +}; +MochiKit.Visual.ScrollTo.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype,{__class__:MochiKit.Visual.ScrollTo,__init__:function(_66c,_66d){ +this.element=MochiKit.DOM.getElement(_66c); +this.start(_66d||{}); +},setup:function(){ +var p=MochiKit.Position; +p.prepare(); +var _66f=p.cumulativeOffset(this.element); +if(this.options.offset){ +_66f.y+=this.options.offset; +} +var max; +if(window.innerHeight){ +max=window.innerHeight-window.height; +}else{ +if(document.documentElement&&document.documentElement.clientHeight){ +max=document.documentElement.clientHeight-document.body.scrollHeight; +}else{ +if(document.body){ +max=document.body.clientHeight-document.body.scrollHeight; +} +} +} +this.scrollStart=p.windowOffset.y; +this.delta=(_66f.y>max?max:_66f.y)-this.scrollStart; +},update:function(_671){ +var p=MochiKit.Position; +p.prepare(); +window.scrollTo(p.windowOffset.x,this.scrollStart+(_671*this.delta)); +}}); +MochiKit.Visual.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; +MochiKit.Visual.Morph=function(_673,_674){ +var cls=arguments.callee; +if(!(this instanceof cls)){ +return new cls(_673,_674); +} +this.__init__(_673,_674); +}; +MochiKit.Visual.Morph.prototype=new MochiKit.Visual.Base(); +MochiKit.Base.update(MochiKit.Visual.Morph.prototype,{__class__:MochiKit.Visual.Morph,__init__:function(_676,_677){ +this.element=MochiKit.DOM.getElement(_676); +this.start(_677||{}); +},setup:function(){ +var b=MochiKit.Base; +var _679=this.options.style; +this.styleStart={}; +this.styleEnd={}; +this.units={}; +var _67a,unit; +for(var s in _679){ +_67a=_679[s]; +s=b.camelize(s); +if(MochiKit.Visual.CSS_LENGTH.test(_67a)){ +var _67d=_67a.match(/^([\+\-]?[0-9\.]+)(.*)$/); +_67a=parseFloat(_67d[1]); +unit=(_67d.length==3)?_67d[2]:null; +this.styleEnd[s]=_67a; +this.units[s]=unit; +_67a=MochiKit.Style.getStyle(this.element,s); +_67d=_67a.match(/^([\+\-]?[0-9\.]+)(.*)$/); +_67a=parseFloat(_67d[1]); +this.styleStart[s]=_67a; +}else{ +var c=MochiKit.Color.Color; +_67a=c.fromString(_67a); +if(_67a){ +this.units[s]="color"; +this.styleEnd[s]=_67a.toHexString(); +_67a=MochiKit.Style.getStyle(this.element,s); +this.styleStart[s]=c.fromString(_67a).toHexString(); +this.styleStart[s]=b.map(b.bind(function(i){ +return parseInt(this.styleStart[s].slice(i*2+1,i*2+3),16); +},this),[0,1,2]); +this.styleEnd[s]=b.map(b.bind(function(i){ +return parseInt(this.styleEnd[s].slice(i*2+1,i*2+3),16); +},this),[0,1,2]); +} +} +} +},update:function(_681){ +var _682; +for(var s in this.styleStart){ +if(this.units[s]=="color"){ +var m="#"; +var _685=this.styleStart[s]; +var end=this.styleEnd[s]; +MochiKit.Base.map(MochiKit.Base.bind(function(i){ +m+=MochiKit.Color.toColorPart(Math.round(_685[i]+(end[i]-_685[i])*_681)); +},this),[0,1,2]); +this.element.style[s]=m; +}else{ +_682=this.styleStart[s]+Math.round((this.styleEnd[s]-this.styleStart[s])*_681*1000)/1000+this.units[s]; +this.element.style[s]=_682; +} +} +}}); +MochiKit.Visual.fade=function(_688,_689){ +var s=MochiKit.Style; +var _68b=s.getStyle(_688,"opacity"); +_689=MochiKit.Base.update({from:s.getStyle(_688,"opacity")||1,to:0,afterFinishInternal:function(_68c){ +if(_68c.options.to!==0){ +return; +} +s.hideElement(_68c.element); +s.setStyle(_68c.element,{"opacity":_68b}); +}},_689||{}); +return new MochiKit.Visual.Opacity(_688,_689); +}; +MochiKit.Visual.appear=function(_68d,_68e){ +var s=MochiKit.Style; +var v=MochiKit.Visual; +_68e=MochiKit.Base.update({from:(s.getStyle(_68d,"display")=="none"?0:s.getStyle(_68d,"opacity")||0),to:1,afterFinishInternal:function(_691){ +v.forceRerendering(_691.element); +},beforeSetupInternal:function(_692){ +s.setStyle(_692.element,{"opacity":_692.options.from}); +s.showElement(_692.element); +}},_68e||{}); +return new v.Opacity(_68d,_68e); +}; +MochiKit.Visual.puff=function(_693,_694){ +var s=MochiKit.Style; +var v=MochiKit.Visual; +_693=MochiKit.DOM.getElement(_693); +var _697={position:s.getStyle(_693,"position"),top:_693.style.top,left:_693.style.left,width:_693.style.width,height:_693.style.height,opacity:s.getStyle(_693,"opacity")}; +_694=MochiKit.Base.update({beforeSetupInternal:function(_698){ +MochiKit.Position.absolutize(_698.effects[0].element); +},afterFinishInternal:function(_699){ +s.hideElement(_699.effects[0].element); +s.setStyle(_699.effects[0].element,_697); +},scaleContent:true,scaleFromCenter:true},_694||{}); +return new v.Parallel([new v.Scale(_693,200,{sync:true,scaleFromCenter:_694.scaleFromCenter,scaleContent:_694.scaleContent,restoreAfterFinish:true}),new v.Opacity(_693,{sync:true,to:0})],_694); +}; +MochiKit.Visual.blindUp=function(_69a,_69b){ +var d=MochiKit.DOM; +_69a=d.getElement(_69a); +var _69d=d.makeClipping(_69a); +_69b=MochiKit.Base.update({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(_69e){ +MochiKit.Style.hideElement(_69e.element); +d.undoClipping(_69e.element,_69d); +}},_69b||{}); +return new MochiKit.Visual.Scale(_69a,0,_69b); +}; +MochiKit.Visual.blindDown=function(_69f,_6a0){ +var d=MochiKit.DOM; +var s=MochiKit.Style; +_69f=d.getElement(_69f); +var _6a3=s.getElementDimensions(_69f); +var _6a4; +_6a0=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_6a3.h,originalWidth:_6a3.w},restoreAfterFinish:true,afterSetupInternal:function(_6a5){ +_6a4=d.makeClipping(_6a5.element); +s.setStyle(_6a5.element,{height:"0px"}); +s.showElement(_6a5.element); +},afterFinishInternal:function(_6a6){ +d.undoClipping(_6a6.element,_6a4); +}},_6a0||{}); +return new MochiKit.Visual.Scale(_69f,100,_6a0); +}; +MochiKit.Visual.switchOff=function(_6a7,_6a8){ +var d=MochiKit.DOM; +_6a7=d.getElement(_6a7); +var _6aa=MochiKit.Style.getStyle(_6a7,"opacity"); +var _6ab; +_6a8=MochiKit.Base.update({duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetupInternal:function(_6ac){ +d.makePositioned(_6ac.element); +_6ab=d.makeClipping(_6ac.element); +},afterFinishInternal:function(_6ad){ +MochiKit.Style.hideElement(_6ad.element); +d.undoClipping(_6ad.element,_6ab); +d.undoPositioned(_6ad.element); +MochiKit.Style.setStyle(_6ad.element,{"opacity":_6aa}); +}},_6a8||{}); +var v=MochiKit.Visual; +return new v.appear(_6a7,{duration:0.4,from:0,transition:v.Transitions.flicker,afterFinishInternal:function(_6af){ +new v.Scale(_6af.element,1,_6a8); +}}); +}; +MochiKit.Visual.dropOut=function(_6b0,_6b1){ +var d=MochiKit.DOM; +var s=MochiKit.Style; +_6b0=d.getElement(_6b0); +var _6b4={top:s.getStyle(_6b0,"top"),left:s.getStyle(_6b0,"left"),opacity:s.getStyle(_6b0,"opacity")}; +_6b1=MochiKit.Base.update({duration:0.5,distance:100,beforeSetupInternal:function(_6b5){ +d.makePositioned(_6b5.effects[0].element); +},afterFinishInternal:function(_6b6){ +s.hideElement(_6b6.effects[0].element); +d.undoPositioned(_6b6.effects[0].element); +s.setStyle(_6b6.effects[0].element,_6b4); +}},_6b1||{}); +var v=MochiKit.Visual; +return new v.Parallel([new v.Move(_6b0,{x:0,y:_6b1.distance,sync:true}),new v.Opacity(_6b0,{sync:true,to:0})],_6b1); +}; +MochiKit.Visual.shake=function(_6b8,_6b9){ +var d=MochiKit.DOM; +var v=MochiKit.Visual; +var s=MochiKit.Style; +_6b8=d.getElement(_6b8); +_6b9=MochiKit.Base.update({x:-20,y:0,duration:0.05,afterFinishInternal:function(_6bd){ +d.undoPositioned(_6bd.element); +s.setStyle(_6bd.element,_6be); +}},_6b9||{}); +var _6be={top:s.getStyle(_6b8,"top"),left:s.getStyle(_6b8,"left")}; +return new v.Move(_6b8,{x:20,y:0,duration:0.05,afterFinishInternal:function(_6bf){ +new v.Move(_6bf.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(_6c0){ +new v.Move(_6c0.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(_6c1){ +new v.Move(_6c1.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(_6c2){ +new v.Move(_6c2.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(_6c3){ +new v.Move(_6c3.element,_6b9); +}}); +}}); +}}); +}}); +}}); +}; +MochiKit.Visual.slideDown=function(_6c4,_6c5){ +var d=MochiKit.DOM; +var b=MochiKit.Base; +var s=MochiKit.Style; +_6c4=d.getElement(_6c4); +if(!_6c4.firstChild){ +throw "MochiKit.Visual.slideDown must be used on a element with a child"; +} +d.removeEmptyTextNodes(_6c4); +var _6c9=s.getStyle(_6c4.firstChild,"bottom")||0; +var _6ca=s.getElementDimensions(_6c4); +var _6cb; +_6c5=b.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_6ca.h,originalWidth:_6ca.w},restoreAfterFinish:true,afterSetupInternal:function(_6cc){ +d.makePositioned(_6cc.element); +d.makePositioned(_6cc.element.firstChild); +if(/Opera/.test(navigator.userAgent)){ +s.setStyle(_6cc.element,{top:""}); +} +_6cb=d.makeClipping(_6cc.element); +s.setStyle(_6cc.element,{height:"0px"}); +s.showElement(_6cc.element); +},afterUpdateInternal:function(_6cd){ +s.setStyle(_6cd.element.firstChild,{bottom:(_6cd.dims[0]-_6cd.element.clientHeight)+"px"}); +},afterFinishInternal:function(_6ce){ +d.undoClipping(_6ce.element,_6cb); +if(/MSIE/.test(navigator.userAgent)){ +d.undoPositioned(_6ce.element); +d.undoPositioned(_6ce.element.firstChild); +}else{ +d.undoPositioned(_6ce.element.firstChild); +d.undoPositioned(_6ce.element); +} +s.setStyle(_6ce.element.firstChild,{bottom:_6c9}); +}},_6c5||{}); +return new MochiKit.Visual.Scale(_6c4,100,_6c5); +}; +MochiKit.Visual.slideUp=function(_6cf,_6d0){ +var d=MochiKit.DOM; +var b=MochiKit.Base; +var s=MochiKit.Style; +_6cf=d.getElement(_6cf); +if(!_6cf.firstChild){ +throw "MochiKit.Visual.slideUp must be used on a element with a child"; +} +d.removeEmptyTextNodes(_6cf); +var _6d4=s.getStyle(_6cf.firstChild,"bottom"); +var _6d5; +_6d0=b.update({scaleContent:false,scaleX:false,scaleMode:"box",scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(_6d6){ +d.makePositioned(_6d6.element); +d.makePositioned(_6d6.element.firstChild); +if(/Opera/.test(navigator.userAgent)){ +s.setStyle(_6d6.element,{top:""}); +} +_6d5=d.makeClipping(_6d6.element); +s.showElement(_6d6.element); +},afterUpdateInternal:function(_6d7){ +s.setStyle(_6d7.element.firstChild,{bottom:(_6d7.dims[0]-_6d7.element.clientHeight)+"px"}); +},afterFinishInternal:function(_6d8){ +s.hideElement(_6d8.element); +d.undoClipping(_6d8.element,_6d5); +d.undoPositioned(_6d8.element.firstChild); +d.undoPositioned(_6d8.element); +s.setStyle(_6d8.element.firstChild,{bottom:_6d4}); +}},_6d0||{}); +return new MochiKit.Visual.Scale(_6cf,0,_6d0); +}; +MochiKit.Visual.squish=function(_6d9,_6da){ +var d=MochiKit.DOM; +var b=MochiKit.Base; +var _6dd; +_6da=b.update({restoreAfterFinish:true,beforeSetupInternal:function(_6de){ +_6dd=d.makeClipping(_6de.element); +},afterFinishInternal:function(_6df){ +MochiKit.Style.hideElement(_6df.element); +d.undoClipping(_6df.element,_6dd); +}},_6da||{}); +return new MochiKit.Visual.Scale(_6d9,/Opera/.test(navigator.userAgent)?1:0,_6da); +}; +MochiKit.Visual.grow=function(_6e0,_6e1){ +var d=MochiKit.DOM; +var v=MochiKit.Visual; +var s=MochiKit.Style; +_6e0=d.getElement(_6e0); +_6e1=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.full,scaleContent:true,scaleFromCenter:false},_6e1||{}); +var _6e5={top:_6e0.style.top,left:_6e0.style.left,height:_6e0.style.height,width:_6e0.style.width,opacity:s.getStyle(_6e0,"opacity")}; +var dims=s.getElementDimensions(_6e0); +var _6e7,_6e8; +var _6e9,_6ea; +switch(_6e1.direction){ +case "top-left": +_6e7=_6e8=_6e9=_6ea=0; +break; +case "top-right": +_6e7=dims.w; +_6e8=_6ea=0; +_6e9=-dims.w; +break; +case "bottom-left": +_6e7=_6e9=0; +_6e8=dims.h; +_6ea=-dims.h; +break; +case "bottom-right": +_6e7=dims.w; +_6e8=dims.h; +_6e9=-dims.w; +_6ea=-dims.h; +break; +case "center": +_6e7=dims.w/2; +_6e8=dims.h/2; +_6e9=-dims.w/2; +_6ea=-dims.h/2; +break; +} +var _6eb=MochiKit.Base.update({beforeSetupInternal:function(_6ec){ +s.setStyle(_6ec.effects[0].element,{height:"0px"}); +s.showElement(_6ec.effects[0].element); +},afterFinishInternal:function(_6ed){ +d.undoClipping(_6ed.effects[0].element); +d.undoPositioned(_6ed.effects[0].element); +s.setStyle(_6ed.effects[0].element,_6e5); +}},_6e1||{}); +return new v.Move(_6e0,{x:_6e7,y:_6e8,duration:0.01,beforeSetupInternal:function(_6ee){ +s.hideElement(_6ee.element); +d.makeClipping(_6ee.element); +d.makePositioned(_6ee.element); +},afterFinishInternal:function(_6ef){ +new v.Parallel([new v.Opacity(_6ef.element,{sync:true,to:1,from:0,transition:_6e1.opacityTransition}),new v.Move(_6ef.element,{x:_6e9,y:_6ea,sync:true,transition:_6e1.moveTransition}),new v.Scale(_6ef.element,100,{scaleMode:{originalHeight:dims.h,originalWidth:dims.w},sync:true,scaleFrom:/Opera/.test(navigator.userAgent)?1:0,transition:_6e1.scaleTransition,scaleContent:_6e1.scaleContent,scaleFromCenter:_6e1.scaleFromCenter,restoreAfterFinish:true})],_6eb); +}}); +}; +MochiKit.Visual.shrink=function(_6f0,_6f1){ +var d=MochiKit.DOM; +var v=MochiKit.Visual; +var s=MochiKit.Style; +_6f0=d.getElement(_6f0); +_6f1=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.none,scaleContent:true,scaleFromCenter:false},_6f1||{}); +var _6f5={top:_6f0.style.top,left:_6f0.style.left,height:_6f0.style.height,width:_6f0.style.width,opacity:s.getStyle(_6f0,"opacity")}; +var dims=s.getElementDimensions(_6f0); +var _6f7,_6f8; +switch(_6f1.direction){ +case "top-left": +_6f7=_6f8=0; +break; +case "top-right": +_6f7=dims.w; +_6f8=0; +break; +case "bottom-left": +_6f7=0; +_6f8=dims.h; +break; +case "bottom-right": +_6f7=dims.w; +_6f8=dims.h; +break; +case "center": +_6f7=dims.w/2; +_6f8=dims.h/2; +break; +} +var _6f9; +var _6fa=MochiKit.Base.update({beforeStartInternal:function(_6fb){ +_6f9=d.makePositioned(_6fb.effects[0].element); +d.makeClipping(_6fb.effects[0].element); +},afterFinishInternal:function(_6fc){ +s.hideElement(_6fc.effects[0].element); +d.undoClipping(_6fc.effects[0].element,_6f9); +d.undoPositioned(_6fc.effects[0].element); +s.setStyle(_6fc.effects[0].element,_6f5); +}},_6f1||{}); +return new v.Parallel([new v.Opacity(_6f0,{sync:true,to:0,from:1,transition:_6f1.opacityTransition}),new v.Scale(_6f0,/Opera/.test(navigator.userAgent)?1:0,{sync:true,transition:_6f1.scaleTransition,scaleContent:_6f1.scaleContent,scaleFromCenter:_6f1.scaleFromCenter,restoreAfterFinish:true}),new v.Move(_6f0,{x:_6f7,y:_6f8,sync:true,transition:_6f1.moveTransition})],_6fa); +}; +MochiKit.Visual.pulsate=function(_6fd,_6fe){ +var d=MochiKit.DOM; +var v=MochiKit.Visual; +var b=MochiKit.Base; +var _702=MochiKit.Style.getStyle(_6fd,"opacity"); +_6fe=b.update({duration:3,from:0,afterFinishInternal:function(_703){ +MochiKit.Style.setStyle(_703.element,{"opacity":_702}); +}},_6fe||{}); +var _704=_6fe.transition||v.Transitions.sinoidal; +var _705=b.bind(function(pos){ +return _704(1-v.Transitions.pulse(pos,_6fe.pulses)); +},_704); +b.bind(_705,_704); +return new v.Opacity(_6fd,b.update({transition:_705},_6fe)); +}; +MochiKit.Visual.fold=function(_707,_708){ +var d=MochiKit.DOM; +var v=MochiKit.Visual; +var s=MochiKit.Style; +_707=d.getElement(_707); +var _70c={top:_707.style.top,left:_707.style.left,width:_707.style.width,height:_707.style.height}; +var _70d=d.makeClipping(_707); +_708=MochiKit.Base.update({scaleContent:false,scaleX:false,afterFinishInternal:function(_70e){ +new v.Scale(_707,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(_70f){ +s.hideElement(_70f.element); +d.undoClipping(_70f.element,_70d); +s.setStyle(_70f.element,_70c); +}}); +}},_708||{}); +return new v.Scale(_707,5,_708); +}; MochiKit.Visual.Color=MochiKit.Color.Color; MochiKit.Visual.getElementsComputedStyle=MochiKit.DOM.computedStyle; MochiKit.Visual.__new__=function(){ @@ -4008,8 +6792,8 @@ this.EXPORT_TAGS={":common":this.EXPORT, m.nameFunctions(this); this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; }; -MochiKit.Visual.EXPORT=["roundElement","roundClass"]; -MochiKit.Visual.EXPORT_OK=[]; +MochiKit.Visual.EXPORT=["roundElement","roundClass","tagifyText","multiple","toggle","Parallel","Opacity","Move","Scale","Highlight","ScrollTo","Morph","fade","appear","puff","blindUp","blindDown","switchOff","dropOut","shake","slideDown","slideUp","squish","grow","shrink","pulsate","fold"]; +MochiKit.Visual.EXPORT_OK=["Base","PAIRS"]; MochiKit.Visual.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Visual); if(typeof (MochiKit)=="undefined"){ @@ -4019,102 +6803,100 @@ MochiKit.MochiKit.NAME="MochiKit.MochiKi MochiKit.MochiKit={}; } MochiKit.MochiKit.NAME="MochiKit.MochiKit"; -MochiKit.MochiKit.VERSION="1.2"; +MochiKit.MochiKit.VERSION="1.4"; MochiKit.MochiKit.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.MochiKit.toString=function(){ return this.__repr__(); }; -MochiKit.MochiKit.SUBMODULES=["Base","Iter","Logging","DateTime","Format","Async","DOM","LoggingPane","Color","Visual"]; +MochiKit.MochiKit.SUBMODULES=["Base","Iter","Logging","DateTime","Format","Async","DOM","Selector","Style","LoggingPane","Color","Signal","Position","Visual"]; if(typeof (JSAN)!="undefined"||typeof (dojo)!="undefined"){ if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.MochiKit"); dojo.require("MochiKit.*"); } if(typeof (JSAN)!="undefined"){ -JSAN.use("MochiKit.Base",[]); -JSAN.use("MochiKit.Iter",[]); -JSAN.use("MochiKit.Logging",[]); -JSAN.use("MochiKit.DateTime",[]); -JSAN.use("MochiKit.Format",[]); -JSAN.use("MochiKit.Async",[]); -JSAN.use("MochiKit.DOM",[]); -JSAN.use("MochiKit.LoggingPane",[]); -JSAN.use("MochiKit.Color",[]); -JSAN.use("MochiKit.Visual",[]); +(function(lst){ +for(var i=0;i"+"<"+"/script"+">"; -document.write(tag); +document.write(""); } } })(); ============================================================ --- www/viewmtn/static/viewmtn.js b36f4e737e9b054f563c320becbc74d7a4c1df48 +++ www/viewmtn/static/viewmtn.js 001ab5902c148053998d2d6877c82ee3da848d70 @@ -1,105 +1,143 @@ var pendingDeferred = null; var theBox; var callbacksInstalled = false; var pendingDeferred = null; +var pendingFor = null; +var baseURI = null; -function installCallbacks() +function installCallbacks(for_uri) { - if (callbacksInstalled != false) { - return; - } - callbacksInstalled = true; + if (callbacksInstalled != false) { + return; + } + callbacksInstalled = true; + + if (for_uri == null) { + return; + } + // if anyone knows of a decent urljoin() for JS, that'd + // be much nicer than this hack.. + if (for_uri[for_uri.length-1] == '/') { + for_uri = for_uri.substr(0, for_uri.length-1); + } + baseURI = for_uri; - cbinst = function (e) { - updateNodeAttributes(e, { "onmouseover" : partial(mouseOverHandler, e), - 'onmouseout' : partial(mouseOutHandler, e) } ); - } + cbinst = function (e) { + updateNodeAttributes(e, { "onmouseover" : partial(mouseOverHandler, e), + "onmouseout" : partial(mouseOutHandler, e) } ); + } - var elems = getElementsByTagAndClassName(null, "branchLink"); - map(cbinst, elems); + var elems = getElementsByTagAndClassName(null, "BranchLink"); + map(cbinst, elems); - var elems = getElementsByTagAndClassName(null, "revisionLink"); - map(cbinst, elems); + var elems = getElementsByTagAndClassName(null, "RevisionLink"); + map(cbinst, elems); - var elems = getElementsByTagAndClassName(null, "manifestLink"); - map(cbinst, elems); + var elems = getElementsByTagAndClassName(null, "DirLink"); + map(cbinst, elems); - theBox = $("popupBox"); + theBox = $("popupBox"); } function updatePopup(boundTo, className) { - jsonData = boundTo.jsonData; + var jsonData = boundTo.jsonData; + var error_string = null; + var info = null; + var pos = elementPosition(boundTo); + var newBox; + + if (jsonData == null) { + error_string = "JSON-RPC error - please report"; + } else if (jsonData.error_string != null) { + error_string = jsonData.error_string; + } + + if (error_string == null) { + if (jsonData.type == "branch") { + info = "branch changed " + jsonData.ago + " ago by " + jsonData.author; + } else if (jsonData.type == "revision") { + info = "revision made " + jsonData.ago + " ago by " + jsonData.author; + } else if (jsonData.type == "manifest") { + info = "manifest contains " + jsonData.file_count + " files in " + jsonData.directory_count + " directories."; + } else { + info = "unknown type: " + jsonData.type; + } + } else { + info = "error: " + error_string; + } - var pos = elementPosition(boundTo); - var newBox; + newBox = DIV({ 'id' : 'popupBox', 'style' : 'font-size: small'}, info); - info = null; - if (jsonData.type == "branch") { - info = "branch last updated " + jsonData.ago + " by " + jsonData.author; - } else if (jsonData.type == "revision") { - info = jsonData.ago + " ago by " + jsonData.author; - } else if (jsonData.type == "manifest") { - info = "manifest contains " + jsonData.file_count + " files in " + jsonData.directory_count + " directories."; - } else { - info = "unknown type: " + jsonData.type; - } + if (boundTo.offsetHeight) { + offset_height = boundTo.offsetHeight; + } else { + offset_height = 24; // yick + } - newBox = DIV({ 'id' : 'popupBox'}, - info); + newY = pos.y + offset_height; + newX = pos.x; - if (boundTo.offsetHeight) { - offset_height = boundTo.offsetHeight; - } else { - offset_height = 24; // yick - } - - newY = pos.y + offset_height + 20; - newX = pos.x + 20; - - newBox.style.top = newY + 'px'; - newBox.style.left = newX + 'px'; - swapDOM(theBox, newBox); - theBox = newBox; + newBox.style.position = "absolute" + newBox.style.top = newY + 'px'; + newBox.style.left = newX + 'px'; + swapDOM(theBox, newBox); + theBox = newBox; } function jsonLoadComplete(boundTo, className, jsonData) { - boundTo.jsonData = jsonData; - updatePopup(boundTo, className); - pendingDeferred = null; + boundTo.jsonData = jsonData; + updatePopup(boundTo, className); + pendingDeferred = null; + pendingFor = null; } -function mouseOverHandler(boundTo, evt) +function squashPendingRequest() { - var className = getNodeAttribute(boundTo, "class"); + if (pendingFor != null) { + pendingFor = null; + pendingDeferred.cancel(); + } +} - if (boundTo.jsonData) { - return updatePopup(boundTo, className); - } +// there should only ever be one pendingDeferred +// if we get a mouse over, we check whether or not +// this is a duplicate of the existing pending (do +// nothing) or otherwise cancel the pending and +// schedule what has happened now. - links = getElementsByTagAndClassName('a', null, boundTo); +function dampenedJSON(uri, boundTo, className) +{ + // bit of a catch all in case somehow we've leaked through + // and not been cancelled + if (pendingFor == boundTo) { + pendingDeferred = loadJSONDoc(uri); + pendingDeferred.addCallback(jsonLoadComplete, boundTo, className); + } +} - if (links.length > 0) { - linkHref = links[0].href; - } else { - return; - } +function mouseOverHandler(boundTo, evt) +{ + var className = getNodeAttribute(boundTo, "class"); - var uri = "getjson.py?className=" + encodeURIComponent(className) + "&linkUri=" + encodeURIComponent(linkHref); - var d = loadJSONDoc(uri); - - d.addCallback(jsonLoadComplete, boundTo, className); - pendingDeferred = d; + if (boundTo != pendingFor) { + squashPendingRequest(); + } + if (boundTo.jsonData) { + return updatePopup(boundTo, className); + } + if (boundTo.id) { + var uri = baseURI + "/json/" + encodeURIComponent(className) + "/" + encodeURIComponent(boundTo.id); + pendingDeferred = callLater(1, partial(dampenedJSON, uri, boundTo, className)); + pendingFor = boundTo; + } } function mouseOutHandler(boundTo, evt) { - if (pendingDeferred != null) { - pendingDeferred.cancel(); - pendingDeferred = null; - } - var newBox = DIV({'id' : 'popupBox', 'class' : 'invisible'}); - swapDOM(theBox, newBox); - theBox = newBox; + squashPendingRequest(); + var newBox = DIV({'id' : 'popupBox', 'class' : 'invisible'}); + swapDOM(theBox, newBox); + theBox = newBox; } ============================================================ --- www/viewmtn/syntax.py cf93e6e6a6166204daaae6ec50819ba175fa3ed1 +++ www/viewmtn/syntax.py 10c283ecef173382775ae1924a7bf33c220743d2 @@ -1,3 +1,12 @@ +# Copyright (C) 2005 Grahame Bowland +# +# This program is made available under the GNU GPL version 2.0 or +# greater. See the accompanying file COPYING for details. +# +# This program is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. + #!/usr/bin/env python2.4 from common import set_nonblocking, terminate_popen3 @@ -89,3 +98,7 @@ if __name__ == '__main__': yield line for i in highlight(l()): print i + +### +### vi:expandtab:sw=4:ts=4 +### ============================================================ --- www/viewmtn/templates/about.html fea245cb17ab46a7766e03293800361db0b325b2 +++ www/viewmtn/templates/about.html 309dd178a83ac76781efe00745d453d6e8dc0227 @@ -42,11 +42,15 @@ Foundation, Inc., 59 Temple Place, Suite

Dependencies

-ViewMTN is written in Python and runs under web.py. Code highlighting via Highlight. -Graphing via GraphViz. Graph colour generation algorithm -from monotone-viz with modifications from Matt Johnston. AJAX funtionality uses the MochiKit Javascript library. +ViewMTN is written in Python and +runs under web.py. Code highlighting +via Highlight. Graphing via GraphViz. Graph +colour generation algorithm from monotone-viz with +modifications from Matt Johnston. AJAX funtionality uses the MochiKit Javascript library and json.py by Patrick +D. Logan

============================================================ --- www/viewmtn/templates/base.html 96ce364d2dfd62b29e6557bebfd3e5f9a6c87b46 +++ www/viewmtn/templates/base.html 1be9c3a8bbe40d46dcd5bc6507b2547233c659dc @@ -1,6 +1,7 @@ - + + ViewMTN: $(page_title) @@ -24,13 +25,15 @@ #block extramenu #end block +#block extramenu1 +#end block #block body #end block