diff --git a/configure.in b/configure.in index dedc29c52e..b854d6d56e 100644 --- a/configure.in +++ b/configure.in @@ -81,6 +81,7 @@ AC_SUBST(COMM_BUILD) dnl export this, so the var is set for mozilla/configure MOZCONFIG=`$_AUTOCONF_TOOLS_DIR/mozconfig-find $topsrcdir` export MOZCONFIG +echo "MOZCONFIG = $MOZCONFIG" MOZ_DEB_TIMESTAMP=`date +"%a, %d %b %Y %T %z" 2>&1` AC_SUBST(MOZ_DEB_TIMESTAMP) diff --git a/mail/test/mozmill/Makefile.in b/mail/test/mozmill/Makefile.in index 8d5456babe..c744e19eaf 100644 --- a/mail/test/mozmill/Makefile.in +++ b/mail/test/mozmill/Makefile.in @@ -75,9 +75,22 @@ libs:: $(_HARNESS_FILES) # Copy the mailnews and mail resources that we require. libs:: - $(INSTALL) $(topsrcdir)/mailnews/test/resources/* $(MOZDEPTH)/_tests/mozmill/resources - $(INSTALL) $(topsrcdir)/mailnews/test/fakeserver/* $(MOZDEPTH)/_tests/mozmill/resources - $(INSTALL) $(topsrcdir)/mail/base/test/unit/resources/* $(MOZDEPTH)/_tests/mozmill/resources + $(INSTALL) $(topsrcdir)/mailnews/test/resources/* $(_DEST_DIR)/resources + $(INSTALL) $(topsrcdir)/mailnews/test/fakeserver/* $(_DEST_DIR)/resources + $(INSTALL) $(topsrcdir)/mail/base/test/unit/resources/* $(_DEST_DIR)/resources + +# Copy MozMill and its dependencies over, and set up a virtualenv. The +# virtualenv directory is outside because we don't want to bundle it up during +# stage-package. +VIRTUALENV_DIR = $(_DEST_DIR)/../mozmill-virtualenv +mozmill-virtualenv: NSDISTMODE=copy +mozmill-virtualenv: + $(DIR_INSTALL) $(topsrcdir)/mail/test/resources $(_DEST_DIR) + rm -rf $(VIRTUALENV_DIR) && \ + mkdir $(VIRTUALENV_DIR) && \ + $(PYTHON) $(_DEST_DIR)/resources/installmozmill.py $(VIRTUALENV_DIR) + +libs:: mozmill-virtualenv PKG_STAGE = $(DIST)/test-package-stage diff --git a/mail/test/mozmill/folder-display/test-message-commands.js b/mail/test/mozmill/folder-display/test-message-commands.js index 7e3641a261..f8656fbc39 100644 --- a/mail/test/mozmill/folder-display/test-message-commands.js +++ b/mail/test/mozmill/folder-display/test-message-commands.js @@ -482,3 +482,8 @@ function test_tag_keys_disabled_in_content_tab() { mc.keypress(null, "1", {}); check_tag_in_message(curMessage, tagArray[0], false); } + +function teardownModule() { + // Make sure archiving is enabled at the end + enable_archiving(true); +} \ No newline at end of file diff --git a/mail/test/mozmill/message-header/test-message-header.js b/mail/test/mozmill/message-header/test-message-header.js index f42e7268db..b9819390e7 100644 --- a/mail/test/mozmill/message-header/test-message-header.js +++ b/mail/test/mozmill/message-header/test-message-header.js @@ -49,6 +49,7 @@ var elib = {}; Cu.import('resource://mozmill/modules/elementslib.js', elib); var folder; +var gInterestingMessage; function setupModule(module) { let fdh = collector.getModule('folder-display-helpers'); @@ -62,20 +63,20 @@ function setupModule(module) { // create a message that has the interesting headers that commonly // show up in the message header pane for testing - let msg = create_message({cc: msgGen.makeNamesAndAddresses(20), // YYY - subject: "This is a really, really, really, really, really, really, really, really, long subject.", - clobberHeaders: { - "Newsgroups": "alt.test", - "Reply-To": "J. Doe ", - "Content-Base": "http://example.com/", - "Bcc": "Richard Roe " - }}); + gInterestingMessage = create_message({cc: msgGen.makeNamesAndAddresses(20), // YYY + subject: "This is a really, really, really, really, really, really, really, really, long subject.", + clobberHeaders: { + "Newsgroups": "alt.test", + "Reply-To": "J. Doe ", + "Content-Base": "http://example.com/", + "Bcc": "Richard Roe " + }}); - add_message_to_folder(folder, msg); + add_message_to_folder(folder, gInterestingMessage); // create a message that has boring headers to be able to switch to and // back from, to force the more button to collapse again. - msg = create_message(); + let msg = create_message(); add_message_to_folder(folder, msg); } @@ -132,151 +133,6 @@ function test_add_tag_with_really_long_label() { mc.keypress(mc.eid("expandedHeadersNameColumn"), "1", {}); } -/** - * @param headerName used for pretty-printing in exceptions - * @param headerValueElement code to be eval()ed returning the DOM element - * with the data. - * @param expectedName code to be eval()ed returning the expected value of - * nsIAccessible.name for the DOM element in question - * @param expectedRole the expected value for nsIAccessible.role - */ -let headersToTest = [ -{ - headerName: "Subject", - headerValueElement: "mc.a('expandedsubjectBox', {class: 'headerValue'})", - expectedName: "mc.e('expandedsubjectLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.textContent", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Content-Base", - headerValueElement: "mc.a('expandedcontent-baseBox', {class: 'headerValue text-link headerValueUrl'})", - expectedName: "mc.e('expandedcontent-baseLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.textContent", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "From", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandedfromBox', {tagName: 'mail-emailaddress'})," + - "'class', 'emailDisplayButton')", - expectedName: "mc.e('expandedfromLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.getAttribute('fullAddress')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "To", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandedtoBox', {tagName: 'mail-emailaddress'})," + - "'class', 'emailDisplayButton')", - expectedName: "mc.e('expandedtoLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.getAttribute('fullAddress')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Cc", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandedccBox', {tagName: 'mail-emailaddress'})," + - "'class', 'emailDisplayButton')", - expectedName: "mc.e('expandedccLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.getAttribute('fullAddress')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Bcc", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandedbccBox', {tagName: 'mail-emailaddress'})," + - "'class', 'emailDisplayButton')", - expectedName: "mc.e('expandedbccLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.getAttribute('fullAddress')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Reply-To", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandedreply-toBox', {tagName: 'mail-emailaddress'})," + - "'class', 'emailDisplayButton')", - expectedName: "mc.e('expandedreply-toLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.getAttribute('fullAddress')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Newsgroups", - headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + - "mc.a('expandednewsgroupsBox', {tagName: 'mail-newsgroup'})," + - "'class', 'newsgrouplabel')", - expectedName: "mc.e('expandednewsgroupsLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.parentNode.parentNode.getAttribute('newsgroup')", - expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY -}, -{ - headerName: "Tags", - headerValueElement: "mc.a('expandedtagsBox', {class: 'tagvalue blc-FF0000'})", - expectedName: "mc.e('expandedtagsLabel').value.slice(0,-1) + ': ' + " + - "headerValueElement.getAttribute('value')", - expectedRole: Ci.nsIAccessibleRole.ROLE_LABEL -} -]; - -// used to get the accessible object for a DOM node -let gAccRetrieval = Cc["@mozilla.org/accessibleRetrieval;1"]. - getService(Ci.nsIAccessibleRetrieval); - -/** - * Use the information from aHeaderInfo to verify that screenreaders will - * do the right thing with the given message header. - * - * @param {Object} aHeaderInfo Information about how to do the verification; - * See the comments above the headersToTest array - * for details. - */ -function verify_header_a11y(aHeaderInfo) { - // XXX Don't use eval here. - let headerValueElement = eval(aHeaderInfo.headerValueElement); - - let headerAccessible = gAccRetrieval.getAccessibleFor(headerValueElement); - if (headerAccessible.role != aHeaderInfo.expectedRole) { - throw new Error("role for " + aHeaderInfo.headerName + " was " + - headerAccessible.role + "; should have been " + - aHeaderInfo.expectedRole); - } - - // XXX Don't use eval here. - let expectedName = eval(aHeaderInfo.expectedName); - if (headerAccessible.name != expectedName) { - throw new Error("headerAccessible.name for " + aHeaderInfo.headerName + - " was '" + headerAccessible.name + "'; expected '" + - expectedName + "'"); - } -} - -/** - * Test the accessibility attributes of the various message headers. - * - * XXX This test used to be after test_more_button_with_many_recipients, - * however, there were some accessibility changes that it didn't seem to play - * nicely with, and the toggling of the "more" button on the cc field was - * causing this test to fail on the cc element. Tests with accessibilty - * hardware/software showed that the code was working fine. Therefore the test - * may be suspect. - */ -function test_a11y_attrs() { - // skip this test on platforms that don't support accessibility - if (!("nsIAccessibleRole" in Components.interfaces)) - return; - - be_in_folder(folder); - - // select and open the first message - let curMessage = select_click_row(0); - - // make sure it loads - wait_for_message_display_completion(mc); - assert_selected_and_displayed(mc, curMessage); - - headersToTest.forEach(verify_header_a11y); -} - function test_more_button_with_many_recipients() { // Start on the interesting message. @@ -882,3 +738,147 @@ function test_get_msg_button_customize_header_toolbar(){ } } +// Some platforms (notably Mac) don't have a11y, so disable these tests there. +if ("nsIAccessibleRole" in Ci) { + /** + * @param headerName used for pretty-printing in exceptions + * @param headerValueElement code to be eval()ed returning the DOM element + * with the data. + * @param expectedName code to be eval()ed returning the expected value of + * nsIAccessible.name for the DOM element in question + * @param expectedRole the expected value for nsIAccessible.role + */ + let headersToTest = [ + { + headerName: "Subject", + headerValueElement: "mc.a('expandedsubjectBox', {class: 'headerValue'})", + expectedName: "mc.e('expandedsubjectLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.textContent", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Content-Base", + headerValueElement: "mc.a('expandedcontent-baseBox', {class: 'headerValue text-link headerValueUrl'})", + expectedName: "mc.e('expandedcontent-baseLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.textContent", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "From", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandedfromBox', {tagName: 'mail-emailaddress'})," + + "'class', 'emailDisplayButton')", + expectedName: "mc.e('expandedfromLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.getAttribute('fullAddress')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "To", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandedtoBox', {tagName: 'mail-emailaddress'})," + + "'class', 'emailDisplayButton')", + expectedName: "mc.e('expandedtoLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.getAttribute('fullAddress')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Cc", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandedccBox', {tagName: 'mail-emailaddress'})," + + "'class', 'emailDisplayButton')", + expectedName: "mc.e('expandedccLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.getAttribute('fullAddress')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Bcc", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandedbccBox', {tagName: 'mail-emailaddress'})," + + "'class', 'emailDisplayButton')", + expectedName: "mc.e('expandedbccLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.getAttribute('fullAddress')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Reply-To", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandedreply-toBox', {tagName: 'mail-emailaddress'})," + + "'class', 'emailDisplayButton')", + expectedName: "mc.e('expandedreply-toLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.getAttribute('fullAddress')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Newsgroups", + headerValueElement: "mc.window.document.getAnonymousElementByAttribute(" + + "mc.a('expandednewsgroupsBox', {tagName: 'mail-newsgroup'})," + + "'class', 'newsgrouplabel')", + expectedName: "mc.e('expandednewsgroupsLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.parentNode.parentNode.getAttribute('newsgroup')", + expectedRole: Ci.nsIAccessibleRole.ROLE_ENTRY + }, + { + headerName: "Tags", + headerValueElement: "mc.a('expandedtagsBox', {class: 'tagvalue blc-FF0000'})", + expectedName: "mc.e('expandedtagsLabel').value.slice(0,-1) + ': ' + " + + "headerValueElement.getAttribute('value')", + expectedRole: Ci.nsIAccessibleRole.ROLE_LABEL + } + ]; + + // used to get the accessible object for a DOM node + let gAccRetrieval = Cc["@mozilla.org/accessibleRetrieval;1"]. + getService(Ci.nsIAccessibleRetrieval); + + /** + * Use the information from aHeaderInfo to verify that screenreaders will + * do the right thing with the given message header. + * + * @param {Object} aHeaderInfo Information about how to do the verification; + * See the comments above the headersToTest array + * for details. + */ + function verify_header_a11y(aHeaderInfo) { + // XXX Don't use eval here. + let headerValueElement = eval(aHeaderInfo.headerValueElement); + + let headerAccessible = gAccRetrieval.getAccessibleFor(headerValueElement) + if (headerAccessible.role != aHeaderInfo.expectedRole) { + throw new Error("role for " + aHeaderInfo.headerName + " was " + + headerAccessible.role + "; should have been " + + aHeaderInfo.expectedRole); + } + + // XXX Don't use eval here. + let expectedName = eval(aHeaderInfo.expectedName); + if (headerAccessible.name != expectedName) { + throw new Error("headerAccessible.name for " + aHeaderInfo.headerName + + " was '" + headerAccessible.name + "'; expected '" + + expectedName + "'"); + } + } + + /** + * Test the accessibility attributes of the various message headers. + * + * XXX This test used to be after test_more_button_with_many_recipients, + * however, there were some accessibility changes that it didn't seem to play + * nicely with, and the toggling of the "more" button on the cc field was + * causing this test to fail on the cc element. Tests with accessibilty + * hardware/software showed that the code was working fine. Therefore the test + * may be suspect. + */ + function test_a11y_attrs() { + be_in_folder(folder); + + // select and open the interesting message + + let curMessage = select_click_row(mc.dbView.findIndexOfMsgHdr( + gInterestingMessage, false)); + + // make sure it loads + assert_selected_and_displayed(mc, curMessage); + + headersToTest.forEach(verify_header_a11y); + } +} // if ("nsIAccessibleRole" in Ci) diff --git a/mail/test/mozmill/runtest.py b/mail/test/mozmill/runtest.py index 7834e4321f..150a30da89 100755 --- a/mail/test/mozmill/runtest.py +++ b/mail/test/mozmill/runtest.py @@ -202,6 +202,9 @@ class ThunderTestProfile(mozrunner.ThunderbirdProfile): if os.path.exists(PROFILE_DIR): shutil.rmtree(PROFILE_DIR, onerror=rmtree_onerror) os.makedirs(PROFILE_DIR) + print 'Using profile dir:', PROFILE_DIR + if not os.path.exists(PROFILE_DIR): + raise Exception('somehow failed to create profile dir!') if wrapper is not None and hasattr(wrapper, "on_profile_created"): # It's a little dangerous to allow on_profile_created access to the @@ -292,6 +295,30 @@ class ThunderTestRunner(mozrunner.ThunderbirdRunner): print '!!! Exception during killing VNC server:', ex +def monkeypatched_15_run_tests(self, tests, sleeptime=0): + frame = mozmill.jsbridge.JSObject(self.bridge, + "Components.utils.import('resource://mozmill/modules/frame.js')") + sleep(sleeptime) + + # transfer persisted data + frame.persisted = self.persisted + + if len(tests) == 1 and not os.path.isdir(tests[0]): + # tests[0] isn't necessarily an abspath'd path, so do that now + test = os.path.abspath(tests[0]) + frame.runTestFile(test) + else: + # run the test files + for test_dir in self.test_dirs: + frame.runTestDirectory(test_dir) + + # Give a second for any callbacks to finish. + sleep(1) +if hasattr(mozmill.MozMill, 'find_tests'): + # Monkey-patch run_tests + mozmill.MozMill.old_run_tests = mozmill.MozMill.run_tests + mozmill.MozMill.run_tests = monkeypatched_15_run_tests + class ThunderTestCLI(mozmill.CLI): profile_class = ThunderTestProfile @@ -302,31 +329,47 @@ class ThunderTestCLI(mozmill.CLI): def __init__(self, *args, **kwargs): global SYMBOLS_PATH, TEST_NAME - # invoke jsbridge.CLI's constructor directly since we are explicitly - # trying to replace mozmill's CLI constructor here (which hardcodes - # the firefox runner and profile in 1.3 for no clear reason). - jsbridge.CLI.__init__(self, *args, **kwargs) + + # mozmill 1.5.4 still explicitly hardcodes references to Firefox; in + # order to avoid missing out on initializer logic or needing to copy + # it, we monkeypatch mozmill's view of mozrunner. (Keep in mind that + # the python module import process shallow copies dictionaries...) + mozmill.mozrunner.FirefoxRunner = self.runner_class + mozmill.mozrunner.FirefoxProfile = self.profile_class + + # note: we previously hardcoded a JS bridge timeout of 300 seconds, + # but the default is now 60 seconds... + mozmill.CLI.__init__(self, *args, **kwargs) + SYMBOLS_PATH = self.options.symbols - TEST_NAME = os.path.basename(self.options.test) + if isinstance(self.options.test, basestring): + test_paths = [self.options.test] + else: + test_paths = self.options.test + TEST_NAME = ', '.join([os.path.basename(t) for t in test_paths]) + + test_dirs = self.test_dirs = [] + for test_file in test_paths: + test_file = os.path.abspath(test_file) + if not os.path.isdir(test_file): + test_file = os.path.dirname(test_file) + if not test_file in test_dirs: + test_dirs.append(test_file) + + # if we are monkeypatching, give it the test directories. + if hasattr(self.mozmill, 'find_tests'): + self.mozmill.test_dirs = test_dirs self._load_wrapper() - self.mozmill = self.mozmill_class(runner_class=self.runner_class, - profile_class=self.profile_class, - jsbridge_port=int(self.options.port), - jsbridge_timeout=300) - - self.mozmill.add_global_listener(mozmill.LoggerListener()) - def _load_wrapper(self): global wrapper """ Load the wrapper module if it is present in the test directory. """ - if os.path.isdir(self.options.test): - testdir = self.options.test - else: - testdir = os.path.dirname(self.options.test) + if len(self.test_dirs) > 1: + raise Exception("Wrapper semantics require only a single test dir") + testdir = self.test_dirs[0] try: (fd, path, desc) = imp.find_module(WRAPPER_MODULE_NAME, [testdir]) @@ -340,6 +383,7 @@ class ThunderTestCLI(mozmill.CLI): if fd is not None: fd.close() + TEST_RESULTS = [] # Versions of MozMill prior to 1.5 did not output test-pass / # TEST-UNEXPECTED-FAIL. Since 1.5 happened this gets output, so we only want @@ -361,7 +405,15 @@ ORIGINAL_FAILURE_LOGGER = mozmill.LoggerListener.cases['mozmill.fail'] def logFailure(obj): if isinstance(obj, basestring): obj = json.loads(obj) - ORIGINAL_FAILURE_LOGGER(obj["exception"]["message"]) + if 'exception' in obj: + objex = obj['exception'] + if 'message' in objex: + report_as = objex['message'] + else: + report_as = 'Empty object thrown as an exception somehow' + else: + report_as = 'No exception provided' + ORIGINAL_FAILURE_LOGGER(report_as) mozmill.LoggerListener.cases['mozmill.fail'] = logFailure @@ -409,8 +461,12 @@ def prettyPrintResults(): if 'summary' in result: testOrSummary = 'SUMMARY' if len(result['fails']) == 0: + if result.get('skipped', False): + kind = 'SKIP' + else: + kind = 'PASS' if result['name'] not in TEST_BLACKLIST: - print '%s-PASS | %s' % (testOrSummary, result['name']) + print '%s-%s | %s' % (testOrSummary, kind, result['name']) else: print '%s-UNEXPECTED-FAIL | %s | %s' % (testOrSummary, prettifyFilename(result['filename']), result['name']) for failure in result['fails']: diff --git a/mail/test/mozmill/runtestlist.py b/mail/test/mozmill/runtestlist.py index 1a28e0e280..219b152f77 100755 --- a/mail/test/mozmill/runtestlist.py +++ b/mail/test/mozmill/runtestlist.py @@ -103,7 +103,7 @@ for directory in f: testDirectory = os.path.join(options.dir, directory.rstrip()) else: testDirectory = directory.rstrip() - args = ["python", "runtest.py", "-t", testDirectory, + args = [sys.executable, "runtest.py", "-t", testDirectory, "--binary", options.binary, "--symbols-path", options.symbols] print args outputPipe = subprocess.PIPE diff --git a/mail/test/mozmill/shared-modules/test-folder-display-helpers.js b/mail/test/mozmill/shared-modules/test-folder-display-helpers.js index ebb0b9b50a..8b3c6cb519 100644 --- a/mail/test/mozmill/shared-modules/test-folder-display-helpers.js +++ b/mail/test/mozmill/shared-modules/test-folder-display-helpers.js @@ -197,11 +197,27 @@ function setupModule() { // can mutate it. (The jsbridge is a global listener and so is guaranteed // to be invoked after our specific named listener.) frame.events.addListener("fail", function(obj) { + // normalize nsIExceptions so they look like JS exceptions... + rawex = obj.exception; + if (obj.exception != null && + (obj.exception instanceof Ci.nsIException)) { + obj.exception = { + message: "nsIException: " + rawex.message + " (" + rawex.result + ")", + fileName: rawex.filename, + lineNumber: rawex.lineNumber, + name: rawex.name, + result: rawex.result, + stack: "", + }; + } + // generate the failure notification now so it shows up in the event log // bucket for presentation purposes. testHelperModule._xpcshellLogger.info( testHelperModule._testLoggerActiveContext, - new testHelperModule._Failure(obj.exception.message, obj.exception)); + new testHelperModule._Failure( + obj.exception ? obj.exception.message : "No Exception!", + rawex)); try { obj.failureContext = { diff --git a/mail/test/mozmill/shared-modules/test-migration-helpers.js b/mail/test/mozmill/shared-modules/test-migration-helpers.js index c1d8fbd1e0..6b47f24ad1 100644 --- a/mail/test/mozmill/shared-modules/test-migration-helpers.js +++ b/mail/test/mozmill/shared-modules/test-migration-helpers.js @@ -129,8 +129,8 @@ function get_subpage(fc) { // broken: // Lie to mozmill to convince it to not explode because these frames never - // get a documentLoaded attribute. - contentWindow.documentLoaded = true; + // get a mozmillDocumentLoaded attribute. + contentWindow.mozmillDocumentLoaded = true; // And sleep so the page has a chance to load controller.sleep(1000); let aController = new controller.MozMillController(contentWindow); diff --git a/mail/test/resources/ManifestDestiny/PKG-INFO b/mail/test/resources/ManifestDestiny/PKG-INFO new file mode 100644 index 0000000000..072abf723f --- /dev/null +++ b/mail/test/resources/ManifestDestiny/PKG-INFO @@ -0,0 +1,189 @@ +Metadata-Version: 1.0 +Name: ManifestDestiny +Version: 0.2.2 +Summary: universal reader for manifests +Home-page: https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny +Author: Jeff Hammel +Author-email: jhammel@mozilla.com +License: MPL +Description: ManifestDestiny + =============== + + Universal manifests for Mozilla test harnesses + + + What is ManifestDestiny? + ------------------------ + + What ManifestDestiny gives you is: + + * manifests are (ordered) lists of tests + * tests may have an arbitrary number of key, value pairs + * the parser returns an ordered list of test data structures, which + are just dicts with some keys. For example, a test with no + user-specified metadata looks like this:: + + [{'path': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js', + 'name': 'testToolbar/testBackForwardButtons.js', 'here': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests', + 'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}] + + The keys displayed here (path, name, here, and manifest) are reserved + words for ManifestDestiny and any consuming APIs. + + + Manifest Format + --------------- + + Manifests are .ini file with the section names denoting the path + relative to the manifest:: + + [foo.js] + [bar.js] + [fleem.js] + + The sections are read in order. In addition, tests may include + arbitrary key, value metadata to be used by the harness. You can also + have a ``[DEFAULT]`` section that will give key, value pairs that will + be inherited by each test unless overridden:: + + [DEFAULT] + type = restart + + [lilies.js] + color = white + + [daffodils.js] + color = yellow + type = other + # override type from DEFAULT + + [roses.js] + color = red + + You can also include other manifests:: + + [include:subdir/anothermanifest.ini] + + + Data + ---- + + Manifest Destiny gives tests as a list of dictionary (in python + terms). + + * path: full path to the test + * name: short name of the test; this is the (usually) relative path + specified in the section name + * here: the parent directory of the manifest + * manifest: the path to the manifest containing the test + + This data corresponds to a one-line manifest:: + + [testToolbar/testBackForwardButtons.js] + + If additional key, values were specified, they would be in this dict + as well. + + Outside of the reserved keys, the remaining key, values + are up to convention to use. There is a (currently very minimal) + generic integration layer in ManifestDestiny for use of all tests. + For instance, if the 'disabled' key is present, you can get the set of + tests without disabled (various other queries are doable as well). + + Since the system is convention-based, the harnesses may do whatever + they want with the data. They may ignore it completely, they may use + the provided integration layer, or they may provide their own + integration layer. This should allow whatever sort of logic they + want. For instance, if in yourtestharness you wanted to run only on + mondays for a certain class of tests:: + + tests = [] + for test in manifests.tests: + if 'runOnDay' in test: + if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower(): + tests.append(test) + else: + tests.append(test) + + To recap: + * the manifests allow you to specify test data + * the parser gives you this data + * you can use it however you want or process it further as you need + + Tests are denoted by sections in an .ini file (see + http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini). + + Additional manifest files may be included with a [include:] directive:: + + [include:path-to-additional-file.manifest] + + The path to included files is relative to the current manifest. + + The ``[DEFAULT]`` section contains variables that all tests inherit from. + + Included files will inherit the top-level variables but may override + in their own ``[DEFAULT]`` section. + + + Creating Manifests + ------------------ + + ManifestDestiny comes with a console script, ``manifestparser create``, that + may be used to create a seed manifest structure from a directory of + files. Run ``manifestparser help create`` for usage information. + + + Copying Manifests + ----------------- + + To copy tests and manifests from a source:: + + manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ... + + + Upating Tests + ------------- + + To update the tests associated with with a manifest from a source + directory:: + + manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... + + + Tests + ----- + + ManifestDestiny includes a suite of tests: + + http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests + + ``test_manifest.txt`` is a doctest that may be helpful in figuring out + how to use the API. Tests are run via ``python test.py``. + + + CLI + --- + + Run ``manifestparser help`` for usage information. + + To create a manifest from a set of directories:: + + manifestparser [options] create directory <...> [create-options] + + To output a manifest of tests:: + + manifestparser [options] write manifest <...> -tag1 -tag2 --key1=value1 --key2=value2 ... + + To copy tests and manifests from a source:: + + manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ... + + To update the tests associated with with a manifest from a source + directory:: + + manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... + +Keywords: mozilla manifests +Platform: UNKNOWN diff --git a/mail/test/resources/ManifestDestiny/README.txt b/mail/test/resources/ManifestDestiny/README.txt new file mode 100644 index 0000000000..dec8b17147 --- /dev/null +++ b/mail/test/resources/ManifestDestiny/README.txt @@ -0,0 +1,178 @@ +ManifestDestiny +=============== + +Universal manifests for Mozilla test harnesses + + +What is ManifestDestiny? +------------------------ + +What ManifestDestiny gives you is: + +* manifests are (ordered) lists of tests +* tests may have an arbitrary number of key, value pairs +* the parser returns an ordered list of test data structures, which + are just dicts with some keys. For example, a test with no + user-specified metadata looks like this:: + + [{'path': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js', + 'name': 'testToolbar/testBackForwardButtons.js', 'here': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests', + 'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}] + +The keys displayed here (path, name, here, and manifest) are reserved +words for ManifestDestiny and any consuming APIs. + + +Manifest Format +--------------- + +Manifests are .ini file with the section names denoting the path +relative to the manifest:: + + [foo.js] + [bar.js] + [fleem.js] + +The sections are read in order. In addition, tests may include +arbitrary key, value metadata to be used by the harness. You can also +have a ``[DEFAULT]`` section that will give key, value pairs that will +be inherited by each test unless overridden:: + + [DEFAULT] + type = restart + + [lilies.js] + color = white + + [daffodils.js] + color = yellow + type = other + # override type from DEFAULT + + [roses.js] + color = red + +You can also include other manifests:: + + [include:subdir/anothermanifest.ini] + + +Data +---- + +Manifest Destiny gives tests as a list of dictionary (in python +terms). + +* path: full path to the test +* name: short name of the test; this is the (usually) relative path + specified in the section name +* here: the parent directory of the manifest +* manifest: the path to the manifest containing the test + +This data corresponds to a one-line manifest:: + + [testToolbar/testBackForwardButtons.js] + +If additional key, values were specified, they would be in this dict +as well. + +Outside of the reserved keys, the remaining key, values +are up to convention to use. There is a (currently very minimal) +generic integration layer in ManifestDestiny for use of all tests. +For instance, if the 'disabled' key is present, you can get the set of +tests without disabled (various other queries are doable as well). + +Since the system is convention-based, the harnesses may do whatever +they want with the data. They may ignore it completely, they may use +the provided integration layer, or they may provide their own +integration layer. This should allow whatever sort of logic they +want. For instance, if in yourtestharness you wanted to run only on +mondays for a certain class of tests:: + + tests = [] + for test in manifests.tests: + if 'runOnDay' in test: + if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower(): + tests.append(test) + else: + tests.append(test) + +To recap: +* the manifests allow you to specify test data +* the parser gives you this data +* you can use it however you want or process it further as you need + +Tests are denoted by sections in an .ini file (see +http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini). + +Additional manifest files may be included with a [include:] directive:: + + [include:path-to-additional-file.manifest] + +The path to included files is relative to the current manifest. + +The ``[DEFAULT]`` section contains variables that all tests inherit from. + +Included files will inherit the top-level variables but may override +in their own ``[DEFAULT]`` section. + + +Creating Manifests +------------------ + +ManifestDestiny comes with a console script, ``manifestparser create``, that +may be used to create a seed manifest structure from a directory of +files. Run ``manifestparser help create`` for usage information. + + +Copying Manifests +----------------- + +To copy tests and manifests from a source:: + + manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ... + + +Upating Tests +------------- + +To update the tests associated with with a manifest from a source +directory:: + + manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... + + +Tests +----- + +ManifestDestiny includes a suite of tests: + +http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests + +``test_manifest.txt`` is a doctest that may be helpful in figuring out +how to use the API. Tests are run via ``python test.py``. + + +CLI +--- + +Run ``manifestparser help`` for usage information. + +To create a manifest from a set of directories:: + + manifestparser [options] create directory <...> [create-options] + +To output a manifest of tests:: + + manifestparser [options] write manifest <...> -tag1 -tag2 --key1=value1 --key2=value2 ... + +To copy tests and manifests from a source:: + + manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ... + +To update the tests associated with with a manifest from a source +directory:: + + manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... diff --git a/mail/test/resources/ManifestDestiny/manifestparser.py b/mail/test/resources/ManifestDestiny/manifestparser.py new file mode 100644 index 0000000000..5dc7b65275 --- /dev/null +++ b/mail/test/resources/ManifestDestiny/manifestparser.py @@ -0,0 +1,776 @@ +#!/usr/bin/env python + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2010 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hammel (Original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +""" +Mozilla universal manifest parser +""" + +# this file lives at +# http://hg.mozilla.org/automation/ManifestDestiny/raw-file/tip/manifestparser.py + +__all__ = ['ManifestParser', 'TestManifest', 'convert'] + +import os +import shutil +import sys +from fnmatch import fnmatch +from optparse import OptionParser + +version = '0.2.2' # package version +try: + from setuptools import setup +except ImportError: + setup = None + +def read_ini(fp, variables=None, default='DEFAULT', + comments=';#', separators=('=', ':'), + strict=True): + """ + read an .ini file and return a list of [(section, values)] + - fp : file pointer or path to read + - variables : default set of variables + - default : name of the section for the default section + - comments : characters that if they start a line denote a comment + - separators : strings that denote key, value separation in order + - strict : whether to be strict about parsing + """ + + if variables is None: + variables = {} + + if isinstance(fp, basestring): + fp = file(fp) + + sections = [] + key = value = None + section_names = set([]) + + # read the lines + for line in fp.readlines(): + + stripped = line.strip() + + # ignore blank lines + if not stripped: + # reset key and value to avoid continuation lines + key = value = None + continue + + # ignore comment lines + if stripped[0] in comments: + continue + + # check for a new section + if len(stripped) > 2 and stripped[0] == '[' and stripped[-1] == ']': + section = stripped[1:-1].strip() + key = value = None + + # deal with DEFAULT section + if section.lower() == default.lower(): + if strict: + assert default not in section_names + section_names.add(default) + current_section = variables + continue + + if strict: + # make sure this section doesn't already exist + assert section not in section_names + + section_names.add(section) + current_section = {} + sections.append((section, current_section)) + continue + + # if there aren't any sections yet, something bad happen + if not section_names: + raise Exception('No sections yet :(') + + # (key, value) pair + for separator in separators: + if separator in stripped: + key, value = stripped.split(separator, 1) + key = key.strip() + value = value.strip() + + if strict: + # make sure this key isn't already in the section or empty + assert key + if current_section is not variables: + assert key not in current_section + + current_section[key] = value + break + else: + # continuation line ? + if line[0].isspace() and key: + value = '%s%s%s' % (value, os.linesep, stripped) + current_section[key] = value + else: + # something bad happen! + raise Exception("Not sure what you're trying to do") + + # interpret the variables + def interpret_variables(global_dict, local_dict): + variables = global_dict.copy() + variables.update(local_dict) + return variables + + sections = [(i, interpret_variables(variables, j)) for i, j in sections] + return sections + + +### objects for parsing manifests + +class ManifestParser(object): + """read .ini manifests""" + + ### methods for reading manifests + + def __init__(self, manifests=(), defaults=None, strict=True): + self._defaults = defaults or {} + self.tests = [] + self.strict = strict + self.rootdir = None + if manifests: + self.read(*manifests) + + def read(self, *filenames, **defaults): + + # ensure all files exist + missing = [ filename for filename in filenames + if not os.path.exists(filename) ] + if missing: + raise IOError('Missing files: %s' % ', '.join(missing)) + + # process each file + for filename in filenames: + + # set the per file defaults + defaults = defaults.copy() or self._defaults.copy() + here = os.path.dirname(os.path.abspath(filename)) + defaults['here'] = here + + if self.rootdir is None: + # set the root directory + # == the directory of the first manifest given + self.rootdir = here + + # read the configuration + sections = read_ini(filename, variables=defaults) + + # get the tests + for section, data in sections: + + # a file to include + # TODO: keep track of structure: + # self.manifests = {'manifest.ini': 'relative/path.ini'} + if section.startswith('include:'): + include_file = section.split('include:', 1)[-1] + include_file = os.path.join(here, include_file) + if not os.path.exists(include_file): + if strict: + raise IOError("File '%s' does not exist" % include_file) + else: + continue + include_defaults = data.copy() + self.read(include_file, **include_defaults) + continue + + # otherwise a test + test = data + test['name'] = section + test['path'] = os.path.join(here, section) + test['manifest'] = os.path.abspath(filename) + self.tests.append(test) + + ### methods for querying manifests + + def query(self, *checks): + """ + general query function for tests + - checks : callable conditions to test if the test fulfills the query + """ + retval = [] + for test in self.tests: + for check in checks: + if not check(test): + break + else: + retval.append(test) + return retval + + def get(self, _key=None, inverse=False, tags=None, **kwargs): + # TODO: pass a dict instead of kwargs since you might hav + # e.g. 'inverse' as a key in the dict + + # TODO: tags should just be part of kwargs with None values + # (None == any is kinda weird, but probably still better) + + # fix up tags + if tags: + tags = set(tags) + else: + tags = set() + + # make some check functions + if inverse: + has_tags = lambda test: tags.isdisjoint(test.keys()) + def dict_query(test): + for key, value in kwargs.items(): + if test.get(key) == value: + return False + return True + else: + has_tags = lambda test: tags.issubset(test.keys()) + def dict_query(test): + for key, value in kwargs.items(): + if test.get(key) != value: + return False + return True + + # query the tests + tests = self.query(has_tags, dict_query) + + # if a key is given, return only a list of that key + # useful for keys like 'name' or 'path' + if _key: + return [test[_key] for test in tests] + + # return the tests + return tests + + def missing(self, tests=None): + """return list of tests that do not exist on the filesystem""" + if tests is None: + tests = self.tests + return [test for test in tests + if not os.path.exists(test['path'])] + + def manifests(self, tests=None): + """ + return manifests in order in which they appear in the tests + """ + if tests is None: + tests = self.tests + manifests = [] + for test in tests: + manifest = test.get('manifest') + if not manifest: + continue + if manifest not in manifests: + manifests.append(manifest) + return manifests + + + ### methods for outputting from manifests + + def write(self, fp=sys.stdout, rootdir=None, + global_tags=None, global_kwargs=None, + local_tags=None, local_kwargs=None): + """ + write a manifest given a query + global and local options will be munged to do the query + globals will be written to the top of the file + locals (if given) will be written per test + """ + + # root directory + if rootdir is None: + rootdir = self.rootdir + + # sanitize input + global_tags = global_tags or set() + local_tags = local_tags or set() + global_kwargs = global_kwargs or {} + local_kwargs = local_kwargs or {} + + # create the query + tags = set([]) + tags.update(global_tags) + tags.update(local_tags) + kwargs = {} + kwargs.update(global_kwargs) + kwargs.update(local_kwargs) + + # get matching tests + tests = self.get(tags=tags, **kwargs) + + # print the .ini manifest + if global_tags or global_kwargs: + print >> fp, '[DEFAULT]' + for tag in global_tags: + print >> fp, '%s =' % tag + for key, value in global_kwargs.items(): + print >> fp, '%s = %s' % (key, value) + print >> fp + + for test in tests: + test = test.copy() # don't overwrite + + path = test['name'] + if not os.path.isabs(path): + path = os.path.relpath(test['path'], self.rootdir) + print >> fp, '[%s]' % path + + # reserved keywords: + reserved = ['path', 'name', 'here', 'manifest'] + for key in sorted(test.keys()): + if key in reserved: + continue + if key in global_kwargs: + continue + if key in global_tags and not test[key]: + continue + print >> fp, '%s = %s' % (key, test[key]) + print >> fp + + def copy(self, directory, rootdir=None, *tags, **kwargs): + """ + copy the manifests and associated tests + - directory : directory to copy to + - rootdir : root directory to copy to (if not given from manifests) + - tags : keywords the tests must have + - kwargs : key, values the tests must match + """ + # XXX note that copy does *not* filter the tests out of the + # resulting manifest; it just stupidly copies them over. + # ideally, it would reread the manifests and filter out the + # tests that don't match *tags and **kwargs + + # destination + if not os.path.exists(directory): + os.path.makedirs(directory) + else: + # sanity check + assert os.path.isdir(directory) + + # tests to copy + tests = self.get(tags=tags, **kwargs) + if not tests: + return # nothing to do! + + # root directory + if rootdir is None: + rootdir = self.rootdir + + # copy the manifests + tests + manifests = [os.path.relpath(manifest, rootdir) for manifest in self.manifests()] + for manifest in manifests: + destination = os.path.join(directory, manifest) + dirname = os.path.dirname(destination) + if not os.path.exists(dirname): + os.makedirs(dirname) + else: + # sanity check + assert os.path.isdir(dirname) + shutil.copy(os.path.join(rootdir, manifest), destination) + for test in tests: + if os.path.isabs(test['name']): + continue + source = test['path'] + if not os.path.exists(source): + print >> sys.stderr, "Missing test: '%s' does not exist!" % source + continue + # TODO: should err on strict + destination = os.path.join(directory, os.path.relpath(test['path'], rootdir)) + shutil.copy(source, destination) + # TODO: ensure that all of the tests are below the from_dir + + def update(self, from_dir, rootdir=None, *tags, **kwargs): + """ + update the tests as listed in a manifest from a directory + - from_dir : directory where the tests live + - rootdir : root directory to copy to (if not given from manifests) + - tags : keys the tests must have + - kwargs : key, values the tests must match + """ + + # get the tests + tests = self.get(tags=tags, **kwargs) + + # get the root directory + if not rootdir: + rootdir = self.rootdir + + # copy them! + for test in tests: + if not os.path.isabs(test['name']): + relpath = os.path.relpath(test['path'], rootdir) + source = os.path.join(from_dir, relpath) + if not os.path.exists(source): + # TODO err on strict + print >> sys.stderr, "Missing test: '%s'; skipping" % test['name'] + continue + destination = os.path.join(rootdir, relpath) + shutil.copy(source, destination) + + +class TestManifest(ManifestParser): + """ + apply logic to manifests; this is your integration layer :) + specific harnesses may subclass from this if they need more logic + """ + + def active_tests(self): + + # ignore disabled tests + tests = self.get(inverse=True, tags=['disabled']) + + # TODO: could filter out by current platform, existence, etc + return tests + + def test_paths(self): + return [test['path'] for test in self.active_tests()] + + +### utility function(s); probably belongs elsewhere + +def convert(directories, pattern=None, ignore=(), write=None): + """ + convert directories to a simple manifest + """ + + retval = [] + include = [] + for directory in directories: + for dirpath, dirnames, filenames in os.walk(directory): + + # filter out directory names + dirnames = [ i for i in dirnames if i not in ignore ] + dirnames.sort() + + # reference only the subdirectory + _dirpath = dirpath + dirpath = dirpath.split(directory, 1)[-1].strip('/') + + if dirpath.split(os.path.sep)[0] in ignore: + continue + + # filter by glob + if pattern: + filenames = [filename for filename in filenames + if fnmatch(filename, pattern)] + + filenames.sort() + + # write a manifest for each directory + if write and (dirnames or filenames): + manifest = file(os.path.join(_dirpath, write), 'w') + for dirname in dirnames: + print >> manifest, '[include:%s]' % os.path.join(dirname, write) + for filename in filenames: + print >> manifest, '[%s]' % filename + manifest.close() + + # add to the list + retval.extend([os.path.join(dirpath, filename) + for filename in filenames]) + + if write: + return # the manifests have already been written! + + retval.sort() + retval = ['[%s]' % filename for filename in retval] + return '\n'.join(retval) + +### command line attributes + +class ParserError(Exception): + """error for exceptions while parsing the command line""" + +def parse_args(_args): + """ + parse and return: + --keys=value (or --key value) + -tags + args + """ + + + # return values + _dict = {} + tags = [] + args = [] + + # parse the arguments + key = None + for arg in _args: + if arg.startswith('---'): + raise ParserError("arguments should start with '-' or '--' only") + elif arg.startswith('--'): + if key: + raise ParserError("Key %s still open" % key) + key = arg[2:] + if '=' in key: + key, value = key.split('=', 1) + _dict[key] = value + key = None + continue + elif arg.startswith('-'): + if key: + raise ParserError("Key %s still open" % key) + tags.append(arg[1:]) + continue + else: + if key: + _dict[key] = arg + continue + args.append(arg) + + # return values + return (_dict, tags, args) + + +### classes for subcommands + +class CLICommand(object): + usage = '%prog [options] command' + def __init__(self, parser): + self._parser = parser # master parser + def parser(self): + return OptionParser(usage=self.usage, description=self.__doc__, + add_help_option=False) + +class Copy(CLICommand): + usage = '%prog [options] copy manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...' + def __call__(self, options, args): + # parse the arguments + try: + kwargs, tags, args = parse_args(args) + except ParserError, e: + self._parser.error(e.message) + + # make sure we have some manifests, otherwise it will + # be quite boring + if not len(args) == 2: + HelpCLI(self._parser)(options, ['copy']) + return + + # read the manifests + # TODO: should probably ensure these exist here + manifests = ManifestParser() + manifests.read(args[0]) + + # print the resultant query + manifests.copy(args[1], None, *tags, **kwargs) + + +class CreateCLI(CLICommand): + """ + create a manifest from a list of directories + """ + usage = '%prog [options] create directory <...>' + + def parser(self): + parser = CLICommand.parser(self) + parser.add_option('-p', '--pattern', dest='pattern', + help="glob pattern for files") + parser.add_option('-i', '--ignore', dest='ignore', + default=[], action='append', + help='directories to ignore') + parser.add_option('-w', '--in-place', dest='in_place', + help='Write .ini files in place; filename to write to') + return parser + + def __call__(self, _options, args): + parser = self.parser() + options, args = parser.parse_args(args) + + # need some directories + if not len(args): + parser.print_usage() + return + + # add the directories to the manifest + for arg in args: + assert os.path.exists(arg) + assert os.path.isdir(arg) + manifest = convert(args, pattern=options.pattern, ignore=options.ignore, + write=options.in_place) + if manifest: + print manifest + + +class WriteCLI(CLICommand): + """ + write a manifest based on a query + """ + usage = '%prog [options] write manifest -tag1 -tag2 --key1=value1 --key2=value2 ...' + def __call__(self, options, args): + + # parse the arguments + try: + kwargs, tags, args = parse_args(args) + except ParserError, e: + self._parser.error(e.message) + + # make sure we have some manifests, otherwise it will + # be quite boring + if not args: + HelpCLI(self._parser)(options, ['write']) + return + + # read the manifests + # TODO: should probably ensure these exist here + manifests = ManifestParser() + manifests.read(*args) + + # print the resultant query + manifests.write(global_tags=tags, global_kwargs=kwargs) + + +class HelpCLI(CLICommand): + """ + get help on a command + """ + usage = '%prog [options] help [command]' + + def __call__(self, options, args): + if len(args) == 1 and args[0] in commands: + commands[args[0]](self._parser).parser().print_help() + else: + self._parser.print_help() + print '\nCommands:' + for command in sorted(commands): + print ' %s : %s' % (command, commands[command].__doc__.strip()) + +class SetupCLI(CLICommand): + """ + setup using setuptools + """ + usage = '%prog [options] setup [setuptools options]' + + def __call__(self, options, args): + sys.argv = [sys.argv[0]] + args + assert setup is not None, "You must have setuptools installed to use SetupCLI" + here = os.path.dirname(os.path.abspath(__file__)) + try: + filename = os.path.join(here, 'README.txt') + description = file(filename).read() + except: + description = '' + os.chdir(here) + + setup(name='ManifestDestiny', + version=version, + description="universal reader for manifests", + long_description=description, + classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords='mozilla manifests', + author='Jeff Hammel', + author_email='jhammel@mozilla.com', + url='https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny', + license='MPL', + zip_safe=False, + py_modules=['manifestparser'], + install_requires=[ + # -*- Extra requirements: -*- + ], + entry_points=""" + [console_scripts] + manifestparser = manifestparser:main + """, + ) + + +class UpdateCLI(CLICommand): + """ + update the tests as listed in a manifest from a directory + """ + usage = '%prog [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...' + + def __call__(self, options, args): + # parse the arguments + try: + kwargs, tags, args = parse_args(args) + except ParserError, e: + self._parser.error(e.message) + + # make sure we have some manifests, otherwise it will + # be quite boring + if not len(args) == 2: + HelpCLI(self._parser)(options, ['update']) + return + + # read the manifests + # TODO: should probably ensure these exist here + manifests = ManifestParser() + manifests.read(args[0]) + + # print the resultant query + manifests.update(args[1], None, *tags, **kwargs) + + +# command -> class mapping +commands = { 'create': CreateCLI, + 'help': HelpCLI, + 'update': UpdateCLI, + 'write': WriteCLI } +if setup is not None: + commands['setup'] = SetupCLI + +def main(args=sys.argv[1:]): + """console_script entry point""" + + # set up an option parser + usage = '%prog [options] [command] ...' + description = __doc__ + parser = OptionParser(usage=usage, description=description) + parser.add_option('-s', '--strict', dest='strict', + action='store_true', default=False, + help='adhere strictly to errors') + parser.disable_interspersed_args() + + options, args = parser.parse_args(args) + + if not args: + HelpCLI(parser)(options, args) + parser.exit() + + # get the command + command = args[0] + if command not in commands: + parser.error("Command must be one of %s (you gave '%s')" % (', '.join(sorted(commands.keys())), command)) + + handler = commands[command](parser) + handler(options, args[1:]) + +if __name__ == '__main__': + main() diff --git a/mail/test/resources/ManifestDestiny/setup.cfg b/mail/test/resources/ManifestDestiny/setup.cfg new file mode 100644 index 0000000000..861a9f5542 --- /dev/null +++ b/mail/test/resources/ManifestDestiny/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/mail/test/resources/ManifestDestiny/setup.py b/mail/test/resources/ManifestDestiny/setup.py new file mode 100644 index 0000000000..c49fbadb09 --- /dev/null +++ b/mail/test/resources/ManifestDestiny/setup.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hammel (Original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# The real details are in manifestparser.py; this is just a front-end + + +import sys +from manifestparser import SetupCLI +SetupCLI(None)(None, sys.argv[1:]) + + + diff --git a/mail/test/resources/installmozmill.py b/mail/test/resources/installmozmill.py new file mode 100644 index 0000000000..fd2a1fbc20 --- /dev/null +++ b/mail/test/resources/installmozmill.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2010 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hammel (Original author) +# Siddharth Agarwal +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +""" +install mozmill and its dependencies +""" + +import os +import sys +from subprocess import call + +### utility functions for cross-platform + +def is_windows(): + return sys.platform.startswith('win') + +def esc(path): + """quote and escape a path for cross-platform use""" + return '"%s"' % repr(path)[1:-1] + +def scripts_path(virtual_env): + """path to scripts directory""" + if is_windows(): + return os.path.join(virtual_env, 'Scripts') + return os.path.join(virtual_env, 'bin') + +def python_script_path(virtual_env, script_name): + """path to a python script in a virtualenv""" + scripts_dir = scripts_path(virtual_env) + if is_windows(): + script_name = script_name + '-script.py' + return os.path.join(scripts_dir, script_name) + +def entry_point_path(virtual_env, entry_point): + path = os.path.join(scripts_path(virtual_env), entry_point) + if is_windows(): + path += '.exe' + return path + +### command-line entry point + +def main(args=None): + """command line front-end function""" + + # parse command line arguments + args = args or sys.argv[1:] + usage = "Usage: %prog [destination]" + + # Print the python version + print 'Python: %s' % sys.version + + # The data is kept in the same directory as the script + source=os.path.abspath(os.path.dirname(__file__)) + + # directory to install to + if not len(args): + destination = source + elif len(args) == 1: + destination = os.path.abspath(args[0]) + else: + print "Usage: %s [destination]" % sys.argv[0] + sys.exit(1) + + os.chdir(source) + + # check for existence of necessary files + if not os.path.exists('virtualenv'): + print "File not found: virtualenv" + sys.exit(1) + + # packages to install in dependency order + packages = ["ManifestDestiny", "simplejson-2.1.6", "mozrunner", "jsbridge", + "mozmill"] + + # create the virtualenv and install packages + env = os.environ.copy() + env.pop('PYTHONHOME', None) + returncode = call([sys.executable, os.path.join('virtualenv', 'virtualenv.py'), destination], env=env) + if returncode: + print 'Failure to install virtualenv' + sys.exit(returncode) + pip = entry_point_path(destination, 'pip') + returncode = call([pip, 'install'] + [os.path.abspath(package) for package in packages], env=env) + if returncode: + print 'Failure to install packages' + sys.exit(returncode) + +if __name__ == '__main__': + main() diff --git a/mail/test/resources/jsbridge/MANIFEST.in b/mail/test/resources/jsbridge/MANIFEST.in new file mode 100644 index 0000000000..d3d8f762f7 --- /dev/null +++ b/mail/test/resources/jsbridge/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include jsbridge/extension * +recursive-include jsbridge/xpi * diff --git a/mail/test/resources/jsbridge/jsbridge/__init__.py b/mail/test/resources/jsbridge/jsbridge/__init__.py new file mode 100644 index 0000000000..cf4af09b51 --- /dev/null +++ b/mail/test/resources/jsbridge/jsbridge/__init__.py @@ -0,0 +1,181 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Corporation Code. +# +# The Initial Developer of the Original Code is +# Mikeal Rogers. +# Portions created by the Initial Developer are Copyright (C) 2008 -2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mikeal Rogers +# Henrik Skupin +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import socket +import os +import copy +import asyncore + +from time import sleep +from network import Bridge, BackChannel, create_network +from jsobjects import JSObject + +import mozrunner + +settings_env = 'JSBRIDGE_SETTINGS_FILE' + +parent = os.path.abspath(os.path.dirname(__file__)) +extension_path = os.path.join(parent, 'extension') + +window_string = "Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('')" + +wait_to_create_timeout = 60 + +def wait_and_create_network(host, port, timeout=wait_to_create_timeout): + ttl = 0 + while ttl < timeout: + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, port)) + s.close() + break + except socket.error: + pass + sleep(.25) + ttl += .25 + if ttl == timeout: + raise Exception("Sorry, cannot connect to jsbridge extension, port %s" % port) + + back_channel, bridge = create_network(host, port) + sleep(.5) + + while back_channel.registered is False: + back_channel.close() + bridge.close() + asyncore.socket_map = {} + sleep(1) + back_channel, bridge = create_network(host, port) + + return back_channel, bridge + +class CLI(mozrunner.CLI): + """Command line interface.""" + + module = "jsbridge" + + parser_options = copy.copy(mozrunner.CLI.parser_options) + parser_options[('-D', '--debug',)] = dict(dest="debug", + action="store_true", + help="Debug mode", + metavar="JSBRIDGE_DEBUG", + default=False ) + parser_options[('-s', '--shell',)] = dict(dest="shell", + action="store_true", + help="Start a Python shell", + metavar="JSBRIDGE_SHELL", + default=False ) + parser_options[('-u', '--usecode',)] = dict(dest="usecode", action="store_true", + help="Use code module instead of iPython", + default=False) + parser_options[('-P', '--port')] = dict(dest="port", default="24242", + help="TCP port to run jsbridge on.") + + def get_profile(self, *args, **kwargs): + if self.options.debug: + kwargs.setdefault('preferences', {}).update({ + 'extensions.checkCompatibility':False, + 'devtools.errorconsole.enabled':True, + 'javascript.options.strict': True + }) + profile = mozrunner.CLI.get_profile(self, *args, **kwargs) + profile.install_addon(extension_path) + return profile + + def get_runner(self, *args, **kwargs): + runner = super(CLI, self).get_runner(*args, **kwargs) + if self.options.debug: + runner.cmdargs.append('-jsconsole') + if not '-jsbridge' in runner.cmdargs: + runner.cmdargs += ['-jsbridge', self.options.port] + return runner + + def run(self): + runner = self.create_runner() + runner.start() + self.start_jsbridge_network() + if self.options.shell: + self.start_shell(runner) + else: + try: + runner.wait() + except KeyboardInterrupt: + runner.stop() + + runner.profile.cleanup() + + def start_shell(self, runner): + try: + import IPython + except: + IPython = None + if not hasattr(self, 'bridge'): + self.start_jsbridge_network() + jsobj = JSObject(self.bridge, window_string) + + if IPython is None or self.options.usecode: + import code + code.interact(local={"jsobj":jsobj, + "getBrowserWindow":lambda : getBrowserWindow(self.bridge), + "back_channel":self.back_channel, + }) + else: + from IPython.Shell import IPShellEmbed + ipshell = IPShellEmbed([]) + ipshell(local_ns={"jsobj":jsobj, + "getBrowserWindow":lambda : getBrowserWindow(self.bridge), + "back_channel":self.back_channel, + }) + runner.stop() + + def start_jsbridge_network(self, timeout=10): + port = int(self.options.port) + host = '127.0.0.1' + self.back_channel, self.bridge = wait_and_create_network(host, port, timeout) + +def cli(): + CLI().run() + +def getBrowserWindow(bridge): + return JSObject(bridge, "Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('')") + + + + + + + diff --git a/mail/test/resources/jsbridge/jsbridge/extension/chrome.manifest b/mail/test/resources/jsbridge/jsbridge/extension/chrome.manifest new file mode 100644 index 0000000000..bf89c63b5c --- /dev/null +++ b/mail/test/resources/jsbridge/jsbridge/extension/chrome.manifest @@ -0,0 +1,14 @@ +resource jsbridge resource/ + +content jsbridge chrome/content/ + +overlay chrome://browser/content/browser.xul chrome://jsbridge/content/overlay.xul +overlay chrome://messenger/content/mailWindowOverlay.xul chrome://jsbridge/content/overlay.xul + +overlay chrome://calendar/content/calendar.xul chrome://jsbridge/content/overlay.xul + +overlay windowtype:Songbird:Main chrome://jsbridge/content/overlay.xul + +component {2872d428-14f6-11de-ac86-001f5bd9235c} components/cmdarg.js +contract @mozilla.org/commandlinehandler/general-startup;1?type=jsbridge {2872d428-14f6-11de-ac86-001f5bd9235c} +category command-line-handler jsbridge @mozilla.org/commandlinehandler/general-startup;1?type=jsbridge diff --git a/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.js b/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.js new file mode 100644 index 0000000000..c23be89cb6 --- /dev/null +++ b/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.js @@ -0,0 +1,40 @@ +// ***** BEGIN LICENSE BLOCK ***** +// Version: MPL 1.1/GPL 2.0/LGPL 2.1 +// +// The contents of this file are subject to the Mozilla Public License Version +// 1.1 (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the +// License. +// +// The Original Code is Mozilla Corporation Code. +// +// The Initial Developer of the Original Code is +// Mikeal Rogers. +// Portions created by the Initial Developer are Copyright (C) 2008 +// the Initial Developer. All Rights Reserved. +// +// Contributor(s): +// Mikeal Rogers +// +// Alternatively, the contents of this file may be used under the terms of +// either the GNU General Public License Version 2 or later (the "GPL"), or +// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +// in which case the provisions of the GPL or the LGPL are applicable instead +// of those above. If you wish to allow use of your version of this file only +// under the terms of either the GPL or the LGPL, and not to allow others to +// use your version of this file under the terms of the MPL, indicate your +// decision by deleting the provisions above and replace them with the notice +// and other provisions required by the GPL or the LGPL. If you do not delete +// the provisions above, a recipient may use your version of this file under +// the terms of any one of the MPL, the GPL or the LGPL. +// +// ***** END LICENSE BLOCK ***** + +var jsbridgeInit = {}; Components.utils.import('resource://jsbridge/modules/init.js',jsbridgeInit); + + diff --git a/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.xul b/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.xul new file mode 100644 index 0000000000..8cd4724442 --- /dev/null +++ b/mail/test/resources/jsbridge/jsbridge/extension/chrome/content/overlay.xul @@ -0,0 +1,5 @@ + + + + + + + + + + + +
+
+
+
+ + +

Index

+ + Symbols | B | C | E | J | M | P | R | S + +
+ + +

Symbols

+
+
+ +
--report <uri>
+
+
command line option
+
+
--show-errors
+
+
command line option
+
+
--showall
+
+
command line option
+
+
-b <binary>, --binary <binary>
+
+
command line option
+
+
-d <defaultprofile>
+
+
command line option
+
+
-D, --debug
+
+
command line option
+
+
-h, --help
+
+
command line option
+
+
-l <logfile>, --logfile <logfile>
+
+
command line option
+
+
-n, --no-new-profile
+
+
command line option
+
+
-P <port>, --port <port>
+
+
command line option
+
+
-p <profile>, --profile <profile>
+
+
command line option
+
+
-s, --shell
+
+
command line option
+
+
-t <test>, --test <test>
+
+
command line option
+
+
-u, --usecode
+
+
command line option
+
+
-w <plugins>, --plugins <plugins>
+
+
command line option
+
+
+ +

B

+
+
+ +
back_channel (mozmill.MozMill attribute)
+
bridge (mozmill.MozMill attribute)
+
+ +

C

+
+
+ +
CLI (class in mozmill)
+
command line option
+
+
--report <uri>
+
--show-errors
+
--showall
+
-D, --debug
+
-P <port>, --port <port>
+
-b <binary>, --binary <binary>
+
-d <defaultprofile>
+
-h, --help
+
-l <logfile>, --logfile <logfile>
+
-n, --no-new-profile
+
-p <profile>, --profile <profile>
+
-s, --shell
+
-t <test>, --test <test>
+
-u, --usecode
+
-w <plugins>, --plugins <plugins>
+
+
+ +

E

+
+
+ +
endRunner_listener() (mozmill.MozMill method)
+
endTest_listener() (mozmill.MozMill method)
+
+ +

J

+
+
+ +
jsbridge_port (mozmill.MozMill attribute)
+
+ +

M

+
+
+ +
MozMill (class in mozmill)
+
mozmill (module)
+
+ +

P

+
+
+ +
profile (mozmill.MozMill attribute)
+
profile_class (mozmill.CLI attribute)
+
+
(mozmill.MozMill attribute)
+
+
+ +

R

+
+
+ +
run_tests() (mozmill.MozMill method)
+
runner (mozmill.MozMill attribute)
+
runner_class (mozmill.CLI attribute)
+
+
(mozmill.MozMill attribute)
+
+
+ +

S

+
+
+ +
start() (mozmill.MozMill method)
+
+ + + +
+
+
+
+
+ + + +

Quick search

+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/docs/_build/html/index.html b/mail/test/resources/mozmill/docs/_build/html/index.html new file mode 100644 index 0000000000..6285cd2966 --- /dev/null +++ b/mail/test/resources/mozmill/docs/_build/html/index.html @@ -0,0 +1,295 @@ + + + + + mozmill — Full automation of XULRunner applications. — mozmill v1.2.1a1 documentation + + + + + + + + + + + +
+
+
+
+ + +
+

mozmill — Full automation of XULRunner applications.¶

+
+

Command Line Usage¶

+

The mozmill command line is versatile and includes a fair amount of debugging options. Even though all these options are available mozmill should run by default without any arguments and find your locally installed Firefox and run with mozmill.

+

In most modes, ctrl-c will shut down Firefox and exit out of the mozmill Python side as well.

+
$ mozmill
+
+
+
+
+-h, --help¶
+
Show help message.
+ +
+
+-b <binary>, --binary <binary>¶
+

Specify application binary location.

+

Default mozrunner.Profile and mozrunner.Runner are still +mozrunner.FirefoxProfile and mozrunner.FirefoxRunner. You can +change this by creating your own command line utility by subclassing CLI

+
+ +
+
+-d <defaultprofile>¶
+
Specify the path to the default clean profile used to create new profiles.
+ +
+
+-n, --no-new-profile¶
+
Do not create a new fresh profile.
+ +
+
+-p <profile>, --profile <profile>¶
+
Specifies a profile to use. Must be used with –no-new-profile.
+ +
+
+-w <plugins>, --plugins <plugins>¶
+

Comma seperated list of additional paths to plugins to install.

+

Plugins can be either .xpi zip compressed extensions or deflated extension directories.

+
+ +
+
+-l <logfile>, --logfile <logfile>¶
+
Log all events to logfile.
+ +
+
+--report <uri>¶
+

Currently in development.

+

POST results to given brasstacks results server at uri.

+
+ +
+
+-t <test>, --test <test>¶
+
Run test. Can be either single test file or directory of tests.
+ +
+
+--showall¶
+
Show all test output.
+ +
+
+-D, --debug¶
+
Install debugging extensions and run with -jsconole
+ +
+
+--show-errors¶
+
Print all logger errors to the console. When running tests only test failures and skipped +tests are printed, this option print all other errors.
+ +
+
+-s, --shell¶
+
Starts a Python shell for debugging.
+ +
+
+-u, --usecode¶
+
By default –shell mode will use iPython if install and fall back to using the code module. +This option forces the use of the code module instead of iPython even when installed.
+ +
+
+-P <port>, --port <port>¶
+
Specify port for jsbridge.
+ +
+
+

Command Line Class¶

+
+
+class mozmill.CLI¶
+

Inherits from jsbridge.CLI which inherits from mozrunner.CLI.

+

All the heavy lifting is handled by jsbridge and mozrunner. If you are subclassing +this in order to creat a new command line interface be sure to call super() on all +related methods.

+
+
+runner_class¶
+
Default runner class. Should be subclass of mozrunner.Runner. +Defaults to mozrunner.FirefoxRunner.
+ +
+
+profile_class¶
+
Default profile class. Should be subclass of mozruner.Profile. +Defaults to mozrunner.FirefoxProfile.
+ +
+ +
+
+

Running MozMill from Python¶

+
+
+class mozmill.MozMill([runner_class[, profile_class[, jsbridge_port]]])¶
+

Manages an instance of Firefox w/ jsbridge and provides facilities for running tests and +keeping track of results with callback methods.

+

Default runner_class is mozrunner.FirefoxRunner. Value should be a subclass of +mozrunner.Runner.

+

Default profile_class is mozrunner.FirefoxProfile. Value should be a subclass of +mozrunner.Profile.

+

Default jsbridge_port is 24242.

+
+
+runner_class¶
+
Set during initialization to subclass of mozrunner.Runner.
+ +
+
+profile_class¶
+
Set during initialization to subclass of mozrunner.Profile.
+ +
+
+jsbridge_port¶
+
Set during initialization to numbers.Integral.
+ +
+
+start([profile[, runner]])¶
+

Start mozrunner and jsbridge pre-requisites.

+

profile should be an instance of a mozrunner.Profile subclass. If one is not passed +an instance of self.profile_class is created. self.profile will be set to this +value.

+

runner should be an instance of a mozrunner.Runner subclass. If one is not passed an +instance of runner_class will be created. runner will be set to this value.

+

This method will also run runner.start() and mozrunner.wait_and_create_network() +and sets back_channel and bridge to instances of +jsbridge.BackChannel and jsbridge.Bridge respectively.

+
+ +
+
+profile¶
+
Set during start() to subclass of mozrunner.Profile.
+ +
+
+runner¶
+
Set during start() to subclass of mozrunner.Runner.
+ +
+
+back_channel¶
+
Set during start() to subclass of jsbridge.BackChannel.
+ +
+
+bridge¶
+
Set during start() to subclass of jsbridge.Bridge
+ +
+
+run_tests(test[, report])¶
+

Run test in live Firefox using bridge.

+

Adds local listeners endTest_listener() and endRunner_listener() to +“endTest” and “endRunner” events using jsbridge.BackChannel.add_listener() of +back_channel.

+

When tests are done the results are posted to a results server at report if passed.

+
+ +
+
+endTest_listener(test)¶
+
When a test is finished the test object will be passed to this callback.
+ +
+
+endRunner_listener(obj)¶
+
When all the tests are done running this callback will be fired.
+ +
+ +
+
+ + +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/docs/_build/html/modindex.html b/mail/test/resources/mozmill/docs/_build/html/modindex.html new file mode 100644 index 0000000000..c49df1a6e1 --- /dev/null +++ b/mail/test/resources/mozmill/docs/_build/html/modindex.html @@ -0,0 +1,97 @@ + + + + + Global Module Index — mozmill v1.2.1a1 documentation + + + + + + + + + + + + + + + + +
+
+
+
+ + +

Global Module Index

+ + + M +
+ + + + + +
 
M
+ mozmill + Full automation of XULRunner applications.
+ + +
+
+
+
+
+

Quick search

+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/docs/_build/html/objects.inv b/mail/test/resources/mozmill/docs/_build/html/objects.inv new file mode 100644 index 0000000000..c6703f3451 --- /dev/null +++ b/mail/test/resources/mozmill/docs/_build/html/objects.inv @@ -0,0 +1,19 @@ +# Sphinx inventory version 1 +# Project: mozmill +# Version: 1.2.1a1 +mozmill mod index.html +mozmill.MozMill.start method index.html +mozmill.CLI class index.html +mozmill.MozMill class index.html +mozmill.MozMill.endTest_listener method index.html +mozmill.MozMill.runner_class attribute index.html +mozmill.MozMill.back_channel attribute index.html +mozmill.CLI.profile_class attribute index.html +mozmill.MozMill.jsbridge_port attribute index.html +mozmill.MozMill.profile_class attribute index.html +mozmill.CLI.runner_class attribute index.html +mozmill.MozMill.profile attribute index.html +mozmill.MozMill.bridge attribute index.html +mozmill.MozMill.endRunner_listener method index.html +mozmill.MozMill.runner attribute index.html +mozmill.MozMill.run_tests method index.html diff --git a/mail/test/resources/mozmill/docs/_build/html/search.html b/mail/test/resources/mozmill/docs/_build/html/search.html new file mode 100644 index 0000000000..067a66c723 --- /dev/null +++ b/mail/test/resources/mozmill/docs/_build/html/search.html @@ -0,0 +1,89 @@ + + + + + Search — mozmill v1.2.1a1 documentation + + + + + + + + + + + + +
+
+
+
+ +

Search

+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+
+ + + +
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/docs/_build/html/searchindex.js b/mail/test/resources/mozmill/docs/_build/html/searchindex.js new file mode 100644 index 0000000000..f3fe1a8286 --- /dev/null +++ b/mail/test/resources/mozmill/docs/_build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({desctypes:{"0":"method","1":"class","2":"attribute"},terms:{all:0,code:0,help:0,show:0,skip:0,fall:0,tcp:[],jsbridge_port:0,mozmil:0,endrunn:0,comma:0,jsbridg:0,still:0,find:0,jsconol:0,current:0,onli:0,locat:0,cli:0,binari:0,should:0,add:0,logger:0,local:0,sure:0,applic:0,firefoxrunn:0,python:0,initi:0,autom:0,util:0,failur:0,report:0,run_test:0,requir:[],docutil:0,list:0,server:0,provid:0,either:0,debug:0,output:0,side:0,"int":[],set:0,fair:0,seper:0,back:0,defaultprofil:0,mozrun:0,result:0,mozrunn:0,pass:0,event:0,out:0,even:0,xulrunn:0,back_channel:0,subclass:0,profil:0,exit:0,print:0,"new":0,method:0,attribut:[],shut:0,full:0,run:0,usag:0,given:0,extens:0,ipython:0,path:0,post:0,"super":0,valu:0,addit:0,deflat:0,obj:0,brasstack:0,plugin:0,forc:0,manag:0,amount:0,instanc:0,chang:0,own:0,firefox:0,modul:0,number:0,down:0,done:0,instal:0,facil:0,your:0,xpi:0,span:0,log:0,zip:0,messag:0,usecod:0,avail:0,start:0,live:0,interfac:0,includ:0,handl:0,call:0,default_profil:[],type:[],listen:0,wait_and_create_network:0,from:0,shell:0,consol:0,option:0,fire:0,relat:0,specifi:0,ani:0,lift:0,line:0,must:0,heavi:0,"default":0,endtest:0,directori:0,bridg:0,can:0,error:0,pre:0,firefoxprofil:0,add_listen:0,creat:0,ctrl:0,runner:0,dure:0,argument:0,mode:0,showal:0,liter:0,versatil:0,file:0,requisit:0,keep:0,integr:0,develop:0,self:0,when:0,backchannel:0,port:0,also:0,other:0,"__init__":[],which:0,test:0,instead:0,you:0,endrunner_listen:0,singl:0,finish:0,clean:0,though:0,track:0,object:0,compress:0,endtest_listen:0,most:0,logfil:0,profile_class:0,"class":0,url:[],runner_class:0,well:0,uri:0,inherit:0,callback:0,without:0,command:0,thi:0,fresh:0,order:0,respect:0},titles:["mozmill — Full automation of XULRunner applications."],modules:{mozmill:0},descrefs:{"mozmill.CLI":{runner_class:[0,2],profile_class:[0,2]},"mozmill.MozMill":{profile:[0,2],bridge:[0,2],endRunner_listener:[0,0],endTest_listener:[0,0],jsbridge_port:[0,2],runner:[0,2],back_channel:[0,2],runner_class:[0,2],run_tests:[0,0],start:[0,0],profile_class:[0,2]},mozmill:{CLI:[0,1],MozMill:[0,1]}},filenames:["index"]}) \ No newline at end of file diff --git a/mail/test/resources/mozmill/docs/conf.py b/mail/test/resources/mozmill/docs/conf.py new file mode 100644 index 0000000000..8c5d4cd226 --- /dev/null +++ b/mail/test/resources/mozmill/docs/conf.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# +# mozmill documentation build configuration file, created by +# sphinx-quickstart on Mon Mar 16 14:30:49 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ["sphinx.ext.intersphinx"] + +intersphinx_mapping = {'http://docs.python.org/dev': None, + 'http://mozrunner.googlecode.com/svn/trunk/docs/_build/html/': None, + 'http://jsbridge.googlecode.com/svn/trunk/docs/_build/html/': None} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'mozmill' +copyright = u'2009, Mikeal Rogers ' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.2.1a1' +# The full version, including alpha/beta/rc tags. +release = '1.2.1a1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# Options for HTML output +# ----------------------- + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'mozmilldoc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('index', 'mozmill.tex', ur'mozmill Documentation', + ur'Mikeal Rogers ', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/mail/test/resources/mozmill/docs/index.rst b/mail/test/resources/mozmill/docs/index.rst new file mode 100644 index 0000000000..094b2ff403 --- /dev/null +++ b/mail/test/resources/mozmill/docs/index.rst @@ -0,0 +1,188 @@ +:mod:`mozmill` --- Full automation of XULRunner applications. +============================================================= + +.. module:: mozmill + :synopsis: Full automation of XULRunner applications. +.. moduleauthor:: Mikeal Rogers +.. sectionauthor:: Mikeal Rogers + +Command Line Usage +------------------ + +The mozmill command line is versatile and includes a fair amount of debugging options. Even though all these options are available mozmill should run by default without any arguments and find your locally installed Firefox and run with mozmill. + +In most modes, ctrl-c will shut down Firefox and exit out of the mozmill Python side as well. + +.. code-block:: none + + $ mozmill + +.. cmdoption:: -h, --help + + Show help message. + +.. cmdoption:: -b , --binary + + Specify application binary location. + + Default :class:`mozrunner.Profile` and :class:`mozrunner.Runner` are still + :class:`mozrunner.FirefoxProfile` and :class:`mozrunner.FirefoxRunner`. You can + change this by creating your own command line utility by subclassing :class:`CLI` + +.. cmdoption:: -d + + Specify the path to the default **clean** profile used to create new profiles. + +.. cmdoption:: -n, --no-new-profile + + Do not create a new fresh profile. + +.. cmdoption:: -p , --profile + + Specifies a profile to use. Must be used with --no-new-profile. + +.. cmdoption:: -w , --plugins + + Comma seperated list of additional paths to plugins to install. + + Plugins can be either .xpi zip compressed extensions or deflated extension directories. + +.. cmdoption:: -l , --logfile + + Log all events to *logfile*. + +.. cmdoption:: --report + + *Currently in development.* + + POST results to given brasstacks results server at *uri*. + +.. cmdoption:: -t , --test + + Run *test*. Can be either single test file or directory of tests. + +.. cmdoption:: --showall + + Show all test output. + +.. cmdoption:: -D, --debug + + Install debugging extensions and run with -jsconole + +.. cmdoption:: --show-errors + + Print all logger errors to the console. When running tests only test failures and skipped + tests are printed, this option print all other errors. + +.. cmdoption:: -s, --shell + + Starts a Python shell for debugging. + +.. cmdoption:: -u, --usecode + + By default --shell mode will use iPython if install and fall back to using the code module. + This option forces the use of the code module instead of iPython even when installed. + +.. cmdoption:: -P , --port + + Specify port for jsbridge. + +Command Line Class +------------------ + +.. class:: CLI + + Inherits from :class:`jsbridge.CLI` which inherits from :class:`mozrunner.CLI`. + + All the heavy lifting is handled by jsbridge and mozrunner. If you are subclassing + this in order to creat a new command line interface be sure to call :func:`super` on all + related methods. + + .. attribute:: runner_class + + Default runner class. Should be subclass of :class:`mozrunner.Runner`. + Defaults to :class:`mozrunner.FirefoxRunner`. + + .. attribute:: profile_class + + Default profile class. Should be subclass of :class:`mozruner.Profile`. + Defaults to :class:`mozrunner.FirefoxProfile`. + +Running MozMill from Python +--------------------------- + +.. class:: MozMill([runner_class[, profile_class[, jsbridge_port]]]) + + Manages an instance of Firefox w/ jsbridge and provides facilities for running tests and + keeping track of results with callback methods. + + Default *runner_class* is :class:`mozrunner.FirefoxRunner`. Value should be a subclass of + :class:`mozrunner.Runner`. + + Default *profile_class* is :class:`mozrunner.FirefoxProfile`. Value should be a subclass of + :class:`mozrunner.Profile`. + + Default *jsbridge_port* is `24242`. + + .. attribute:: runner_class + + Set during initialization to subclass of :class:`mozrunner.Runner`. + + .. attribute:: profile_class + + Set during initialization to subclass of :class:`mozrunner.Profile`. + + .. attribute:: jsbridge_port + + Set during initialization to :class:`numbers.Integral`. + + .. method:: start([profile[, runner]]) + + Start mozrunner and jsbridge pre-requisites. + + *profile* should be an instance of a `mozrunner.Profile` subclass. If one is not passed + an instance of `self.profile_class` is created. `self.profile` will be set to this + value. + + *runner* should be an instance of a `mozrunner.Runner` subclass. If one is not passed an + instance of :attr:`runner_class` will be created. :attr:`runner` will be set to this value. + + This method will also run `runner.start()` and :func:`mozrunner.wait_and_create_network` + and sets :attr:`back_channel` and :attr:`bridge` to instances of + :class:`jsbridge.BackChannel` and :class:`jsbridge.Bridge` respectively. + + .. attribute:: profile + + Set during :meth:`start` to subclass of :class:`mozrunner.Profile`. + + .. attribute:: runner + + Set during :meth:`start` to subclass of :class:`mozrunner.Runner`. + + .. attribute:: back_channel + + Set during :meth:`start` to subclass of :class:`jsbridge.BackChannel`. + + .. attribute:: bridge + + Set during :meth:`start` to subclass of :class:`jsbridge.Bridge` + + .. method:: run_tests(test[, report]) + + Run *test* in live Firefox using :attr:`bridge`. + + Adds local listeners :meth:`endTest_listener` and :meth:`endRunner_listener` to + `"endTest"` and `"endRunner"` events using :meth:`jsbridge.BackChannel.add_listener` of + :attr:`back_channel`. + + When tests are done the results are posted to a results server at *report* if passed. + + .. method:: endTest_listener(test) + + When a test is finished the test object will be passed to this callback. + + .. method:: endRunner_listener(obj) + + When all the tests are done running this callback will be fired. + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/mozmill/__init__.py b/mail/test/resources/mozmill/mozmill/__init__.py new file mode 100644 index 0000000000..d037e8a3d1 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/__init__.py @@ -0,0 +1,845 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Corporation Code. +# +# The Initial Developer of the Original Code is +# Mikeal Rogers. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mikeal Rogers +# Henrik Skupin +# Clint Talbert +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import copy +import httplib +import imp +import os +import socket +import sys +import traceback +import urllib +import urlparse + +from datetime import datetime, timedelta +import manifestparser + +try: + import json +except: + import simplejson as json + +# setup logger +import logging +logger = logging.getLogger('mozmill') + +import jsbridge +from jsbridge.network import JSBridgeDisconnectError +import mozrunner + +from time import sleep + +basedir = os.path.abspath(os.path.dirname(__file__)) + +extension_path = os.path.join(basedir, 'extension') + +mozmillModuleJs = "Components.utils.import('resource://mozmill/modules/mozmill.js')" + +class LoggerListener(object): + cases = { + 'mozmill.pass': lambda string: logger.info('Step Pass: ' + string), + 'mozmill.fail': lambda string: logger.error('Test Failure: ' + string), + 'mozmill.skip': lambda string: logger.info('Test Skipped: ' + string) + } + + class default(object): + def __init__(self, eName): self.eName = eName + def __call__(self, string): + if string: + logger.debug(self.eName + ' | ' + string) + else: + logger.debug(self.eName) + + def __call__(self, eName, obj): + if obj == {}: + string = '' + else: + string = json.dumps(obj) + + if eName not in self.cases: + self.cases[eName] = self.default(eName) + self.cases[eName](string) + + +class TestsFailedException(Exception): + """exception to be raised when the tests fail""" + # XXX unused + + +class MozMill(object): + """ + MozMill is a one-shot test runner You should use MozMill as follows: + + m = MozMill(...) + m.start(...) + m.run_tests() + m.stop() + + You should *NOT* vary from this order of execution. If you have need to + run different sets of tests, create a new instantiation of MozMill + """ + + report_type = 'mozmill-test' + + def __init__(self, + runner_class=mozrunner.FirefoxRunner, + profile_class=mozrunner.FirefoxProfile, + jsbridge_port=24242, + jsbridge_timeout=60): + """ + - runner_class : which mozrunner class to use + - profile_class : which class to use to generate application profiles + - jsbridge_port : port jsbridge uses to connect to to the application + - jsbridge_timeout : how long to go without jsbridge communication + """ + + self.runner_class = runner_class + self.profile_class = profile_class + self.jsbridge_port = jsbridge_port + self.jsbridge_timeout = jsbridge_timeout + + self.passes = [] ; self.fails = [] ; self.skipped = [] + self.alltests = [] + + self.persisted = {} + self.endRunnerCalled = False + self.shutdownModes = enum('default', 'user_shutdown', 'user_restart') + self.currentShutdownMode = self.shutdownModes.default + self.userShutdownEnabled = False + self.tests = [] + + # test time + self.starttime = self.endtime = None + + # setup event listeners + self.global_listeners = [] + self.listeners = [] + self.add_listener(self.persist_listener, eventType="mozmill.persist") + self.add_listener(self.endTest_listener, eventType='mozmill.endTest') + self.add_listener(self.endRunner_listener, eventType='mozmill.endRunner') + self.add_listener(self.startTest_listener, eventType='mozmill.setTest') + self.add_listener(self.userShutdown_listener, eventType='mozmill.userShutdown') + + # disable the crashreporter + os.environ['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + + def add_listener(self, callback, **kwargs): + self.listeners.append((callback, kwargs,)) + + def add_global_listener(self, callback): + self.global_listeners.append(callback) + + def persist_listener(self, obj): + self.persisted = obj + + def fire_python_callback(self, method, arg, python_callbacks_module): + meth = getattr(python_callbacks_module, method) + try: + meth(arg) + except Exception, e: + self.endTest_listener({"name":method, "failed":1, + "python_exception_type":e.__class__.__name__, + "python_exception_string":str(e), + "python_traceback":traceback.format_exc(), + "filename":python_callbacks_module.__file__}) + return False + self.endTest_listener({"name":method, "failed":0, + "filename":python_callbacks_module.__file__}) + return True + + def firePythonCallback_listener(self, obj): + callback_file = "%s_callbacks.py" % os.path.splitext(obj['filename'])[0] + if os.path.isfile(callback_file): + python_callbacks_module = imp.load_source('callbacks', callback_file) + else: + raise Exception("No valid callback file") + self.fire_python_callback(obj['method'], obj['arg'], python_callbacks_module) + + def create_network(self): + + # get the bridge and the back-channel + self.back_channel, self.bridge = jsbridge.wait_and_create_network("127.0.0.1", + self.jsbridge_port) + + # set a timeout on jsbridge actions in order to ensure termination + self.back_channel.timeout = self.bridge.timeout = self.jsbridge_timeout + + # Assign listeners to the back channel + for listener in self.listeners: + self.back_channel.add_listener(listener[0], **listener[1]) + for global_listener in self.global_listeners: + self.back_channel.add_global_listener(global_listener) + + def start(self, profile=None, runner=None): + + if not profile: + profile = self.profile_class(addons=[jsbridge.extension_path, extension_path]) + self.profile = profile + + if not runner: + runner = self.runner_class(profile=self.profile, + cmdargs=["-jsbridge", str(self.jsbridge_port)]) + + self.add_listener(self.firePythonCallback_listener, eventType='mozmill.firePythonCallback') + self.runner = runner + self.endRunnerCalled = False + + self.runner.start() + self.create_network() + self.appinfo = self.get_appinfo(self.bridge) + + # set the starttime for the tests + # XXX assumes run_tests will be called soon after (currently true) + self.starttime = datetime.utcnow() + + def find_tests(self, tests, files=None): + if files is None: + files = [] + for test in tests: + + # tests have to be absolute paths to be loaded from JS + test = os.path.abspath(test) + + if os.path.isdir(test): + directory = test + for f in os.listdir(directory): + if not f.startswith('test'): + continue + path = os.path.join(directory, f) + if os.path.isdir(path): + self.find_tests([path], files) + else: + if f.endswith('.js') and path not in files: + files.append(path) + else: + files.append(test) + return files + + + def run_tests(self, tests, sleeptime=0): + """ + run test files or directories + - test : test files or directories to run + - sleeptime : initial time to sleep [s] (not sure why the default is 4) + """ + + tests = self.find_tests(tests) + self.tests.extend(tests) + + frame = jsbridge.JSObject(self.bridge, + "Components.utils.import('resource://mozmill/modules/frame.js')") + sleep(sleeptime) + + # transfer persisted data + frame.persisted = self.persisted + + # run the test files + for test in tests: + frame.runTestFile(test) + + # Give a second for any callbacks to finish. + sleep(1) + + def startTest_listener(self, test): + self.current_test = test + print "TEST-START | %s | %s" % (test['filename'], test['name']) + + def endTest_listener(self, test): + self.alltests.append(test) + if test.get('skipped', False): + print "WARNING | %s | (SKIP) %s" % (test['name'], test.get('skipped_reason', '')) + self.skipped.append(test) + elif test['failed'] > 0: + print "TEST-UNEXPECTED-FAIL | %s | %s" % (test['filename'], test['name']) + self.fails.append(test) + else: + print "TEST-PASS | %s | %s" % (test['filename'], test['name']) + self.passes.append(test) + + def endRunner_listener(self, obj): + self.endRunnerCalled = True + + def userShutdown_listener(self, obj): + if obj in [self.shutdownModes.default, self.shutdownModes.user_restart, self.shutdownModes.user_shutdown]: + self.currentShutdownMode = obj + self.userShutdownEnabled = not self.userShutdownEnabled + + ### methods for reporting + + def printStats(self): + """print pass/failed/skipped statistics""" + print "INFO Passed: %d" % len(self.passes) + print "INFO Failed: %d" % len(self.fails) + print "INFO Skipped: %d" % len(self.skipped) + + def report_disconnect(self): + test = self.current_test + test['passes'] = [] + test['fails'] = [{ + 'exception' : { + 'message': 'Disconnect Error: Application unexpectedly closed' + } + }] + test['passed'] = 0 + test['failed'] = 1 + self.alltests.append(test) + self.fails.append(test) + + def get_appinfo(self, bridge): + """ Collect application specific information """ + + mozmill = jsbridge.JSObject(bridge, mozmillModuleJs) + appInfo = mozmill.appInfo + + results = {'application_id': str(appInfo.ID), + 'application_name': str(appInfo.name), + 'application_version': str(appInfo.version), + 'application_locale': str(mozmill.locale), + 'platform_buildid': str(appInfo.platformBuildID), + 'platform_version': str(appInfo.platformVersion), + } + + return results + + def get_platform_information(self): + """ Retrieves platform information for test reports. Parts of that code + come from the dirtyharry application: + http://github.com/harthur/dirtyharry/blob/master/dirtyutils.py """ + + import platform + import re + + (system, node, release, version, machine, processor) = platform.uname() + (bits, linkage) = platform.architecture() + service_pack = '' + + if system in ["Microsoft", "Windows"]: + # There is a Python bug on Windows to determine platform values + # http://bugs.python.org/issue7860 + if "PROCESSOR_ARCHITEW6432" in os.environ: + processor = os.environ.get("PROCESSOR_ARCHITEW6432", processor) + else: + processor = os.environ.get('PROCESSOR_ARCHITECTURE', processor) + system = os.environ.get("OS", system).replace('_', ' ') + service_pack = os.sys.getwindowsversion()[4] + elif system == "Linux": + (distro, version, codename) = platform.dist() + version = distro + " " + version + if not processor: + processor = machine + elif system == "Darwin": + system = "Mac" + (release, versioninfo, machine) = platform.mac_ver() + version = "OS X " + release + + if processor in ["i386", "i686"]: + if bits == "32bit": + processor = "x86" + elif bits == "64bit": + processor = "x86_64" + elif processor == "AMD64": + bits = "64bit" + processor = "x86_64" + elif processor == "Power Macintosh": + processor = "ppc" + + bits = re.search('(\d+)bit', bits).group(1) + + platform = {'hostname': node, + 'system': system, + 'version': version, + 'service_pack': service_pack, + 'processor': processor, + 'bits': bits + } + + return platform + + def get_report(self): + """get the report results""" + format = "%Y-%m-%dT%H:%M:%SZ" + + assert self.tests, 'no tests have been run!' + assert self.starttime, 'starttime not set; have you started the tests?' + if not self.endtime: + self.endtime = datetime.utcnow() + + report = {'report_type': self.report_type, + 'time_start': self.starttime.strftime(format), + 'time_end': self.endtime.strftime(format), + 'time_upload': 'n/a', + 'tests_passed': len(self.passes), + 'tests_failed': len(self.fails), + 'tests_skipped': len(self.skipped), + 'results': self.alltests + } + + report.update(self.appinfo) + report.update(self.runner.get_repositoryInfo()) + report['system_info'] = self.get_platform_information() + + return report + + def send_report(self, results, report_url): + """ Send a report of the results to a CouchdB instance or a file. """ + + # report to file or stdout + f = None + if report_url == 'stdout': # stdout + f = sys.stdout + if report_url.startswith('file://'): + filename = report_url.split('file://', 1)[1] + try: + f = file(filename, 'w') + except Exception, e: + print "Printing results to '%s' failed (%s)." % (filename, e) + return + if f: + print >> f, json.dumps(results) + return + + # report to CouchDB + try: + # Set the upload time of the report + now = datetime.utcnow() + results['time_upload'] = now.strftime("%Y-%m-%dT%H:%M:%SZ") + + # Parse URL fragments and send data + url_fragments = urlparse.urlparse(report_url) + connection = httplib.HTTPConnection(url_fragments.netloc) + connection.request("POST", url_fragments.path, json.dumps(results), + {"Content-type": "application/json"}) + + # Get response which contains the id of the new document + response = connection.getresponse() + data = json.loads(response.read()) + connection.close() + + # Check if the report has been created + if not data['ok']: + print "Creating report document failed (%s)" % data + return data + + # Print document location to the console and return + print "Report document created at '%s%s'" % (report_url, data['id']) + return data + except Exception, e: + print "Sending results to '%s' failed (%s)." % (report_url, e) + + def report(self, report_url): + """print statistics and send the JSON report""" + self.printStats() + + if report_url: + results = self.get_report() + return self.send_report(results, report_url) + + ### methods for shutting down and cleanup + + def stop_runner(self, timeout=30, close_bridge=False, hard=False): + sleep(1) + try: + mozmill = jsbridge.JSObject(self.bridge, mozmillModuleJs) + mozmill.cleanQuit() + except (socket.error, JSBridgeDisconnectError): + pass + except: + self.runner.cleanup() + raise + + if not close_bridge: + starttime = datetime.utcnow() + self.runner.wait(timeout=timeout) + endtime = datetime.utcnow() + if ( endtime - starttime ) > timedelta(seconds=timeout): + try: + self.runner.stop() + except: + pass + self.runner.wait() + else: # TODO: unify this logic with the above better + if hard: + self.runner.cleanup() + return + + # XXX this call won't actually finish in the specified timeout time + self.runner.wait(timeout=timeout) + + self.back_channel.close() + self.bridge.close() + x = 0 + while x < timeout: + if self.endRunnerCalled: + break + sleep(1) + x += 1 + else: + print "WARNING | endRunner was never called. There must have been a failure in the framework." + self.runner.cleanup() + sys.exit(1) + + def stop(self, fatal=False): + """cleanup""" + + # stop the runner + self.stop_runner(timeout=10, close_bridge=True, hard=fatal) + + # cleanup the profile if you need to + if self.runner is not None: + self.runner.cleanup() + + +class MozMillRestart(MozMill): + + report_type = 'mozmill-restart-test' + + def __init__(self, *args, **kwargs): + MozMill.__init__(self, *args, **kwargs) + self.python_callbacks = [] + + def add_listener(self, callback, **kwargs): + self.listeners.append((callback, kwargs,)) + + def add_global_listener(self, callback): + self.global_listeners.append(callback) + + def start(self, runner=None, profile=None): + + if not profile: + profile = self.profile_class(addons=[jsbridge.extension_path, extension_path]) + self.profile = profile + + if not runner: + runner = self.runner_class(profile=self.profile, + cmdargs=["-jsbridge", str(self.jsbridge_port)]) + self.runner = runner + self.endRunnerCalled = False + self.add_listener(self.firePythonCallback_listener, eventType='mozmill.firePythonCallback') + + # set the starttime for the tests + # XXX assumes run_tests will be called soon after (currently true) + self.starttime = datetime.utcnow() + + def firePythonCallback_listener(self, obj): + if obj['fire_now']: + self.fire_python_callback(obj['method'], obj['arg'], self.python_callbacks_module) + else: + self.python_callbacks.append(obj) + + def start_runner(self): + + # if user_restart we don't need to start the browser back up + if self.currentShutdownMode != self.shutdownModes.user_restart: + self.runner.start() + + self.create_network() + self.appinfo = self.get_appinfo(self.bridge) + frame = jsbridge.JSObject(self.bridge, + "Components.utils.import('resource://mozmill/modules/frame.js')") + return frame + + def run_dir(self, test_dir, sleeptime=0): + """run a directory of restart tests resetting the profile per directory""" + + # TODO: document this behaviour! + if os.path.isfile(os.path.join(test_dir, 'testPre.js')): + pre_test = os.path.join(test_dir, 'testPre.js') + post_test = os.path.join(test_dir, 'testPost.js') + if not os.path.exists(pre_test) or not os.path.exists(post_test): + print "Skipping "+test_dir+" does not contain both pre and post test." + return + + tests = [pre_test, post_test] + else: + if not os.path.isfile(os.path.join(test_dir, 'test1.js')): + print "Skipping "+test_dir+" does not contain any known test file names" + return + tests = [] + counter = 1 + while os.path.isfile(os.path.join(test_dir, "test"+str(counter)+".js")): + tests.append(os.path.join(test_dir, "test"+str(counter)+".js")) + counter += 1 + + self.add_listener(self.endRunner_listener, eventType='mozmill.endRunner') + + if os.path.isfile(os.path.join(test_dir, 'callbacks.py')): + self.python_callbacks_module = imp.load_source('callbacks', os.path.join(test_dir, 'callbacks.py')) + + for test in tests: + frame = self.start_runner() + self.currentShutdownMode = self.shutdownModes.default + self.endRunnerCalled = False + sleep(sleeptime) + + frame.persisted = self.persisted + try: + frame.runTestFile(test) + while not self.endRunnerCalled: + sleep(.25) + self.currentShutdownMode = self.shutdownModes.default + self.stop_runner() + sleep(2) # Give mozrunner some time to shutdown the browser + except JSBridgeDisconnectError: + if not self.userShutdownEnabled: + raise JSBridgeDisconnectError() + self.userShutdownEnabled = False + + for callback in self.python_callbacks: + self.fire_python_callback(callback['method'], callback['arg'], self.python_callbacks_module) + self.python_callbacks = [] + + self.python_callbacks_module = None + + # Reset the profile. + profile = self.runner.profile + profile.cleanup() + if profile.create_new: + profile.profile = profile.create_new_profile(self.runner.binary) + for addon in profile.addons: + profile.install_addon(addon) + if jsbridge.extension_path not in profile.addons: + profile.install_addon(jsbridge.extension_path) + if extension_path not in profile.addons: + profile.install_addon(extension_path) + profile.set_preferences(profile.preferences) + + def find_tests(self, tests): + files = [] + + # make sure these are all directories + not_dir = [ i for i in tests + if not os.path.isdir(i) ] + if not_dir: + raise IOError('Restart tests must be directories (%s)' % ', '.join(not_dir)) + + for test_dir in tests: + + # tests have to be absolute paths, for some reason + test_dir = os.path.abspath(test_dir) + + # XXX this allows for only one sub-level of test directories + # is this a spec or a side-effect? + # If the former, it should be documented + test_dirs = [os.path.join(test_dir, d) + for d in os.listdir(test_dir) + if d.startswith('test') and os.path.isdir(os.path.join(test_dir, d))] + + if len(test_dirs): + files.extend(test_dirs) + else: + files.append(test_dir) + + return files + + def run_tests(self, tests, sleeptime=0): + + test_dirs = self.find_tests(tests) + self.tests.extend(test_dirs) + + for test_dir in test_dirs: + self.run_dir(test_dir, sleeptime) + + # cleanup the profile + self.runner.cleanup() + + # Give a second for any pending callbacks to finish + sleep(1) + + def stop(self, fatal=False): + """MozmillRestart doesn't need to do cleanup as this is already done per directory""" + + # XXX this is a one-off to fix bug 581733 + # really, the entire shutdown sequence should be reconsidered and + # made more robust. + # See https://bugzilla.mozilla.org/show_bug.cgi?id=581733#c20 + # This will *NOT* work with all edge cases and it shouldn't be + # expected that adding on more kills() for each edge case will ever + # be able to fix a systematic issue by patching holes + if fatal: + self.runner.cleanup() + + +class CLI(jsbridge.CLI): + mozmill_class = MozMill + module = "mozmill" + + parser_options = copy.copy(jsbridge.CLI.parser_options) + parser_options[("-t", "--test",)] = dict(dest="test", action='append', default=[], + help="Run test") + parser_options[("-l", "--logfile",)] = dict(dest="logfile", default=None, + help="Log all events to file.") + parser_options[("--show-errors",)] = dict(dest="showerrors", default=False, + action="store_true", + help="Print logger errors to the console.") + parser_options[("--report",)] = dict(dest="report", default=False, + help="Report the results. Requires url to results server. Use 'stdout' for stdout.") + parser_options[("--show-all",)] = dict(dest="showall", default=False, action="store_true", + help="Show all test output.") + parser_options[("--timeout",)] = dict(dest="timeout", type="float", + default=60., + help="seconds before harness timeout if no communication is taking place") + parser_options[("-m", "--manifest")] = dict(dest='manifests', action='append', + help='test manifest .ini file') + parser_options[("--app-arg",)] = dict(dest='appArgs', action='append', default=[], + help='provides an argument to the test application') + + def __init__(self, *args, **kwargs): + jsbridge.CLI.__init__(self, *args, **kwargs) + self.mozmill = self.mozmill_class(runner_class=mozrunner.FirefoxRunner, + profile_class=mozrunner.FirefoxProfile, + jsbridge_port=int(self.options.port), + jsbridge_timeout=self.options.timeout, + ) + + self.tests = [] + + # read tests from manifests + if self.options.manifests: + manifest_parser = manifestparser.TestManifest(manifests=self.options.manifests) + + self.tests.extend(manifest_parser.test_paths()) + + # expand user directory for individual tests + for test in self.options.test: + test = os.path.expanduser(test) + self.tests.append(test) + + # check existence for the tests + missing = [ test for test in self.tests + if not os.path.exists(test) ] + if missing: + raise IOError("Not a valid test file/directory: %s" % ', '.join(["'%s'" % test for test in missing])) + + + # setup log formatting + self.mozmill.add_global_listener(LoggerListener()) + log_options = { 'format': "%(levelname)s | %(message)s", + 'level': logging.CRITICAL } + if self.options.showerrors: + log_options['level'] = logging.ERROR + if self.options.logfile: + log_options['filename'] = self.options.logfile + log_options['filemode'] = 'w' + log_options['level'] = logging.DEBUG + if self.options.test and self.options.showall: + log_options['level'] = logging.DEBUG + logging.basicConfig(**log_options) + + def get_profile(self, *args, **kwargs): + profile = jsbridge.CLI.get_profile(self, *args, **kwargs) + profile.install_addon(extension_path) + return profile + + def run(self): + + # create a Mozrunner + runner = self.create_runner() + + runner.cmdargs.extend(self.options.appArgs) + + # make sure the application starts in the foreground + if '-foreground' not in runner.cmdargs: + runner.cmdargs.append('-foreground') + + try: + self.mozmill.start(runner=runner, profile=runner.profile) + except: + runner.cleanup() + raise + + if self.tests: + + # run the tests + disconnected = False + try: + self.mozmill.run_tests(self.tests) + except JSBridgeDisconnectError: + disconnected = True + if not self.mozmill.userShutdownEnabled: + self.mozmill.report_disconnect() + print 'TEST-UNEXPECTED-FAIL | Disconnect Error: Application unexpectedly closed' + runner.cleanup() + except: + runner.cleanup() + raise + + # shutdown the test harness + self.mozmill.stop(fatal=disconnected) + + # print statistics and send the JSON report + self.mozmill.report(self.options.report) + + if self.mozmill.fails or disconnected: + sys.exit(1) + else: + if self.options.shell: + self.start_shell(runner) + else: + try: + if not hasattr(runner, 'process_handler'): + runner.start() + runner.wait() + except KeyboardInterrupt: + runner.stop() + + if self.mozmill.runner is not None: + self.mozmill.runner.cleanup() + + +class RestartCLI(CLI): + mozmill_class = MozMillRestart + + +class ThunderbirdCLI(CLI): + profile_class = mozrunner.ThunderbirdProfile + runner_class = mozrunner.ThunderbirdRunner + + +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + return type('Enum', (), enums) + +def cli(): + CLI().run() + +def tbird_cli(): + ThunderbirdCLI().run() + +def restart_cli(): + RestartCLI().run() diff --git a/mail/test/resources/mozmill/mozmill/extension/build.xml b/mail/test/resources/mozmill/mozmill/extension/build.xml new file mode 100644 index 0000000000..5d7aeea49c --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/build.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mail/test/resources/mozmill/mozmill/extension/chrome-jar.manifest b/mail/test/resources/mozmill/mozmill/extension/chrome-jar.manifest new file mode 100644 index 0000000000..332def2b02 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/chrome-jar.manifest @@ -0,0 +1,12 @@ +resource mozmill resource/ +content mozmill jar:chrome/mozmill.jar!/content/ +locale mozmill en-US jar:chrome/mozmill-locale.jar!/locale/en-US/ +skin mozmill classic/1.0 jar:chrome/mozmill-skin.jar!/skin/ + +overlay chrome://browser/content/browser.xul chrome://mozmill/content/overlay.xul +overlay chrome://messenger/content/mailWindowOverlay.xul chrome://mozmill/content/overlay_tb.xul +overlay chrome://calendar/content/calendar.xul chrome://mozmill/content/overlay.xul +overlay chrome://navigator/content/navigatorOverlay.xul chrome://mozmill/content/overlay_tb.xul +overlay windowtype:Songbird:Main chrome://mozmill/content/overlay.xul + +style chrome://global/content/customizeToolbar.xul chrome://mozmill/skin/overlay.css diff --git a/mail/test/resources/mozmill/mozmill/extension/chrome.manifest b/mail/test/resources/mozmill/mozmill/extension/chrome.manifest new file mode 100644 index 0000000000..5d2bb0631d --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/chrome.manifest @@ -0,0 +1,15 @@ +resource mozmill resource/ + +content mozmill content/ +overlay chrome://browser/content/browser.xul chrome://mozmill/content/overlay.xul +overlay chrome://messenger/content/mailWindowOverlay.xul chrome://mozmill/content/overlay_tb.xul +overlay chrome://calendar/content/calendar.xul chrome://mozmill/content/overlay.xul +overlay chrome://sunbird/content/calendar.xul chrome://mozmill/content/overlay.xul +overlay chrome://navigator/content/navigatorOverlay.xul chrome://mozmill/content/overlay_tb.xul + +overlay windowtype:Songbird:Main chrome://mozmill/content/overlay.xul + +locale mozmill en-US locale/en-US/ + +skin mozmill classic/1.0 skin/ +style chrome://global/content/customizeToolbar.xul chrome://mozmill/skin/overlay.css diff --git a/mail/test/resources/mozmill/mozmill/extension/content/chrome.js b/mail/test/resources/mozmill/mozmill/extension/content/chrome.js new file mode 100644 index 0000000000..79e0ef893b --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/chrome.js @@ -0,0 +1,40 @@ +/* Adds tooltip support to the Mozmill window. Taken from browser.js in Firefox */ +function fillTooltip(tipElement) { + var retVal = false; + if (tipElement.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") + return retVal; + const XLinkNS = "http://www.w3.org/1999/xlink"; + var titleText = null; + var XLinkTitleText = null; + var direction = tipElement.ownerDocument.dir; + + while (!titleText && !XLinkTitleText && tipElement) { + if (tipElement.nodeType == Node.ELEMENT_NODE) { + titleText = tipElement.getAttribute("title"); + XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title"); + var defView = tipElement.ownerDocument.defaultView; + // Work around bug 350679: "Tooltips can be fired in documents with no view" + if (!defView) + return retVal; + direction = defView.getComputedStyle(tipElement, "") + .getPropertyValue("direction"); + } + tipElement = tipElement.parentNode; + } + + var tipNode = document.getElementById("mozmill-tooltip"); + tipNode.style.direction = direction; + + for each (var t in [titleText, XLinkTitleText]) { + if (t && /\S/.test(t)) { + // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be + // replaced with spaces, and LFs should be removed entirely + t = t.replace(/[\r\t]/g, ' '); + t = t.replace(/\n/g, ''); + + tipNode.setAttribute("label", t); + retVal = true; + } + } + return retVal; +} diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/fg.menu.css b/mail/test/resources/mozmill/mozmill/extension/content/css/fg.menu.css new file mode 100644 index 0000000000..820db38d89 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/css/fg.menu.css @@ -0,0 +1,114 @@ +/* Styles for jQuery menu widget +Author: Maggie Wachs, maggie@filamentgroup.com +Date: September 2008 +*/ + + +/* REQUIRED STYLES - the menus will only render correctly with these rules */ + +.fg-menu-container { position: absolute; top:0; left:-999px; padding: .4em; overflow: hidden; } +.fg-menu-container.fg-menu-flyout { overflow: visible; } + +.fg-menu, .fg-menu ul { list-style-type:none; padding: 0; margin:0; } + +.fg-menu { position:relative; } +.fg-menu-flyout .fg-menu { position:static; } + +.fg-menu ul { position:absolute; top:0; } +.fg-menu ul ul { top:-1px; } + +.fg-menu-container.fg-menu-ipod .fg-menu-content, +.fg-menu-container.fg-menu-ipod .fg-menu-content ul { background: none !important; } + +.fg-menu.fg-menu-scroll, +.fg-menu ul.fg-menu-scroll { overflow: scroll; overflow-x: hidden; } + +.fg-menu li { clear:both; float:left; width:100%; margin: 0; padding:0; border: 0; } +.fg-menu li li { font-size:1em; } /* inner li font size must be reset so that they don't blow up */ + +.fg-menu-flyout ul ul { padding: .4em; } +.fg-menu-flyout li { position:relative; } + +.fg-menu-scroll { overflow: scroll; overflow-x: hidden; } + +.fg-menu-breadcrumb { margin: 0; padding: 0; } + +.fg-menu-footer { margin-top: .4em; padding: .4em; } +.fg-menu-header { margin-bottom: .4em; padding: .4em; } + +.fg-menu-breadcrumb li { float: left; list-style: none; margin: 0; padding: 0 .2em; font-size: .9em; opacity: .7; } +.fg-menu-breadcrumb li.fg-menu-prev-list, +.fg-menu-breadcrumb li.fg-menu-current-crumb { clear: left; float: none; opacity: 1; } +.fg-menu-breadcrumb li.fg-menu-current-crumb { padding-top: .2em; } + +.fg-menu-breadcrumb a, +.fg-menu-breadcrumb span { float: left; } + +.fg-menu-footer a:link, +.fg-menu-footer a:visited { float:left; width:100%; text-decoration: none; } +.fg-menu-footer a:hover, +.fg-menu-footer a:active { } + +.fg-menu-footer a span { float:left; cursor: pointer; } + +.fg-menu-breadcrumb .fg-menu-prev-list a:link, +.fg-menu-breadcrumb .fg-menu-prev-list a:visited, +.fg-menu-breadcrumb .fg-menu-prev-list a:hover, +.fg-menu-breadcrumb .fg-menu-prev-list a:active { background-image: none; text-decoration:none; } + +.fg-menu-breadcrumb .fg-menu-prev-list a { float: left; padding-right: .4em; } +.fg-menu-breadcrumb .fg-menu-prev-list a .ui-icon { float: left; } + +.fg-menu-breadcrumb .fg-menu-current-crumb a:link, +.fg-menu-breadcrumb .fg-menu-current-crumb a:visited, +.fg-menu-breadcrumb .fg-menu-current-crumb a:hover, +.fg-menu-breadcrumb .fg-menu-current-crumb a:active { display:block; background-image:none; font-size:1.3em; text-decoration:none; } + + + +/* REQUIRED LINK STYLES: links are "display:block" by default; if the menu options are split into + selectable node links and 'next' links, the script floats the node links left and floats the 'next' links to the right */ + +.fg-menu a:link, +.fg-menu a:visited, +.fg-menu a:hover, +.fg-menu a:active { float:left; width:92%; padding:.3em 3%; text-decoration:none; outline: 0 !important; } + +.fg-menu a { border: 1px dashed transparent; } + +.fg-menu a.ui-state-default:link, +.fg-menu a.ui-state-default:visited, +.fg-menu a.ui-state-default:hover, +.fg-menu a.ui-state-default:active, +.fg-menu a.ui-state-hover:link, +.fg-menu a.ui-state-hover:visited, +.fg-menu a.ui-state-hover:hover, +.fg-menu a.ui-state-hover:active, + .fg-menu a.ui-state-active:link, + .fg-menu a.ui-state-active:visited, + .fg-menu a.ui-state-active:hover, +.fg-menu a.ui-state-active:active { border-style: solid; font-weight: normal; } + +.fg-menu a span { display:block; cursor:pointer; } + + + /* SUGGESTED STYLES - for use with jQuery UI Themeroller CSS */ + +.fg-menu-indicator span { float:left; } +.fg-menu-indicator span.ui-icon { float:right; } + +.fg-menu-content.ui-widget-content, +.fg-menu-content ul.ui-widget-content { border:0; } + + +/* ICONS AND DIVIDERS */ + +.fg-menu.fg-menu-has-icons a:link, +.fg-menu.fg-menu-has-icons a:visited, +.fg-menu.fg-menu-has-icons a:hover, +.fg-menu.fg-menu-has-icons a:active { padding-left:20px; } + +.fg-menu .horizontal-divider hr, .fg-menu .horizontal-divider span { padding:0; margin:5px .6em; } +.fg-menu .horizontal-divider hr { border:0; height:1px; } +.fg-menu .horizontal-divider span { font-size:.9em; text-transform: uppercase; padding-left:.2em; } + diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/mozmill.css b/mail/test/resources/mozmill/mozmill/extension/content/css/mozmill.css new file mode 100644 index 0000000000..4389c3d784 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/css/mozmill.css @@ -0,0 +1,108 @@ +body{ font: 11px Corbel, Verdana, Helvetica, arial, sans-serif; background-color: window; margin: 0; padding: 0;} + +#tabs { position: relative;} + +.tab { + font-size: 12px; +} + +.bespin { + margin: 0; + padding: 0; + height: 100%; + width: 100%; +} + +#fileMenuButton { + float: left; + cursor: pointer; +} + +#openTabs { + position: relative; + top: -4px; + margin-left: 16%; +} + + +#editor-tab-select { + min-width: 70px; + padding-left: 8px; + padding-right: 10px; +} + +.menu { + font-size: 12px; + padding-top: 3px; + padding-left: 10px; + padding-right: 10px; + padding-bottom: 3px; + background-color: #f5f5f5; + border: 1px solid #ddd; + -moz-border-radius: 1px; +} + +.menu:hover { + background-color: #ccc; +} + +.menu:active { + background-color: #bbb; +} + +.menuitem { cursor: pointer; padding-top: 2px; font-size: 12px; padding-bottom: 2px;} + +.menuitem a { width: 100%} + +.menuitem:hover { background-color: #E7E7E7;} + +#dxContainer {margin-top: 16px; height: 160px;} + +#inspectOptions {margin-right: 10px;} + +#outClear {margin-left: 20px;} + +#elementStr { margin-bottom: 8px;} + +#recordToggle { margin-left: 4px;} + +#outClear { position: relative; top: -3px;} + +#shellInput { + width: 99%; + height: 50px; +} + +#shellOutput { + overflow: auto; + width: 99%; + border: 1px solid #aaa; +} + +.log, +.logger, +.pass, +.fail, +.test { + width:98%; +} +.log:hover, +.logger:hover, +.pass:hover, +.fail:hover, +.test:hover {} +.pass { + background:lightgreen; +} +.fail { + background:lightpink; +} +.test { + background:lightyellow; +} +.log { + background:lightyellow; +} +.logger { + background:lightyellow; +} diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000..5b5dab2ab7 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000..ac8b229af9 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000..ad3d6346e0 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000000..42ccba269b Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000..5a46b47cb1 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000000..86c2baa655 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000000..4443fdc1a1 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000..7c9fa6c6ed Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_222222_256x240.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000..67560da9be Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_222222_256x240.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_2e83ff_256x240.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000..b425c446d2 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_2e83ff_256x240.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_454545_256x240.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000..0cd64a21a9 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_454545_256x240.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_888888_256x240.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_888888_256x240.png new file mode 100644 index 0000000000..2e5180e473 Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_888888_256x240.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_cd0a0a_256x240.png b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000..2db88b796a Binary files /dev/null and b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/images/ui-icons_cd0a0a_256x240.png differ diff --git a/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/jquery-ui-1.7.1.custom.css b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/jquery-ui-1.7.1.custom.css new file mode 100644 index 0000000000..04cb7bd0be --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/css/smoothness/jquery-ui-1.7.1.custom.css @@ -0,0 +1,404 @@ +/* +* jQuery UI CSS Framework +* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +*/ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute; left: -99999999px; } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + +/* +* jQuery UI CSS Framework +* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) +* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +*/ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #333; outline: none; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #333; text-decoration: none; outline: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; outline: none; } +.ui-state-active, .ui-widget-content .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; outline: none; text-decoration: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text { color: #cd0a0a; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; }/* Accordion +----------------------------------*/ +.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } +.ui-accordion .ui-accordion-li-fix { display: inline; } +.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } +.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; } +.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } +.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; } +.ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker +----------------------------------*/ +.ui-datepicker { width: 17em; padding: .2em .2em 0; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; } +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}/* Dialog +----------------------------------*/ +.ui-dialog { position: relative; padding: .2em; width: 300px; } +.ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative; } +.ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } +.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } +.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } +.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } +.ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } +.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } +.ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; } +.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } +.ui-draggable .ui-dialog-titlebar { cursor: move; } +/* Progressbar +----------------------------------*/ +.ui-progressbar { height:2em; text-align: left; } +.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable +----------------------------------*/ +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider +----------------------------------*/ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs +----------------------------------*/ +.ui-tabs { padding: .2em; zoom: 1; } +.ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .6em .2em 0 .2em; } +.ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; } +.ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { padding: 1.4em 1.2em; display: block; border-width: 0; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } diff --git a/mail/test/resources/mozmill/mozmill/extension/content/dx.js b/mail/test/resources/mozmill/mozmill/extension/content/dx.js new file mode 100644 index 0000000000..594f2b69cd --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/dx.js @@ -0,0 +1,231 @@ +// ***** BEGIN LICENSE BLOCK ***** +// Version: MPL 1.1/GPL 2.0/LGPL 2.1 +// +// The contents of this file are subject to the Mozilla Public License Version +// 1.1 (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the +// License. +// +// The Original Code is Mozilla Corporation Code. +// +// The Initial Developer of the Original Code is +// Adam Christian. +// Portions created by the Initial Developer are Copyright (C) 2008 +// the Initial Developer. All Rights Reserved. +// +// Contributor(s): +// Adam Christian +// Mikeal Rogers +// +// Alternatively, the contents of this file may be used under the terms of +// either the GNU General Public License Version 2 or later (the "GPL"), or +// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +// in which case the provisions of the GPL or the LGPL are applicable instead +// of those above. If you wish to allow use of your version of this file only +// under the terms of either the GPL or the LGPL, and not to allow others to +// use your version of this file under the terms of the MPL, indicate your +// decision by deleting the provisions above and replace them with the notice +// and other provisions required by the GPL or the LGPL. If you do not delete +// the provisions above, a recipient may use your version of this file under +// the terms of any one of the MPL, the GPL or the LGPL. +// +// ***** END LICENSE BLOCK ***** + +var inspection = {}; Components.utils.import('resource://mozmill/modules/inspection.js', inspection); +var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils); + +var DomInspectorConnector = function() { + this.lastEvent = null; + this.lastTime = null; + this.on = false; +} +DomInspectorConnector.prototype.grab = function(){ + var disp = $('dxDisplay').textContent; + var dispArr = disp.split(': '); + $('editorInput').value += 'new elementslib.'+dispArr[0].toUpperCase()+"('"+dispArr[1]+"')\n"; +} + +DomInspectorConnector.prototype.changeClick = function(e) { + if (this.on){ + this.dxOff() + this.dxOn(); + } +} + +DomInspectorConnector.prototype.evtDispatch = function(e) { + + //if this function was called less than a second ago, exit + //this should solve the flickering problem + var currentTime = new Date(); + var newTime = currentTime.getTime(); + + if (this.lastTime != null){ + var timeDiff = newTime - this.lastTime; + this.lastTime = newTime; + + if (timeDiff < 2){ + this.lastEvent = e; + return; + } + } else { this.lastTime = newTime; } + + //Fix the scroll bar exception Bug 472124 + try { var i = inspection.inspectElement(e); } + catch(err){ return; } + + var dxC = i.controllerText; + var dxE = i.elementText; + var dxV = String(i.validation); + + document.getElementById('dxController').innerHTML = dxC; + document.getElementById('dxValidation').innerHTML = dxV; + document.getElementById('dxElement').innerHTML = dxE; + + return dxE; +} +DomInspectorConnector.prototype.dxToggle = function(){ + if (this.on) + this.dxOff(); + else + this.dxOn(); +} +//Turn on the recorder +//Since the click event does things like firing twice when a double click goes also +//and can be obnoxious im enabling it to be turned off and on with a toggle check box +DomInspectorConnector.prototype.dxOn = function() { + this.on = true; + $("#dxToggle").text("Stop"); + + //defined the click method, default to dblclick + var clickMethod = "dblclick"; + if (document.getElementById('inspectSingle').checked){ + clickMethod = 'click'; + } + + var enumerator = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator) + .getEnumerator(""); + while(enumerator.hasMoreElements()) { + var win = enumerator.getNext(); + if (win.document.title != 'MozMill IDE'){ + this.dxRecursiveBind(win, clickMethod); + } + } + + var observerService = + Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + + observerService.addObserver(this.observer, "toplevel-window-ready", false); +}; + +//when a new dom window gets opened +DomInspectorConnector.prototype.observer = { + observe: function(subject,topic,data){ + var clickMethod = "dblclick"; + if ($('inspectSingle').selected){ + clickMethod = 'click'; + } + //Attach listener to new window here + MozMilldx.dxRecursiveBind(subject, clickMethod); + } +}; + +DomInspectorConnector.prototype.dxOff = function() { + this.on = false; + $("#dxToggle").text("Start"); + $("#dxCopy").show(); + + //try to cleanup left over outlines + if (this.lastEvent){ + this.lastEvent.target.style.outline = ""; + } + + for each(win in utils.getWindows()) { + this.dxRecursiveUnBind(win, 'click'); + } + + for each(win in utils.getWindows()) { + this.dxRecursiveUnBind(win, 'dblclick'); + } + + var observerService = + Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + + try { + observerService.removeObserver(this.observer, "toplevel-window-ready"); + } catch(err){} +}; + +DomInspectorConnector.prototype.getFoc = function(e){ + MozMilldx.dxOff(); + e.target.style.outline = ""; + e.stopPropagation(); + e.preventDefault(); + window.focus(); +} + +DomInspectorConnector.prototype.inspectorToClipboard = function(){ + copyToClipboard($('#dxController')[0].innerHTML +'\n'+$('#dxElement')[0].innerHTML); +}; + +//Copy inspector output to clipboard if alt,shift,c is pressed +DomInspectorConnector.prototype.clipCopy = function(e){ + if (e == true){ + copyToClipboard($('#dxElement')[0].innerHTML + ' '+$('#dxValidation')[0].innerHTML + ' ' + $('#dxController')[0].innerHTML); + } + else if (e.altKey && e.shiftKey && (e.charCode == 199)){ + copyToClipboard($('#dxElement')[0].innerHTML + ' '+$('#dxValidation')[0].innerHTML + ' ' + $('#dxController')[0].innerHTML); + } +} + +//Recursively bind to all the iframes and frames within +DomInspectorConnector.prototype.dxRecursiveBind = function(frame, clickMethod) { + + frame.addEventListener('mouseover', this.evtDispatch, true); + frame.addEventListener('mouseout', this.evtDispatch, true); + frame.addEventListener(clickMethod, this.getFoc, true); + frame.addEventListener('keypress', this.clipCopy, true); + + + var iframeCount = frame.window.frames.length; + var iframeArray = frame.window.frames; + + for (var i = 0; i < iframeCount; i++) + this.dxRecursiveBind(iframeArray[i], clickMethod); +} + +//Recursively bind to all the iframes and frames within +DomInspectorConnector.prototype.dxRecursiveUnBind = function(frame, clickMethod) { + try { + frame.removeEventListener('mouseover', this.evtDispatch, true); + frame.removeEventListener('mouseout', this.evtDispatch, true); + frame.removeEventListener(clickMethod, this.getFoc, true); + frame.removeEventListener('keypress', this.clipCopy, true); + } + catch(e) { + // don't want to prevent the rest of the frames from removing listeners + } + + var iframeCount = frame.window.frames.length; + var iframeArray = frame.window.frames; + + for (var i = 0; i < iframeCount; i++) + this.dxRecursiveUnBind(iframeArray[i], clickMethod); +} + +var MozMilldx = new DomInspectorConnector(); + +// Scoping bug workarounds +var enableDX = function () { + MozMilldx.dxOn(); +} +var disableDX = function () { + MozMilldx.dxOff(); +} diff --git a/mail/test/resources/mozmill/mozmill/extension/content/dxwindow.xul b/mail/test/resources/mozmill/mozmill/extension/content/dxwindow.xul new file mode 100644 index 0000000000..46f6ef80b0 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/dxwindow.xul @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.css b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.css new file mode 100644 index 0000000000..cc350b362b --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.css @@ -0,0 +1,123 @@ +.bespin-completion-panel { + font-family: Helvetica, Arial, sans-serif; + position: absolute; + cursor: default; + line-height: normal; + -moz-user-select: none; + -webkit-user-select: none; +} + +.bespin-completion-pointer { + position: absolute; + z-index: 2; + height: 21px; + width: 21px; +} + +.bespin-completion-pointer-up { + top: 1px; + border-top: solid #555 1px; + border-left: solid #555 1px; + background-image: -moz-linear-gradient(top left, #333333, #333333 50%, transparent 50%, transparent); + background-image: -webkit-gradient(linear, left top, right bottom, from(#333333), color-stop(0.5, #333333), color-stop(0.5, transparent), to(transparent)); + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); +} + +.bespin-completion-pointer-down { + bottom: 1px; + border-top: solid #000 1px; + border-left: solid #000 1px; + background-image: -moz-linear-gradient(top left, #000, #000 50%, transparent 50%, transparent); + background-image: -webkit-gradient(linear, left top, right bottom, from(#000), color-stop(0.5, #000), color-stop(0.5, transparent), to(transparent)); + -moz-transform: rotate(225deg); + -webkit-transform: rotate(225deg); +} + +.bespin-completion-bubble-outer { + position: relative; + z-index: 1; + margin: 11px 0px 11px 0px; + border-top: solid #555 1px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} + +.bespin-completion-bubble-inner { + position: relative; + z-index: 3; + padding: 6px; + background: -moz-linear-gradient(top, #333333, #000000); + background: -webkit-gradient(linear, center top, center bottom, from(#333333), to(#000000)); + color: #ffffff; + font-size: 10.5pt; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -moz-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0px 6px 16px 2px rgba(0, 0, 0, 0.5); +} + +.bespin-completion-panel ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +.bespin-completion-panel li { + text-indent: 0px; + margin: 0px; + padding: 6px 16px; +} + +.bespin-completion-highlight { + position: absolute; + z-index: -1; + background-image: -moz-linear-gradient(top, #3e59be, #312d80); + background-image: -webkit-gradient(linear, center top, center bottom, from(#3e59be), to(#312d80)); + border: solid rgba(37, 34, 91, 1.0) 1px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; +} + +.bespin-completion-kind { + display: block; + float: left; + top: 0px; + left: 0px; + width: 8px; + height: 8px; + padding: 2px; + margin: 0px 5px 0px 0px; + font-size: 6.5pt; + font-weight: bold; + text-transform: uppercase; + text-align: center; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} + +.bespin-completion-kind-m { + background-color: maroon; +} + +.bespin-completion-kind-f { + background-color: green; +} + +.bespin-completion-top-row { + position: relative; +} + +.bespin-completion-second-row { + margin: 6px 0px 0px 17px; + display: none; +} + +.bespin-completion-ident { + font-weight: bold; +} + +.bespin-completion-container { + color: #a0a0a0; +} + diff --git a/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.js b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.js new file mode 100644 index 0000000000..3442ee6603 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinEmbedded.js @@ -0,0 +1,144 @@ +if(typeof bespin==="undefined")bespin={};if(typeof document!=="undefined"){var link=document.getElementById("bespin_base");if(link){var href=link.href;bespin.base=href.substring(href.length-1)!=="/"?href+"/":href}else bespin.base=""} +(function(){if("undefined"===typeof y)var y=function(){function j(w,t){x.push({m:w,a:t})}var x=[],E={isBootstrap:true,queue:x,register:function(w,t){if(w.match(/^tiki/)&&this.ENV)if(this.ENV.app==="tiki"&&this.ENV.mode==="test"){if(!t.dependencies)t.dependencies={};t.dependencies.core_test="~"}j("register",arguments);return this},module:function(w,t){if(w.match(/\:tiki$/))this.tikiFactory=t;j("module",arguments);return this},start:function(){var w={};this.tikiFactory(null,w,null);w=w.Browser.start(this.ENV, +this.ARGS,x);x=null;return w}};if("undefined"!==typeof ENV)E.ENV=ENV;if("undefined"!==typeof ARGV)E.ARGS=ARGV;if("undefined"!==typeof ARGS)E.ARGS=ARGS;return E}();y.register("::tiki/1.0.0",{name:"tiki",version:"1.0.0"});y.module("::tiki/1.0.0:tiki",function(j,x){var E=/^::/,w=function(a){return!!E.exec(a)},t=function(){x.debug.apply(this,arguments)};x.debug=function(){var a=Array.prototype.join.call(arguments,"");j("sys").debug(a)};var h;h=Array.isArray?Array.isArray:function(a){if("object"!==typeof a)return false; +if(a instanceof Array)return true;return a.constructor&&a.constructor.name==="Array"};x.isArray=h;var o;if(Object.create)o=Object.create;else{var s=function(){},C=s.prototype;o=function(a){if(!a)a=Object.prototype;s.prototype=a;var b=new s;b.prototype=a;s.prototype=C;return b}}x.createObject=o;var v,B,F;v=function(){return function(){return this.init?this.init.apply(this,arguments):this}};B=function(){return F(this)};F=function(a){var b=v();b.prototype=o(a.prototype);b.prototype.constructor=b;b.super_= +a;b.extend=B;return b};x.extend=F;var f=function(a,b){if(b&&!b.displayName)b.displayName="parallel#fn";return function(c){if(a.length===0)return c(null,[]);var d=a.length,l=d,m=false,z,D=function(H){if(!m){if(H){m=true;return c(H)}--l<=0&&c()}};D.displayName="parallel#tail";for(z=0;z=48&&d<=57},c=function(d,l){for(var m=0,z=0,D=0,H,M;;z++,D++){H=d.charAt(z);M=l.charAt(D);if(!b(H)&&!b(M))return m;else if(b(H))if(b(M)){if(HM)if(m===0)m=+1;else if(H===0&&M===0)return m}else return+1;else return-1}};return function(d,l){for(var m=0,z=0,D=0,H=0,M,O,W;;){D=H=0;M=d.charAt(m);for(O= +l.charAt(z);a(M)||M=="0";){if(M=="0")D++;else D=0;M=d.charAt(++m)}for(;a(O)||O=="0";){if(O=="0")H++;else H=0;O=l.charAt(++z)}if(b(M)&&b(O))if((W=c(d.substring(m),l.substring(z)))!==0)return W;if(M===0&&O===0)return D-H;if(MO)return+1;++m;++z}}}();x.natcompare=L;var I=function(a){return new Error(""+a+" is an invalid version string")};I.displayName="invalidVers";var N=function(a,b,c,d){c=Number(c);d=Number(d);if(isNaN(c))throw I(a);if(isNaN(d))throw I(b);return c-d};N.displayName= +"compareNum";var i,J={parse:function(a){a=a.match(/^(=|~)?([\d]+?)(\.([\d]+?)(\.(.+))?)?$/);if(!a)return null;return[a,a[2],a[4]||"0",a[6]||"0",a[1]]},major:function(a){return Number(i(a)[1])},minor:function(a){return Number(i(a)[2])},patch:function(a){a=i(a)[3];return isNaN(Number(a))?a:Number(a)},STRICT:"strict",NORMAL:"normal",mode:function(a){return i(a)[4]==="="?J.STRICT:J.NORMAL},comparePatch:function(a,b){var c,d;if(a===b)return 0;c=Number(a);d=Number(b);return isNaN(c)?isNaN(d)?L(a,b):-1: +isNaN(d)?1:cd?1:0},compare:function(a,b){var c;if(a===b)return 0;if(a)a=i(a);if(b)b=i(b);if(!a&&!b)return 0;if(!a)return-1;if(!b)return 1;c=N(a[0],b[0],a[1],b[1]);if(c===0){c=N(a[0],b[0],a[2],b[2]);if(c===0)c=J.comparePatch(a[3],b[3])}return c<0?-1:c>0?1:0},compatible:function(a,b){if(!a)return true;if(a===b)return true;if(a&&!i(a))a=null;if(b&&!i(b))b=null;if(!a)return true;if(a===b)return true;if(J.mode(a)===J.STRICT)return b&&J.compare(a,b)===0;else{if(!b)return true;if(J.major(a)!==J.major(b))return false; +return J.compare(a,b)<=0}},normalize:function(a){var b;if(!a||a.length===0)return null;a=J.parse(a);if(!a)return null;b=Number(a[3]);if(isNaN(b))b=a[3];return[Number(a[1]),Number(a[2]),b].join(".")}};x.semver=J;i=J.parse;var U=x.extend(Object);x.Factory=U;U.prototype.init=function(a,b,c){this.id=a;this.pkg=b;this.factory=c};U.prototype.call=function(a,b){var c=this.factory,d=this.__filename,l=this.__dirname;if("string"===typeof c)c=this.factory=U.compile(c,this.pkg.id+":"+this.id);a=a.createRequire(b); +var m=b.exports;c.call(m,a,m,b,d,l);return b.exports};var R=["(function(require, exports, module) {",null,"\n});\n//@ sourceURL=",null,"\n"];U.compile=function(a,b){R[1]=a;R[3]=b||"(unknown module)";a=R.join("");a=eval(a);R[1]=R[3]=null;return a};x.Factory=U;var S=x.extend(Object);x.Module=S;S.prototype.init=function(a,b,c){this.id=a;this.ownerPackage=b;this.exports={};var d=this;this.resource=function(l){return c.resource(l,d.id,b)}};var Q=x.extend(Object);x.Package=Q;Q.prototype.init=function(a, +b){w(a)||(a="::"+a);this.id=a;this.config=b;this.isReady=true};Q.prototype.get=function(a){return this.config?this.config[a]:undefined};Q.prototype.set=function(a,b){if(!this.config)this.config={};this.config[a]=b;return this};Q.prototype.requiredVersion=function(a){var b=this.get("dependencies");return b?b[a]:null};Q.prototype.canonicalPackageId=function(a,b){if(a===this.get("name")&&J.compatible(b,this.get("version")))return this.id;return null};Q.prototype.packageFor=function(a){if(a===this.id)return this; +return null};Q.prototype.ensurePackage=function(a,b){return a===this.id?b():b(new u(a,this))};Q.prototype.catalogPackages=function(){return[this]};Q.prototype.exists=function(a){return!!(this.factories&&this.factories[a])};Q.prototype.load=function(a){return this.factories?this.factories[a]:null};var e=function(a,b){return a+":"+b},k=x.extend(Object);x.Loader=k;k.prototype.init=function(a){this.sources=a||[];this.clear()};k.prototype.clear=function(){this.factories={};this.canonicalIds={};this.packages= +{};this.packageSources={};this.canonicalPackageIds={}};k.prototype.defaultPackage=new Q("default",{name:"default"});k.prototype.anonymousPackage=new Q("(anonymous)",{name:"(anonymous)"});k.prototype.canonical=function(a,b,c){var d,l,m,z;if(b&&"string"!==typeof b){c=b;b=null}if(w(a))return a;if(!c)c=this.anonymousPackage;a=this._resolve(a,b,c);if(w(a))return a;d=c?c.id:"(null)";b=this.canonicalIds;if(!b)b=this.canonicalIds={};b[d]||(b[d]={});b=b[d];if(b[a])return b[a];d=a;l=a.indexOf(":");if(l>=0){m= +a.slice(0,l);a=a.slice(l+1);if(a[0]==="/")throw new Error("Absolute path not allowed with packageId");}l=null;if(m&&m.length>0){if(m=this._canonicalPackageId(m,null,c))l=e(m,a)}else{if(c&&c.exists(a))l=e(c.id,a);else{if(m=this._canonicalPackageId(a,null,c))z=this._packageFor(m,c);if(z)if(z.exists("index"))l=e(z.id,"index");else if(z.exists(a))l=e(z.id,a)}if(!l){if(this.defaultPackage)m=this.defaultPackage.id;else if(this.workingPackage)m=this.workingPackage.id;else if(this.anonymousPackage)m=this.anonymousPackage.id; +else return null;if(m)l=e(m,a)}}return b[d]=l};k.prototype.load=function(a,b,c){var d,l,m;if(!b)b=this.anonymousPackage;d=this.factories;if(!d)d=this.factories={};if(d[a])return d[a];l=a.indexOf(":",2);m=a.slice(0,l);l=a.slice(l+1);(b=this._packageFor(m,b))||t("Loader#load - "+m+" not found for "+l);if(!b)return null;c=b.load(l,c);return d[a]=c};k.prototype.catalogPackages=function(a){if(!a)a=this.anonymousPackage;var b=[],c,d,l={};this.defaultPackage&&b.push(this.defaultPackage);var m=function(z){var D, +H,M,O;if(z){H=z.length;for(D=0;D=0)a=a.slice(0,d);return a}if(b&&"string"!==typeof b){c=b;b=null}if(!c)c=this.anonymousPackage;d=a.indexOf(":");if(d>=0)a= +a.slice(0,d);return this._canonicalPackageId(a,b,c)};k.prototype.packageFor=function(a,b){if(!b)b=this.anonymousPackage;var c=a.indexOf(":",2);if(c>=0)a=a.slice(0,c);return this._packageFor(a,b)};k.prototype.ready=function(a,b){if(!b)b=this.anonymousPackage;var c=a.indexOf(":",2),d;if(c>=0){d=a.slice(c+1);a=a.slice(0,c)}if(this._packageReady(a,b,{})){a=this._packageFor(a,b);if(!a)return false;return!!a.exists(d)}else return false};k.prototype.ensurePackage=function(a,b,c,d){if(b&&"string"!==typeof b){d= +c;c=b;b=null}if(c&&"function"===typeof c){d=c;c=null}if(!c)c=this.anonymousPackage;this._ensurePackage(a,b,c,{},d)};k.prototype._ensurePackage=function(a,b,c,d,l){var m=this,z;z=this._canonicalPackageId(a,b,c);if(!z)return l(new u(a,c));if(d[z])return l();d[z]=true;a=this._sourceForCanonicalPackageId(z,c);if(!a)return l(new u(z,c));a.ensurePackage(z,function(D){var H,M,O;if(D)return l(D);H=m.packageFor(z,c);if(!H)return l(new u(z,c));D=H.get("dependencies");if(!D)return l();O=[];for(M in D)D.hasOwnProperty(M)&& +O.push({packageId:M,vers:D[M]});f(O,function(W,V){m._ensurePackage(W.packageId,W.vers,H,d,V)})(l)})};k.prototype._canonicalPackageId=function(a,b,c){if(a instanceof Q)return a.id;if(w(a))return a;if(a==="default"&&this.defaultPackage)return this.defaultPackage.id;var d=this.canonicalPackageIds,l,m,z,D,H;if(!c)c=this.anonymousPackage;if(!c)throw new Error("working package is required");b||(b=c.requiredVersion(a));l=c.id;if(!d)d=this.canonicalPackageIds={};d[l]||(d[l]={});d=d[l];d[a]||(d[a]={});d=d[a]; +if(d[b])return d[b];l=this.sources;m=c.canonicalPackageId(a,b);H=c;if(!m)if(m=c.canonicalPackageId(a,null))throw new Error(c.get("name")+" contains an incompatible nested package "+a+" (expected: "+b+")");if(!m&&l){D=l.length;for(z=0;!m&&z=0){z=a.slice(0, +d);a=a.slice(d+1);b=[]}else if(a.match(/^\.\.?\//)){if(!b)throw new Error("id required to resolve relative id: "+a);if(b.indexOf(":")>=0)throw new Error("current moduleId cannot contain packageId");if(c)z=c.id;b=b.split("/");b.pop()}else b=[];m=a.split("/");c=m.length;for(d=0;d=0)c[l]=c[l].slice(0,m).concat(c[l].slice(m+1));c[l].push(b)}}return this};q.prototype.module=function(a,b){w(a)||(a="::"+a);this.factories[a]=b;return this};q.prototype.script=function(a){w(a)||(a="::"+a);this._resolve(this.scriptActions,a,true)};q.prototype.stylesheet= +function(a){w(a)||(a="::"+a);this._resolve(this.stylesheetActions,a,true)};var P="undefined"!==typeof document&&document.createElement,X="undefined"!==typeof XMLHttpRequest;q.prototype.xhr=!P;q.prototype.autowrap=false;var Z=function(a){if(!a)return null;for(var b=a.length;--b>=0;)if(!a[b]["tiki:private"])return a[b];return null};q.prototype.canonicalPackageId=function(a,b){a=this.packageInfoByName[a];var c,d,l;if(b)b=J.normalize(b);if(!a)return null;if(a[b]&&a[b].length===1)return a[b][0].id;for(d in a)if(a.hasOwnProperty(d))if(J.compatible(b, +d))if(!c||J.compare(l,d)<0)if(c=Z(a[d]))l=d;return c?c.id:null};q.prototype.packageFor=function(a){var b=this.packages[a];if(b)return b;if((b=this.packageInfoById[a])&&!b["tiki:external"]){b=new this.Package(a,b,this);return this.packages[a]=b}return null};q.prototype.ensurePackage=function(a,b){var c=this.ensureActions[a];if(c)return c(b);var d=this.packageInfoById[a];if(!d)return b(new u(a,"browser package info"));var l=this;c=n(function(m){var z=1,D=false,H,M=function(Y){if(!H){if(Y){H=true;return m(Y)}z-= +1;if(z<=0&&D)return m(null,d)}},O=d.dependencies,W=d["tiki:nested"],V,T;for(V in O)if(O.hasOwnProperty(V)){T=W[V];if(!T){T=O[V];T=l.canonicalPackageId(V,T)}if(T&&l.packageInfoById[a]){z++;l.ensurePackage(T,M)}}W=(O=d["tiki:resources"])?O.length:0;for(V=0;V=0;){var J=L[I];if(i.exec(J))g.push(J);else for(var U=N.length;--U>=0;)if(N[U].exec(J)){g.push(J);break}}g.forEach(function(R){delete A.exports[R];delete A.modules[R];delete A.usedExports[R]});g=function(){this.catalog.loadPlugin(this.name).then(function(){for(u in n)this.catalog.plugins[u].register();for(u in n)if(n[u].callPointer){var R=C(u,n[u].callPointer);(R=v(R))&&R(p)}f&&f()}.bind(this))}.bind(this);L=function(){t.error("Failed to load metadata from "+ +this.reloadURL)}.bind(this);this.catalog.loadMetadataFromURL(this.reloadURL).then(g,L)}}};var B=function(f,g,n){g=g.split(".");f=f;var p=g.length-1;if(p>0)for(var u=0;u1){u.reject(new Error("For object "+f+", create a simple factory function and change the action to call because JS cannot handle this case."));return}N=new N(A[0])}else if(i==="value")N=N;else{u.reject(new Error("Create action must be call|new|value. Found"+i));return}this.instances[f]=N;u.resolve(N)}.bind(this))}.bind(this)); +return u},getObject:function(f){return this.instances[f]||(this.parent?this.parent.getObject(f):undefined)},getExtensionPoint:function(f,g){if(g&&this.points[f]===undefined)this.points[f]=new j.ExtensionPoint(f,this);return this.points[f]},getExtensions:function(f){f=this.getExtensionPoint(f);if(f===undefined)return[];return f.extensions},orderExtensions:function(f){f=f||this._extensionsOrdering;for(name in this.points)this.points[name].orderExtensions(f);this._extensionsOrdering=f},getExtensionsOrdering:function(){return this._extensionsOrdering}, +getExtensionByKey:function(f,g){f=this.getExtensionPoint(f);if(f!==undefined)return f.getByKey(g)},_toposort:function(f){var g=[],n={},p=function(A){if(!(A in n||!(A in f))){n[A]=true;var L=f[A].dependencies;if(!h.none(L))for(var I in L)p(I);g.push(A)}};for(var u in f)p(u);return g},registerMetadata:function(f){if(this.parent)this.parent.registerMetadata(f);else{for(var g in f){var n=f[g];if(n.errors){t.error("Plugin ",g," has errors:");n.errors.forEach(function(p){t.error(p)});delete f[g]}else{if(n.dependencies)n.depends= +Object.keys(n.dependencies);n.name=g;n.version=null;s.canonicalPackageId(g)===null&&s.register("::"+g,n)}}h.mixin(this.metadata,h.clone(f,true));this.children.forEach(function(p){p._registerMetadata(h.clone(f,true))});this._registerMetadata(h.clone(f,true))}},_registerMetadata:function(f){var g,n=this.plugins;this._toposort(f).forEach(function(p){if(this.plugins[p])if(this.isPluginLoaded(p))return;else{var u=this.plugins[p];u.unregister()}var A=f[p],L=!this.deactivatedPlugins[p];if(L&&A.depends&& +A.depends.length!=0)if(!A.depends.some(function(i){return!this.deactivatedPlugins[i]},this)){this.deactivatedPlugins[p]="DEPENDS";L=false}A.catalog=this;A.name=p;u=new j.Plugin(A);n[p]=u;if(A.provides){u=A.provides;for(A=0;An)p=n;u=f.slice(u,p);g=g[u];u=p+1}if(u0;)f.exec(n[p])&& +delete g[n[p]]};j.getUserPlugins=function(){return j.catalog.getPlugins({onlyType:"user"})}}); +bespin.tiki.module("bespin:promise",function(y,j){var x=y("bespin:console").console;y("bespin:util/stacktrace");var E=0;j._outstanding=[];j._recent=[];j.Promise=function(){this._status=0;this._value=undefined;this._onSuccessHandlers=[];this._onErrorHandlers=[];this._id=E++;j._outstanding[this._id]=this};j.Promise.prototype.isPromise=true;j.Promise.prototype.isComplete=function(){return this._status!=0};j.Promise.prototype.isResolved=function(){return this._status==1};j.Promise.prototype.isRejected= +function(){return this._status==-1};j.Promise.prototype.then=function(w,t){if(typeof w==="function")if(this._status===1)w.call(null,this._value);else this._status===0&&this._onSuccessHandlers.push(w);if(typeof t==="function")if(this._status===-1)t.call(null,this._value);else this._status===0&&this._onErrorHandlers.push(t);return this};j.Promise.prototype.chainPromise=function(w){var t=new j.Promise;t._chainedFrom=this;this.then(function(h){try{t.resolve(w(h))}catch(o){t.reject(o)}},function(h){t.reject(h)}); +return t};j.Promise.prototype.resolve=function(w){return this._complete(this._onSuccessHandlers,1,w,"resolve")};j.Promise.prototype.reject=function(w){return this._complete(this._onErrorHandlers,-1,w,"reject")};j.Promise.prototype._complete=function(w,t,h,o){if(this._status!=0){x.group("Promise already closed");x.error("Attempted "+o+"() with ",h);x.error("Previous status = ",this._status,", previous value = ",this._value);x.trace();if(this._completeTrace){x.error("Trace of previous completion:"); +this._completeTrace.log(5)}x.groupEnd();return this}this._status=t;this._value=h;w.forEach(function(s){s.call(null,this._value)},this);this._onSuccessHandlers.length=0;this._onErrorHandlers.length=0;delete j._outstanding[this._id];for(j._recent.push(this);j._recent.length>20;)j._recent.shift();return this};j.group=function(w){w instanceof Array||(w=Array.prototype.slice.call(arguments));if(w.length===0)return(new j.Promise).resolve([]);var t=new j.Promise,h=[],o=0,s=function(C){return function(v){h[C]= +v;o++;t._status!==-1&&o===w.length&&t.resolve(h)}};w.forEach(function(C,v){v=s(v);var B=t.reject.bind(t);C.then(v,B)});return t}}); +bespin.tiki.module("bespin:proxy",function(y,j){y("util/util");var x=y("promise").Promise;j.xhr=function(E,w,t,h){var o=new x;if(!bespin.proxy||!bespin.proxy.xhr){var s=new XMLHttpRequest;s.onreadystatechange=function(){if(s.readyState===4){var C=s.status;if(C!==0&&C!==200){C=new Error(s.responseText+" (Status "+s.status+")");C.xhr=s;o.reject(C)}else o.resolve(s.responseText)}}.bind(this);s.open("GET",w,t);h&&h(s);s.send()}else bespin.proxy.xhr.call(this,E,w,t,h,o);return o};j.Worker=function(E){return!bespin.proxy|| +!bespin.proxy.worker?new Worker(E):new bespin.proxy.worker(E)}}); +bespin.tiki.module("bespin:sandbox",function(y,j){var x=y("tiki"),E=y("bespin:util/util"),w=y("bespin:plugins").catalog;if(w.parent)throw new Error("The sandbox module can't be used inside of a slave catalog!");y=function(){x.Sandbox.call(this,bespin.tiki.require.loader,{},[]);var t=this.require("bespin:plugins").catalog;t.parent=w;w.children.push(t);t.deactivatePlugin=E.clone(w.deactivatePlugin);t._extensionsOrdering=E.clone(w._extensionsOrdering);t._registerMetadata(E.clone(w.metadata,true))};y.prototype= +new x.Sandbox;y.prototype.require=function(t,h,o){var s=this.loader.canonical(t,h,o).substring(2).split(":")[0];return w.plugins[s].share?bespin.tiki.sandbox.require(t,h,o):x.Sandbox.prototype.require.call(this,t,h,o)};j.Sandbox=y}); +bespin.tiki.module("bespin:util/cookie",function(y,j){var x=function(E,w){return E.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,function(t){if(w&&w.indexOf(t)!=-1)return t;return"\\"+t})};j.get=function(E){E=new RegExp("(?:^|; )"+x(E)+"=([^;]*)");return(E=document.cookie.match(E))?decodeURIComponent(E[1]):undefined};j.set=function(E,w,t){t=t||{};if(typeof t.expires=="number"){var h=new Date;h.setTime(h.getTime()+t.expires*24*60*60*1E3);t.expires=h}if(t.expires&&t.expires.toUTCString)t.expires=t.expires.toUTCString(); +w=encodeURIComponent(w);E=E+"="+w;var o;for(o in t){E+="; "+o;w=t[o];if(w!==true)E+="="+w}document.cookie=E};j.remove=function(E){j.set(E,"",{expires:-1})};j.isSupported=function(){if(!("cookieEnabled"in navigator)){j.set("__djCookieTest__","CookiesAllowed");navigator.cookieEnabled=j.get("__djCookieTest__")=="CookiesAllowed";navigator.cookieEnabled&&j.remove("__djCookieTest__")}return navigator.cookieEnabled}}); +bespin.tiki.module("bespin:util/scratchcanvas",function(y,j){var x=y("bespin:util/util"),E=function(){this._canvas=document.getElementById("bespin-scratch-canvas");if(x.none(this._canvas)){this._canvas=document.createElement("canvas");this._canvas.id="bespin-scratch-canvas";this._canvas.width=400;this._canvas.height=300;this._canvas.style.position="absolute";this._canvas.style.top="-10000px";this._canvas.style.left="-10000px";document.body.appendChild(this._canvas)}};E.prototype.getContext=function(){return this._canvas.getContext("2d")}; +E.prototype.measureStringWidth=function(t,h){if(x.none(h))h="M";var o=this.getContext();o.save();o.font=t;t=o.measureText(h).width;o.restore();return t};var w=null;j.get=function(){if(w===null)w=new E;return w}}); +bespin.tiki.module("bespin:util/stacktrace",function(y,j){function x(v){for(var B=0;B\s*\(/gm,"{anonymous}()@").split("\n")},firefox:function(v){var B=v.stack;if(!B){t.log(v);return[]}B=B.replace(/(?:\n@:0)?\s+$/m,"");B=B.replace(/^\(/gm,"{anonymous}(");return B.split("\n")},opera:function(v){v=v.message.split("\n"); +var B=/Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i,F,f,g;F=4;f=0;for(g=v.length;F-1};j.indexOfProperty=function(h,o,s){for(var C=0;C=0;j.isWindows=w.indexOf("Win")>=0;j.isWebKit=parseFloat(y.split("WebKit/")[1])|| +undefined;j.isChrome=parseFloat(y.split("Chrome/")[1])||undefined;j.isMac=w.indexOf("Macintosh")>=0;j.isMozilla=w.indexOf("Gecko/")>=0;if(y.indexOf("AdobeAIR")>=0)j.isAIR=1;var t=Math.max(w.indexOf("WebKit"),w.indexOf("Safari"),0);if(t&&!j.isChrome){j.isSafari=parseFloat(w.split("Version/")[1]);if(!j.isSafari||parseFloat(w.substr(t+7))<=419.3)j.isSafari=2}if(y.indexOf("Gecko")>=0&&!j.isWebKit)j.isMozilla=parseFloat(w);j.getOS=function(){return j.isMac?j.OS.MAC:j.isLinux?j.OS.LINUX:j.OS.WINDOWS};j.contains= +typeof document!=="undefined"&&document.compareDocumentPosition?function(h,o){return h.compareDocumentPosition(o)&16}:function(h,o){return h!==o&&(h.contains?h.contains(o):true)};j.stopEvent=function(h){h.preventDefault();h.stopPropagation()};j.randomPassword=function(h){h=h||16;for(var o="",s=0;ss)return false;if(h.x!=o.x&&Math.abs(h.x-o.x)>s)return false; +if(h.width!=o.width&&Math.abs(h.width-o.width)>s)return false;if(h.height!=o.height&&Math.abs(h.height-o.height)>s)return false;return true}});bespin.tiki.register("::syntax_directory",{name:"syntax_directory",dependencies:{}}); +bespin.tiki.module("syntax_directory:index",function(y,j){function x(t){this.extension=t;this.name=t.name;this.fileExts=t.hasOwnProperty("fileexts")?t.fileexts:[]}function E(t){w.register(t)}y("bespin:plugins");var w={_fileExts:{},_syntaxInfo:{},get:function(t){return this._syntaxInfo[t]},hasSyntax:function(t){return this._syntaxInfo.hasOwnProperty(t)},register:function(t){var h=new x(t);this._syntaxInfo[h.name]=h;var o=this._fileExts;h.fileExts.forEach(function(s){o[s]=h.name})},syntaxForFileExt:function(t){t= +t.toLowerCase();var h=this._fileExts;return h.hasOwnProperty(t)?h[t]:"plain"}};j.syntaxDirectory=w;j.discoveredNewSyntax=E});bespin.tiki.register("::underscore",{name:"underscore",dependencies:{}}); +bespin.tiki.module("underscore:index",function(y,j){(function(){var x=this,E=x._,w=typeof StopIteration!=="undefined"?StopIteration:"__break__",t=function(e){return e.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},h=Array.prototype,o=Object.prototype,s=h.slice,C=h.unshift,v=o.toString,B=o.hasOwnProperty,F=h.forEach,f=h.map,g=h.reduce,n=h.reduceRight,p=h.filter,u=h.every,A=h.some,L=h.indexOf,I=h.lastIndexOf;o=Array.isArray;var N=Object.keys,i=function(e){return new R(e)};if(typeof j!=="undefined")j._= +i;x._=i;i.VERSION="1.0.2";var J=i.forEach=function(e,k,r){try{if(F&&e.forEach===F)e.forEach(k,r);else if(i.isNumber(e.length))for(var q=0,G=e.length;q=q.computed&&(q={value:G,computed:K})});return q.value};i.min=function(e,k,r){if(!k&&i.isArray(e))return Math.min.apply(Math,e);var q={computed:Infinity};J(e,function(G,K,P){K=k?k.call(r,G,K,P):G;KG?1:0}),"value")};i.sortedIndex=function(e,k,r){r=r||i.identity;for(var q=0,G=e.length;q>1;r(e[K])=0})})};i.zip=function(){for(var e=i.toArray(arguments),k=i.max(i.pluck(e,"length")),r=new Array(k),q=0;q0?G-k:k-G)>=0)return q;q[K++]=G}};i.bind=function(e,k){var r=i.rest(arguments,2);return function(){return e.apply(k||{},r.concat(i.toArray(arguments)))}};i.bindAll=function(e){var k=i.rest(arguments);if(k.length==0)k=i.functions(e); +J(k,function(r){e[r]=i.bind(e[r],e)});return e};i.delay=function(e,k){var r=i.rest(arguments,2);return setTimeout(function(){return e.apply(e,r)},k)};i.defer=function(e){return i.delay.apply(i,[e,1].concat(i.rest(arguments)))};i.wrap=function(e,k){return function(){var r=[e].concat(i.toArray(arguments));return k.apply(k,r)}};i.compose=function(){var e=i.toArray(arguments);return function(){for(var k=i.toArray(arguments),r=e.length-1;r>=0;r--)k=[e[r].apply(this,k)];return k[0]}};i.keys=N||function(e){if(i.isArray(e))return i.range(0, +e.length);var k=[];for(var r in e)B.call(e,r)&&k.push(r);return k};i.values=function(e){return i.map(e,i.identity)};i.functions=function(e){return i.filter(i.keys(e),function(k){return i.isFunction(e[k])}).sort()};i.extend=function(e){J(i.rest(arguments),function(k){for(var r in k)e[r]=k[r]});return e};i.clone=function(e){if(i.isArray(e))return e.slice(0);return i.extend({},e)};i.tap=function(e,k){k(e);return e};i.isEqual=function(e,k){if(e===k)return true;var r=typeof e;if(r!=typeof k)return false; +if(e==k)return true;if(!e&&k||e&&!k)return false;if(e.isEqual)return e.isEqual(k);if(i.isDate(e)&&i.isDate(k))return e.getTime()===k.getTime();if(i.isNaN(e)&&i.isNaN(k))return true;if(i.isRegExp(e)&&i.isRegExp(k))return e.source===k.source&&e.global===k.global&&e.ignoreCase===k.ignoreCase&&e.multiline===k.multiline;if(r!=="object")return false;if(e.length&&e.length!==k.length)return false;r=i.keys(e);var q=i.keys(k);if(r.length!=q.length)return false;for(var G in e)if(!(G in k)||!i.isEqual(e[G],k[G]))return false; +return true};i.isEmpty=function(e){if(i.isArray(e)||i.isString(e))return e.length===0;for(var k in e)if(B.call(e,k))return false;return true};i.isElement=function(e){return!!(e&&e.nodeType==1)};i.isArray=o||function(e){return!!(e&&e.concat&&e.unshift&&!e.callee)};i.isArguments=function(e){return e&&e.callee};i.isFunction=function(e){return!!(e&&e.constructor&&e.call&&e.apply)};i.isString=function(e){return!!(e===""||e&&e.charCodeAt&&e.substr)};i.isNumber=function(e){return e===+e||v.call(e)==="[object Number]"}; +i.isBoolean=function(e){return e===true||e===false};i.isDate=function(e){return!!(e&&e.getTimezoneOffset&&e.setUTCFullYear)};i.isRegExp=function(e){return!!(e&&e.test&&e.exec&&(e.ignoreCase||e.ignoreCase===false))};i.isNaN=function(e){return i.isNumber(e)&&isNaN(e)};i.isNull=function(e){return e===null};i.isUndefined=function(e){return typeof e=="undefined"};i.noConflict=function(){x._=E;return this};i.identity=function(e){return e};i.times=function(e,k,r){for(var q=0;q",interpolate:/<%=(.+?)%>/g};i.template=function(e,k){var r=i.templateSettings,q=new RegExp("'(?=[^"+r.end.substr(0,1)+"]*"+t(r.end)+")","g");e=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+e.replace(/[\r\t\n]/g," ").replace(q,"\t").split("'").join("\\'").split("\t").join("'").replace(r.interpolate, +"',$1,'").split(r.start).join("');").split(r.end).join("p.push('")+"');}return p.join('');");return k?e(k):e};i.each=i.forEach;i.foldl=i.inject=i.reduce;i.foldr=i.reduceRight;i.select=i.filter;i.all=i.every;i.any=i.some;i.head=i.first;i.tail=i.rest;i.methods=i.functions;var R=function(e){this._wrapped=e},S=function(e,k){return k?i(e).chain():e},Q=function(e,k){R.prototype[e]=function(){var r=i.toArray(arguments);C.call(r,this._wrapped);return S(k.apply(i,r),this._chain)}};i.mixin(i);J(["pop","push", +"reverse","shift","sort","splice","unshift"],function(e){var k=h[e];R.prototype[e]=function(){k.apply(this._wrapped,arguments);return S(this._wrapped,this._chain)}});J(["concat","join","slice"],function(e){var k=h[e];R.prototype[e]=function(){return S(k.apply(this._wrapped,arguments),this._chain)}});R.prototype.chain=function(){this._chain=true;return this};R.prototype.value=function(){return this._wrapped}})();j._.noConflict()}); +bespin.tiki.require("bespin:plugins").catalog.registerMetadata({bespin:{testmodules:[],resourceURL:"resources/bespin/",name:"bespin",environments:{main:true,worker:true},type:"plugins/boot"},syntax_directory:{resourceURL:"resources/syntax_directory/",name:"syntax_directory",environments:{main:true,worker:true},dependencies:{},testmodules:[],provides:[{register:"#discoveredNewSyntax",ep:"extensionhandler",name:"syntax"}],type:"plugins/supported",description:"Catalogs the available syntax engines"}, +underscore:{testmodules:[],type:"plugins/thirdparty",resourceURL:"resources/underscore/",description:"Functional Programming Aid for Javascript. Works well with jQuery.",name:"underscore"}});typeof window==="undefined"?importScripts("BespinWorker.js"):function(){var y=document.createElement("script");y.setAttribute("src",bespin.base+"BespinMain.js");document.getElementsByTagName("head")[0].appendChild(y)}(); diff --git a/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinMain.js b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinMain.js new file mode 100644 index 0000000000..739efb9bf7 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/editor/bespin/BespinMain.js @@ -0,0 +1,471 @@ +bespin.tiki.register("::text_editor",{name:"text_editor",dependencies:{completion:"0.0.0",undomanager:"0.0.0",settings:"0.0.0",canon:"0.0.0",rangeutils:"0.0.0",traits:"0.0.0",theme_manager:"0.0.0",keyboard:"0.0.0",edit_session:"0.0.0",syntax_manager:"0.0.0"}}); +bespin.tiki.module("text_editor:commands/editing",function(y,s){var v=y("settings").settings,r=y("environment").env,l=y("rangeutils:utils/range");s.backspace=function(){r.view.performBackspaceOrDelete(true)};s.deleteCommand=function(){r.view.performBackspaceOrDelete(false)};s.deleteLines=function(){if(!r.model.readOnly)if(r.model.lines.length!=1){var d=r.view;d.groupChanges(function(){var f=d.getSelectedRange(),m=r.model.lines,i=m.length-1,g;g=f.start.row==i?{col:m[i-1].length,row:i-1}:{col:0,row:f.start.row}; +d.replaceCharacters({start:g,end:f.end.row==i?{col:m[i].length,row:i}:{col:0,row:f.end.row+1}},"");d.moveCursorTo(g)})}};var h=function(d,f){var m=f.getSelectedRange().start;d=/^\s*/.exec(d.lines[m.row].substring(0,m.col));f.insertText("\n"+d)};s.insertText=function(d){r.view.insertText(d.text)};s.newline=function(){h(r.model,r.view)};s.joinLines=function(){var d=r.model;if(!d.readOnly){var f=r.view,m=f.getSelectedRange(),i=d.lines,g=m.end.row;i.length!=g&&f.groupChanges(function(){f.replaceCharacters({start:{col:i[g].length, +row:g},end:{col:/^\s*/.exec(i[g+1])[0].length,row:g+1}},"")})}};s.openLine=function(){if(!r.model.readOnly){var d=r.model,f=r.view,m=f.getSelectedRange().end.row;f.moveCursorTo({row:m,col:d.lines[m].length});h(d,f)}};s.tab=function(){var d=r.view;d.groupChanges(function(){var f=v.get("tabstop"),m=d.getSelectedRange(),i="";if(l.isZeroLength(m)){var g=r.model.lines[m.start.row].substring(m.start.col).match(/^\s*/)[0].length;f=f-(m.start.col+g)%f;for(var j=0;j-1;){if(q=m.isDelimiter(i[g]))t++;else B=true;if((q||t>1)&&B)break;g+=j}j<0&&g++;return g},d=function(m){var i=v.view,g=v.model.lines,j=i.getSelectedRange(true).end,q=j.row;j=j.col;var t=g[q],B=false;if(j>=t.length){q++;B=true;if(qt.length)j=t.length;else if(j==0){q--;B=true;if(q>-1){t=g[q];j=t.length}else t=""}j=h(i,t,j,-1,B);i.moveCursorTo({row:q,col:j},m)};s.moveNextWord=function(){d(false)};s.selectNextWord=function(){d(true)};s.movePreviousWord=function(){f(false)};s.selectPreviousWord=function(){f(true)};s.selectAll=function(){v.view.selectAll()}}); +bespin.tiki.module("text_editor:commands/scrolling",function(y,s){var v=y("environment").env;s.scrollDocStart=function(){v.view.scrollToPosition({col:0,row:0})};s.scrollDocEnd=function(){v.view.scrollToPosition(v.model.range.end)};s.scrollPageDown=function(){v.view.scrollPageDown()};s.scrollPageUp=function(){v.view.scrollPageUp()}}); +bespin.tiki.module("text_editor:controllers/layoutmanager",function(y,s){var v=y("bespin:util/util"),r=y("events").Event;y("rangeutils:utils/range");var l=y("syntax_manager").SyntaxManager,h=y("models/textstorage").TextStorage,d=y("bespin:plugins").catalog,f=y("settings").settings,m=y("bespin:util/scratchcanvas"),i={};y=function(){var g=f.get("fontsize"),j=f.get("fontface");j=g+"px "+j;for(var q=m.get(),t="",B=0;B<100;B++)t+="M";j=q.measureStringWidth(j,t)/100;i.characterWidth=j;i.lineHeight=Math.floor(g* +1.6);i.lineAscent=Math.floor(g*1.3)};y();d.registerExtension("settingChange",{match:"font[size|face]",pointer:y});s.LayoutManager=function(g){this.changedTextAtRow=new r;this.invalidatedRects=new r;this.fontDimension=i;if(g.textStorage){g._textStorage=g.textStorage;delete g.textStorage}else this._textStorage=new h;v.mixin(this,g);this._textStorage.changed.add(this.textStorageChanged.bind(this));this.textLines=[{characters:"",colors:[{start:0,end:0,color:"plain"}]}];this.syntaxManager=g=new l(this); +g.attrsChanged.add(this._attrsChanged.bind(this));this._size={width:0,height:0};this.sizeChanged=new r;this._height=0;this._recomputeEntireLayout()};s.LayoutManager.prototype={_maximumWidth:0,_textStorage:null,_size:null,sizeChanged:null,_theme:{},margin:{left:5,bottom:6,top:0,right:12},pluginCatalog:d,syntaxManager:null,textLines:null,_attrsChanged:function(g,j){this.updateTextRows(g,j);this.invalidatedRects(this,this.rectsForRange({start:{row:g,col:0},end:{row:j,col:0}}))},_computeInvalidRects:function(g, +j){var q=this.characterRectForPosition(g.start),t={x:q.x,y:q.y,width:Number.MAX_VALUE,height:q.height};return g.end.row===j.end.row?[t]:[t,{x:0,y:q.y+i.lineHeight,width:Number.MAX_VALUE,height:Number.MAX_VALUE}]},_lastCharacterPosition:function(){return{row:this.textLines.length-1,col:this._maximumWidth}},_recalculateMaximumWidth:function(){var g=0;this.textLines.forEach(function(j){j=j.characters.length;if(g=r.row;f--){d=this._findMatchesInString(h[f]);if(d.length!==0)return this._makeRange(d[d.length-1],f)}return null}}}); +bespin.tiki.module("text_editor:controllers/undo",function(y,s){var v=y("bespin:console").console,r=y("environment").env;s.EditorUndoController=function(l){this.editor=l;l=this.textView=l.textView;l.beganChangeGroup.add(function(h,d){this._beginTransaction();this._record.selectionBefore=d}.bind(this));l.endedChangeGroup.add(function(h,d){this._record.selectionAfter=d;this._endTransaction()}.bind(this));l.replacedCharacters.add(function(h,d,f){if(!this._inTransaction)throw new Error("UndoController.textViewReplacedCharacters() called outside a transaction"); +this._record.patches.push({oldCharacters:this._deletedCharacters,oldRange:d,newCharacters:f,newRange:this.editor.layoutManager.textStorage.resultingRangeForReplacement(d,f.split("\n"))});this._deletedCharacters=null}.bind(this));l.willReplaceRange.add(function(h,d){if(!this._inTransaction)throw new Error("UndoController.textViewWillReplaceRange() called outside a transaction");this._deletedCharacters=this.editor.layoutManager.textStorage.getCharacters(d)}.bind(this))};s.EditorUndoController.prototype= +{_inTransaction:false,_record:null,textView:null,_beginTransaction:function(){if(this._inTransaction){v.trace();throw new Error("UndoController._beginTransaction() called with a transaction already in place");}this._inTransaction=true;this._record={patches:[]}},_endTransaction:function(){if(!this._inTransaction)throw new Error("UndoController._endTransaction() called without a transaction in place");this.editor.buffer.undoManager.registerUndo(this,this._record);this._record=null;this._inTransaction= +false},_tryApplyingPatches:function(l){var h=this.editor.layoutManager.textStorage;l.forEach(function(d){h.replaceCharacters(d.oldRange,d.newCharacters)});return true},_undoOrRedo:function(l,h){if(this._inTransaction)throw new Error("UndoController._undoOrRedo() called while in a transaction");if(!this._tryApplyingPatches(l))return false;this.textView.setSelection(h,true);return true},redo:function(l){var h=l.patches.concat();h.reverse();return this._undoOrRedo(h,l.selectionAfter)},undo:function(l){return this._undoOrRedo(l.patches.map(function(h){return{oldCharacters:h.newCharacters, +oldRange:h.newRange,newCharacters:h.oldCharacters,newRange:h.oldRange}}),l.selectionBefore)}};s.undoManagerCommand=function(l,h){r.editor.buffer.undoManager[h.commandExt.name]()}}); +bespin.tiki.module("text_editor:models/buffer",function(y,s){var v=y("environment").env,r=y("bespin:util/util"),l=y("bespin:promise").Promise,h=y("models/textstorage").TextStorage,d=y("controllers/layoutmanager").LayoutManager,f=y("undomanager").UndoManager;s.Buffer=function(m,i){this._file=m;this._model=new h(i);this._layoutManager=new d({textStorage:this._model});this.undoManager=new f;if(m)this.reload().then(function(){this._updateSyntaxManagerInitialContext()}.bind(this));else{this.loadPromise= +new l;this.loadPromise.resolve()}i=v.session?v.session.history:null;var g,j,q;if(i&&m&&(g=i.getHistoryForPath(m.path))){j=g.selection;q=g.scroll}this._selectedRange=j||{start:{row:0,col:0},end:{row:0,col:0}};this._scrollOffset=q||{x:0,y:0}};s.Buffer.prototype={undoManager:null,loadPromise:null,_scrollOffset:null,_selectedRange:null,_selectedRangeEndVirtual:null,_layoutManager:null,_file:null,_model:null,save:function(){return this._file.saveContents(this._model.value)},saveAs:function(m){var i=new l; +m.saveContents(this._model.value).then(function(){this._file=m;this._updateSyntaxManagerInitialContext();i.resolve()}.bind(this),function(g){i.reject(g)});return i},reload:function(){var m=this,i;return this.loadPromise=i=this._file.loadContents().then(function(g){m._model.value=g})},_updateSyntaxManagerInitialContext:function(){var m=this._file.extension();this._layoutManager.syntaxManager.setSyntaxFromFileExt(m===null?"":m)},untitled:function(){return r.none(this._file)}};Object.defineProperties(s.Buffer.prototype, +{layoutManager:{get:function(){return this._layoutManager}},syntaxManager:{get:function(){}},file:{get:function(){return this._file}},model:{get:function(){return this._model}}})}); +bespin.tiki.module("text_editor:models/textstorage",function(y,s){var v=y("events").Event,r=y("bespin:util/util");y=function(l){this._lines=l!==null&&l!==undefined?l.split("\n"):[""];this.changed=new v;return this};y.prototype={_lines:null,readOnly:false,clampPosition:function(l){var h=this._lines,d=l.row;if(d<0)return{row:0,col:0};else if(d>=h.length)return this.range.end;l=Math.max(0,Math.min(l.col,h[d].length));return{row:d,col:l}},clampRange:function(l){var h=this.clampPosition(l.start);l=this.clampPosition(l.end); +return{start:h,end:l}},deleteCharacters:function(l){this.replaceCharacters(l,"")},displacePosition:function(l,h){var d=h>0,f=this._lines,m=f.length;for(h=Math.abs(h);h!==0;h--)if(d){var i=f[l.row].length;if(l.row===m-1&&l.col===i)return l;l=l.col===i?{row:l.row+1,col:0}:{row:l.row,col:l.col+1}}else{if(l.row===0&&l.col==0)return l;if(l.col===0){f=this._lines;l={row:l.row-1,col:f[l.row-1].length}}else l={row:l.row,col:l.col-1}}return l},getCharacters:function(l){var h=this._lines,d=l.start,f=l.end, +m=d.row;l=f.row;var i=d.col;d=f.col;if(m===l)return h[m].substring(i,d);f=h[m].substring(i);m=h.slice(m+1,l);h=h[l].substring(0,d);return[f].concat(m,h).join("\n")},getLines:function(){return this._lines},getRange:function(){var l=this._lines,h=l.length-1;return{start:{row:0,col:0},end:{row:h,col:l[h].length}}},getValue:function(){return this._lines.join("\n")},insertCharacters:function(l,h){this.replaceCharacters({start:l,end:l},h)},replaceCharacters:function(l,h){if(this.readOnly)throw new Error("Attempt to modify a read-only text storage object"); +var d=h.split("\n"),f=d.length,m=this.resultingRangeForReplacement(l,d),i=l.start,g=l.end,j=i.row,q=g.row,t=this._lines;d[0]=t[j].substring(0,i.col)+d[0];d[f-1]+=t[q].substring(g.col);this._lines=r.replace(t,j,q-j+1,d);this.changed(l,m,h)},resultingRangeForReplacement:function(l,h){var d=h.length;l=l.start;return{start:l,end:{row:l.row+d-1,col:(d===1?l.col:0)+h[d-1].length}}},setLines:function(l){this.setValue(l.join("\n"))},setValue:function(l){this.replaceCharacters(this.range,l)}};s.TextStorage= +y;Object.defineProperties(s.TextStorage.prototype,{lines:{get:function(){return this.getLines()},set:function(l){return this.setLines(l)}},range:{get:function(){return this.getRange()}},value:{get:function(){return this.getValue()},set:function(l){this.setValue(l)}}})}); +bespin.tiki.module("text_editor:utils/rect",function(y,s){s._distanceFromBounds=function(v,r,l){if(v=l)return v-l;return 0};s.merge=function(v){var r;do{r=false;for(var l=[],h=0;h=s.minX(r)&&v.y>=s.minY(r)&&v.x<=s.maxX(r)&&v.y<=s.maxY(r)};s.unionRects=function(v,r){v={x:Math.min(s.minX(v),s.minX(r)),y:Math.min(s.minY(v),s.minY(r)),width:Math.max(s.maxX(v),s.maxX(r)),height:Math.max(s.maxY(v),s.maxY(r))};v.width=Math.max(0, +v.width-v.x);v.height=Math.max(0,v.height-v.y);return v};s.rectsEqual=function(v,r,l){if(!v||!r)return v==r;if(!l&&l!==0)l=0.1;if(v.y!=r.y&&Math.abs(v.y-r.y)>l)return false;if(v.x!=r.x&&Math.abs(v.x-r.x)>l)return false;if(v.width!=r.width&&Math.abs(v.width-r.width)>l)return false;if(v.height!=r.height&&Math.abs(v.height-r.height)>l)return false;return true}}); +bespin.tiki.module("text_editor:views/canvas",function(y,s){var v=y("bespin:util/util"),r=y("utils/rect"),l=y("events").Event;s.CanvasView=function(h,d,f){if(h){this._preventDownsize=d||false;this._clearOnFullInvalid=f||false;this._clippingFrame=this._frame={x:0,y:0,width:0,height:0};this._invalidRects=[];d=document.createElement("canvas");d.setAttribute("style","position: absolute");d.innerHTML="canvas tag not supported by your browser";h.appendChild(d);this.domNode=d;this.clippingChanged=new l; +this.clippingChanged.add(this.clippingFrameChanged.bind(this))}};s.CanvasView.prototype={domNode:null,clippingChanged:null,_canvasContext:null,_canvasId:null,_invalidRects:null,_lastRedrawTime:null,_redrawTimer:null,_clippingFrame:null,_preventDownsize:false,_clearOnFullInvalid:false,_frame:null,_getContext:function(){if(this._canvasContext===null)this._canvasContext=this.domNode.getContext("2d");return this._canvasContext},computeWithClippingFrame:function(h,d){var f=this.clippingFrame;return{x:h+ +f.x,y:d+f.y}},minimumRedrawDelay:1E3/30,clippingFrameChanged:function(){this.invalidate()},drawRect:function(){},render:function(){if(!(this._renderTimer||this._redrawTimer))this._renderTimer=setTimeout(this._tryRedraw.bind(this),0)},invalidate:function(){this._invalidRects="all";this.render()},invalidateRect:function(h){var d=this._invalidRects;if(d!=="all"){d.push(h);this.render()}},_tryRedraw:function(){this._renderTimer=null;var h=(new Date).getTime(),d=this._lastRedrawTime,f=this.minimumRedrawDelay; +if(d===null||h-d>=f)this._redraw();else if(this._redrawTimer===null)this._redrawTimer=window.setTimeout(this._redraw.bind(this),f)},_redraw:function(){var h=this.clippingFrame;h={x:Math.round(h.x),y:Math.round(h.y),width:h.width,height:h.height};var d=this._getContext();d.save();d.translate(-h.x,-h.y);var f=this._invalidRects;if(f==="all"){this._clearOnFullInvalid&&d.clearRect(0,0,this.domNode.width,this.domNode.height);this.drawRect(h,d)}else r.merge(f).forEach(function(m){m=r.intersectRects(m,h); +if(m.width!==0&&m.height!==0){d.save();var i=m.x,g=m.y,j=m.width,q=m.height;d.beginPath();d.moveTo(i,g);d.lineTo(i+j,g);d.lineTo(i+j,g+q);d.lineTo(i,g+q);d.closePath();d.clip();this.drawRect(m,d);d.restore()}},this);d.restore();this._invalidRects=[];this._redrawTimer=null;this._lastRedrawTime=(new Date).getTime()}};Object.defineProperties(s.CanvasView.prototype,{clippingFrame:{get:function(){return this._clippingFrame},set:function(h){h=v.mixin(v.clone(this._clippingFrame),h);if(this._clippingFrame=== +null||!r.rectsEqual(h,this._clippingFrame)){this._clippingFrame=h;this.clippingChanged()}}},frame:{get:function(){return this._frame},set:function(h){var d=this.domNode,f=d.style,m=this._preventDownsize,i=d.width,g=d.height;f=d.style;f.left=h.x+"px";f.top=h.y+"px";var j,q;if(h.width!==i)if(h.width1?{row:n.start.row+T.length-1,col:T[T.length-1].length}:r.addPositions(n.start,{row:0,col:w.length});Q.moveCursorTo(T)}})},getText:function(n){if(!r.isRange(n))throw new Error('getText(): expected range but found "'+n+'"');return this.layoutManager.textStorage.getCharacters(r.normalizeRange(n))},setLineNumber:function(n){if(!h.isNumber(n))throw new Error("setLineNumber(): lineNumber must be a number"); +this.textView.moveCursorTo({row:n-1,col:0})},setCursor:function(n){if(!r.isPosition(n))throw new Error('setCursor(): expected position but found "'+n+'"');this.textView.moveCursorTo(n)},changeGroup:function(n){return this.textView.groupChanges(function(){n(this)}.bind(this))},addTags:function(n){this.completionController.tags.add(n)}};Object.defineProperties(s.EditorView.prototype,{themeData:{get:function(){return this._themeData},set:function(){throw new Error("themeData can't be changed directly. Use themeManager."); +}},font:{get:function(){return this._font},set:function(){throw new Error("font can't be changed directly. Use settings fontsize and fontface.");}},buffer:{set:function(n){if(n!==this._buffer){if(!n.loadPromise.isResolved())throw new Error("buffer.set(): the new buffer must first be loaded!");if(this._buffer!==null){this.layoutManager.sizeChanged.remove(this);this.layoutManager.textStorage.changed.remove(this);this.textView.selectionChanged.remove(this)}this.willChangeBuffer(n);C.publish(this,"editorChange", +"buffer",n);this.layoutManager=n.layoutManager;this._buffer=n;var w=this.layoutManager,D=this.textView;w.sizeChanged.add(this,this._layoutManagerSizeChanged.bind(this));w.textStorage.changed.add(this,this.textChanged.bind(this));D.selectionChanged.add(this,this.selectionChanged.bind(this));this.textView.setSelection(n._selectedRange,false);this.scrollOffsetChanged(n._scrollOffset);this.layoutManager.sizeChanged(this.layoutManager.size);this._recomputeLayout()}},get:function(){return this._buffer}}, +frame:{get:function(){return{width:this.container.offsetWidth,height:this.container.offsetHeight}}},textViewPaddingFrame:{get:function(){var n=h.clone(this.textView.frame),w=this.textView.padding;n.width-=w.left+w.right;n.height-=w.top+w.bottom;return n}},scrollOffset:{set:function(n){if(n.x===undefined)n.x=this.scrollOffset.x;if(n.y===undefined)n.y=this.scrollOffset.y;var w=this.textViewPaddingFrame;if(n.y<0)n.y=0;else if(this._textViewSize.heightthis._textViewSize.height)n.y= +this._textViewSize.height-w.height;if(n.x<0)n.x=0;else if(this._textViewSize.widththis._textViewSize.width)n.x=this._textViewSize.width-w.width;if(!(n.x===this.scrollOffset.x&&n.y===this.scrollOffset.y)){this.buffer._scrollOffset=n;this.scrollOffsetChanged(n);C.publish(this,"editorChange","scrollOffset",n)}},get:function(){return this.buffer._scrollOffset}},readOnly:{get:function(){return this._buffer.model.readOnly},set:function(n){this._buffer.model.readOnly=n}}, +focus:{get:function(){return this.textView.hasFocus},set:function(n){if(!h.isBoolean(n))throw new Error('set focus: expected boolean but found "'+n+'"');this.textView.hasFocus=n}},selection:{get:function(){return h.clone(this.textView.getSelectedRange(false))},set:function(n){if(!r.isRange(n))throw new Error("set selection: position/selection must be supplied");this.textView.setSelection(n)}},selectedText:{get:function(){return this.getText(this.selection)},set:function(n){if(!h.isString(n))throw new Error('set selectedText: expected string but found "'+ +n+'"');return this.replace(this.selection,n)}},value:{get:function(){return this.layoutManager.textStorage.value},set:function(n){if(!h.isString(n))throw new Error('set value: expected string but found "'+n+'"');return this.replace(this.layoutManager.textStorage.range,n,false)}},syntax:{get:function(){return this.layoutManager.syntaxManager.getSyntax()},set:function(n){if(!h.isString(n))throw new Error('set syntax: expected string but found "'+newValue+'"');return this.layoutManager.syntaxManager.setSyntax(n)}}})}); +bespin.tiki.module("text_editor:views/gutter",function(y,s){var v=y("bespin:util/util"),r=y("views/canvas").CanvasView;s.GutterView=function(l,h){r.call(this,l,true);this.editor=h};s.GutterView.prototype=new r;v.mixin(s.GutterView.prototype,{drawRect:function(l,h){var d=this.editor.themeData.gutter;h.fillStyle=d.backgroundColor;h.fillRect(l.x,l.y,l.width,l.height);h.save();h.translate(d.paddingLeft,0);var f=this.editor.layoutManager,m=f.characterRangeForBoundingRect(l);l=Math.min(m.end.row,f.textLines.length- +1);var i=f.fontDimension.lineAscent;h.fillStyle=d.color;h.font=this.editor.font;for(d=m.start.row;d<=l;d++)h.fillText(""+(d+1),-0.5,f.lineRectForRow(d).y+i-0.5);h.restore()},computeWidth:function(){var l=this.editor.themeData.gutter,h=this.editor.layoutManager;return h.fontDimension.characterWidth*(""+h.textLines.length).length+(l.paddingLeft+l.paddingRight)}})}); +bespin.tiki.module("text_editor:views/scroller",function(y,s){var v=y("bespin:util/util"),r=y("events").Event,l=y("bespin:console").console,h=y("utils/rect"),d=y("views/canvas").CanvasView,f=s.LAYOUT_HORIZONTAL=0,m=s.LAYOUT_VERTICAL=1;s.ScrollerCanvasView=function(i,g){d.call(this,i.container,false,true);this.editor=i;this.layoutDirection=g;i=function(j,q,t){t=t||this.domNode;t.addEventListener(j,function(B){q.call(this,B);v.stopEvent(B)}.bind(this),false)}.bind(this);i("mouseover",this.mouseEntered); +i("mouseout",this.mouseExited);i("mousedown",this.mouseDown);i("mouseup",this.mouseUp,window);i("mousemove",this.mouseMove,window);this.valueChanged=new r};s.ScrollerCanvasView.prototype=new d;v.mixin(s.ScrollerCanvasView.prototype,{lineHeight:20,proportion:0,layoutDirection:m,_isVisible:false,_maximum:0,_value:0,valueChanged:null,padding:{left:0,bottom:0,top:0,right:0},_mouseDownScreenPoint:null,_mouseDownValue:null,_isMouseOver:false,_scrollTimer:null,_mouseEventPosition:null,_mouseOverHandle:false, +_drawNib:function(i){var g=this.editor.themeData.scroller,j,q;j=g.nibStyle;q=g.nibArrowStyle;g=g.nibStrokeStyle;var t=Math.floor(7.5);i.fillStyle=j;i.beginPath();i.arc(0,0,Math.floor(7.5),0,Math.PI*2,true);i.closePath();i.fill();i.strokeStyle=g;i.stroke();i.fillStyle=q;i.beginPath();i.moveTo(0,-t+3);i.lineTo(-t+3,t-5);i.lineTo(t-3,t-5);i.closePath();i.fill()},_drawNibs:function(i,g){var j=this._getClientThickness(),q=this._value,t=this._maximum,B=this._isHighlighted();if(B||q!==0){i.save();i.translate(8, +j/2);i.rotate(Math.PI*1.5);i.moveTo(0,0);this._drawNib(i,g);i.restore()}if(B||q!==t){i.save();i.translate(this._getClientLength()-8,j/2);i.rotate(Math.PI*0.5);i.moveTo(0,0);this._drawNib(i,g);i.restore()}},_getClientFrame:function(){var i=this.frame,g=this.padding;return{x:g.left,y:g.top,width:i.width-(g.left+g.right),height:i.height-(g.top+g.bottom)}},_getClientLength:function(){var i=this._getClientFrame();switch(this.layoutDirection){case f:return i.width;case m:return i.height;default:l.error("unknown layout direction"); +return null}},_getClientThickness:function(){var i=this.padding,g=this.editor.themeData.scroller.thickness;switch(this.layoutDirection){case m:return g-(i.left+i.right);case f:return g-(i.top+i.bottom);default:l.error("unknown layout direction");return null}},_getFrameLength:function(){switch(this.layoutDirection){case f:return this.frame.width;case m:return this.frame.height;default:l.error("unknown layout direction");return null}},_getGutterFrame:function(){var i=this._getClientFrame(),g=this._getClientThickness(); +switch(this.layoutDirection){case m:return{x:i.x,y:i.y+15,width:g,height:Math.max(0,i.height-30)};case f:return{x:i.x+15,y:i.y,width:Math.max(0,i.width-30),height:g};default:l.error("unknown layout direction");return null}},_getGutterLength:function(){var i=this._getGutterFrame(),g;switch(this.layoutDirection){case f:g=i.width;break;case m:g=i.height;break;default:l.error("unknown layout direction");break}return g},_getHandleFrame:function(){var i=this._getGutterFrame(),g=this._getHandleOffset(), +j=this._getHandleLength();switch(this.layoutDirection){case m:return{x:i.x,y:i.y+g,width:i.width,height:j};case f:return{x:i.x+g,y:i.y,width:j,height:i.height}}},_getHandleLength:function(){var i=this._getGutterLength();return Math.max(i*this.proportion,20)},_getHandleOffset:function(){var i=this._maximum;if(i===0)return 0;var g=this._getGutterLength(),j=this._getHandleLength();return(g-j)*this._value/i},_isHighlighted:function(){return this._isMouseOver===true||this._mouseDownScreenPoint!==null}, +_segmentForMouseEvent:function(i){i={x:i.layerX,y:i.layerY};var g=this._getClientFrame(),j=this.padding;if(!h.pointInRect(i,g))return null;var q=this.layoutDirection;switch(q){case f:if(i.x-j.left<15)return"nib-start";else if(i.x>=g.width-15)return"nib-end";break;case m:if(i.y-j.top<15)return"nib-start";else if(i.y>=g.height-15)return"nib-end";break;default:l.error("unknown layout direction");break}j=this._getHandleFrame();if(h.pointInRect(i,j))return"handle";switch(q){case f:if(i.x=j.x+j.width)return"gutter-after";break;case m:if(i.y=j.y+j.height)return"gutter-after";break;default:l.error("unknown layout direction");break}l.error("_segmentForMouseEvent: point ",i," outside view with handle frame ",j," and client frame ",g);return null},adjustFrame:function(){var i=this.frame;this.set("layout",{left:0,top:0,width:i.width,height:i.height})},drawRect:function(i,g){if(this._isVisible){var j=this._isHighlighted();i=this.editor.themeData.scroller; +var q=j?i.fullAlpha:i.particalAlpha,t=this.frame;g.clearRect(0,0,t.width,t.height);g.save();t=this.padding;g.translate(t.left,t.top);this._getHandleFrame();t=this._getGutterLength();var B=this._getClientThickness(),C=B/2,e=this.layoutDirection,K=this._getHandleOffset()+15,L=this._getHandleLength();if(e===m){g.translate(B+1,0);g.rotate(Math.PI*0.5)}if(!(t<=L)){g.globalAlpha=q;if(j){j=this._getClientLength();g.fillStyle=i.trackFillStyle;g.fillRect(8.5,0.5,j-16,B-1);g.strokeStyle=i.trackStrokeStyle; +g.strokeRect(8.5,0.5,j-16,B-1)}j=function(){g.beginPath();g.arc(K+C+0.5,C,C-0.5,Math.PI/2,3*Math.PI/2,false);g.arc(K+L-C-0.5,C,C-0.5,3*Math.PI/2,Math.PI/2,false);g.lineTo(K+C+0.5,B-0.5);g.closePath()};j();t=g.createLinearGradient(K,0,K,B);t.addColorStop(0,i.barFillGradientTopStart);t.addColorStop(0.4,i.barFillGradientTopStop);t.addColorStop(0.41,i.barFillStyle);t.addColorStop(0.8,i.barFillGradientBottomStart);t.addColorStop(1,i.barFillGradientBottomStop);g.fillStyle=t;g.fill();g.save();g.clip();g.fillStyle= +i.barFillStyle;g.beginPath();g.moveTo(K+C*0.4,C*0.6);g.lineTo(K+C*0.9,B*0.4);g.lineTo(K,B*0.4);g.closePath();g.fill();g.beginPath();g.moveTo(K+L-C*0.4,0+C*0.6);g.lineTo(K+L-C*0.9,0+B*0.4);g.lineTo(K+L,0+B*0.4);g.closePath();g.fill();g.restore();g.save();j();g.strokeStyle=i.trackStrokeStyle;g.stroke();g.restore();this._drawNibs(g,q);g.restore()}}},_repeatAction:function(i,g){if(i()!==false){var j=function(){this._repeatAction(i,100)}.bind(this);this._scrollTimer=setTimeout(j,g)}},_scrollByDelta:function(i){this.value= +this._value+i},_scrollUpOneLine:function(){this._scrollByDelta(-this.lineHeight);return true},_scrollDownOneLine:function(){this._scrollByDelta(this.lineHeight);return true},_scrollPage:function(){switch(this._segmentForMouseEvent(this._mouseEventPosition)){case "gutter-before":this._scrollByDelta(this._getGutterLength()*-1);break;case "gutter-after":this._scrollByDelta(this._getGutterLength());break;case null:break;default:return false}return true},mouseDown:function(i){this._mouseEventPosition= +i;this._mouseOverHandle=false;this._getGutterLength();switch(this._segmentForMouseEvent(i)){case "nib-start":this._repeatAction(this._scrollUpOneLine.bind(this),500);break;case "nib-end":this._repeatAction(this._scrollDownOneLine.bind(this),500);break;case "gutter-before":this._repeatAction(this._scrollPage.bind(this),500);break;case "gutter-after":this._repeatAction(this._scrollPage.bind(this),500);break;case "handle":break;default:l.error("_segmentForMouseEvent returned an unknown value");break}switch(this.layoutDirection){case f:this._mouseDownScreenPoint= +i.pageX;break;case m:this._mouseDownScreenPoint=i.pageY;break;default:l.error("unknown layout direction");break}},mouseMove:function(i){if(this._mouseDownScreenPoint!==null){if(this._segmentForMouseEvent(i)=="handle"||this._mouseOverHandle===true){this._mouseOverHandle=true;if(this._scrollTimer!==null){clearTimeout(this._scrollTimer);this._scrollTimer=null}var g;switch(this.layoutDirection){case f:g=i.pageX;break;case m:g=i.pageY;break;default:l.error("unknown layout direction");break}var j=g-this._mouseDownScreenPoint, +q=this._maximum,t=this._value,B=this._getGutterLength(),C=this._getHandleLength();this.value=t+q*j/(B-C);this._mouseDownScreenPoint=g}this._mouseEventPosition=i}},mouseEntered:function(){this._isMouseOver=true;this.invalidate()},mouseExited:function(){this._isMouseOver=false;this.invalidate()},mouseUp:function(){this._mouseDownValue=this._mouseDownScreenPoint=null;if(this._scrollTimer){clearTimeout(this._scrollTimer);this._scrollTimer=null}this.invalidate()}});Object.defineProperties(s.ScrollerCanvasView.prototype, +{isVisible:{set:function(i){if(this._isVisible!==i){this._isVisible=i;this.domNode.style.display=i?"block":"none";i&&this.invalidate()}}},maximum:{set:function(i){if(this._value>this._maximum)this._value=this._maximum;if(i!==this._maximum){this._maximum=i;this.invalidate()}}},value:{set:function(i){if(i<0)i=0;else if(i>this._maximum)i=this._maximum;if(i!==this._value){this._value=i;this.valueChanged(i);this.invalidate()}}}})}); +bespin.tiki.module("text_editor:views/text",function(y,s){var v=y("bespin:plugins").catalog,r=y("bespin:util/util"),l=y("events").Event,h=y("views/canvas").CanvasView;y("controllers/layoutmanager");var d=y("rangeutils:utils/range"),f=y("utils/rect"),m=y("views/textinput").TextInput,i=y("bespin:console").console,g=y("settings").settings;s.TextView=function(j,q){h.call(this,j,true);this.editor=q;this.textInput=new m(j,this);this.padding={top:0,bottom:30,left:0,right:30};this.clippingChanged.add(this.clippingFrameChanged.bind(this)); +j=this.domNode;j.style.cursor="text";j.addEventListener("mousedown",this.mouseDown.bind(this),false);j.addEventListener("mousemove",this.mouseMove.bind(this),false);window.addEventListener("mouseup",this.mouseUp.bind(this),false);q.willChangeBuffer.add(this.editorWillChangeBuffer.bind(this));this.selectionChanged=new l;this.beganChangeGroup=new l;this.endedChangeGroup=new l;this.willReplaceRange=new l;this.replacedCharacters=new l};s.TextView.prototype=new h;r.mixin(s.TextView.prototype,{_dragPoint:null, +_dragTimer:null,_enclosingScrollView:null,_inChangeGroup:false,_insertionPointBlinkTimer:null,_insertionPointVisible:true,_keyBuffer:"",_keyMetaBuffer:"",_keyState:"start",_hasFocus:false,_mouseIsDown:false,selectionChanged:null,beganChangeGroup:null,endedChangeGroup:null,willReplaceRange:null,replacedCharacters:null,editorWillChangeBuffer:function(j){if(this.editor.layoutManager){var q=this.editor.layoutManager;q.invalidatedRects.remove(this);q.changedTextAtRow.remove(this)}q=j.layoutManager;q.invalidatedRects.add(this, +this.layoutManagerInvalidatedRects.bind(this));q.changedTextAtRow.add(this,this.layoutManagerChangedTextAtRow.bind(this))},didFocus:function(){this._setFocus(true,true)},didBlur:function(){this._setFocus(false,true)},_drag:function(){var j=this._dragPoint,q=f.offsetFromRect(this.clippingFrame,j);this.moveCursorTo(this._selectionPositionForPoint({x:j.x-q.x,y:j.y-q.y}),true)},_drawInsertionPoint:function(j,q){if(this._insertionPointVisible){var t=this.editor.layoutManager.characterRectForPosition(this.editor.buffer._selectedRange.start); +j=Math.floor(t.x);var B=t.y,C=Math.ceil(t.width);t=t.height;q.save();var e=this.editor.themeData.editor;if(this._hasFocus){q.strokeStyle=e.cursorColor;q.beginPath();q.moveTo(j+0.5,B);q.lineTo(j+0.5,B+t);q.closePath();q.stroke()}else{q.fillStyle=e.unfocusedCursorBackgroundColor;q.fillRect(j+0.5,B,C-0.5,t);q.strokeStyle=e.unfocusedCursorColor;q.strokeRect(j+0.5,B+0.5,C-1,t-1)}q.restore()}},_drawLines:function(j,q){var t=this.editor.layoutManager,B=t.textLines,C=t.fontDimension.lineAscent,e=this.editor.themeData.highlighter; +q.save();q.font=this.editor.font;var K=t.characterRangeForBoundingRect(j),L=K.start;K=K.end;for(var n=K.row,w=L.row;w<=n;w++){var D=B[w];if(!r.none(D)){var J=D.characters,Q=J.length,Z=Math.min(K.col,Q),T=L.col;if(!(T>=Q)){D=D.colors;if(D==null)D=[];for(Q=0;Q1?{row:q.start.row+t.length-1,col:t[t.length-1].length}:d.addPositions(q.start,{row:0,col:j.length}))}.bind(this));return true},isDelimiter:function(j){return"\"',;.!~@#$%^&*?[]<>():/\\-+ \t".indexOf(j)!==-1},keyDown:function(j){if(j.charCode===0||j._charCode===0)return this.editor.processKeyEvent(j,this,{isTextView:true});else if(j.keyCode=== +9)j.preventDefault();else return false},layoutManagerChangedTextAtRow:function(){this._repositionSelection()},layoutManagerInvalidatedRects:function(j,q){q.forEach(this.invalidateRect,this)},mouseDown:function(j){r.stopEvent(j);this._mouseIsDown=this.hasFocus=true;var q=this.computeWithClippingFrame(j.layerX,j.layerY);r.mixin(q,{layerX:j.layerX,layerY:j.layerY});switch(j.detail){case 1:var t=this._selectionPositionForPoint(q);this.moveCursorTo(t,j.shiftKey);break;case 2:t=this._selectionPositionForPoint(q); +var B=this.editor.layoutManager.textStorage.lines[t.row];if(B.length===0)return true;t.col-=t.col==B.length?1:0;var C=!this.isDelimiter(B[t.col]),e=this,K=function(L,n){for(;L>-1&&L0&&t=e+30&&j+B=K&&t+ +q>>0,i=0;i>>0,i=new Array(m),g=0;g>>0,m=0;if(f===0&&arguments.length===1)throw new TypeError; +if(arguments.length>=2)var i=arguments[1];else{do{if(m in this){i=this[m++];break}if(++m>=f)throw new TypeError;}while(1)}for(;m=m)return-1;if(f<0)f+=m;for(;f=B+t[j].length&&j0){i=i.replace(/\/\*(?:[^*]|\*+[^\/*])*\*+\//g,function(ca){return e.optimization>1?"":ca.replace(/\n(\s*\n)+/g,"\n")});t=i.split(/^(?=\n)/mg)}else t=[i];D=new h.Ruleset([],f(this.parsers.primary));D.root=true;D.toCSS=function(ca){var ha,ga;return function(){try{return ca.call(this)}catch(la){ga= +i.split("\n");ha=(i.slice(0,la.index).match(/\n/g)||"").length+1;for(var ma=la.index,na=-1;ma>=0&&i.charAt(ma)!=="\n";ma--)na++;throw{name:"NameError",message:la.message,line:ha,column:na,extract:[ga[ha-2],ga[ha-1],ga[ha]]};}}}(D.toCSS);if(g=0&&i.charAt(Z)!=="\n";Z--)T++;Q={name:"ParseError",message:"Syntax Error on line "+n,filename:d.filename,line:n,column:T,extract:[J[n-2],J[n-1],J[n]]}}if(this.imports.queue.length> +0)K=function(){w(Q,D)};else w(Q,D)},parsers:{primary:function(){for(var n,w=[];n=f(this.mixin.definition)||f(this.rule)||f(this.ruleset)||f(this.mixin.call)||f(this.comment)||f(/[\n\s]+/g)||f(this.directive);)w.push(n);return w},comment:function(){var n;if(i.charAt(g)==="/")return(n=f(/\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/g))?new h.Comment(n):f(/\/\/.*/g)},entities:{quoted:function(){var n;if(!(i.charAt(g)!=='"'&&i.charAt(g)!=="'"))if(n=f(/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/g))return new h.Quoted(n[0], +n[1]||n[2])},keyword:function(){var n;if(n=f(/[A-Za-z-]+/g))return new h.Keyword(n)},call:function(){var n,w;if(n=f(/([a-zA-Z0-9_-]+|%)\(/g)){if(n[1].toLowerCase()==="alpha")return f(this.alpha);w=f(this.entities.arguments);if(f(")"))if(n)return new h.Call(n[1],w)}},arguments:function(){for(var n=[],w;w=f(this.expression);){n.push(w);if(!f(","))break}return n},literal:function(){return f(this.entities.dimension)||f(this.entities.color)||f(this.entities.quoted)},url:function(){var n;if(!(i.charAt(g)!== +"u"||!f(/url\(/g))){n=f(this.entities.quoted)||f(/[-a-zA-Z0-9_%@$\/.&=:;#+?]+/g);if(!f(")"))throw new Error("missing closing ) for url()");return new h.URL(n.value?n:new h.Anonymous(n))}},variable:function(){var n,w=g;if(i.charAt(g)==="@"&&(n=f(/@[a-zA-Z0-9_-]+/g)))return new h.Variable(n,w)},color:function(){var n;if(i.charAt(g)==="#"&&(n=f(/#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/g)))return new h.Color(n[1])},dimension:function(){var n;n=i.charCodeAt(g);if(!(n>57||n<45||n===47))if(n=f(/(-?[0-9]*\.?[0-9]+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm)?/g))return new h.Dimension(n[1], +n[2])}},variable:function(){var n;if(i.charAt(g)==="@"&&(n=f(/(@[a-zA-Z0-9_-]+)\s*:/g)))return n[1]},shorthand:function(){var n,w;if(m(/[@\w.-]+\/[@\w.-]+/g))if((n=f(this.entity))&&f("/")&&(w=f(this.entity)))return new h.Shorthand(n,w)},mixin:{call:function(){for(var n=[],w,D,J,Q=g;w=f(/[#.][a-zA-Z0-9_-]+/g);){n.push(new h.Element(D,w));D=f(">")}f("(")&&(J=f(this.entities.arguments))&&f(")");if(n.length>0&&(f(";")||m("}")))return new h.mixin.Call(n,J,Q)},definition:function(){var n,w=[],D,J;if(!(i.charAt(g)!== +"."||m(/[^{]*(;|})/g)))if(n=f(/([#.][a-zA-Z0-9_-]+)\s*\(/g)){for(n=n[1];D=f(/@[\w-]+/g)||f(this.entities.literal)||f(this.entities.keyword);){if(D[0]==="@")if(f(":"))if(J=f(this.expression))w.push({name:D,value:J});else throw new Error("Expected value");else w.push({name:D});else w.push({value:D});if(!f(","))break}if(!f(")"))throw new Error("Expected )");if(D=f(this.block))return new h.mixin.Definition(n,w,D)}}},entity:function(){return f(this.entities.literal)||f(this.entities.variable)||f(this.entities.url)|| +f(this.entities.call)||f(this.entities.keyword)},end:function(){return f(";")||m("}")},alpha:function(){var n;if(f(/opacity=/gi))if(n=f(/[0-9]+/g)||f(this.entities.variable)){if(!f(")"))throw new Error("missing closing ) for alpha()");return new h.Alpha(n)}},element:function(){var n;c=f(this.combinator);if(n=f(/[.#:]?[a-zA-Z0-9_-]+/g)||f("*")||f(this.attribute)||f(/\([^)@]+\)/g))return new h.Element(c,n)},combinator:function(){var n;return(n=f(/[+>~]/g)||f("&")||f(/::/g))?new h.Combinator(n):new h.Combinator(i.charAt(g- +1)===" "?" ":null)},selector:function(){for(var n,w=[];n=f(this.element);)w.push(n);if(w.length>0)return new h.Selector(w)},tag:function(){return f(/[a-zA-Z][a-zA-Z-]*[0-9]?/g)||f("*")},attribute:function(){var n="",w,D,J;if(f("[")){if(w=f(/[a-z-]+/g)||f(this.entities.quoted))n=(J=f(/[|~*$^]?=/g))&&(D=f(this.entities.quoted)||f(/[\w-]+/g))?[w,J,D.toCSS?D.toCSS():D].join(""):w;if(f("]"))if(n)return"["+n+"]"}},block:function(){var n;if(f("{")&&(n=f(this.primary))&&f("}"))return n},ruleset:function(){var n= +[],w,D,J=g;if(w=m(/([a-z.#: _-]+)[\s\n]*\{/g)){g+=w[0].length-1;n=[new h.Selector([new h.Element(null,w[1])])]}else{for(;w=f(this.selector);){n.push(w);if(!f(","))break}w&&f(this.comment)}if(n.length>0&&(D=f(this.block)))return new h.Ruleset(n,D);else{q=g;g=J}},rule:function(){var n,w=g;if(name=f(this.property)||f(this.variable)){if(name.charAt(0)!="@"&&(match=m(/([^@+\/*(;{}-]*);/g))){g+=match[0].length-1;n=new h.Anonymous(match[1])}else n=name==="font"?f(this.font):f(this.value);if(f(this.end))return new h.Rule(name, +n,w);else{q=g;g=w}}},"import":function(){var n;if(f(/@import\s+/g)&&(n=f(this.entities.quoted)||f(this.entities.url))&&f(";"))return new h.Import(n,L)},directive:function(){var n,w,D;if(i.charAt(g)==="@")if(w=f(this["import"]))return w;else if(n=f(/@media|@page/g)){D=f(/[^{]+/g).trim();if(w=f(this.block))return new h.Directive(n+" "+D,w)}else if(n=f(/@[-a-z]+/g))if(n==="@font-face"){if(w=f(this.block))return new h.Directive(n,w)}else if((w=f(this.entity))&&f(";"))return new h.Directive(n,w)},font:function(){for(var n= +[],w=[],D;D=f(this.shorthand)||f(this.entity);)w.push(D);n.push(new h.Expression(w));if(f(","))for(;D=f(this.expression);){n.push(D);if(!f(","))break}return new h.Value(n,f(this.important))},value:function(){for(var n,w=[];n=f(this.expression);){w.push(n);if(!f(","))break}n=f(this.important);if(w.length>0)return new h.Value(w,n)},important:function(){return f(/!\s*important/g)},sub:function(){var n;if(f("(")&&(n=f(this.expression))&&f(")"))return n},multiplication:function(){var n,w,D,J;if(n=f(this.operand)){for(;(D= +f(/[\/*]/g))&&(w=f(this.operand));)J=new h.Operation(D,[J||n,w]);return J||n}},addition:function(){var n,w,D,J;if(n=f(this.multiplication)){for(;(D=f(/[-+]\s+/g)||i.charAt(g-1)!=" "&&f(/[-+]/g))&&(w=f(this.multiplication));)J=new h.Operation(D,[J||n,w]);return J||n}},operand:function(){return f(this.sub)||f(this.entities.dimension)||f(this.entities.color)||f(this.entities.variable)},expression:function(){for(var n,w=[];n=f(this.addition)||f(this.entity);)w.push(n);if(w.length>0)return new h.Expression(w)}, +property:function(){var n;if(n=f(/(\*?-?[-a-z_0-9]+)\s*:/g))return n[1]}}}};l.Parser.importer=null;h.functions={rgb:function(d,f,m){return this.rgba(d,f,m,1)},rgba:function(d,f,m,i){d=[d,f,m].map(function(g){return v(g)});i=v(i);return new h.Color(d,i)},hsl:function(d,f,m){return this.hsla(d,f,m,1)},hsla:function(d,f,m,i){function g(t){t=t<0?t+1:t>1?t-1:t;return t*6<1?q+(j-q)*t*6:t*2<1?j:t*3<2?q+(j-q)*(2/3-t)*6:q}d=(v(d)%360+360)%360/360;f=v(f);m=v(m);i=v(i);var j=m<=0.5?m*(f+1):m+f-m*f,q=m*2-j;return this.rgba(g(d+ +1/3)*255,g(d)*255,g(d-1/3)*255,i)},opacity:function(d,f){v(f);return new h.Color(d.rgb,v(f))},saturate:function(d,f){d=d.toHSL();d.s+=f.value/100;d.s=r(d.s);return this.hsl(d.h,d.s,d.l)},desaturate:function(d,f){d=d.toHSL();d.s-=f.value/100;d.s=r(d.s);return this.hsl(d.h,d.s,d.l)},lighten:function(d,f){d=d.toHSL();d.l*=1+f.value/100;d.l=r(d.l);return this.hsl(d.h,d.s,d.l)},darken:function(d,f){d=d.toHSL();d.l*=1-f.value/100;d.l=r(d.l);return this.hsl(d.h,d.s,d.l)},greyscale:function(d){return this.desaturate(d, +new h.Dimension(100))},e:function(d){return new h.Anonymous(d)},"%":function(d){for(var f=Array.prototype.slice.call(arguments,1),m=d.content,i=0;i255?255:d<0?0:d).toString(16);return d.length===1?"0"+d:d}).join("")},operate:function(d,f){var m=[];f instanceof h.Color||(f=f.toColor());for(var i=0;i<3;i++)m[i]=h.operate(d,this.rgb[i],f.rgb[i]);return new h.Color(m)},toHSL:function(){var d= +this.rgb[0]/255,f=this.rgb[1]/255,m=this.rgb[2]/255,i=Math.max(d,f,m),g=Math.min(d,f,m),j,q=(i+g)/2,t=i-g;if(i===g)j=g=0;else{g=q>0.5?t/(2-i-g):t/(i+g);switch(i){case d:j=(f-m)/t+(f":return" > "}};h.Expression=function(d){this.value=d};h.Expression.prototype={eval:function(d){return this.value.length>1?new h.Expression(this.value.map(function(f){return f.eval(d)})):this.value[0].eval(d)},toCSS:function(){return this.value.map(function(d){return d.toCSS()}).join(" ")}}; +h.Import=function(d,f){var m=this;this._path=d;this.path=d instanceof h.Quoted?/\.(le?|c)ss$/.test(d.content)?d.content:d.content+".less":d.value.content||d.value;(this.css=/css$/.test(this.path))||f.push(this.path,function(i){m.root=i})};h.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(){if(this.css)return this;else{for(var d=0;d0){for(g=0;g1?Array.prototype.push.apply(m,g.find(new h.Selector(d.elements.slice(1)),f)):m.push(g);break}});return this._lookups[i]=m},toCSS:function(d,f){var m=[],i=[],g=[],j=[];if(this.root){d=[];f={frames:[]};for(var q=0;q0){j=j.map(function(B){return B.map(function(C){return C.toCSS()}).join("").trim()}).join(j.length>3?",\n":", ");m.push(j," {\n "+i.join("\n ")+"\n}\n")}m.push(g);f.frames.shift();return m.join("")}};h.Selector=function(d){this.elements=d;if(this.elements[0].combinator.value=== +"")this.elements[0].combinator.value=" "};h.Selector.prototype.match=function(d){return this.elements[0].value===d.elements[0].value?true:false};h.Selector.prototype.toCSS=function(){if(this._css)return this._css;return this._css=this.elements.map(function(d){return typeof d==="string"?" "+d.trim():d.toCSS()}).join("")};h.URL=function(d){this.value=d};h.URL.prototype={toCSS:function(){return"url("+this.value.toCSS()+")"},eval:function(){return this}};h.Variable=function(d,f){this.name=d;this.index= +f};h.Variable.prototype={eval:function(d){var f,m,i=this.name;if(f=h.find(d.frames,function(g){if(m=g.variable(i))return m.value.eval(d)}))return f;else throw{message:"variable "+this.name+" is undefined",index:this.index};}};h.find=function(d,f){for(var m=0,i;m=200&&L.status<300)e(L.responseText, +L.getResponseHeader("Last-Modified"));else typeof K==="function"&&K(L.status)};L.send(null)}}function g(){if(window.XMLHttpRequest)return new XMLHttpRequest;else try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(C){j("less: browser doesn't support AJAX.");return null}}function j(C){l.env=="development"&&typeof console!=="undefined"&&console.log(C)}function q(C,e){var K=document.createElement("div"),L;K.id="less-error-message";K.innerHTML="

"+(C.message||"There is an error in your .less file")+ +'

'+e+" on line "+C.line+", column "+(C.column+1)+":

"+'
\n
[-1]{0}
\n
[0]{current}
\n
[1]{2}
\n
'.replace(/\[(-?\d)\]/g,function(n,w){return C.line+parseInt(w)}).replace(/\{(\d)\}/g,function(n,w){return C.extract[parseInt(w)]}).replace(/\{current\}/,C.extract[1].slice(0,C.column)+''+C.extract[1].slice(C.column)+"");m("#less-error-message span {margin-right: 15px;}#less-error-message pre {color: #ee4444;padding: 4px 0;margin: 0;}#less-error-message pre.ctx {color: #dd7777;}#less-error-message h3 {padding: 15px 0 5px 0;margin: 0;}#less-error-message a {color: #10a}#less-error-message .error {color: red;font-weight: bold;padding-bottom: 2px;border-bottom: 1px dashed red;}"); +K.style.cssText="font-family: Arial, sans-serif;border: 1px solid #e00;background-color: #eee;border-radius: 5px;color: #e00;padding: 15px;margin-bottom: 15px";if(l.env=="development")L=setInterval(function(){if(document.body){document.body.insertBefore(K,document.body.childNodes[0]);clearInterval(L)}},10)}var t=[];l.env=location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.protocol=="file:"?"development":"production";var B=setInterval(function(){if(document.body){if(!document.querySelectorAll&& +typeof jQuery==="undefined")j("No selector method found");else t=(document.querySelectorAll||jQuery).call(document,'link[rel="stylesheet/less"]');clearInterval(B);d(function(C,e,K){m(C.toCSS(),e,K.lastModified);K.local?j("less: loading "+e.href+" from local storage."):j("less: parsed "+e.href+" successfully.")})}},10);if(l.env==="development")refreshTimer=setInterval(function(){/!refresh/.test(location.hash)&&d(function(C,e,K){m(C.toCSS(),e,K)})},1E3);l.Parser.importer=function(C,e,K){f({href:C,title:C}, +function(L){K(L)})}})()});bespin.tiki.register("::theme_manager_base",{name:"theme_manager_base",dependencies:{}});bespin.tiki.module("theme_manager_base:index",function(){});bespin.tiki.register("::canon",{name:"canon",dependencies:{environment:"0.0.0",events:"0.0.0",settings:"0.0.0"}}); +bespin.tiki.module("canon:history",function(y,s){var v=y("bespin:util/stacktrace").Trace,r=y("bespin:plugins").catalog;s.requests=[];s.addRequestOutput=function(l){for(s.requests.push(l);s.requests.length>100;)s.requests.shiftObject();r.publish(this,"addedRequestOutput",null,l)};s.execute=function(l,h){if(h.command)try{h.command(l,h)}catch(d){var f=new v(d,true);console.group("Error executing command '"+h.typed+"'");console.log("command=",h.commandExt);console.log("args=",l);console.error(d);f.log(3); +console.groupEnd();h.doneWithError(d)}else h.doneWithError("Command not found.")}}); +bespin.tiki.module("canon:request",function(y,s){var v=y("events").Event,r=y("canon:history");s.Request=function(l){l=l||{};this.command=l.command;this.commandExt=l.commandExt;this.args=l.args;this.typed=l.typed;this._begunOutput=false;this.start=new Date;this.end=null;this.error=this.completed=false;this.changed=new v};s.Request.prototype._beginOutput=function(){this._begunOutput=true;this.outputs=[];r.addRequestOutput(this)};s.Request.prototype.doneWithError=function(l){this.error=true;this.done(l)}; +s.Request.prototype.async=function(){this._begunOutput||this._beginOutput()};s.Request.prototype.output=function(l){this._begunOutput||this._beginOutput();if(typeof l!=="string"&&!(l instanceof Node))l=l.toString();this.outputs.push(l);this.changed();return this};s.Request.prototype.done=function(l){this.completed=true;this.end=new Date;this.duration=this.end.getTime()-this.start.getTime();l?this.output(l):this.changed()}});bespin.tiki.module("canon:index",function(){}); +bespin.tiki.register("::traits",{name:"traits",dependencies:{}}); +bespin.tiki.module("traits:index",function(y,s){s.Trait=function(){function v(O){var P=function(){throw new Error("Conflicting property: "+O);};T(P.prototype);return T(P)}function r(){return T({value:undefined,enumerable:false,required:true})}function l(O){O=v(O);return n?T({get:O,set:O,enumerable:false,conflict:true}):T({value:O,enumerable:false,conflict:true})}function h(O,P){return O===P?O!==0||1/O===1/P:O!==O&&P!==P}function d(O,P){return O.conflict&&P.conflict?true:O.get===P.get&&O.set===P.set&& +h(O.value,P.value)&&O.enumerable===P.enumerable&&O.required===P.required&&O.conflict===P.conflict}function f(O,P){return T(D(O,P))}function m(O){var P={};Z(O,function(S){P[S]=true});return T(P)}function i(O){var P={};Z(ca(O),function(S){var U=ha(O,S);if(U.value===na)U=r(S);else if(typeof U.value==="function"){U.method=true;"prototype"in U.value&&T(U.value.prototype)}else{U.get&&U.get.prototype&&T(U.get.prototype);U.set&&U.set.prototype&&T(U.set.prototype)}P[S]=U});return P}function g(){var O=Q(arguments, +0),P={};Z(O,function(S){Z(ca(S),function(U){var aa=S[U];if(J(P,U)&&!P[U].required)aa.required||d(P[U],aa)||(P[U]=l(U));else P[U]=aa})});return T(P)}function j(O,P){var S=m(O),U={};Z(ca(P),function(aa){U[aa]=!J(S,aa)||P[aa].required?P[aa]:r(aa)});return T(U)}function q(){var O=Q(arguments,0),P={};Z(O,function(S){Z(ca(S),function(U){var aa=S[U];if(!J(P,U)||P[U].required)P[U]=aa})});return T(P)}function t(O,P){var S={};Z(ca(P),function(U){if(J(O,U)&&!P[U].required){var aa=O[U];S[aa]=J(S,aa)&&!S[aa].required? +l(aa):P[U];J(S,U)||(S[U]=r(U))}else if(J(S,U))P[U].required||(S[U]=l(U));else S[U]=P[U]});return T(S)}function B(O,P){var S={},U=[];for(var aa in O)if(J(O,aa))if(O[aa])S[aa]=O[aa];else U.push(aa);return t(S,j(U,P))}function C(O,P){var S=ma(O),U={};Z(ca(P),function(aa){var fa=P[aa];if(fa.required&&!(aa in O))throw new Error("Missing required property: "+aa);else if(fa.conflict)throw new Error("Remaining conflicting property: "+aa);else U[aa]="value"in fa?fa.method?{value:f(fa.value,S),enumerable:fa.enumerable, +configurable:fa.configurable,writable:fa.writable}:fa:{get:fa.get?f(fa.get,S):undefined,set:fa.set?f(fa.set,S):undefined,enumerable:fa.enumerable,configurable:fa.configurable,writable:fa.writable}});la(S,U);return T(S)}function e(O,P){return C(Object.prototype,i(O),P)}function K(O,P){var S=ca(O),U=ca(P);if(S.length!==U.length)return false;for(var aa=0;aa"}});if(!Object.create)Object.create=ma;if(!Object.getOwnProperties)Object.getOwnProperties= +w;L.required=T(na);L.compose=T(g);L.resolve=T(B);L.override=T(q);L.create=T(C);L.eqv=T(K);L.object=T(e);return T(L)}()});bespin.tiki.register("::keyboard",{name:"keyboard",dependencies:{canon:"0.0.0",settings:"0.0.0"}}); +bespin.tiki.module("keyboard:keyboard",function(y,s){var v=y("bespin:plugins").catalog;y("bespin:console");y("bespin:util/stacktrace");var r=y("bespin:util/util"),l=y("settings").settings,h=y("keyboard:keyutil"),d=y("canon:history"),f=y("canon:request").Request,m=y("environment").env;s.buildFlags=function(i){i.context=m.contexts[0];return i};y=function(){};r.mixin(y.prototype,{_customKeymappingCache:{states:{}},processKeyEvent:function(i,g,j){i=h.commandCodes(i,true)[0];if(r.none(i))return false; +s.buildFlags(j);j.isCommandKey=true;return this._matchCommand(i,g,j)},_matchCommand:function(i,g,j){var q=this._findCommandExtension(i,g,j);if(q&&q.commandExt!=="no command"){j.isTextView&&g.resetKeyBuffers();var t=q.commandExt;t.load(function(B){B=new f({command:B,commandExt:t});d.execute(q.args,B)});return true}return q&&q.commandExt==="no command"?true:false},_buildBindingsRegex:function(i){i.forEach(function(g){if(r.none(g.key))if(Array.isArray(g.regex)){g.key=new RegExp("^"+g.regex[1]+"$");g.regex= +new RegExp(g.regex.join("")+"$")}else g.regex=new RegExp(g.regex+"$");else g.key=new RegExp("^"+g.key+"$")})},_buildKeymappingRegex:function(i){for(state in i.states)this._buildBindingsRegex(i.states[state]);i._convertedRegExp=true},_findCommandExtension:function(i,g,j){if(j.isTextView){var q=g._keyState;if(!j.isCommandKey||i.indexOf("alt_")===-1){g._keyBuffer+=i.replace(/ctrl_meta|meta/,"ctrl");g._keyMetaBuffer+=i}var t=[this._customKeymappingCache];t=t.concat(v.getExtensions("keymapping"));for(var B= +0;B47&&d<58)j=l.altKey}if(m){if(l.altKey)g+="alt_";if(l.ctrlKey)g+="ctrl_";if(l.metaKey)g+="meta_"}else if(l.ctrlKey||l.metaKey)return false}if(!m){d=l.which;i=m=String.fromCharCode(d);d=m.toLowerCase();if(l.metaKey){g="meta_";m=d}else m=null}if(l.shiftKey&&m&&j)g+="shift_";if(m)m=g+m;if(!h&&m)m=m.replace(/ctrl_meta|meta/,"ctrl");return[m,i]};s.addKeyDownListener=function(l,h){var d= +function(f){var m=h(f);m&&v.stopEvent(f);return m};l.addEventListener("keydown",function(f){if(v.isMozilla)if(s.KeyHelper.FUNCTION_KEYS[f.keyCode])return true;else if((f.ctrlKey||f.metaKey)&&s.KeyHelper.PRINTABLE_KEYS[f.keyCode])return true;if(r(f))return d(f);return true},false);l.addEventListener("keypress",function(f){if(v.isMozilla)if(s.KeyHelper.FUNCTION_KEYS[f.keyCode])return d(f);else if((f.ctrlKey||f.metaKey)&&s.KeyHelper.PRINTABLE_KEYS_CHARCODE[f.charCode]){f._keyCode=s.KeyHelper.PRINTABLE_KEYS_CHARCODE[f.charCode]; +f._charCode=0;return d(f)}if(f.charCode!==undefined&&f.charCode===0)return true;return d(f)},false)}});bespin.tiki.module("keyboard:index",function(){});bespin.tiki.register("::worker_manager",{name:"worker_manager",dependencies:{canon:"0.0.0",events:"0.0.0",underscore:"0.0.0"}}); +bespin.tiki.module("worker_manager:index",function(y,s){function v(g){var j=/^([^#:]+)(?::([^#:]+))?#([^#:]+)$/.exec(g);if(j==null)throw new Error('WorkerSupervisor: invalid pointer specification: "'+g+'"');g=j[1];var q=j[3];j=g+":"+(j[2]!=null?j[2]:"index");var t=bespin!=null&&bespin.base!=null?bespin.base:"";this._packageId=g;this._moduleId=j;this._base=t;this._target=q;this._worker=null;this._currentId=0;this.started=new f}function r(){i.restartAll()}if(window==null)throw new Error('The "worker_manager" plugin can only be loaded in the browser, not a web worker. Use "worker" instead.'); +var l=y("bespin:proxy");y("bespin:plugins");var h=y("bespin:console").console,d=y("underscore")._,f=y("events").Event,m=y("bespin:promise").Promise;y("environment");var i={_workers:[],add:function(g){this._workers.push(g)},remove:function(g){this._workers=d(this._workers).without(g)},restartAll:function(){var g=this._workers;d(g).invoke("kill");d(g).invoke("start")}};v.prototype={_onError:function(g){this._worker=null;i.remove(this);h.error("WorkerSupervisor: worker failed at file "+g.filename+":"+ +g.lineno+"; fix the worker and use 'worker restart' to restart it")},_onMessage:function(g){g=JSON.parse(g.data);switch(g.op){case "finish":if(g.id===this._currentId){var j=this._promise;this._promise=null;j.resolve(g.result)}break;case "log":h[g.method].apply(h,g.args);break}},_promise:null,started:null,kill:function(){var g=this._promise;if(g!=null){g.reject("killed");this._promise=null}this._worker.terminate();this._worker=null;i.remove(this)},send:function(g,j){var q=this._promise;if(q!=null){q.reject("interrupted"); +this._currentId++}q=this._currentId;var t=new m;this._promise=t;this._worker.postMessage(JSON.stringify({op:"invoke",id:q,method:g,args:j}));return t},start:function(){if(this._worker!=null)throw new Error("WorkerSupervisor: worker already started");var g=this._base,j=this._target,q=this._packageId,t=this._moduleId,B=new l.Worker(g+"BespinEmbedded.js");B.onmessage=this._onMessage.bind(this);B.onerror=this._onError.bind(this);B.postMessage(JSON.stringify({op:"load",base:g,pkg:q,module:t,target:j})); +this._worker=B;this._currentId=0;i.add(this);this.started()}};s.WorkerSupervisor=v;s.workerManager=i;s.workerRestartCommand=r});bespin.tiki.register("::edit_session",{name:"edit_session",dependencies:{events:"0.0.0"}}); +bespin.tiki.module("edit_session:index",function(y,s){y("bespin:promise");y("bespin:plugins");y("bespin:util/util");y("events");s.EditSession=function(){};s.EditSession.prototype={_currentView:null,currentUser:null,history:null,getCompletePath:function(v){if(v==null)v="";if(v==null||v.substring(0,1)!="/"){var r;if(this._currentView&&this._currentView.buffer)r=this._currentView.buffer;var l;if(r)l=r.file;v=l?l.parentdir()+v:"/"+v}return v}};Object.defineProperties(s.EditSession.prototype,{currentView:{set:function(v){if(v!== +this._currentView)this._currentView=v},get:function(){return this._currentView}}});s.createSession=function(v,r){var l=new s.EditSession;if(v)l.currentView=v.textView;if(r)l.currentUser=r;return l}});bespin.tiki.register("::syntax_manager",{name:"syntax_manager",dependencies:{worker_manager:"0.0.0",events:"0.0.0",underscore:"0.0.0",syntax_directory:"0.0.0"}}); +bespin.tiki.module("syntax_manager:index",function(y,s){function v(g,j,q,t){for(;g.length=this._getRowCount()){this._invalidRow=null;this._active=false}else{this._invalidRow=j;this._annotate()}}},_createWorker:function(){if(this._syntaxInfo==null)return false;var g=new m("syntax_worker#syntaxWorker");this._worker=g;g.started.add(this._workerStarted.bind(this));g.start(); +return true},_getRowCount:function(){return this._syntaxManager.getTextLines().length},_workerStarted:function(){this._worker.send("loadSyntax",[this._syntaxInfo.name]);this._active&&this._annotate()},activateAndAnnotate:function(){this._active=true;this._annotate()},contextsAtPosition:function(){var g=this._syntaxInfo;if(g==null)return["plain"];return[g.name]},cut:function(g){var j=this._getRowCount();if(g<0||g>=j)throw new Error("Attempt to cut the context at an invalid row");if(!(this._invalidRow!= +null&&this._invalidRow
    ';r(m).append(g);this.panel=r(g);this.parent=r(m)}var r=y("jquery").$,l=y("underscore")._, +h=l.template(' — <%= container %>'),d=l.template('
    <%= type %>
    '),f=l.template('
  • <%= kind %><%= ident %><%= container %>
    <%= second_row %>
  • ');v.prototype={_fromBottom:false,_index:0,_tags:null,_getHighlightDimensions:function(m){var i= +m.position(),g=m.outerHeight()-2;m=m.outerWidth()-2;return{left:i.left,top:i.top,height:g,width:m}},_listItemForIndex:function(m){return this.panel.find("li:eq("+m+")")},_populate:function(){var m=l(this._tags).map(function(i){var g=i["class"],j=i.module,q=i.namespace;g=g!=null?g:q!=null?q:"";if(j!=null)g=j+(g!=""?"#"+g:"");j=g==""?"":h({container:g});g=i.type;g=g==null?"":d({type:g});return f({kind:i.kind,ident:i.name,container:j,second_row:g})});this.panel.find("ul").html(m.join("\n"))},panel:null, +visible:false,getCompletion:function(){return this.visible?this._tags[this._index]:null},hide:function(){if(this.visible){this.panel.fadeOut(100);this.visible=false}},move:function(m){var i=this._index,g=this._listItemForIndex(i),j=m==="up"?g.prev():g.next();if(j.length!==0){this._index=i=m==="up"?i-1:i+1;i=r(g).find(".bespin-completion-top-row");var q=r(g).find(".bespin-completion-second-row");g=r(j).find(".bespin-completion-top-row");var t=r(j).find(".bespin-completion-second-row");q.hide();t.show(); +var B=this.panel.find(".bespin-completion-highlight");B.stop(true,true);j=this._getHighlightDimensions(j);B.animate(j,100);t.hide();if(m==="down"){m=q.height();g.css("top",m);g.animate({top:0},100)}else{m=t.height();i.css("top",-m);i.animate({top:0},100)}t.fadeIn()}},show:function(m,i,g){this._tags=m=l(m).clone();this._populate();var j=this.visible,q=this.panel;q.stop(true,true);j||q.show();var t=this.parent.offset(),B=t.left,C=B+i.x,e=t.top+i.y;t=q.outerWidth();var K=q.outerHeight(),L=r(window).width(), +n=r(window).height();this._fromBottom=e=e+K+g>n;if(this._index>=m.length)this._index=m.length-1;if(e){e=q.find(".bespin-completion-pointer");e.removeClass("bespin-completion-pointer-up");e.addClass("bespin-completion-pointer-down");q.css({bottom:-i.y,top:""});this._tags.reverse();this._populate();if(!j)this._index=m.length-1}else{e=q.find(".bespin-completion-pointer");e.removeClass("bespin-completion-pointer-down");e.addClass("bespin-completion-pointer-up");q.css({top:i.y+g,bottom:""});if(!j)this._index= +0}if(!j){if(C+i.x+t>L){e.css({left:"",right:32});q.css("left",Math.min(L-t-B,i.x-t+43))}else{e.css({left:32,right:""});q.css("left",Math.max(B,i.x-43))}q.hide().animate({opacity:"show"},100)}m=q.find(".bespin-completion-highlight");m.stop(true,true);i=this._listItemForIndex(this._index);i.find(".bespin-completion-second-row").show();i=this._getHighlightDimensions(i);m.css(i);this.visible=true}};s.CompletionUI=v});bespin.tiki.module("completion:index",function(){}); +bespin.tiki.register("::rangeutils",{name:"rangeutils",dependencies:{}}); +bespin.tiki.module("rangeutils:utils/range",function(y,s){var v=y("bespin:util/util");s.addPositions=function(r,l){return{row:r.row+l.row,col:r.col+l.col}};s.cloneRange=function(r){var l=r.start;r=r.end;return{start:{row:l.row,col:l.col},end:{row:r.row,col:r.col}}};s.comparePositions=function(r,l){var h=r.row-l.row;return h===0?r.col-l.col:h};s.equal=function(r,l){return s.comparePositions(r.start,l.start)===0&&s.comparePositions(r.end,l.end)===0};s.extendRange=function(r,l){var h=r.end;return{start:r.start, +end:{row:h.row+l.row,col:h.col+l.col}}};s.intersectRangeSets=function(r,l){r=v.clone(r);l=v.clone(l);for(var h=[];r.length>0&&l.length>0;){var d=r.shift(),f=l.shift(),m=s.comparePositions(d.start,f.start),i=s.comparePositions(d.end,f.end);if(s.comparePositions(d.end,f.start)<0){h.push(d);l.unshift(f)}else if(s.comparePositions(f.end,d.start)<0){h.push(f);r.unshift(d)}else if(m<0){h.push({start:d.start,end:f.start});r.unshift({start:f.start,end:d.end});l.unshift(f)}else if(m===0)if(i<0)l.unshift({start:d.end, +end:f.end});else i>0&&r.unshift({start:f.end,end:d.end});else if(m>0){h.push({start:f.start,end:d.start});r.unshift(d);l.unshift({start:d.start,end:f.end})}}return h.concat(r,l)};s.isZeroLength=function(r){return r.start.row===r.end.row&&r.start.col===r.end.col};s.maxPosition=function(r,l){return s.comparePositions(r,l)>0?r:l};s.normalizeRange=function(r){return this.comparePositions(r.start,r.end)<0?r:{start:r.end,end:r.start}};s.rangeSetBoundaries=function(r){return{start:r[0].start,end:r[r.length- +1].end}};s.toString=function(r){var l=r.start;r=r.end;return"[ "+l.row+", "+l.col+" "+r.row+","+ +r.col+" ]"};s.unionRanges=function(r,l){return{start:r.start.rowl.end.row||r.end.row===l.end.row&&r.end.col>l.end.col?r.end:l.end}};s.isPosition=function(r){return!v.none(r)&&!v.none(r.row)&&!v.none(r.col)};s.isRange=function(r){return!v.none(r)&&s.isPosition(r.start)&&s.isPosition(r.end)}}); +bespin.tiki.module("rangeutils:index",function(){});bespin.tiki.register("::undomanager",{name:"undomanager",dependencies:{}}); +bespin.tiki.module("undomanager:index",function(y,s){var v=y("bespin:util/util");y("environment");s.UndoManager=function(){};v.mixin(s.UndoManager.prototype,{_redoStack:[],_undoStack:[],_undoOrRedo:function(r,l,h){if(l.length===0)return false;l=l.pop();if(!l.target[r](l.context)){this._redoStack=[];this._undoStack=[];return false}h.push(l);return true},redo:function(){return this._undoOrRedo("redo",this._redoStack,this._undoStack)},registerUndo:function(r,l){this._redoStack=[];this._undoStack.push({target:r, +context:l})},undo:function(){return this._undoOrRedo("undo",this._undoStack,this._redoStack)}});s.global=new s.UndoManager;s.undoManagerCommand=function(r,l){s.global[l.commandExt.name]()}});bespin.tiki.register("::environment",{name:"environment",dependencies:{settings:"0.0.0"}}); +bespin.tiki.module("environment:index",function(y,s){var v=y("bespin:util/util"),r=y("bespin:console").console,l=y("bespin:plugins").catalog,h=y("settings").settings;s.Environment=function(){this.commandLine=null;window.addEventListener("resize",this.dimensionsChanged.bind(this),false)};Object.defineProperties(s.Environment.prototype,{settings:{value:{set:function(d,f){if(v.none(d))throw new Error("setSetting(): key must be supplied");if(v.none(f))throw new Error("setSetting(): value must be supplied"); +h.set(d,f)},get:function(d){if(v.none(d))throw new Error("getSetting(): key must be supplied");return h.get(d)}}},dimensionsChanged:{value:function(){l.publish(this,"dimensionsChanged")}},session:{get:function(){return l.getObject("session")}},view:{get:function(){if(!this.session)return null;return this.session.currentView}},editor:{get:function(){if(!this.session)return null;return this.session.currentView.editor}},contexts:{get:function(){if(!this.view)return[];var d=this.view.editor.layoutManager.syntaxManager, +f=this.view.getSelectedRange().start;return d.contextsAtPosition(f)}},buffer:{get:function(){if(this.session)return this.view.editor.buffer;else r.error("command attempted to get buffer but there's no session")}},model:{get:function(){if(this.buffer)return this.view.editor.layoutManager.textStorage;else r.error("Session has no current buffer")}},file:{get:function(){if(this.buffer)return this.buffer.file;else r.error("Session has no current buffer")}},files:{get:function(){return l.getObject("files")}}}); +s.env=new s.Environment});bespin.tiki.register("::ctags",{name:"ctags",dependencies:{traits:"0.0.0",underscore:"0.0.0"}}); +bespin.tiki.module("ctags:index",function(y,s){var v=y("underscore")._,r=y("./reader").TagReader;y=y("traits").Trait;s.Tags=function(){this.tags=[]};s.Tags.prototype=Object.create(Object.prototype,y.compose(y({_search:function(l,h){var d={name:l};l=this.tags;var f=v(l).sortedIndex(d,function(m){return m.name});for(f=d=f;d>=0&&d=0&&f3&&h[3].indexOf(":")===-1){d.kind=h[3];f=4}else f=3;var m={};v(h.slice(f)).each(function(i){i=/^([^:]+):(.*)/.exec(i);m[i[1]]=i[2]});d.fields=m;l.push(d)}}});this.add(l)},readString:function(r){this.readLines(r.split("\n"))}})}); +bespin.tiki.register("::theme_manager",{name:"theme_manager",dependencies:{theme_manager_base:"0.0.0",settings:"0.0.0",events:"0.0.0",less:"0.0.0"}}); +bespin.tiki.module("theme_manager:index",function(y,s){y("bespin:promise");var v=y("bespin:plugins").catalog;y("events");var r=y("themestyles"),l=y("settings").settings,h=null,d=null;s.themestyles=r;s.themeSettingChanged=function(f,m,i){var g=v.getExtensionByKey("theme",i);if(i==="standard"||!i||!g){g=null;if(d!==null)g=v.getExtensionByKey("theme",d)}if(g)g.load().then(function(j){h&&r.unregisterThemeStyles(h);r.currentThemeVariables=j();h=g;r.parseGlobalVariables();r.reparse();g.url&&r.registerThemeStyles(g); +v.publish(s,"themeChange")});else if(h){r.unregisterThemeStyles(h);h=null;r.currentThemeVariables=null;r.parseGlobalVariables();r.reparse();v.publish(this,"themeChange")}};v.registerExtension("settingChange",{match:"theme",pointer:s.themeSettingChanged.bind(s)});s.setStandardTheme=function(f){d=f;f!==l.get("theme")&&s.themeSettingChanged(this)};s.setBasePlugin=function(f){r.basePluginName=f};s.startParsing=function(){r.preventParsing=false;return r.reparse()};s.registerTheme=function(f){var m=l.get("theme"); +f.name===m&&s.themeSettingChanged(this,"theme",f.name)};s.unregisterTheme=function(f){f.name===l.get("theme")&&s.themeSettingChanged(this)};s.appLaunched=function(){v.publish(s,"themeChange")}}); +bespin.tiki.module("theme_manager:themestyles",function(y,s){var v=y("bespin:util/util"),r=y("bespin:plugins").catalog,l=y("bespin:console").console,h=y("bespin:promise").Promise,d=y("bespin:promise").group,f=y("bespin:proxy"),m=new (y("less").Parser)({optimization:3}),i=1;s.currentThemeVariables=null;s.basePluginName=null;s.preventParsing=true;var g="";s.globalThemeVariables={};var j={},q={},t=function(w){var D={},J=[],Q=function(Z,T){J.push(Z);if(typeof T!="object")D[J.join("_")]=T;else for(prop in T)Q(prop, +T[prop]);J.pop()};Q("global",w);return D},B={},C={font:"arial, lucida, helvetica, sans-serif",font_size:"14px",line_height:"1.8em",color:"#DAD4BA",text_shadow:"1px 1px rgba(0, 0, 0, 0.4)",error_color:"#F99",header_color:"white",link_color:"#ACF",control:{color:"#E1B41F",border:"1px solid rgba(0, 0, 0, 0.2)",border_radius:"0.25em",background:"rgba(0, 0, 0, 0.2)",active:{color:"#FF9600",border:"1px solid #E1B41F",inset_color:"#ff9600",background:"rgba(0, 0, 0, 0.2)"}},pane:{h1:{font:"'MuseoSans', Helvetica", +font_size:"2.8em",color:"white"},color:"#DAD4BA",text_shadow:"1px 1px rgba(0, 0, 0, 0.4)",link_color:"white",background:"#45443C",border_radius:".5em"},form:{color:"white",text_shadow:"1px 1px rgba(0, 0, 0, 0.4)",font:"'Lucida Sans','Lucida Grande',Verdana,Arial,sans-serif",font_size:"@global_font_size",line_height:"@global_line_height"},button:{color:"white",background:"#3E6CB9"},container:{background:"#1E1916",border:"1px solid black"},selectable:{color:"white",border:"0px solid transparent",background:"transparent", +active:{color:"black",border:"0px solid transparent",background:"#FF8E00"},hover:{color:"black",border:"0px solid transparent",background:"#FF8E00"}},hint:{color:"#AAA",active:{color:"black"},hover:{color:"black"}},accelerator:{color:"#996633",active:{color:"black"},hover:{color:"black"}},menu:{border_color:"black",inset_color_right:"#1E1916",inset_color_top_left:"#3E3936",background:"transparent"}};C=t(C);s.getPluginThemeVariables=function(w){var D=r.plugins[w];if(!D)return null;var J={};if(s.currentThemeVariables&& +s.currentThemeVariables[w])J=s.currentThemeVariables[w];D.provides.forEach(function(Q){if(Q.ep==="themevariable"){var Z=Q.name;J[Z]=J[Z]||Q.defaultValue}});return J};s.parseGlobalVariables=function(){var w={},D="",J=s.currentThemeVariables;v.mixin(w,C);J&&J.global&&v.mixin(w,t(J.global));s.globalThemeVariables=w;for(prop in w)D+="@"+prop+":"+w[prop]+";";g=D};s.parseGlobalVariables();var e=function(w,D,J){if(j[D])styleElem=document.getElementById("_bespin_theme_style_"+j[D]);else{styleElem=document.createElement("style"); +styleElem.setAttribute("id","_bespin_theme_style_"+i);j[D]=i;i++;document.body.appendChild(styleElem)}m.parse(g+J+q[D],function(Q,Z){if(Q){Q="Error less parsing "+D+" "+Q.message;l.error(Q);w.reject(Q)}else{try{var T=Z.toCSS()}catch(ca){Q="Error less parsing "+D+" "+ca;l.error(Q);w.reject(Q);return}if(styleElem&&styleElem.firstChild)styleElem.firstChild.textContent=T;else{Q=document.createTextNode(T);styleElem.appendChild(Q)}w.resolve()}})},K={};s.parsePlugin=function(w){if(s.preventParsing)return(new h).resolve(); +var D=r.plugins[w];if(!D)throw"reparsePlugin: plugin "+w+" is not defined!";if(!K[w]){K[w]=new h;setTimeout(function(){var J=s.getPluginThemeVariables(w),Q="";for(prop in J)Q+="@"+prop+":"+J[prop]+";";J=new h;J.then(function(Z){K[this.name].resolve(Z);K[this.name]=null}.bind(this),function(){K[this.name].reject(data);K[this.name]=null}.bind(this));e(J,w,Q)}.bind(D),0)}return K[w]};var L=function(w,D,J,Q){J=J.replace(/url\(['"]*([^'")]*)(['"]*)\)/g,"url("+w+"$1)");q[D]+=J;Q&&Q.resolve()},n=null;s.registerThemeStyles= +function(w){var D=w.getPluginName(),J=r.getResourceURL(D);if(!(w.url instanceof Array))w.url=[w.url];q[D]="";var Q=[],Z=s.preventParsing;w.url.forEach(function(T){if(B[D]&&B[D][T])L(J,D,B[D][T]);else{var ca=new h;Q.push(ca);var ha=J+T+"?"+(new Date).getTime();f.xhr("GET",ha,true,function(ga){ga.overrideMimeType("text/plain")}).then(function(ga){L(J,D,ga,ca)},function(){l.error("registerLessFile: Could not load "+J+T);ca.resolve()})}});if(Q.length===0)s.parsePlugin(D);else{Z||d(Q).then(function(){s.parsePlugin(D)}); +if(n!==null)Q=Q.concat(n);n=d(Q)}};s.reparse=function(){var w=new h;if(s.preventParsing)return w.resolve();n?n.then(function(){var D=[],J=s.basePluginName;J!==null&&q[J]&&D.push(s.parsePlugin(J));for(var Q in q)Q!==J&&D.push(s.parsePlugin(Q));d(D).then(w.resolve.bind(w),w.reject.bind(w))},function(D){w.reject(D)}):w.resolve();return w};s.unregisterThemeStyles=function(w){w=w.getPluginName();if(j[w]){var D=document.getElementById("_bespin_theme_style_"+j[w]);D.parentNode.removeChild(D);delete j[w]; +delete q[w]}}});bespin.tiki.register("::types",{name:"types",dependencies:{}}); +bespin.tiki.module("types:basic",function(y,s){var v=y("bespin:plugins").catalog,r=y("bespin:console").console,l=y("bespin:promise").Promise;s.text={isValid:function(h){return typeof h=="string"},toString:function(h){return h},fromString:function(h){return h}};s.number={isValid:function(h){if(isNaN(h))return false;if(h===null)return false;if(h===undefined)return false;if(h===Infinity)return false;return typeof h=="number"},toString:function(h){if(!h)return null;return""+h},fromString:function(h){if(!h)return null; +var d=parseInt(h,10);if(isNaN(d))throw new Error("Can't convert \""+h+'" to a number.');return d}};s.bool={isValid:function(h){return typeof h=="boolean"},toString:function(h){return""+h},fromString:function(h){if(h===null)return null;if(!h.toLowerCase)return!!h;var d=h.toLowerCase();if(d=="true")return true;else if(d=="false")return false;return!!h}};s.object={isValid:function(h){return typeof h=="object"},toString:function(h){return JSON.stringify(h)},fromString:function(h){return JSON.parse(h)}}; +s.selection={isValid:function(h,d){if(typeof h!="string")return false;if(!d.data){r.error("Missing data on selection type extension. Skipping");return true}var f=false;d.data.forEach(function(m){if(h==m)f=true});return f},toString:function(h){return h},fromString:function(h){return h},resolveTypeSpec:function(h,d){var f=new l;if(d.data){h.data=d.data;f.resolve()}else if(d.pointer)v.loadObjectForPropertyPath(d.pointer).then(function(m){m=m(d);if(typeof m.then==="function")m.then(function(i){h.data= +i;f.resolve()});else{h.data=m;f.resolve()}},function(m){f.reject(m)});else{r.warn("Missing data/pointer for selection",d);f.resolve()}return f}}}); +bespin.tiki.module("types:types",function(y,s){function v(d){var f=new h,m=l.getExtensionByKey("type",d.name);m?f.resolve({ext:m,typeSpec:d}):f.reject(new Error("Unknown type: "+d.name));return f}function r(d){if(typeof d==="string")return v({name:d});if(typeof d==="object")if(d.name==="deferred"){var f=new h;s.undeferTypeSpec(d).then(function(m){r(m).then(function(i){f.resolve(i)},function(i){f.reject(i)})});return f}else return v(d);throw new Error("Unknown typeSpec type: "+typeof d);}var l=y("bespin:plugins").catalog; +y("bespin:console");var h=y("bespin:promise").Promise;s.getSimpleName=function(d){if(!d)throw new Error("null|undefined is not a valid typeSpec");if(typeof d=="string")return d;if(typeof d=="object"){if(!d.name)throw new Error("Missing name member to typeSpec");return d.name}throw new Error("Not a typeSpec: "+d);};s.equals=function(d,f){return s.getSimpleName(d)==s.getSimpleName(f)};s.undeferTypeSpec=function(d){var f=new h;if(!d.pointer){f.reject(new Error("Missing deferred pointer"));return f}l.loadObjectForPropertyPath(d.pointer).then(function(m){m= +m(d);typeof m.then==="function"?m.then(function(i){f.resolve(i)},function(i){f.reject(i)}):f.resolve(m)},function(m){f.reject(m)});return f};s.resolveType=function(d){var f=new h;r(d).then(function(m){m.ext.load(function(i){typeof i.resolveTypeSpec==="function"?i.resolveTypeSpec(m.ext,m.typeSpec).then(function(){f.resolve({type:i,ext:m.ext})},function(g){f.reject(g)}):f.resolve({type:i,ext:m.ext})})},function(m){f.reject(m)});return f};s.fromString=function(d,f){var m=new h;s.resolveType(f).then(function(i){m.resolve(i.type.fromString(d, +i.ext))});return m};s.toString=function(d,f){var m=new h;s.resolveType(f).then(function(i){m.resolve(i.type.toString(d,i.ext))});return m};s.isValid=function(d,f){var m=new h;s.resolveType(f).then(function(i){m.resolve(i.type.isValid(d,i.ext))});return m}});bespin.tiki.module("types:index",function(){});bespin.tiki.register("::jquery",{name:"jquery",dependencies:{}}); +bespin.tiki.module("jquery:index",function(y,s){function v(){if(!e.isReady){try{n.documentElement.doScroll("left")}catch(a){setTimeout(v,1);return}e.ready()}}function r(a,b){b.src?e.ajax({url:b.src,async:false,dataType:"script"}):e.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function l(a,b,k,p,o,z){var A=a.length;if(typeof b==="object"){for(var I in b)l(a,I,b[I],p,o,k);return a}if(k!==undefined){p=!z&&p&&e.isFunction(k);for(I=0;I)[^>]*$|^#([\w-]+)$/, +J=/^.[^:#\[\.,]*$/,Q=/\S/,Z=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,T=/^<(\w+)\s*\/?>(?:<\/\1>)?$/;y=navigator.userAgent;var ca=false,ha=[],ga,la=Object.prototype.toString,ma=Object.prototype.hasOwnProperty,na=Array.prototype.push,O=Array.prototype.slice,P=Array.prototype.indexOf;e.fn=e.prototype={init:function(a,b){var k,p;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=n;this[0]=n.body;this.selector="body";this.length=1;return this}if(typeof a=== +"string")if((k=D.exec(a))&&(k[1]||!b))if(k[1]){p=b?b.ownerDocument||b:n;if(a=T.exec(a))if(e.isPlainObject(b)){a=[n.createElement(a[1])];e.fn.attr.call(a,b,true)}else a=[p.createElement(a[1])];else{a=t([k[1]],[p]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return e.merge(this,a)}else{if(b=n.getElementById(k[2])){if(b.id!==k[2])return w.find(a);this.length=1;this[0]=b}this.context=n;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=n;a=n.getElementsByTagName(a); +return e.merge(this,a)}else return!b||b.jquery?(b||w).find(a):e(b).find(a);else if(e.isFunction(a))return w.ready(a);if(a.selector!==undefined){this.selector=a.selector;this.context=a.context}return e.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return O.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,k){var p=e();e.isArray(a)?na.apply(p,a):e.merge(p,a);p.prevObject=this; +p.context=this.context;if(b==="find")p.selector=this.selector+(this.selector?" ":"")+k;else if(b)p.selector=this.selector+"."+b+"("+k+")";return p},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady();if(e.isReady)a.call(n,e);else ha&&ha.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(O.apply(this,arguments),"slice",O.call(arguments).join(","))}, +map:function(a){return this.pushStack(e.map(this,function(b,k){return a.call(b,k,b)}))},end:function(){return this.prevObject||e(null)},push:na,sort:[].sort,splice:[].splice};e.fn.init.prototype=e.fn;e.extend=e.fn.extend=function(){var a=arguments[0]||{},b=1,k=arguments.length,p=false,o,z,A,I;if(typeof a==="boolean"){p=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!e.isFunction(a))a={};if(k===b){a=this;--b}for(;b";a=n.createDocumentFragment();a.appendChild(k.firstChild);e.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;e(function(){var E=n.createElement("div");E.style.width=E.style.paddingLeft="1px";n.body.appendChild(E); +e.boxModel=e.support.boxModel=E.offsetWidth===2;n.body.removeChild(E).style.display="none"});a=function(E){var H=n.createElement("div");E="on"+E;var R=E in H;if(!R){H.setAttribute(E,"return;");R=typeof H[E]==="function"}return R};e.support.submitBubbles=a("submit");e.support.changeBubbles=a("change");a=b=k=o=z=null}})();e.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap", +frameborder:"frameBorder"};var S="jQuery"+h(),U=0,aa={};e.extend({cache:{},expando:S,noData:{embed:true,object:true,applet:true},data:function(a,b,k){if(!(a.nodeName&&e.noData[a.nodeName.toLowerCase()])){a=a==window?aa:a;var p=a[S],o=e.cache;if(!p&&typeof b==="string"&&k===undefined)return null;p||(p=++U);if(typeof b==="object"){a[S]=p;o[p]=e.extend(true,{},b)}else if(!o[p]){a[S]=p;o[p]={}}a=o[p];if(k!==undefined)a[b]=k;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&& +e.noData[a.nodeName.toLowerCase()])){a=a==window?aa:a;var k=a[S],p=e.cache,o=p[k];if(b){if(o){delete o[b];e.isEmptyObject(o)&&e.removeData(a)}}else{if(e.support.deleteExpando)delete a[e.expando];else a.removeAttribute&&a.removeAttribute(e.expando);delete p[k]}}}});e.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return e.data(this[0]);else if(typeof a==="object")return this.each(function(){e.data(this,a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===undefined){var p=this.triggerHandler("getData"+ +k[1]+"!",[k[0]]);if(p===undefined&&this.length)p=e.data(this[0],a);return p===undefined&&k[1]?this.data(k[0]):p}else return this.trigger("setData"+k[1]+"!",[k[0],b]).each(function(){e.data(this,a,b)})},removeData:function(a){return this.each(function(){e.removeData(this,a)})}});e.extend({queue:function(a,b,k){if(a){b=(b||"fx")+"queue";var p=e.data(a,b);if(!k)return p||[];if(!p||e.isArray(k))p=e.data(a,b,e.makeArray(k));else p.push(k);return p}},dequeue:function(a,b){b=b||"fx";var k=e.queue(a,b),p= +k.shift();if(p==="inprogress")p=k.shift();if(p){b==="fx"&&k.unshift("inprogress");p.call(a,function(){e.dequeue(a,b)})}}});e.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===undefined)return e.queue(this[0],a);return this.each(function(){var k=e.queue(this,a,b);a==="fx"&&k[0]!=="inprogress"&&e.dequeue(this,a)})},dequeue:function(a){return this.each(function(){e.dequeue(this,a)})},delay:function(a,b){a=e.fx?e.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var k= +this;setTimeout(function(){e.dequeue(k,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var fa=/[\n\t]/g,xa=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,cb=/^(a|area)$/i,Ka=/radio|checkbox/;e.fn.extend({attr:function(a,b){return l(this,a,b,true,e.attr)},removeAttr:function(a){return this.each(function(){e.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(e.isFunction(a))return this.each(function(H){var R= +e(this);R.addClass(a.call(this,H,R.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(xa),k=0,p=this.length;k-1)return true;return false},val:function(a){if(a===undefined){var b=this[0];if(b){if(e.nodeName(b, +"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(e.nodeName(b,"select")){var k=b.selectedIndex,p=[],o=b.options;b=b.type==="select-one";if(k<0)return null;var z=b?k:0;for(k=b?k+1:o.length;z=0;else if(e.nodeName(this,"select")){var W=e.makeArray(R);e("option",this).each(function(){this.selected=e.inArray(e(this).val(),W)>=0});if(!W.length)this.selectedIndex=-1}else this.value=R}})}}});e.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,k,p){if(!(!a||a.nodeType===3||a.nodeType===8)){if(p&& +b in e.attrFn)return e(a)[b](k);p=a.nodeType!==1||!e.isXMLDoc(a);var o=k!==undefined;b=p&&e.props[b]||b;if(a.nodeType===1){var z=$a.test(b);if(b in a&&p&&!z){if(o){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&e.error("type property can't be changed");a[b]=k}if(e.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:undefined;return a[b]}if(!e.support.style&& +p&&b==="style"){if(o)a.style.cssText=""+k;return a.style.cssText}o&&a.setAttribute(b,""+k);a=!e.support.hrefNormalized&&p&&z?a.getAttribute(b,2):a.getAttribute(b);return a===null?undefined:a}return e.style(a,b,k)}}});var sa=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};e.event={add:function(a,b,k,p){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==window&&!a.frameElement)a=window;var o,z;if(k.handler){o=k;k=o.handler}if(!k.guid)k.guid=e.guid++;if(z= +e.data(a)){var A=z.events=z.events||{},I=z.handle;if(!I)z.handle=I=function(){return typeof e!=="undefined"&&!e.event.triggered?e.event.handle.apply(I.elem,arguments):undefined};I.elem=a;b=b.split(" ");for(var E,H=0,R;E=b[H++];){z=o?e.extend({},o):{handler:k,data:p};if(E.indexOf(".")>-1){R=E.split(".");E=R.shift();z.namespace=R.slice(0).sort().join(".")}else{R=[];z.namespace=""}z.type=E;z.guid=k.guid;var W=A[E],ba=e.event.special[E]||{};if(!W){W=A[E]=[];if(!ba.setup||ba.setup.call(a,p,R,I)===false)if(a.addEventListener)a.addEventListener(E, +I,false);else a.attachEvent&&a.attachEvent("on"+E,I)}if(ba.add){ba.add.call(a,z);if(!z.handler.guid)z.handler.guid=k.guid}W.push(z);e.event.global[E]=true}a=null}}},global:{},remove:function(a,b,k,p){if(!(a.nodeType===3||a.nodeType===8)){var o,z=0,A,I,E,H,R,W,ba=e.data(a),ea=ba&&ba.events;if(ba&&ea){if(b&&b.type){k=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(o in ea)e.event.remove(a,o+b)}else{for(b=b.split(" ");o=b[z++];){H=o;A=o.indexOf(".")<0;I=[];if(!A){I=o.split("."); +o=I.shift();E=new RegExp("(^|\\.)"+e.map(I.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(R=ea[o])if(k){H=e.event.special[o]||{};for(da=p||0;da=0){a.type=o=o.slice(0,-1);a.exclusive=true}if(!k){a.stopPropagation();e.event.global[o]&&e.each(e.cache,function(){this.events&&this.events[o]&&e.event.trigger(a,b,this.handle.elem)})}if(!k||k.nodeType===3||k.nodeType=== +8)return;a.result=undefined;a.target=k;b=e.makeArray(b);b.unshift(a)}a.currentTarget=k;(p=e.data(k,"handle"))&&p.apply(k,b);p=k.parentNode||k.ownerDocument;try{if(!(k&&k.nodeName&&e.noData[k.nodeName.toLowerCase()]))if(k["on"+o]&&k["on"+o].apply(k,b)===false)a.result=false}catch(z){}if(!a.isPropagationStopped()&&p)e.event.trigger(a,b,p,true);else if(!a.isDefaultPrevented()){p=a.target;var A,I=e.nodeName(p,"a")&&o==="click",E=e.event.special[o]||{};if((!E._default||E._default.call(k,a)===false)&&!I&& +!(p&&p.nodeName&&e.noData[p.nodeName.toLowerCase()])){try{if(p[o]){if(A=p["on"+o])p["on"+o]=null;e.event.triggered=true;p[o]()}}catch(H){}if(A)p["on"+o]=A;e.event.triggered=false}}},handle:function(a){var b,k,p,o;a=arguments[0]=e.event.fix(a||window.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){k=a.type.split(".");a.type=k.shift();p=new RegExp("(^|\\.)"+k.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}o=e.data(this,"events");k=o[a.type];if(o&&k){k=k.slice(0);o=0;for(var z= +k.length;o-1?e.map(a.options,function(p){return p.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")k=a.selectedIndex;return k},Aa=function(a,b){var k=a.target,p,o;if(!(!ya.test(k.nodeName)||k.readOnly)){p=e.data(k,"_change_data");o=Oa(k);if(a.type!=="focusout"||k.type!=="radio")e.data(k,"_change_data",o);if(!(p===undefined||o===p))if(p!= +null||o){a.type="change";return e.event.trigger(a,b,k)}}};e.event.special.change={filters:{focusout:Aa,click:function(a){var b=a.target,k=b.type;if(k==="radio"||k==="checkbox"||b.nodeName.toLowerCase()==="select")return Aa.call(this,a)},keydown:function(a){var b=a.target,k=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(k==="checkbox"||k==="radio")||k==="select-multiple")return Aa.call(this,a)},beforeactivate:function(a){a=a.target;e.data(a,"_change_data",Oa(a))}}, +setup:function(){if(this.type==="file")return false;for(var a in za)e.event.add(this,a+".specialChange",za[a]);return ya.test(this.nodeName)},teardown:function(){e.event.remove(this,".specialChange");return ya.test(this.nodeName)}};za=e.event.special.change.filters}n.addEventListener&&e.each({focus:"focusin",blur:"focusout"},function(a,b){function k(p){p=e.event.fix(p);p.type=b;return e.event.handle.call(this,p)}e.event.special[b]={setup:function(){this.addEventListener(a,k,true)},teardown:function(){this.removeEventListener(a, +k,true)}}});e.each(["bind","one"],function(a,b){e.fn[b]=function(k,p,o){if(typeof k==="object"){for(var z in k)this[b](z,p,k[z],o);return this}if(e.isFunction(p)){o=p;p=undefined}var A=b==="one"?e.proxy(o,function(E){e(this).unbind(E,A);return o.apply(this,arguments)}):o;if(k==="unload"&&b!=="one")this.one(k,p,o);else{z=0;for(var I=this.length;z0){$=V;break}}V=V[u]}G[N]=$}}}var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +o=0,z=Object.prototype.toString,A=false,I=true;[0,0].sort(function(){I=false;return 0});var E=function(u,x,F,G){F=F||[];var N=x=x||n;if(x.nodeType!==1&&x.nodeType!==9)return[];if(!u||typeof u!=="string")return F;for(var M=[],X,V,$,ta,oa=true,qa=Y(x),pa=u;(p.exec(""),X=p.exec(pa))!==null;){pa=X[3];M.push(X[1]);if(X[2]){ta=X[3];break}}if(M.length>1&&R.exec(u))if(M.length===2&&H.relative[M[0]])V=Ba(M[0]+M[1],x);else for(V=H.relative[M[0]]?[x]:E(M.shift(),x);M.length;){u=M.shift();if(H.relative[u])u+= +M.shift();V=Ba(u,V)}else{if(!G&&M.length>1&&x.nodeType===9&&!qa&&H.match.ID.test(M[0])&&!H.match.ID.test(M[M.length-1])){X=E.find(M.shift(),x,qa);x=X.expr?E.filter(X.expr,X.set)[0]:X.set[0]}if(x){X=G?{expr:M.pop(),set:ba(G)}:E.find(M.pop(),M.length===1&&(M[0]==="~"||M[0]==="+")&&x.parentNode?x.parentNode:x,qa);V=X.expr?E.filter(X.expr,X.set):X.set;if(M.length>0)$=ba(V);else oa=false;for(;M.length;){var ia=M.pop();X=ia;if(H.relative[ia])X=M.pop();else ia="";if(X==null)X=x;H.relative[ia]($,X,qa)}}else $= +[]}$||($=V);$||E.error(ia||u);if(z.call($)==="[object Array]")if(oa)if(x&&x.nodeType===1)for(u=0;$[u]!=null;u++){if($[u]&&($[u]===true||$[u].nodeType===1&&ja(x,$[u])))F.push(V[u])}else for(u=0;$[u]!=null;u++)$[u]&&$[u].nodeType===1&&F.push(V[u]);else F.push.apply(F,$);else ba($,F);if(ta){E(ta,N,F,G);E.uniqueSort(F)}return F};E.uniqueSort=function(u){if(da){A=I;u.sort(da);if(A)for(var x=1;x":function(u,x){var F=typeof x==="string";if(F&&!/\W/.test(x)){x=x.toLowerCase();for(var G=0,N=u.length;G= +0))F||G.push(X);else if(F)x[M]=false;return false},ID:function(u){return u[1].replace(/\\/g,"")},TAG:function(u){return u[1].toLowerCase()},CHILD:function(u){if(u[1]==="nth"){var x=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(u[2]==="even"&&"2n"||u[2]==="odd"&&"2n+1"||!/\D/.test(u[2])&&"0n+"+u[2]||u[2]);u[2]=x[1]+(x[2]||1)-0;u[3]=x[3]-0}u[0]=o++;return u},ATTR:function(u,x,F,G,N,M){x=u[1].replace(/\\/g,"");if(!M&&H.attrMap[x])u[1]=H.attrMap[x];if(u[2]==="~=")u[4]=" "+u[4]+" ";return u},PSEUDO:function(u,x,F,G, +N){if(u[1]==="not")if((p.exec(u[3])||"").length>1||/^\w/.test(u[3]))u[3]=E(u[3],null,null,x);else{u=E.filter(u[3],x,F,true^N);F||G.push.apply(G,u);return false}else if(H.match.POS.test(u[0])||H.match.CHILD.test(u[0]))return true;return u},POS:function(u){u.unshift(true);return u}},filters:{enabled:function(u){return u.disabled===false&&u.type!=="hidden"},disabled:function(u){return u.disabled===true},checked:function(u){return u.checked===true},selected:function(u){return u.selected===true},parent:function(u){return!!u.firstChild}, +empty:function(u){return!u.firstChild},has:function(u,x,F){return!!E(F[3],u).length},header:function(u){return/h\d/i.test(u.nodeName)},text:function(u){return"text"===u.type},radio:function(u){return"radio"===u.type},checkbox:function(u){return"checkbox"===u.type},file:function(u){return"file"===u.type},password:function(u){return"password"===u.type},submit:function(u){return"submit"===u.type},image:function(u){return"image"===u.type},reset:function(u){return"reset"===u.type},button:function(u){return"button"=== +u.type||u.nodeName.toLowerCase()==="button"},input:function(u){return/input|select|textarea|button/i.test(u.nodeName)}},setFilters:{first:function(u,x){return x===0},last:function(u,x,F,G){return x===G.length-1},even:function(u,x){return x%2===0},odd:function(u,x){return x%2===1},lt:function(u,x,F){return xF[3]-0},nth:function(u,x,F){return F[3]-0===x},eq:function(u,x,F){return F[3]-0===x}},filter:{PSEUDO:function(u,x,F,G){var N=x[1],M=H.filters[N];if(M)return M(u, +F,x,G);else if(N==="contains")return(u.textContent||u.innerText||a([u])||"").indexOf(x[3])>=0;else if(N==="not"){x=x[3];F=0;for(G=x.length;F=0}},ID:function(u,x){return u.nodeType===1&&u.getAttribute("id")===x},TAG:function(u,x){return x==="*"&&u.nodeType===1||u.nodeName.toLowerCase()===x},CLASS:function(u,x){return(" "+(u.className||u.getAttribute("class"))+" ").indexOf(x)>-1},ATTR:function(u,x){var F= +x[1];u=H.attrHandle[F]?H.attrHandle[F](u):u[F]!=null?u[F]:u.getAttribute(F);F=u+"";var G=x[2];x=x[4];return u==null?G==="!=":G==="="?F===x:G==="*="?F.indexOf(x)>=0:G==="~="?(" "+F+" ").indexOf(x)>=0:!x?F&&u!==false:G==="!="?F!==x:G==="^="?F.indexOf(x)===0:G==="$="?F.substr(F.length-x.length)===x:G==="|="?F===x||F.substr(0,x.length+1)===x+"-":false},POS:function(u,x,F,G){var N=H.setFilters[x[2]];if(N)return N(u,F,x,G)}}},R=H.match.POS;for(var W in H.match){H.match[W]=new RegExp(H.match[W].source+/(?![^\[]*\])(?![^\(]*\))/.source); +H.leftMatch[W]=new RegExp(/(^(?:.|\r|\n)*?)/.source+H.match[W].source.replace(/\\(\d+)/g,function(u,x){return"\\"+(x-0+1)}))}var ba=function(u,x){u=Array.prototype.slice.call(u,0);if(x){x.push.apply(x,u);return x}return u};try{Array.prototype.slice.call(n.documentElement.childNodes,0)}catch(ea){ba=function(u,x){x=x||[];if(z.call(u)==="[object Array]")Array.prototype.push.apply(x,u);else if(typeof u.length==="number")for(var F=0,G=u.length;F";var F=n.documentElement;F.insertBefore(u,F.firstChild);if(n.getElementById(x)){H.find.ID=function(G,N,M){if(typeof N.getElementById!=="undefined"&&!M)return(N= +N.getElementById(G[1]))?N.id===G[1]||typeof N.getAttributeNode!=="undefined"&&N.getAttributeNode("id").nodeValue===G[1]?[N]:undefined:[]};H.filter.ID=function(G,N){var M=typeof G.getAttributeNode!=="undefined"&&G.getAttributeNode("id");return G.nodeType===1&&M&&M.nodeValue===N}}F.removeChild(u);F=u=null})();(function(){var u=n.createElement("div");u.appendChild(n.createComment(""));if(u.getElementsByTagName("*").length>0)H.find.TAG=function(x,F){F=F.getElementsByTagName(x[1]);if(x[1]==="*"){x=[]; +for(var G=0;F[G];G++)F[G].nodeType===1&&x.push(F[G]);F=x}return F};u.innerHTML="";if(u.firstChild&&typeof u.firstChild.getAttribute!=="undefined"&&u.firstChild.getAttribute("href")!=="#")H.attrHandle.href=function(x){return x.getAttribute("href",2)};u=null})();n.querySelectorAll&&function(){var u=E,x=n.createElement("div");x.innerHTML="

    ";if(!(x.querySelectorAll&&x.querySelectorAll(".TEST").length===0)){E=function(G,N,M,X){N=N||n;if(!X&&N.nodeType===9&&!Y(N))try{return ba(N.querySelectorAll(G), +M)}catch(V){}return u(G,N,M,X)};for(var F in u)E[F]=u[F];x=null}}();(function(){var u=n.createElement("div");u.innerHTML="
    ";if(!(!u.getElementsByClassName||u.getElementsByClassName("e").length===0)){u.lastChild.className="e";if(u.getElementsByClassName("e").length!==1){H.order.splice(1,0,"CLASS");H.find.CLASS=function(x,F,G){if(typeof F.getElementsByClassName!=="undefined"&&!G)return F.getElementsByClassName(x[1])};u=null}}})();var ja=n.compareDocumentPosition? +function(u,x){return!!(u.compareDocumentPosition(x)&16)}:function(u,x){return u!==x&&(u.contains?u.contains(x):true)},Y=function(u){return(u=(u?u.ownerDocument||u:0).documentElement)?u.nodeName!=="HTML":false},Ba=function(u,x){var F=[],G="",N;for(x=x.nodeType?[x]:x;N=H.match.PSEUDO.exec(u);){G+=N[0];u=u.replace(H.match.PSEUDO,"")}u=H.relative[u]?u+"*":u;N=0;for(var M=x.length;N=0===k})};e.fn.extend({find:function(a){for(var b= +this.pushStack("","find",a),k=0,p=0,o=this.length;p0)for(var z=k;z +0},closest:function(a,b){if(e.isArray(a)){var k=[],p=this[0],o,z={},A;if(p&&a.length){o=0;for(var I=a.length;o-1:e(p).is(o)){k.push({selector:A,elem:p});delete z[A]}}p=p.parentNode}}return k}var E=e.expr.match.POS.test(a)?e(a,b||this.context):null;return this.map(function(H,R){for(;R&&R.ownerDocument&&R!==b;){if(E?E.index(R)>-1:e(R).is(a))return R; +R=R.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return e.inArray(this[0],a?e(a):this.parent().children());return e.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?e(a,b||this.context):e.makeArray(a);b=e.merge(this.get(),a);return this.pushStack(j(a[0])||j(b[0])?b:e.unique(b))},andSelf:function(){return this.add(this.prevObject)}});e.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return e.dir(a,"parentNode")}, +parentsUntil:function(a,b,k){return e.dir(a,"parentNode",k)},next:function(a){return e.nth(a,2,"nextSibling")},prev:function(a){return e.nth(a,2,"previousSibling")},nextAll:function(a){return e.dir(a,"nextSibling")},prevAll:function(a){return e.dir(a,"previousSibling")},nextUntil:function(a,b,k){return e.dir(a,"nextSibling",k)},prevUntil:function(a,b,k){return e.dir(a,"previousSibling",k)},siblings:function(a){return e.sibling(a.parentNode.firstChild,a)},children:function(a){return e.sibling(a.firstChild)}, +contents:function(a){return e.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:e.makeArray(a.childNodes)}},function(a,b){e.fn[a]=function(k,p){var o=e.map(this,b,k);eb.test(a)||(p=k);if(p&&typeof p==="string")o=e.filter(p,o);o=this.length>1?e.unique(o):o;if((this.length>1||gb.test(p))&&fb.test(a))o=o.reverse();return this.pushStack(o,a,O.call(arguments).join(","))}});e.extend({filter:function(a,b,k){if(k)a=":not("+a+")";return e.find.matches(a,b)},dir:function(a,b,k){var p=[];for(a= +a[b];a&&a.nodeType!==9&&(k===undefined||a.nodeType!==1||!e(a).is(k));){a.nodeType===1&&p.push(a);a=a[b]}return p},nth:function(a,b,k){b=b||1;for(var p=0;a;a=a[k])if(a.nodeType===1&&++p===b)break;return a},sibling:function(a,b){for(var k=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&k.push(a);return k}});var Sa=/ jQuery\d+="(?:\d+|null)"/g,va=/^\s+/,Ta=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ua=/<([\w:]+)/,ib=/"},ka={option:[1,""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};ka.optgroup=ka.option;ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead;ka.th= +ka.td;if(!e.support.htmlSerialize)ka._default=[1,"div
    ","
    "];e.fn.extend({text:function(a){if(e.isFunction(a))return this.each(function(b){var k=e(this);k.text(a.call(this,b,k.text()))});if(typeof a!=="object"&&a!==undefined)return this.empty().append((this[0]&&this[0].ownerDocument||n).createTextNode(a));return e.text(this)},wrapAll:function(a){if(e.isFunction(a))return this.each(function(k){e(this).wrapAll(a.call(this,k))});if(this[0]){var b=e(a,this[0].ownerDocument).eq(0).clone(true); +this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var k=this;k.firstChild&&k.firstChild.nodeType===1;)k=k.firstChild;return k}).append(this)}return this},wrapInner:function(a){if(e.isFunction(a))return this.each(function(b){e(this).wrapInner(a.call(this,b))});return this.each(function(){var b=e(this),k=b.contents();k.length?k.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){e(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){e.nodeName(this, +"body")||e(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=e(arguments[0]);a.push.apply(a,this.toArray()); +return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,e(arguments[0]).toArray());return a}},remove:function(a,b){for(var k=0,p;(p=this[k])!=null;k++)if(!a||e.filter(a,[p]).length){if(!b&&p.nodeType===1){e.cleanData(p.getElementsByTagName("*"));e.cleanData([p])}p.parentNode&& +p.parentNode.removeChild(p)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&e.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);return this},clone:function(a){var b=this.map(function(){if(!e.support.noCloneEvent&&!e.isXMLDoc(this)){var k=this.outerHTML,p=this.ownerDocument;if(!k){k=p.createElement("div");k.appendChild(this.cloneNode(true));k=k.innerHTML}return e.clean([k.replace(Sa,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(va, +"")],p)[0]}else return this.cloneNode(true)});if(a===true){q(this,b);q(this.find("*"),b.find("*"))}return b},html:function(a){if(a===undefined)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Sa,""):null;else if(typeof a==="string"&&!Ha.test(a)&&(e.support.leadingWhitespace||!va.test(a))&&!ka[(Ua.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ta,Va);try{for(var b=0,k=this.length;b0||o.cacheable||this.length>1?E.cloneNode(true):E)}I.length&&e.each(I,r)}return this}});e.fragments={};e.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"}, +function(a,b){e.fn[a]=function(k){var p=[];k=e(k);var o=this.length===1&&this[0].parentNode;if(o&&o.nodeType===11&&o.childNodes.length===1&&k.length===1){k[b](this[0]);return this}else{o=0;for(var z=k.length;o0?this.clone(true):this).get();e.fn[b].apply(e(k[o]),A);p=p.concat(A)}return this.pushStack(p,a,k.selector)}}});e.extend({clean:function(a,b,k,p){b=b||n;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||n;for(var o=[],z=0,A;(A=a[z])!=null;z++){if(typeof A=== +"number")A+="";if(A){if(typeof A==="string"&&!jb.test(A))A=b.createTextNode(A);else if(typeof A==="string"){A=A.replace(Ta,Va);var I=(Ua.exec(A)||["",""])[1].toLowerCase(),E=ka[I]||ka._default,H=E[0],R=b.createElement("div");for(R.innerHTML=E[1]+A+E[2];H--;)R=R.lastChild;if(!e.support.tbody){H=ib.test(A);I=I==="table"&&!H?R.firstChild&&R.firstChild.childNodes:E[1]===""&&!H?R.childNodes:[];for(E=I.length-1;E>=0;--E)e.nodeName(I[E],"tbody")&&!I[E].childNodes.length&&I[E].parentNode.removeChild(I[E])}!e.support.leadingWhitespace&& +va.test(A)&&R.insertBefore(b.createTextNode(va.exec(A)[0]),R.firstChild);A=R.childNodes}if(A.nodeType)o.push(A);else o=e.merge(o,A)}}if(k)for(z=0;o[z];z++)if(p&&e.nodeName(o[z],"script")&&(!o[z].type||o[z].type.toLowerCase()==="text/javascript"))p.push(o[z].parentNode?o[z].parentNode.removeChild(o[z]):o[z]);else{o[z].nodeType===1&&o.splice.apply(o,[z+1,0].concat(e.makeArray(o[z].getElementsByTagName("script"))));k.appendChild(o[z])}return o},cleanData:function(a){for(var b,k,p=e.cache,o=e.event.special, +z=e.support.deleteExpando,A=0,I;(I=a[A])!=null;A++)if(k=I[e.expando]){b=p[k];if(b.events)for(var E in b.events)o[E]?e.event.remove(I,E):La(I,E,b.handle);if(z)delete I[e.expando];else I.removeAttribute&&I.removeAttribute(e.expando);delete p[k]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Wa=/alpha\([^)]*\)/,Xa=/opacity=([^)]*)/,Ca=/float/i,Da=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"], +qb=["Top","Bottom"],rb=n.defaultView&&n.defaultView.getComputedStyle,Ya=e.support.cssFloat?"cssFloat":"styleFloat",Ea=function(a,b){return b.toUpperCase()};e.fn.css=function(a,b){return l(this,a,b,true,function(k,p,o){if(o===undefined)return e.curCSS(k,p);if(typeof o==="number"&&!kb.test(p))o+="px";e.style(k,p,o)})};e.extend({style:function(a,b,k){if(!(!a||a.nodeType===3||a.nodeType===8)){if((b==="width"||b==="height")&&parseFloat(k)<0)k=undefined;var p=a.style||a,o=k!==undefined;if(!e.support.opacity&& +b==="opacity"){if(o){p.zoom=1;b=parseInt(k,10)+""==="NaN"?"":"alpha(opacity="+k*100+")";a=p.filter||e.curCSS(a,"filter")||"";p.filter=Wa.test(a)?a.replace(Wa,b):b}return p.filter&&p.filter.indexOf("opacity=")>=0?parseFloat(Xa.exec(p.filter)[1])/100+"":""}if(Ca.test(b))b=Ya;b=b.replace(Da,Ea);if(o)p[b]=k;return p[b]}},css:function(a,b,k,p){if(b==="width"||b==="height"){var o,z=b==="width"?pb:qb;k=function(){o=b==="width"?a.offsetWidth:a.offsetHeight;p!=="border"&&e.each(z,function(){p||(o-=parseFloat(e.curCSS(a, +"padding"+this,true))||0);if(p==="margin")o+=parseFloat(e.curCSS(a,"margin"+this,true))||0;else o-=parseFloat(e.curCSS(a,"border"+this+"Width",true))||0})};a.offsetWidth!==0?k():e.swap(a,ob,k);return Math.max(0,Math.round(o))}return e.curCSS(a,b,k)},curCSS:function(a,b,k){var p,o=a.style;if(!e.support.opacity&&b==="opacity"&&a.currentStyle){p=Xa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return p===""?"1":p}if(Ca.test(b))b=Ya;if(!k&&o&&o[b])p=o[b];else if(rb){if(Ca.test(b))b= +"float";b=b.replace(lb,"-$1").toLowerCase();o=a.ownerDocument.defaultView;if(!o)return null;if(a=o.getComputedStyle(a,null))p=a.getPropertyValue(b);if(b==="opacity"&&p==="")p="1"}else if(a.currentStyle){k=b.replace(Da,Ea);p=a.currentStyle[b]||a.currentStyle[k];if(!mb.test(p)&&nb.test(p)){b=o.left;var z=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;o.left=k==="fontSize"?"1em":p||0;p=o.pixelLeft+"px";o.left=b;a.runtimeStyle.left=z}}return p},swap:function(a,b,k){var p={};for(var o in b){p[o]= +a.style[o];a.style[o]=b[o]}k.call(a);for(o in b)a.style[o]=p[o]}});if(e.expr&&e.expr.filters){e.expr.filters.hidden=function(a){var b=a.offsetWidth,k=a.offsetHeight,p=a.nodeName.toLowerCase()==="tr";return b===0&&k===0&&!p?true:b>0&&k>0&&!p?false:e.curCSS(a,"display")==="none"};e.expr.filters.visible=function(a){return!e.expr.filters.hidden(a)}}var sb=h(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i, +ra=/=\?(&|$)/,Fa=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=e.fn.load;e.fn.extend({load:function(a,b,k){if(typeof a!=="string")return zb.call(this,a);else if(!this.length)return this;var p=a.indexOf(" ");if(p>=0){var o=a.slice(p,a.length);a=a.slice(0,p)}p="GET";if(b)if(e.isFunction(b)){k=b;b=null}else if(typeof b==="object"){b=e.param(b,e.ajaxSettings.traditional);p="POST"}var z=this;e.ajax({url:a,type:p,dataType:"html",data:b,complete:function(A,I){if(I==="success"||I=== +"notmodified")z.html(o?e("
    ").append(A.responseText.replace(tb,"")).find(o):A.responseText);k&&z.each(k,[A.responseText,I,A])}});return this},serialize:function(){return e.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?e.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=e(this).val();return a==null?null:e.isArray(a)?e.map(a, +function(k){return{name:b.name,value:k}}):{name:b.name,value:a}}).get()}});e.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){e.fn[b]=function(k){return this.bind(b,k)}});e.extend({get:function(a,b,k,p){if(e.isFunction(b)){p=p||k;k=b;b=null}return e.ajax({type:"GET",url:a,data:b,success:k,dataType:p})},getScript:function(a,b){return e.get(a,null,b,"script")},getJSON:function(a,b,k){return e.get(a,b,k,"json")},post:function(a,b,k,p){if(e.isFunction(b)){p= +p||k;k=b;b={}}return e.ajax({type:"POST",url:a,data:b,success:k,dataType:p})},ajaxSetup:function(a){e.extend(e.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:window.XMLHttpRequest&&(window.location.protocol!=="file:"||!window.ActiveXObject)?function(){return new window.XMLHttpRequest}:function(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml", +html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){o.success&&o.success.call(E,I,A,Y);o.global&&p("ajaxSuccess",[Y,o])}function k(){o.complete&&o.complete.call(E,Y,A);o.global&&p("ajaxComplete",[Y,o]);o.global&&!--e.active&&e.event.trigger("ajaxStop")}function p(N,M){(o.context?e(o.context):e.event).trigger(N,M)}var o=e.extend(true,{},e.ajaxSettings,a),z, +A,I,E=a&&a.context||o,H=o.type.toUpperCase();if(o.data&&o.processData&&typeof o.data!=="string")o.data=e.param(o.data,o.traditional);if(o.dataType==="jsonp"){if(H==="GET")ra.test(o.url)||(o.url+=(Fa.test(o.url)?"&":"?")+(o.jsonp||"callback")+"=?");else if(!o.data||!ra.test(o.data))o.data=(o.data?o.data+"&":"")+(o.jsonp||"callback")+"=?";o.dataType="json"}if(o.dataType==="json"&&(o.data&&ra.test(o.data)||ra.test(o.url))){z=o.jsonpCallback||"jsonp"+sb++;if(o.data)o.data=(o.data+"").replace(ra,"="+z+ +"$1");o.url=o.url.replace(ra,"="+z+"$1");o.dataType="script";window[z]=window[z]||function(N){I=N;b();k();window[z]=undefined;try{delete window[z]}catch(M){}ba&&ba.removeChild(ea)}}if(o.dataType==="script"&&o.cache===null)o.cache=false;if(o.cache===false&&H==="GET"){var R=h(),W=o.url.replace(wb,"$1_="+R+"$2");o.url=W+(W===o.url?(Fa.test(o.url)?"&":"?")+"_="+R:"")}if(o.data&&H==="GET")o.url+=(Fa.test(o.url)?"&":"?")+o.data;o.global&&!e.active++&&e.event.trigger("ajaxStart");R=(R=xb.exec(o.url))&&(R[1]&& +R[1]!==location.protocol||R[2]!==location.host);if(o.dataType==="script"&&H==="GET"&&R){var ba=n.getElementsByTagName("head")[0]||n.documentElement,ea=n.createElement("script");ea.src=o.url;if(o.scriptCharset)ea.charset=o.scriptCharset;if(!z){var da=false;ea.onload=ea.onreadystatechange=function(){if(!da&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){da=true;b();k();ea.onload=ea.onreadystatechange=null;ba&&ea.parentNode&&ba.removeChild(ea)}}}ba.insertBefore(ea,ba.firstChild)}else{var ja= +false,Y=o.xhr();if(Y){o.username?Y.open(H,o.url,o.async,o.username,o.password):Y.open(H,o.url,o.async);try{if(o.data||a&&a.contentType)Y.setRequestHeader("Content-Type",o.contentType);if(o.ifModified){e.lastModified[o.url]&&Y.setRequestHeader("If-Modified-Since",e.lastModified[o.url]);e.etag[o.url]&&Y.setRequestHeader("If-None-Match",e.etag[o.url])}R||Y.setRequestHeader("X-Requested-With","XMLHttpRequest");Y.setRequestHeader("Accept",o.dataType&&o.accepts[o.dataType]?o.accepts[o.dataType]+", */*": +o.accepts._default)}catch(Ba){}if(o.beforeSend&&o.beforeSend.call(E,Y,o)===false){o.global&&!--e.active&&e.event.trigger("ajaxStop");Y.abort();return false}o.global&&p("ajaxSend",[Y,o]);var u=Y.onreadystatechange=function(N){if(!Y||Y.readyState===0||N==="abort"){ja||k();ja=true;if(Y)Y.onreadystatechange=e.noop}else if(!ja&&Y&&(Y.readyState===4||N==="timeout")){ja=true;Y.onreadystatechange=e.noop;A=N==="timeout"?"timeout":!e.httpSuccess(Y)?"error":o.ifModified&&e.httpNotModified(Y,o.url)?"notmodified": +"success";var M;if(A==="success")try{I=e.httpData(Y,o.dataType,o)}catch(X){A="parsererror";M=X}if(A==="success"||A==="notmodified")z||b();else e.handleError(o,Y,A,M);k();N==="timeout"&&Y.abort();if(o.async)Y=null}};try{var x=Y.abort;Y.abort=function(){Y&&x.call(Y);u("abort")}}catch(F){}o.async&&o.timeout>0&&setTimeout(function(){Y&&!ja&&u("timeout")},o.timeout);try{Y.send(H==="POST"||H==="PUT"||H==="DELETE"?o.data:null)}catch(G){e.handleError(o,Y,null,G);k()}o.async||u();return Y}}},handleError:function(a, +b,k,p){if(a.error)a.error.call(a.context||a,b,k,p);if(a.global)(a.context?e(a.context):e.event).trigger("ajaxError",[b,a,p])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var k=a.getResponseHeader("Last-Modified"),p=a.getResponseHeader("Etag");if(k)e.lastModified[b]=k;if(p)e.etag[b]=p;return a.status===304||a.status===0},httpData:function(a, +b,k){var p=a.getResponseHeader("content-type")||"",o=b==="xml"||!b&&p.indexOf("xml")>=0;a=o?a.responseXML:a.responseText;o&&a.documentElement.nodeName==="parsererror"&&e.error("parsererror");if(k&&k.dataFilter)a=k.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&p.indexOf("json")>=0)a=e.parseJSON(a);else if(b==="script"||!b&&p.indexOf("javascript")>=0)e.globalEval(a);return a},param:function(a,b){function k(A,I){if(e.isArray(I))e.each(I,function(E,H){b||/\[\]$/.test(A)?p(A,H):k(A+"["+(typeof H=== +"object"||e.isArray(H)?E:"")+"]",H)});else!b&&I!=null&&typeof I==="object"?e.each(I,function(E,H){k(A+"["+E+"]",H)}):p(A,I)}function p(A,I){I=e.isFunction(I)?I():I;o[o.length]=encodeURIComponent(A)+"="+encodeURIComponent(I)}var o=[];if(b===undefined)b=e.ajaxSettings.traditional;if(e.isArray(a)||a.jquery)e.each(a,function(){p(this.name,this.value)});else for(var z in a)k(z,a[z]);return o.join("&").replace(yb,"+")}});var Ga={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,wa,Ja=[["height","marginTop", +"marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];e.fn.extend({show:function(a,b){if(a||a===0)return this.animate(B("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");p=o.css("display");if(p==="none")p="block";o.remove();Ga[k]=p}e.data(this[a], +"olddisplay",p)}}a=0;for(b=this.length;a=0;p--)if(k[p].elem===this){b&&k[p](true);k.splice(p,1)}});b||this.dequeue();return this}});e.each({slideDown:B("show",1),slideUp:B("hide",1),slideToggle:B("toggle", +1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){e.fn[a]=function(k,p){return this.animate(b,k,p)}});e.extend({speed:function(a,b,k){var p=a&&typeof a==="object"?a:{complete:k||!k&&b||e.isFunction(a)&&a,duration:a,easing:k&&b||b&&!e.isFunction(b)&&b};p.duration=e.fx.off?0:typeof p.duration==="number"?p.duration:e.fx.speeds[p.duration]||e.fx.speeds._default;p.old=p.complete;p.complete=function(){p.queue!==false&&e(this).dequeue();e.isFunction(p.old)&&p.old.call(this)};return p},easing:{linear:function(a, +b,k,p){return k+p*a},swing:function(a,b,k,p){return(-Math.cos(a*Math.PI)/2+0.5)*p+k}},timers:[],fx:function(a,b,k){this.options=b;this.elem=a;this.prop=k;if(!b.orig)b.orig={}}});e.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(e.fx.step[this.prop]||e.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]== +null))return this.elem[this.prop];return(a=parseFloat(e.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(e.curCSS(this.elem,this.prop))||0},custom:function(a,b,k){function p(z){return o.step(z)}this.startTime=h();this.start=a;this.end=b;this.unit=k||this.unit||"px";this.now=this.start;this.pos=this.state=0;var o=this;p.elem=this.elem;if(p()&&e.timers.push(p)&&!wa)wa=setInterval(e.fx.tick,13)},show:function(){this.options.orig[this.prop]=e.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop=== +"width"||this.prop==="height"?1:0,this.cur());e(this.elem).show()},hide:function(){this.options.orig[this.prop]=e.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=h(),k=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var p in this.options.curAnim)if(this.options.curAnim[p]!==true)k=false;if(k){if(this.options.display!=null){this.elem.style.overflow= +this.options.overflow;a=e.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(e.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&e(this.elem).hide();if(this.options.hide||this.options.show)for(var o in this.options.curAnim)e.style(this.elem,o,this.options.orig[o]);this.options.complete.call(this.elem)}return false}else{o=b-this.startTime;this.state=o/this.options.duration;a=this.options.easing||(e.easing.swing?"swing":"linear");this.pos= +e.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,o,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};e.extend(e.fx,{tick:function(){for(var a=e.timers,b=0;b
    ";a.insertBefore(b,a.firstChild); +k=b.firstChild;p=k.firstChild;o=k.nextSibling.firstChild.firstChild;this.doesNotAddBorder=p.offsetTop!==5;this.doesAddBorderForTableAndCells=o.offsetTop===5;p.style.position="fixed";p.style.top="20px";this.supportsFixedPosition=p.offsetTop===20||p.offsetTop===15;p.style.position=p.style.top="";k.style.overflow="hidden";k.style.position="relative";this.subtractsBorderForOverflowNotVisible=p.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==z;a.removeChild(b);e.offset.initialize=e.noop}, +bodyOffset:function(a){var b=a.offsetTop,k=a.offsetLeft;e.offset.initialize();if(e.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(e.curCSS(a,"marginTop",true))||0;k+=parseFloat(e.curCSS(a,"marginLeft",true))||0}return{top:b,left:k}},setOffset:function(a,b,k){if(/static/.test(e.curCSS(a,"position")))a.style.position="relative";var p=e(a),o=p.offset(),z=parseInt(e.curCSS(a,"top",true),10)||0,A=parseInt(e.curCSS(a,"left",true),10)||0;if(e.isFunction(b))b=b.call(a,k,o);k={top:b.top-o.top+z,left:b.left- +o.left+A};"using"in b?b.using.call(a,k):p.css(k)}};e.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),k=this.offset(),p=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();k.top-=parseFloat(e.curCSS(a,"marginTop",true))||0;k.left-=parseFloat(e.curCSS(a,"marginLeft",true))||0;p.top+=parseFloat(e.curCSS(b[0],"borderTopWidth",true))||0;p.left+=parseFloat(e.curCSS(b[0],"borderLeftWidth",true))||0;return{top:k.top-p.top,left:k.left-p.left}},offsetParent:function(){return this.map(function(){for(var a= +this.offsetParent||n.body;a&&!/^body|html$/i.test(a.nodeName)&&e.css(a,"position")==="static";)a=a.offsetParent;return a})}});e.each(["Left","Top"],function(a,b){var k="scroll"+b;e.fn[k]=function(p){var o=this[0],z;if(!o)return null;if(p!==undefined)return this.each(function(){if(z=C(this))z.scrollTo(!a?p:e(z).scrollLeft(),a?p:e(z).scrollTop());else this[k]=p});else return(z=C(o))?"pageXOffset"in z?z[a?"pageYOffset":"pageXOffset"]:e.support.boxModel&&z.document.documentElement[k]||z.document.body[k]: +o[k]}});e.each(["Height","Width"],function(a,b){var k=b.toLowerCase();e.fn["inner"+b]=function(){return this[0]?e.css(this[0],k,false,"padding"):null};e.fn["outer"+b]=function(p){return this[0]?e.css(this[0],k,false,p?"margin":"border"):null};e.fn[k]=function(p){var o=this[0];if(!o)return p==null?null:this;if(e.isFunction(p))return this.each(function(z){var A=e(this);A[k](p.call(this,z,A[k]()))});return"scrollTo"in o&&o.document?o.document.compatMode==="CSS1Compat"&&o.document.documentElement["client"+ +b]||o.document.body["client"+b]:o.nodeType===9?Math.max(o.documentElement["client"+b],o.body["scroll"+b],o.documentElement["scroll"+b],o.body["offset"+b],o.documentElement["offset"+b]):p===undefined?e.css(o,k):this.css(k,typeof p==="string"?p:p+"px")}});s.$=s.jQuery=e});bespin.tiki.register("::embedded",{name:"embedded",dependencies:{theme_manager:"0.0.0",text_editor:"0.0.0",appconfig:"0.0.0",edit_session:"0.0.0",screen_theme:"0.0.0"}});bespin.tiki.module("embedded:index",function(){}); +bespin.tiki.register("::settings",{name:"settings",dependencies:{types:"0.0.0"}}); +bespin.tiki.module("settings:commands",function(y,s){y("bespin:plugins");y("environment");var v=y("settings").settings;s.setCommand=function(r,l){var h;if(r.setting)if(r.value===undefined)h=""+r.setting+" = "+v.get(r.setting);else{h="Setting: "+r.setting+" = "+r.value;v.set(r.setting,r.value)}else{r=v._list();h="";r.sort(function(d,f){return d.key'+d.key+" = "+d.value+"
    "})}l.done(h)};s.unsetCommand=function(r,l){v.resetValue(r.setting);l.done("Reset "+r.setting+" to default: "+v.get(r.setting))}}); +bespin.tiki.module("settings:cookie",function(y,s){var v=y("bespin:util/cookie");s.CookiePersister=function(){};s.CookiePersister.prototype={loadInitialValues:function(r){r._loadDefaultValues().then(function(){var l=v.get("settings");r._loadFromObject(JSON.parse(l))}.bind(this))},persistValue:function(r){try{var l={};r._getSettingNames().forEach(function(f){l[f]=r.get(f)});var h=JSON.stringify(l);v.set("settings",h)}catch(d){console.error("Unable to JSONify the settings! "+d)}}}}); +bespin.tiki.module("settings:index",function(y,s){var v=y("bespin:plugins").catalog,r=y("bespin:console").console,l=y("bespin:promise").Promise,h=y("bespin:promise").group,d=y("types:types");s.addSetting=function(f){y("settings").settings.addSetting(f)};s.getSettings=function(){return v.getExtensions("setting")};s.getTypeSpecFromAssignment=function(f){var m=f.assignments;f="text";if(m){var i=null;m.forEach(function(g){if(g.param.name==="setting")i=g});if(i)if((m=i.value)&&m!=="")if(m=v.getExtensionByKey("setting", +m))f=m.type}return f};s.MemorySettings=function(){};s.MemorySettings.prototype={_values:{},_deactivated:{},setPersister:function(f){(this._persister=f)&&f.loadInitialValues(this)},get:function(f){return this._values[f]},set:function(f,m){var i=v.getExtensionByKey("setting",f);if(i)if(typeof m=="string"&&i.type=="string")this._values[f]=m;else{var g=false;d.fromString(m,i.type).then(function(j){g=true;this._values[f]=j;v.publish(this,"settingChange",f,j)}.bind(this),function(j){r.error("Error setting", +f,": ",j)});if(!g){r.warn("About to set string version of ",f,"delaying typed set.");this._values[f]=m}}else{r.warn("Setting not defined: ",f,m);this._deactivated[f]=m}this._persistValue(f,m);return this},addSetting:function(f){if(f.name){!f.defaultValue===undefined&&r.error("Setting.defaultValue == undefined",f);d.isValid(f.defaultValue,f.type).then(function(m){m||r.warn("!Setting.isValid(Setting.defaultValue)",f);this.set(f.name,this._deactivated[f.name]||f.defaultValue)}.bind(this),function(m){r.error("Type error ", +m," ignoring setting ",f)})}else r.error("Setting.name == undefined. Ignoring.",f)},resetValue:function(f){var m=v.getExtensionByKey("setting",f);m?this.set(f,m.defaultValue):r.log("ignore resetValue on ",f)},resetAll:function(){this._getSettingNames().forEach(function(f){this.resetValue(f)}.bind(this))},_getSettingNames:function(){var f=[];v.getExtensions("setting").forEach(function(m){f.push(m.name)});return f},_list:function(){var f=[];this._getSettingNames().forEach(function(m){f.push({key:m, +value:this.get(m)})}.bind(this));return f},_persistValue:function(f,m){var i=this._persister;i&&i.persistValue(this,f,m)},_loadInitialValues:function(){var f=this._persister;f?f.loadInitialValues(this):this._loadDefaultValues()},_loadDefaultValues:function(){return this._loadFromObject(this._defaultValues())},_loadFromObject:function(f){var m=[],i=function(B){return function(C){this.set(B,C)}};for(var g in f)if(f.hasOwnProperty(g)){var j=f[g],q=v.getExtensionByKey("setting",g);if(q){j=d.fromString(j, +q.type);q=i(g);j.then(q);m.push(j)}}var t=new l;h(m).then(function(){t.resolve()});return t},_saveToObject:function(){var f=[],m={};this._getSettingNames().forEach(function(g){var j=this.get(g),q=v.getExtensionByKey("setting",g);if(q){j=d.toString(j,q.type);j.then(function(t){m[g]=t});f.push(j)}}.bind(this));var i=new l;h(f).then(function(){i.resolve(m)});return i},_defaultValues:function(){var f={};v.getExtensions("setting").forEach(function(m){f[m.name]=m.defaultValue});return f}};s.settings=new s.MemorySettings}); +bespin.tiki.register("::appconfig",{name:"appconfig",dependencies:{jquery:"0.0.0",canon:"0.0.0",settings:"0.0.0"}}); +bespin.tiki.module("appconfig:index",function(y,s){var v=y("jquery").$,r=y("settings").settings,l=y("bespin:promise").group,h=y("bespin:promise").Promise,d=y("bespin:console").console,f=y("bespin:util/stacktrace").Trace,m=y("bespin:util/util"),i=true;s.launch=function(q){var t=new h;v("#_bespin_loading").remove();var B;if(i){B=bespin.tiki.require;i=false}else B=(new (bespin.tiki.require("bespin:sandbox").Sandbox)).createRequire({id:"index",ownerPackage:bespin.tiki.loader.anonymousPackage});var C= +B("bespin:plugins").catalog;q=q||{};s.normalizeConfig(C,q);var e=q.objects;for(var K in e)C.registerObject(K,e[K]);for(var L in q.settings)r.set(L,q.settings[L]);var n=function(){var D=B("environment").env,J=D.editor;if(J){q.lineNumber&&J.setLineNumber(q.lineNumber);if(q.stealFocus)J.focus=true;if(q.readOnly)J.readOnly=q.readOnly;if(q.syntax)J.syntax=q.syntax}if(J=C.getObject("commandLine"))D.commandLine=J;C.publish(this,"appLaunched");t.resolve(D)}.bind(this),w=new h;w.then(function(){e.loginController? +C.createObject("loginController").then(function(D){D.showLogin().then(function(J){q.objects.session.arguments.push(J);s.launchEditor(C,q).then(n,t.reject.bind(t))})}):s.launchEditor(C,q).then(n,t.reject.bind(t))},function(D){t.reject(D)});C.plugins.theme_manager?bespin.tiki.require.ensurePackage("::theme_manager",function(){var D=B("theme_manager");q.theme.basePlugin&&D.setBasePlugin(q.theme.basePlugin);q.theme.standard&&D.setStandardTheme(q.theme.standard);D.startParsing().then(function(){w.resolve()}, +function(J){w.reject(J)})}):w.resolve();return t};s.normalizeConfig=function(q,t){if(t.objects===undefined)t.objects={};if(t.autoload===undefined)t.autoload=[];if(t.theme===undefined)t.theme={};if(!t.theme.basePlugin&&q.plugins.screen_theme)t.theme.basePlugin="screen_theme";if(!t.initialContent)t.initialContent="";if(!t.settings)t.settings={};if(!t.objects.notifier&&q.plugins.notifier)t.objects.notifier={};if(!t.objects.loginController&&q.plugins.userident)t.objects.loginController={};if(!t.objects.fileHistory&& +q.plugins.file_history)t.objects.fileHistory={factory:"file_history",arguments:["session"],objects:{"0":"session"}};if(!t.objects.server&&q.plugins.bespin_server){t.objects.server={factory:"bespin_server"};t.objects.filesource={factory:"bespin_filesource",arguments:["server"],objects:{"0":"server"}}}if(!t.objects.files&&q.plugins.filesystem&&t.objects.filesource)t.objects.files={arguments:["filesource"],objects:{"0":"filesource"}};if(!t.objects.editor)t.objects.editor={factory:"text_editor",arguments:[t.initialContent]}; +if(!t.objects.session)t.objects.session={arguments:["editor"],objects:{"0":"editor"}};if(!t.objects.commandLine&&q.plugins.command_line)t.objects.commandLine={};if(t.gui===undefined)t.gui={};q={};for(var B in t.gui){var C=t.gui[B];if(C.component)q[C.component]=true}if(!t.gui.center&&t.objects.editor&&!q.editor)t.gui.center={component:"editor"};if(!t.gui.south&&t.objects.commandLine&&!q.commandLine)t.gui.south={component:"commandLine"}};s.launchEditor=function(q,t){var B=new h;if(t===null){d.error("Cannot start editor without a configuration!"); +B.reject("Cannot start editor without a configuration!");return B}g(q,t).then(function(){j(q,t,B)},function(C){d.error("Error while creating objects");(new f(C)).log();B.reject(C)});return B};var g=function(q,t){var B=[];for(var C in t.objects)B.push(q.createObject(C));return l(B)},j=function(q,t,B){var C=document.createElement("div");C.setAttribute("class","container");var e=document.createElement("div");e.setAttribute("class","center-container");C.appendChild(e);var K=t.element||document.body;m.addClass(K, +"bespin");K.appendChild(C);for(var L in t.gui){var n=t.gui[L],w=q.getObject(n.component);if(!w){q="Cannot find object "+n.component+" to attach to the Bespin UI";d.error(q);B.reject(q);return}K=w.element;if(!K){q="Component "+n.component+' does not have an "element" attribute to attach to the Bespin UI';d.error(q);B.reject(q);return}v(K).addClass(L);L=="west"||L=="east"||L=="center"?e.appendChild(K):C.appendChild(K);w.elementAppended&&w.elementAppended()}B.resolve()}}); +bespin.tiki.register("::events",{name:"events",dependencies:{traits:"0.0.0"}});bespin.tiki.module("events:index",function(y,s){s.Event=function(){var v=[],r=function(){var l=arguments;v.forEach(function(h){h.func.apply(null,l)})};r.add=function(){arguments.length==1?v.push({ref:arguments[0],func:arguments[0]}):v.push({ref:arguments[0],func:arguments[1]})};r.remove=function(l){v=v.filter(function(h){return l!==h.ref})};r.removeAll=function(){v=[]};return r}}); +bespin.tiki.register("::screen_theme",{name:"screen_theme",dependencies:{theme_manager:"0.0.0"}});bespin.tiki.module("screen_theme:index",function(){}); +(function(){var y=bespin.tiki.require("jquery").$;y(document).ready(function(){bespin.tiki.require("bespin:plugins").catalog.registerMetadata({text_editor:{resourceURL:"resources/text_editor/",description:"Canvas-based text editor component and many common editing commands",dependencies:{completion:"0.0.0",undomanager:"0.0.0",settings:"0.0.0",canon:"0.0.0",rangeutils:"0.0.0",traits:"0.0.0",theme_manager:"0.0.0",keyboard:"0.0.0",edit_session:"0.0.0",syntax_manager:"0.0.0"},testmodules:["tests/controllers/testLayoutmanager", +"tests/models/testTextstorage","tests/testScratchcanvas","tests/utils/testRect"],provides:[{action:"new",pointer:"views/editor#EditorView",ep:"factory",name:"text_editor"},{pointer:"views/editor#EditorView",ep:"appcomponent",name:"editor_view"},{predicates:{isTextView:true},pointer:"commands/editing#backspace",ep:"command",key:"backspace",name:"backspace"},{predicates:{isTextView:true},pointer:"commands/editing#deleteCommand",ep:"command",key:"delete",name:"delete"},{description:"Delete all lines currently selected", +key:"ctrl_d",predicates:{isTextView:true},pointer:"commands/editing#deleteLines",ep:"command",name:"deletelines"},{description:"Create a new, empty line below the current one",key:"ctrl_return",predicates:{isTextView:true},pointer:"commands/editing#openLine",ep:"command",name:"openline"},{description:"Join the current line with the following",key:"ctrl_shift_j",predicates:{isTextView:true},pointer:"commands/editing#joinLines",ep:"command",name:"joinline"},{params:[{defaultValue:"",type:"text",name:"text", +description:"The text to insert"}],pointer:"commands/editing#insertText",ep:"command",name:"insertText"},{predicates:{completing:false,isTextView:true},pointer:"commands/editing#newline",ep:"command",key:"return",name:"newline"},{predicates:{completing:false,isTextView:true},pointer:"commands/editing#tab",ep:"command",key:"tab",name:"tab"},{predicates:{isTextView:true},pointer:"commands/editing#untab",ep:"command",key:"shift_tab",name:"untab"},{predicates:{isTextView:true},ep:"command",name:"move"}, +{description:"Repeat the last search (forward)",pointer:"commands/editor#findNextCommand",ep:"command",key:"ctrl_g",name:"findnext"},{description:"Repeat the last search (backward)",pointer:"commands/editor#findPrevCommand",ep:"command",key:"ctrl_shift_g",name:"findprev"},{predicates:{completing:false,isTextView:true},pointer:"commands/movement#moveDown",ep:"command",key:"down",name:"move down"},{predicates:{isTextView:true},pointer:"commands/movement#moveLeft",ep:"command",key:"left",name:"move left"}, +{predicates:{isTextView:true},pointer:"commands/movement#moveRight",ep:"command",key:"right",name:"move right"},{predicates:{completing:false,isTextView:true},pointer:"commands/movement#moveUp",ep:"command",key:"up",name:"move up"},{predicates:{isTextView:true},ep:"command",name:"select"},{predicates:{isTextView:true},pointer:"commands/movement#selectDown",ep:"command",key:"shift_down",name:"select down"},{predicates:{isTextView:true},pointer:"commands/movement#selectLeft",ep:"command",key:"shift_left", +name:"select left"},{predicates:{isTextView:true},pointer:"commands/movement#selectRight",ep:"command",key:"shift_right",name:"select right"},{predicates:{isTextView:true},pointer:"commands/movement#selectUp",ep:"command",key:"shift_up",name:"select up"},{predicates:{isTextView:true},pointer:"commands/movement#moveLineEnd",ep:"command",key:["end","ctrl_right"],name:"move lineend"},{predicates:{isTextView:true},pointer:"commands/movement#selectLineEnd",ep:"command",key:["shift_end","ctrl_shift_right"], +name:"select lineend"},{predicates:{isTextView:true},pointer:"commands/movement#moveDocEnd",ep:"command",key:"ctrl_down",name:"move docend"},{predicates:{isTextView:true},pointer:"commands/movement#selectDocEnd",ep:"command",key:"ctrl_shift_down",name:"select docend"},{predicates:{isTextView:true},pointer:"commands/movement#moveLineStart",ep:"command",key:["home","ctrl_left"],name:"move linestart"},{predicates:{isTextView:true},pointer:"commands/movement#selectLineStart",ep:"command",key:["shift_home", +"ctrl_shift_left"],name:"select linestart"},{predicates:{isTextView:true},pointer:"commands/movement#moveDocStart",ep:"command",key:"ctrl_up",name:"move docstart"},{predicates:{isTextView:true},pointer:"commands/movement#selectDocStart",ep:"command",key:"ctrl_shift_up",name:"select docstart"},{predicates:{isTextView:true},pointer:"commands/movement#moveNextWord",ep:"command",key:["alt_right"],name:"move nextword"},{predicates:{isTextView:true},pointer:"commands/movement#selectNextWord",ep:"command", +key:["alt_shift_right"],name:"select nextword"},{predicates:{isTextView:true},pointer:"commands/movement#movePreviousWord",ep:"command",key:["alt_left"],name:"move prevword"},{predicates:{isTextView:true},pointer:"commands/movement#selectPreviousWord",ep:"command",key:["alt_shift_left"],name:"select prevword"},{predicates:{isTextView:true},pointer:"commands/movement#selectAll",ep:"command",key:["ctrl_a","meta_a"],name:"select all"},{predicates:{isTextView:true},ep:"command",name:"scroll"},{predicates:{isTextView:true}, +pointer:"commands/scrolling#scrollDocStart",ep:"command",key:"ctrl_home",name:"scroll start"},{predicates:{isTextView:true},pointer:"commands/scrolling#scrollDocEnd",ep:"command",key:"ctrl_end",name:"scroll end"},{predicates:{isTextView:true},pointer:"commands/scrolling#scrollPageDown",ep:"command",key:"pagedown",name:"scroll down"},{predicates:{isTextView:true},pointer:"commands/scrolling#scrollPageUp",ep:"command",key:"pageup",name:"scroll up"},{pointer:"commands/editor#lcCommand",description:"Change all selected text to lowercase", +withKey:"CMD SHIFT L",ep:"command",name:"lc"},{pointer:"commands/editor#detabCommand",description:"Convert tabs to spaces.",params:[{defaultValue:null,type:"text",name:"tabsize",description:"Optionally, specify a tab size. (Defaults to setting.)"}],ep:"command",name:"detab"},{pointer:"commands/editor#entabCommand",description:"Convert spaces to tabs.",params:[{defaultValue:null,type:"text",name:"tabsize",description:"Optionally, specify a tab size. (Defaults to setting.)"}],ep:"command",name:"entab"}, +{pointer:"commands/editor#trimCommand",description:"trim trailing or leading whitespace from each line in selection",params:[{defaultValue:"both",type:{data:[{name:"left"},{name:"right"},{name:"both"}],name:"selection"},name:"side",description:"Do we trim from the left, right or both"}],ep:"command",name:"trim"},{pointer:"commands/editor#ucCommand",description:"Change all selected text to uppercase",withKey:"CMD SHIFT U",ep:"command",name:"uc"},{predicates:{isTextView:true},pointer:"controllers/undo#undoManagerCommand", +ep:"command",key:["ctrl_shift_z"],name:"redo"},{predicates:{isTextView:true},pointer:"controllers/undo#undoManagerCommand",ep:"command",key:["ctrl_z"],name:"undo"},{description:"The distance in characters between each tab",defaultValue:8,type:"number",ep:"setting",name:"tabstop"},{description:"Customize the keymapping",defaultValue:"{}",type:"text",ep:"setting",name:"customKeymapping"},{description:"The keymapping to use",defaultValue:"standard",type:"text",ep:"setting",name:"keymapping"},{description:"The editor font size in pixels", +defaultValue:14,type:"number",ep:"setting",name:"fontsize"},{description:"The editor font face",defaultValue:"Monaco, Lucida Console, monospace",type:"text",ep:"setting",name:"fontface"},{defaultValue:{color:"#e5c138",paddingLeft:5,backgroundColor:"#4c4a41",paddingRight:10},ep:"themevariable",name:"gutter"},{defaultValue:{color:"#e6e6e6",selectedTextBackgroundColor:"#526da5",backgroundColor:"#2a211c",cursorColor:"#879aff",unfocusedCursorBackgroundColor:"#73171e",unfocusedCursorColor:"#ff0033"},ep:"themevariable", +name:"editor"},{defaultValue:{comment:"#666666",directive:"#999999",keyword:"#42A8ED",plain:"#e6e6e6",error:"#ff0000",operator:"#88BBFF",identifier:"#D841FF",string:"#039A0A"},ep:"themevariable",name:"highlighter"},{defaultValue:{nibStrokeStyle:"rgb(150, 150, 150)",fullAlpha:1,barFillStyle:"rgb(0, 0, 0)",particalAlpha:0.3,barFillGradientBottomStop:"rgb(44, 44, 44)",backgroundStyle:"#2A211C",thickness:17,padding:5,trackStrokeStyle:"rgb(150, 150, 150)",nibArrowStyle:"rgb(255, 255, 255)",barFillGradientBottomStart:"rgb(22, 22, 22)", +barFillGradientTopStop:"rgb(40, 40, 40)",barFillGradientTopStart:"rgb(90, 90, 90)",nibStyle:"rgb(100, 100, 100)",trackFillStyle:"rgba(50, 50, 50, 0.8)"},ep:"themevariable",name:"scroller"},{description:"Event: Notify when something within the editor changed.",params:[{required:true,name:"pointer",description:"Function that is called whenever a change happened."}],ep:"extensionpoint",name:"editorChange"}],type:"plugins/supported",name:"text_editor"},less:{resourceURL:"resources/less/",description:"Leaner CSS", +contributors:[],author:"Alexis Sellier ",url:"http://lesscss.org",version:"1.0.11",dependencies:{},testmodules:[],provides:[],keywords:["css","parser","lesscss","browser"],type:"plugins/thirdparty",name:"less"},theme_manager_base:{resourceURL:"resources/theme_manager_base/",name:"theme_manager_base",share:true,environments:{main:true},dependencies:{},testmodules:[],provides:[{description:"(Less)files holding the CSS style information for the UI.",params:[{required:true,name:"url", +description:"Name of the ThemeStylesFile - can also be an array of files."}],ep:"extensionpoint",name:"themestyles"},{description:"Event: Notify when the theme(styles) changed.",params:[{required:true,name:"pointer",description:"Function that is called whenever the theme is changed."}],ep:"extensionpoint",name:"themeChange"},{indexOn:"name",description:"A theme is a way change the look of the application.",params:[{required:false,name:"url",description:"Name of a ThemeStylesFile that holds theme specific CSS rules - can also be an array of files."}, +{required:true,name:"pointer",description:"Function that returns the ThemeData"}],ep:"extensionpoint",name:"theme"}],type:"plugins/supported",description:"Defines extension points required for theming"},canon:{resourceURL:"resources/canon/",name:"canon",environments:{main:true,worker:false},dependencies:{environment:"0.0.0",events:"0.0.0",settings:"0.0.0"},testmodules:[],provides:[{indexOn:"name",description:"A command is a bit of functionality with optional typed arguments which can do something small like moving the cursor around the screen, or large like cloning a project from VCS.", +ep:"extensionpoint",name:"command"},{description:"An extension point to be called whenever a new command begins output.",ep:"extensionpoint",name:"addedRequestOutput"},{description:"A dimensionsChanged is a way to be notified of changes to the dimension of Bespin",ep:"extensionpoint",name:"dimensionsChanged"},{description:"How many typed commands do we recall for reference?",defaultValue:50,type:"number",ep:"setting",name:"historyLength"},{action:"create",pointer:"history#InMemoryHistory",ep:"factory", +name:"history"}],type:"plugins/supported",description:"Manages commands"},traits:{resourceURL:"resources/traits/",description:"Traits library, traitsjs.org",dependencies:{},testmodules:[],provides:[],type:"plugins/thirdparty",name:"traits"},keyboard:{resourceURL:"resources/keyboard/",description:"Keyboard shortcuts",dependencies:{canon:"0.0",settings:"0.0"},testmodules:["tests/testKeyboard"],provides:[{description:"A keymapping defines how keystrokes are interpreted.",params:[{required:true,name:"states", +description:"Holds the states and all the informations about the keymapping. See docs: pluginguide/keymapping"}],ep:"extensionpoint",name:"keymapping"}],type:"plugins/supported",name:"keyboard"},worker_manager:{resourceURL:"resources/worker_manager/",description:"Manages a web worker on the browser side",dependencies:{canon:"0.0.0",events:"0.0.0",underscore:"0.0.0"},testmodules:[],provides:[{description:"Low-level web worker control (for plugin development)",ep:"command",name:"worker"},{description:"Restarts all web workers (for plugin development)", +pointer:"#workerRestartCommand",ep:"command",name:"worker restart"}],type:"plugins/supported",name:"worker_manager"},edit_session:{resourceURL:"resources/edit_session/",description:"Ties together the files being edited with the views on screen",dependencies:{events:"0.0.0"},testmodules:["tests/testSession"],provides:[{action:"call",pointer:"#createSession",ep:"factory",name:"session"}],type:"plugins/supported",name:"edit_session"},syntax_manager:{resourceURL:"resources/syntax_manager/",name:"syntax_manager", +environments:{main:true,worker:false},dependencies:{worker_manager:"0.0.0",events:"0.0.0",underscore:"0.0.0",syntax_directory:"0.0.0"},testmodules:[],provides:[],type:"plugins/supported",description:"Provides syntax highlighting services for the editor"},completion:{resourceURL:"resources/completion/",description:"Code completion support",dependencies:{jquery:"0.0.0",ctags:"0.0.0",rangeutils:"0.0.0",canon:"0.0.0",underscore:"0.0.0"},testmodules:[],provides:[{indexOn:"name",description:"Code completion support for specific languages", +ep:"extensionpoint",name:"completion"},{description:"Accept the chosen completion",key:["return","tab"],predicates:{completing:true},pointer:"controller#completeCommand",ep:"command",name:"complete"},{description:"Abandon the completion",key:"escape",predicates:{completing:true},pointer:"controller#completeCancelCommand",ep:"command",name:"complete cancel"},{description:"Choose the completion below",key:"down",predicates:{completing:true},pointer:"controller#completeDownCommand",ep:"command",name:"complete down"}, +{description:"Choose the completion above",key:"up",predicates:{completing:true},pointer:"controller#completeUpCommand",ep:"command",name:"complete up"}],type:"plugins/supported",name:"completion"},environment:{testmodules:[],dependencies:{settings:"0.0.0"},resourceURL:"resources/environment/",name:"environment",type:"plugins/supported"},undomanager:{resourceURL:"resources/undomanager/",description:"Manages undoable events",testmodules:["tests/testUndomanager"],provides:[{pointer:"#undoManagerCommand", +ep:"command",key:["ctrl_shift_z"],name:"redo"},{pointer:"#undoManagerCommand",ep:"command",key:["ctrl_z"],name:"undo"}],type:"plugins/supported",name:"undomanager"},rangeutils:{testmodules:["tests/test"],type:"plugins/supported",resourceURL:"resources/rangeutils/",description:"Utility functions for dealing with ranges of text",name:"rangeutils"},stylesheet:{resourceURL:"resources/stylesheet/",name:"stylesheet",environments:{worker:true},dependencies:{standard_syntax:"0.0.0"},testmodules:[],provides:[{pointer:"#CSSSyntax", +ep:"syntax",fileexts:["css","less"],name:"css"}],type:"plugins/supported",description:"CSS syntax highlighter"},html:{resourceURL:"resources/html/",name:"html",environments:{worker:true},dependencies:{standard_syntax:"0.0.0"},testmodules:[],provides:[{pointer:"#HTMLSyntax",ep:"syntax",fileexts:["htm","html"],name:"html"}],type:"plugins/supported",description:"HTML syntax highlighter"},js_syntax:{resourceURL:"resources/js_syntax/",name:"js_syntax",environments:{worker:true},dependencies:{standard_syntax:"0.0.0"}, +testmodules:[],provides:[{pointer:"#JSSyntax",ep:"syntax",fileexts:["js","json"],name:"js"}],type:"plugins/supported",description:"JavaScript syntax highlighter"},ctags:{resourceURL:"resources/ctags/",description:"Reads and writes tag files",dependencies:{traits:"0.0.0",underscore:"0.0.0"},testmodules:[],type:"plugins/supported",name:"ctags"},events:{resourceURL:"resources/events/",description:"Dead simple event implementation",dependencies:{traits:"0.0"},testmodules:["tests/test"],provides:[],type:"plugins/supported", +name:"events"},theme_manager:{resourceURL:"resources/theme_manager/",name:"theme_manager",share:true,environments:{main:true,worker:false},dependencies:{theme_manager_base:"0.0.0",settings:"0.0.0",events:"0.0.0",less:"0.0.0"},testmodules:[],provides:[{unregister:"themestyles#unregisterThemeStyles",register:"themestyles#registerThemeStyles",ep:"extensionhandler",name:"themestyles"},{unregister:"index#unregisterTheme",register:"index#registerTheme",ep:"extensionhandler",name:"theme"},{defaultValue:"standard", +description:"The theme plugin's name to use. If set to 'standard' no theme will be used",type:"text",ep:"setting",name:"theme"},{pointer:"#appLaunched",ep:"appLaunched"}],type:"plugins/supported",description:"Handles colors in Bespin"},standard_syntax:{resourceURL:"resources/standard_syntax/",description:"Easy-to-use basis for syntax engines",environments:{worker:true},dependencies:{syntax_worker:"0.0.0",syntax_directory:"0.0.0",underscore:"0.0.0"},testmodules:[],type:"plugins/supported",name:"standard_syntax"}, +types:{resourceURL:"resources/types/",description:"Defines parameter types for commands",testmodules:["tests/testBasic","tests/testTypes"],provides:[{indexOn:"name",description:"Commands can accept various arguments that the user enters or that are automatically supplied by the environment. Those arguments have types that define how they are supplied or completed. The pointer points to an object with methods convert(str value) and getDefault(). Both functions have `this` set to the command's `takes` parameter. If getDefault is not defined, the default on the command's `takes` is used, if there is one. The object can have a noInput property that is set to true to reflect that this type is provided directly by the system. getDefault must be defined in that case.", +ep:"extensionpoint",name:"type"},{description:"Text that the user needs to enter.",pointer:"basic#text",ep:"type",name:"text"},{description:"A JavaScript number",pointer:"basic#number",ep:"type",name:"number"},{description:"A true/false value",pointer:"basic#bool",ep:"type",name:"boolean"},{description:"An object that converts via JavaScript",pointer:"basic#object",ep:"type",name:"object"},{description:"A string that is constrained to be one of a number of pre-defined values",pointer:"basic#selection", +ep:"type",name:"selection"},{description:"A type which we don't understand from the outset, but which we hope context can help us with",ep:"type",name:"deferred"}],type:"plugins/supported",name:"types"},jquery:{testmodules:[],resourceURL:"resources/jquery/",name:"jquery",type:"plugins/thirdparty"},embedded:{testmodules:[],dependencies:{theme_manager:"0.0.0",text_editor:"0.0.0",appconfig:"0.0.0",edit_session:"0.0.0",screen_theme:"0.0.0"},resourceURL:"resources/embedded/",name:"embedded",type:"plugins/supported"}, +settings:{resourceURL:"resources/settings/",description:"Infrastructure and commands for managing user preferences",share:true,dependencies:{types:"0.0"},testmodules:[],provides:[{description:"Storage for the customizable Bespin settings",pointer:"index#settings",ep:"appcomponent",name:"settings"},{indexOn:"name",description:"A setting is something that the application offers as a way to customize how it works",register:"index#addSetting",ep:"extensionpoint",name:"setting"},{description:"A settingChange is a way to be notified of changes to a setting", +ep:"extensionpoint",name:"settingChange"},{pointer:"commands#setCommand",description:"define and show settings",params:[{defaultValue:null,type:{pointer:"settings:index#getSettings",name:"selection"},name:"setting",description:"The name of the setting to display or alter"},{defaultValue:null,type:{pointer:"settings:index#getTypeSpecFromAssignment",name:"deferred"},name:"value",description:"The new value for the chosen setting"}],ep:"command",name:"set"},{pointer:"commands#unsetCommand",description:"unset a setting entirely", +params:[{type:{pointer:"settings:index#getSettings",name:"selection"},name:"setting",description:"The name of the setting to return to defaults"}],ep:"command",name:"unset"}],type:"plugins/supported",name:"settings"},appconfig:{resourceURL:"resources/appconfig/",description:"Instantiates components and displays the GUI based on configuration.",dependencies:{jquery:"0.0.0",canon:"0.0.0",settings:"0.0.0"},testmodules:[],provides:[{description:"Event: Fired when the app is completely launched.",ep:"extensionpoint", +name:"appLaunched"}],type:"plugins/supported",name:"appconfig"},syntax_worker:{resourceURL:"resources/syntax_worker/",description:"Coordinates multiple syntax engines",environments:{worker:true},dependencies:{syntax_directory:"0.0.0",underscore:"0.0.0"},testmodules:[],type:"plugins/supported",name:"syntax_worker"},screen_theme:{resourceURL:"resources/screen_theme/",description:"Bespins standard theme basePlugin",dependencies:{theme_manager:"0.0.0"},testmodules:[],provides:[{url:["theme.less"],ep:"themestyles"}, +{defaultValue:"@global_font",ep:"themevariable",name:"container_font"},{defaultValue:"@global_font_size",ep:"themevariable",name:"container_font_size"},{defaultValue:"@global_container_background",ep:"themevariable",name:"container_bg"},{defaultValue:"@global_color",ep:"themevariable",name:"container_color"},{defaultValue:"@global_line_height",ep:"themevariable",name:"container_line_height"},{defaultValue:"@global_pane_background",ep:"themevariable",name:"pane_bg"},{defaultValue:"@global_pane_border_radius", +ep:"themevariable",name:"pane_border_radius"},{defaultValue:"@global_form_font",ep:"themevariable",name:"form_font"},{defaultValue:"@global_form_font_size",ep:"themevariable",name:"form_font_size"},{defaultValue:"@global_form_line_height",ep:"themevariable",name:"form_line_height"},{defaultValue:"@global_form_color",ep:"themevariable",name:"form_color"},{defaultValue:"@global_form_text_shadow",ep:"themevariable",name:"form_text_shadow"},{defaultValue:"@global_pane_link_color",ep:"themevariable",name:"pane_a_color"}, +{defaultValue:"@global_font",ep:"themevariable",name:"pane_font"},{defaultValue:"@global_font_size",ep:"themevariable",name:"pane_font_size"},{defaultValue:"@global_pane_text_shadow",ep:"themevariable",name:"pane_text_shadow"},{defaultValue:"@global_pane_h1_font",ep:"themevariable",name:"pane_h1_font"},{defaultValue:"@global_pane_h1_font_size",ep:"themevariable",name:"pane_h1_font_size"},{defaultValue:"@global_pane_h1_color",ep:"themevariable",name:"pane_h1_color"},{defaultValue:"@global_font_size * 1.8", +ep:"themevariable",name:"pane_line_height"},{defaultValue:"@global_pane_color",ep:"themevariable",name:"pane_color"},{defaultValue:"@global_text_shadow",ep:"themevariable",name:"pane_text_shadow"},{defaultValue:"@global_font",ep:"themevariable",name:"button_font"},{defaultValue:"@global_font_size",ep:"themevariable",name:"button_font_size"},{defaultValue:"@global_button_color",ep:"themevariable",name:"button_color"},{defaultValue:"@global_button_background",ep:"themevariable",name:"button_bg"},{defaultValue:"@button_bg - #063A27", +ep:"themevariable",name:"button_bg2"},{defaultValue:"@button_bg - #194A5E",ep:"themevariable",name:"button_border"},{defaultValue:"@global_control_background",ep:"themevariable",name:"control_bg"},{defaultValue:"@global_control_color",ep:"themevariable",name:"control_color"},{defaultValue:"@global_control_border",ep:"themevariable",name:"control_border"},{defaultValue:"@global_control_border_radius",ep:"themevariable",name:"control_border_radius"},{defaultValue:"@global_control_active_background", +ep:"themevariable",name:"control_active_bg"},{defaultValue:"@global_control_active_border",ep:"themevariable",name:"control_active_border"},{defaultValue:"@global_control_active_color",ep:"themevariable",name:"control_active_color"},{defaultValue:"@global_control_active_inset_color",ep:"themevariable",name:"control_active_inset_color"}],type:"plugins/supported",name:"screen_theme"}})})})(); +(function(){var y=bespin.tiki.require("jquery").$,s=function(v,r,l){v=v.style[l]||document.defaultView.getComputedStyle(v,"").getPropertyValue(l);if(!v||v=="auto"||v=="intrinsic")v=r.style[l];return v};bespin.useBespin=function(v,r){var l=bespin.tiki.require("bespin:util/util"),h={},d=h.settings;r=r||{};for(var f in r)h[f]=r[f];r=h.settings;if(d!==undefined)for(f in d)if(r[f]===undefined)h.settings[f]=d[f];var m=null,i=new (bespin.tiki.require("bespin:promise").Promise);bespin.tiki.require.ensurePackage("::appconfig", +function(){var g=bespin.tiki.require("appconfig");if(l.isString(v))v=document.getElementById(v);if(l.none(h.initialContent))h.initialContent=v.value||v.innerHTML;v.innerHTML="";if(v.type=="textarea"){var j=v.parentNode,q=document.createElement("div"),t=function(){var C="position:relative;";["margin-top","margin-left","margin-right","margin-bottom"].forEach(function(L){C+=L+":"+s(v,q,L)+";"});var e=s(v,q,"width"),K=s(v,q,"height");C+="height:"+K+";width:"+e+";";C+="display:inline-block;";q.setAttribute("style", +C)};window.addEventListener("resize",t,false);t();for(v.nextSibling?j.insertBefore(q,v.nextSibling):j.appendChild(q);j!==document;){if(j.tagName.toUpperCase()==="FORM"){var B=j.onsubmit;j.onsubmit=function(C){v.value=m.editor.value;v.innerHTML=m.editor.value;B&&B.call(this,C)};break}j=j.parentNode}v.style.display="none";h.element=q;if(!l.none(v.getAttribute("readonly")))h.readOnly=true}else h.element=v;g.launch(h).then(function(C){m=C;i.resolve(C)})});return i};y(document).ready(function(){for(var v= +[],r=document.querySelectorAll(".bespin"),l=0;l/,tag:"operator",then:e},{regex:/^./,tag:"keyword",then:a+"_attrName"}];c[a+"_attrName"]=[{regex:/^\s+/,tag:"plain",then:a+"_afterAttrName"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^=/,tag:"operator",then:a+"_beforeAttrValue"},{regex:/^>/,tag:"operator", +then:e},{regex:/^["'<]+/,tag:"error"},{regex:/^[^ \t\n\/=>"'<]+/,tag:"keyword"}];c[a+"_afterAttrName"]=[{regex:/^\s+/,tag:"plain"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^=/,tag:"operator",then:a+"_beforeAttrValue"},{regex:/^>/,tag:"operator",then:e},{regex:/^./,tag:"keyword",then:a+"_attrName"}];c[a+"_beforeAttrValue"]=[{regex:/^\s+/,tag:"plain"},{regex:/^"/,tag:"string",then:a+"_attrValueQQ"},{regex:/^(?=&)/,tag:"plain",then:a+"_attrValueU"},{regex:/^'/,tag:"string", +then:a+"_attrValueQ"},{regex:/^>/,tag:"error",then:e},{regex:/^./,tag:"string",then:a+"_attrValueU"}];c[a+"_attrValueQQ"]=[{regex:/^"/,tag:"string",then:a+"_afterAttrValueQ"},{regex:/^[^"]+/,tag:"string"}];c[a+"_attrValueQ"]=[{regex:/^'/,tag:"string",then:a+"_afterAttrValueQ"},{regex:/^[^']+/,tag:"string"}];c[a+"_attrValueU"]=[{regex:/^\s/,tag:"string",then:a+"_beforeAttrName"},{regex:/^>/,tag:"operator",then:e},{regex:/[^ \t\n>]+/,tag:"string"}];c[a+"_afterAttrValueQ"]=[{regex:/^\s/,tag:"plain", +then:a+"_beforeAttrName"},{regex:/^\//,tag:"operator",then:a+"_selfClosingStartTag"},{regex:/^>/,tag:"operator",then:e},{regex:/^(?=.)/,tag:"operator",then:a+"_beforeAttrName"}];c[a+"_selfClosingStartTag"]=[{regex:/^>/,tag:"operator",then:"start"},{regex:/^./,tag:"error",then:a+"_beforeAttrName"}]};c={start:[{regex:/^[^<]+/,tag:"plain"},{regex:/^ + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    Actions
    + + Open Files: + + + + +
    +
    +
    +
    + + Pass: + Fail: + Test: + + +
    +

    +

    +

    +
    +
    +
    + Double Click: + Single Click: +
    +
    +
    + Element: +
    + + Controller: + +
    + Validation: +
    +
    +
    + + +
    +

    +
    +
    +

    + +

    +
    +

    +
    +
    + + + + diff --git a/mail/test/resources/mozmill/mozmill/extension/content/mozmill.js b/mail/test/resources/mozmill/mozmill/extension/content/mozmill.js new file mode 100644 index 0000000000..a9a733417f --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/mozmill.js @@ -0,0 +1,77 @@ +// ***** BEGIN LICENSE BLOCK ***** +// Version: MPL 1.1/GPL 2.0/LGPL 2.1 +// +// The contents of this file are subject to the Mozilla Public License Version +// 1.1 (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the +// License. +// +// The Original Code is Mozilla Corporation Code. +// +// The Initial Developer of the Original Code is +// Adam Christian. +// Portions created by the Initial Developer are Copyright (C) 2008 +// the Initial Developer. All Rights Reserved. +// +// Contributor(s): +// Adam Christian +// Mikeal Rogers +// +// Alternatively, the contents of this file may be used under the terms of +// either the GNU General Public License Version 2 or later (the "GPL"), or +// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +// in which case the provisions of the GPL or the LGPL are applicable instead +// of those above. If you wish to allow use of your version of this file only +// under the terms of either the GPL or the LGPL, and not to allow others to +// use your version of this file under the terms of the MPL, indicate your +// decision by deleting the provisions above and replace them with the notice +// and other provisions required by the GPL or the LGPL. If you do not delete +// the provisions above, a recipient may use your version of this file under +// the terms of any one of the MPL, the GPL or the LGPL. +// +// ***** END LICENSE BLOCK ***** + +var mozmill = {}; Components.utils.import('resource://mozmill/modules/mozmill.js', mozmill); +var utils = {}; Components.utils.import('resource://mozmill/modules/utils.js', utils); + +var updateOutput = function(){ + //get the checkboxes + var pass = document.getElementById('outPass'); + var fail = document.getElementById('outFail'); + var info = document.getElementById('outTest'); + + //get the collections + var passCollect = window.document.getElementsByClassName('pass'); + var failCollect = window.document.getElementsByClassName('fail'); + var infoCollect = window.document.getElementsByClassName('test'); + + //set the htmlcollection display property in accordance item.checked + var setDisplay = function(item, collection){ + for (var i = 0; i < collection.length; i++){ + if (item.checked == true){ + collection[i].style.display = "block"; + } else { + collection[i].style.display = "none"; + } + } + }; + + setDisplay(pass, passCollect); + setDisplay(fail, failCollect); + setDisplay(info, infoCollect); +}; + +function cleanUp(){ + //cleanup frame event listeners for output + removeStateListeners(); + // Just store width and height + utils.setPreference("mozmill.screenX", window.screenX); + utils.setPreference("mozmill.screenY", window.screenY); + utils.setPreference("mozmill.width", window.document.documentElement.clientWidth); + utils.setPreference("mozmill.height", window.document.documentElement.clientHeight); +} diff --git a/mail/test/resources/mozmill/mozmill/extension/content/mozmill.xul b/mail/test/resources/mozmill/mozmill/extension/content/mozmill.xul new file mode 100644 index 0000000000..e2d60f8815 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/mozmill.xul @@ -0,0 +1,50 @@ + + + + + + + + + +
    +
    +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/mail/test/resources/mozmill/mozmill/extension/content/test/test.js b/mail/test/resources/mozmill/mozmill/extension/content/test/test.js new file mode 100644 index 0000000000..d1bb5fe9e0 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/test/test.js @@ -0,0 +1,16 @@ +function drop(event) { + var item = document.getElementById("item1"); + item.parentNode.removeChild(item); +} + +function dragStart(event) { + event.dataTransfer.setData("text/test-type", "test data"); +} + +function dragOver(event) { + event.preventDefault(); +} + +function dragEnter(event) { + event.preventDefault(); +} \ No newline at end of file diff --git a/mail/test/resources/mozmill/mozmill/extension/content/test/test.xul b/mail/test/resources/mozmill/mozmill/extension/content/test/test.xul new file mode 100644 index 0000000000..aac7902b95 --- /dev/null +++ b/mail/test/resources/mozmill/mozmill/extension/content/test/test.xul @@ -0,0 +1,22 @@ + + + + + + + + ' + self.assertEqual( + r'"\u003c/script\u003e\u003cscript\u003e' + r'alert(\"gotcha\")\u003c/script\u003e"', + self.encoder.encode(bad_string)) + self.assertEqual( + bad_string, self.decoder.decode( + self.encoder.encode(bad_string))) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_errors.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_errors.py new file mode 100644 index 0000000000..c8b836a1ca --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_errors.py @@ -0,0 +1,21 @@ +from unittest import TestCase + +import simplejson as json + +class TestErrors(TestCase): + def test_string_keys_error(self): + data = [{'a': 'A', 'b': (2, 4), 'c': 3.0, ('d',): 'D tuple'}] + self.assertRaises(TypeError, json.dumps, data) + + def test_decode_error(self): + err = None + try: + json.loads('{}\na\nb') + except json.JSONDecodeError, e: + err = e + else: + self.fail('Expected JSONDecodeError') + self.assertEquals(err.lineno, 2) + self.assertEquals(err.colno, 1) + self.assertEquals(err.endlineno, 3) + self.assertEquals(err.endcolno, 2) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_fail.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_fail.py new file mode 100644 index 0000000000..646c0f4429 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_fail.py @@ -0,0 +1,91 @@ +from unittest import TestCase + +import simplejson as json + +# Fri Dec 30 18:57:26 2005 +JSONDOCS = [ + # http://json.org/JSON_checker/test/fail1.json + '"A JSON payload should be an object or array, not a string."', + # http://json.org/JSON_checker/test/fail2.json + '["Unclosed array"', + # http://json.org/JSON_checker/test/fail3.json + '{unquoted_key: "keys must be quoted}', + # http://json.org/JSON_checker/test/fail4.json + '["extra comma",]', + # http://json.org/JSON_checker/test/fail5.json + '["double extra comma",,]', + # http://json.org/JSON_checker/test/fail6.json + '[ , "<-- missing value"]', + # http://json.org/JSON_checker/test/fail7.json + '["Comma after the close"],', + # http://json.org/JSON_checker/test/fail8.json + '["Extra close"]]', + # http://json.org/JSON_checker/test/fail9.json + '{"Extra comma": true,}', + # http://json.org/JSON_checker/test/fail10.json + '{"Extra value after close": true} "misplaced quoted value"', + # http://json.org/JSON_checker/test/fail11.json + '{"Illegal expression": 1 + 2}', + # http://json.org/JSON_checker/test/fail12.json + '{"Illegal invocation": alert()}', + # http://json.org/JSON_checker/test/fail13.json + '{"Numbers cannot have leading zeroes": 013}', + # http://json.org/JSON_checker/test/fail14.json + '{"Numbers cannot be hex": 0x14}', + # http://json.org/JSON_checker/test/fail15.json + '["Illegal backslash escape: \\x15"]', + # http://json.org/JSON_checker/test/fail16.json + '["Illegal backslash escape: \\\'"]', + # http://json.org/JSON_checker/test/fail17.json + '["Illegal backslash escape: \\017"]', + # http://json.org/JSON_checker/test/fail18.json + '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', + # http://json.org/JSON_checker/test/fail19.json + '{"Missing colon" null}', + # http://json.org/JSON_checker/test/fail20.json + '{"Double colon":: null}', + # http://json.org/JSON_checker/test/fail21.json + '{"Comma instead of colon", null}', + # http://json.org/JSON_checker/test/fail22.json + '["Colon instead of comma": false]', + # http://json.org/JSON_checker/test/fail23.json + '["Bad value", truth]', + # http://json.org/JSON_checker/test/fail24.json + "['single quote']", + # http://code.google.com/p/simplejson/issues/detail?id=3 + u'["A\u001FZ control characters in string"]', +] + +SKIPS = { + 1: "why not have a string payload?", + 18: "spec doesn't specify any nesting limitations", +} + +class TestFail(TestCase): + def test_failures(self): + for idx, doc in enumerate(JSONDOCS): + idx = idx + 1 + if idx in SKIPS: + json.loads(doc) + continue + try: + json.loads(doc) + except json.JSONDecodeError: + pass + else: + #self.fail("Expected failure for fail{0}.json: {1!r}".format(idx, doc)) + self.fail("Expected failure for fail%d.json: %r" % (idx, doc)) + + def test_array_decoder_issue46(self): + # http://code.google.com/p/simplejson/issues/detail?id=46 + for doc in [u'[,]', '[,]']: + try: + json.loads(doc) + except json.JSONDecodeError, e: + self.assertEquals(e.pos, 1) + self.assertEquals(e.lineno, 1) + self.assertEquals(e.colno, 1) + except Exception, e: + self.fail("Unexpected exception raised %r %s" % (e, e)) + else: + self.fail("Unexpected success parsing '[,]'") \ No newline at end of file diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_float.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_float.py new file mode 100644 index 0000000000..94502c68b6 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_float.py @@ -0,0 +1,19 @@ +import math +from unittest import TestCase + +import simplejson as json + +class TestFloat(TestCase): + def test_floats(self): + for num in [1617161771.7650001, math.pi, math.pi**100, + math.pi**-100, 3.1]: + self.assertEquals(float(json.dumps(num)), num) + self.assertEquals(json.loads(json.dumps(num)), num) + self.assertEquals(json.loads(unicode(json.dumps(num))), num) + + def test_ints(self): + for num in [1, 1L, 1<<32, 1<<64]: + self.assertEquals(json.dumps(num), str(num)) + self.assertEquals(int(json.dumps(num)), num) + self.assertEquals(json.loads(json.dumps(num)), num) + self.assertEquals(json.loads(unicode(json.dumps(num))), num) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_indent.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_indent.py new file mode 100644 index 0000000000..1e6bdb15eb --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_indent.py @@ -0,0 +1,86 @@ +from unittest import TestCase + +import simplejson as json +import textwrap +from StringIO import StringIO + +class TestIndent(TestCase): + def test_indent(self): + h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', + 'i-vhbjkhnth', + {'nifty': 87}, {'field': 'yes', 'morefield': False} ] + + expect = textwrap.dedent("""\ + [ + \t[ + \t\t"blorpie" + \t], + \t[ + \t\t"whoops" + \t], + \t[], + \t"d-shtaeou", + \t"d-nthiouh", + \t"i-vhbjkhnth", + \t{ + \t\t"nifty": 87 + \t}, + \t{ + \t\t"field": "yes", + \t\t"morefield": false + \t} + ]""") + + + d1 = json.dumps(h) + d2 = json.dumps(h, indent='\t', sort_keys=True, separators=(',', ': ')) + d3 = json.dumps(h, indent=' ', sort_keys=True, separators=(',', ': ')) + d4 = json.dumps(h, indent=2, sort_keys=True, separators=(',', ': ')) + + h1 = json.loads(d1) + h2 = json.loads(d2) + h3 = json.loads(d3) + h4 = json.loads(d4) + + self.assertEquals(h1, h) + self.assertEquals(h2, h) + self.assertEquals(h3, h) + self.assertEquals(h4, h) + self.assertEquals(d3, expect.replace('\t', ' ')) + self.assertEquals(d4, expect.replace('\t', ' ')) + # NOTE: Python 2.4 textwrap.dedent converts tabs to spaces, + # so the following is expected to fail. Python 2.4 is not a + # supported platform in simplejson 2.1.0+. + self.assertEquals(d2, expect) + + def test_indent0(self): + h = {3: 1} + def check(indent, expected): + d1 = json.dumps(h, indent=indent) + self.assertEquals(d1, expected) + + sio = StringIO() + json.dump(h, sio, indent=indent) + self.assertEquals(sio.getvalue(), expected) + + # indent=0 should emit newlines + check(0, '{\n"3": 1\n}') + # indent=None is more compact + check(None, '{"3": 1}') + + def test_separators(self): + lst = [1,2,3,4] + expect = '[\n1,\n2,\n3,\n4\n]' + expect_spaces = '[\n1, \n2, \n3, \n4\n]' + # Ensure that separators still works + self.assertEquals( + expect_spaces, + json.dumps(lst, indent=0, separators=(', ', ': '))) + # Force the new defaults + self.assertEquals( + expect, + json.dumps(lst, indent=0, separators=(',', ': '))) + # Added in 2.1.4 + self.assertEquals( + expect, + json.dumps(lst, indent=0)) \ No newline at end of file diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass1.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass1.py new file mode 100644 index 0000000000..c3d6302d68 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass1.py @@ -0,0 +1,76 @@ +from unittest import TestCase + +import simplejson as json + +# from http://json.org/JSON_checker/test/pass1.json +JSON = r''' +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E666, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ], + "compact": [1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066 + + +,"rosebud"] +''' + +class TestPass1(TestCase): + def test_parse(self): + # test in/out equivalence and parsing + res = json.loads(JSON) + out = json.dumps(res) + self.assertEquals(res, json.loads(out)) + try: + json.dumps(res, allow_nan=False) + except ValueError: + pass + else: + self.fail("23456789012E666 should be out of range") diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass2.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass2.py new file mode 100644 index 0000000000..de4ee00bc6 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass2.py @@ -0,0 +1,14 @@ +from unittest import TestCase +import simplejson as json + +# from http://json.org/JSON_checker/test/pass2.json +JSON = r''' +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] +''' + +class TestPass2(TestCase): + def test_parse(self): + # test in/out equivalence and parsing + res = json.loads(JSON) + out = json.dumps(res) + self.assertEquals(res, json.loads(out)) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass3.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass3.py new file mode 100644 index 0000000000..f591aba983 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_pass3.py @@ -0,0 +1,20 @@ +from unittest import TestCase + +import simplejson as json + +# from http://json.org/JSON_checker/test/pass3.json +JSON = r''' +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} +''' + +class TestPass3(TestCase): + def test_parse(self): + # test in/out equivalence and parsing + res = json.loads(JSON) + out = json.dumps(res) + self.assertEquals(res, json.loads(out)) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_recursion.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_recursion.py new file mode 100644 index 0000000000..83a1d887f4 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_recursion.py @@ -0,0 +1,67 @@ +from unittest import TestCase + +import simplejson as json + +class JSONTestObject: + pass + + +class RecursiveJSONEncoder(json.JSONEncoder): + recurse = False + def default(self, o): + if o is JSONTestObject: + if self.recurse: + return [JSONTestObject] + else: + return 'JSONTestObject' + return json.JSONEncoder.default(o) + + +class TestRecursion(TestCase): + def test_listrecursion(self): + x = [] + x.append(x) + try: + json.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on list recursion") + x = [] + y = [x] + x.append(y) + try: + json.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on alternating list recursion") + y = [] + x = [y, y] + # ensure that the marker is cleared + json.dumps(x) + + def test_dictrecursion(self): + x = {} + x["test"] = x + try: + json.dumps(x) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on dict recursion") + x = {} + y = {"a": x, "b": x} + # ensure that the marker is cleared + json.dumps(y) + + def test_defaultrecursion(self): + enc = RecursiveJSONEncoder() + self.assertEquals(enc.encode(JSONTestObject), '"JSONTestObject"') + enc.recurse = True + try: + enc.encode(JSONTestObject) + except ValueError: + pass + else: + self.fail("didn't raise ValueError on default recursion") diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_scanstring.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_scanstring.py new file mode 100644 index 0000000000..a7fcd46808 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_scanstring.py @@ -0,0 +1,117 @@ +import sys +from unittest import TestCase + +import simplejson as json +import simplejson.decoder + +class TestScanString(TestCase): + def test_py_scanstring(self): + self._test_scanstring(simplejson.decoder.py_scanstring) + + def test_c_scanstring(self): + if not simplejson.decoder.c_scanstring: + return + self._test_scanstring(simplejson.decoder.c_scanstring) + + def _test_scanstring(self, scanstring): + self.assertEquals( + scanstring('"z\\ud834\\udd20x"', 1, None, True), + (u'z\U0001d120x', 16)) + + if sys.maxunicode == 65535: + self.assertEquals( + scanstring(u'"z\U0001d120x"', 1, None, True), + (u'z\U0001d120x', 6)) + else: + self.assertEquals( + scanstring(u'"z\U0001d120x"', 1, None, True), + (u'z\U0001d120x', 5)) + + self.assertEquals( + scanstring('"\\u007b"', 1, None, True), + (u'{', 8)) + + self.assertEquals( + scanstring('"A JSON payload should be an object or array, not a string."', 1, None, True), + (u'A JSON payload should be an object or array, not a string.', 60)) + + self.assertEquals( + scanstring('["Unclosed array"', 2, None, True), + (u'Unclosed array', 17)) + + self.assertEquals( + scanstring('["extra comma",]', 2, None, True), + (u'extra comma', 14)) + + self.assertEquals( + scanstring('["double extra comma",,]', 2, None, True), + (u'double extra comma', 21)) + + self.assertEquals( + scanstring('["Comma after the close"],', 2, None, True), + (u'Comma after the close', 24)) + + self.assertEquals( + scanstring('["Extra close"]]', 2, None, True), + (u'Extra close', 14)) + + self.assertEquals( + scanstring('{"Extra comma": true,}', 2, None, True), + (u'Extra comma', 14)) + + self.assertEquals( + scanstring('{"Extra value after close": true} "misplaced quoted value"', 2, None, True), + (u'Extra value after close', 26)) + + self.assertEquals( + scanstring('{"Illegal expression": 1 + 2}', 2, None, True), + (u'Illegal expression', 21)) + + self.assertEquals( + scanstring('{"Illegal invocation": alert()}', 2, None, True), + (u'Illegal invocation', 21)) + + self.assertEquals( + scanstring('{"Numbers cannot have leading zeroes": 013}', 2, None, True), + (u'Numbers cannot have leading zeroes', 37)) + + self.assertEquals( + scanstring('{"Numbers cannot be hex": 0x14}', 2, None, True), + (u'Numbers cannot be hex', 24)) + + self.assertEquals( + scanstring('[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 21, None, True), + (u'Too deep', 30)) + + self.assertEquals( + scanstring('{"Missing colon" null}', 2, None, True), + (u'Missing colon', 16)) + + self.assertEquals( + scanstring('{"Double colon":: null}', 2, None, True), + (u'Double colon', 15)) + + self.assertEquals( + scanstring('{"Comma instead of colon", null}', 2, None, True), + (u'Comma instead of colon', 25)) + + self.assertEquals( + scanstring('["Colon instead of comma": false]', 2, None, True), + (u'Colon instead of comma', 25)) + + self.assertEquals( + scanstring('["Bad value", truth]', 2, None, True), + (u'Bad value', 12)) + + def test_issue3623(self): + self.assertRaises(ValueError, json.decoder.scanstring, "xxx", 1, + "xxx") + self.assertRaises(UnicodeDecodeError, + json.encoder.encode_basestring_ascii, "xx\xff") + + def test_overflow(self): + # Python 2.5 does not have maxsize + maxsize = getattr(sys, 'maxsize', sys.maxint) + self.assertRaises(OverflowError, json.decoder.scanstring, "xxx", + maxsize + 1) + diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_separators.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_separators.py new file mode 100644 index 0000000000..cbda93cd9d --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_separators.py @@ -0,0 +1,42 @@ +import textwrap +from unittest import TestCase + +import simplejson as json + + +class TestSeparators(TestCase): + def test_separators(self): + h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth', + {'nifty': 87}, {'field': 'yes', 'morefield': False} ] + + expect = textwrap.dedent("""\ + [ + [ + "blorpie" + ] , + [ + "whoops" + ] , + [] , + "d-shtaeou" , + "d-nthiouh" , + "i-vhbjkhnth" , + { + "nifty" : 87 + } , + { + "field" : "yes" , + "morefield" : false + } + ]""") + + + d1 = json.dumps(h) + d2 = json.dumps(h, indent=' ', sort_keys=True, separators=(' ,', ' : ')) + + h1 = json.loads(d1) + h2 = json.loads(d2) + + self.assertEquals(h1, h) + self.assertEquals(h2, h) + self.assertEquals(d2, expect) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_speedups.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_speedups.py new file mode 100644 index 0000000000..825ecf26f9 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_speedups.py @@ -0,0 +1,20 @@ +from unittest import TestCase + +from simplejson import encoder, scanner + +def has_speedups(): + return encoder.c_make_encoder is not None + +class TestDecode(TestCase): + def test_make_scanner(self): + if not has_speedups(): + return + self.assertRaises(AttributeError, scanner.c_make_scanner, 1) + + def test_make_encoder(self): + if not has_speedups(): + return + self.assertRaises(TypeError, encoder.c_make_encoder, + None, + "\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", + None) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_unicode.py b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_unicode.py new file mode 100644 index 0000000000..f73e5bfa97 --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tests/test_unicode.py @@ -0,0 +1,99 @@ +from unittest import TestCase + +import simplejson as json + +class TestUnicode(TestCase): + def test_encoding1(self): + encoder = json.JSONEncoder(encoding='utf-8') + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + s = u.encode('utf-8') + ju = encoder.encode(u) + js = encoder.encode(s) + self.assertEquals(ju, js) + + def test_encoding2(self): + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + s = u.encode('utf-8') + ju = json.dumps(u, encoding='utf-8') + js = json.dumps(s, encoding='utf-8') + self.assertEquals(ju, js) + + def test_encoding3(self): + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = json.dumps(u) + self.assertEquals(j, '"\\u03b1\\u03a9"') + + def test_encoding4(self): + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = json.dumps([u]) + self.assertEquals(j, '["\\u03b1\\u03a9"]') + + def test_encoding5(self): + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = json.dumps(u, ensure_ascii=False) + self.assertEquals(j, u'"' + u + u'"') + + def test_encoding6(self): + u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' + j = json.dumps([u], ensure_ascii=False) + self.assertEquals(j, u'["' + u + u'"]') + + def test_big_unicode_encode(self): + u = u'\U0001d120' + self.assertEquals(json.dumps(u), '"\\ud834\\udd20"') + self.assertEquals(json.dumps(u, ensure_ascii=False), u'"\U0001d120"') + + def test_big_unicode_decode(self): + u = u'z\U0001d120x' + self.assertEquals(json.loads('"' + u + '"'), u) + self.assertEquals(json.loads('"z\\ud834\\udd20x"'), u) + + def test_unicode_decode(self): + for i in range(0, 0xd7ff): + u = unichr(i) + #s = '"\\u{0:04x}"'.format(i) + s = '"\\u%04x"' % (i,) + self.assertEquals(json.loads(s), u) + + def test_object_pairs_hook_with_unicode(self): + s = u'{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' + p = [(u"xkd", 1), (u"kcw", 2), (u"art", 3), (u"hxm", 4), + (u"qrt", 5), (u"pad", 6), (u"hoy", 7)] + self.assertEqual(json.loads(s), eval(s)) + self.assertEqual(json.loads(s, object_pairs_hook=lambda x: x), p) + od = json.loads(s, object_pairs_hook=json.OrderedDict) + self.assertEqual(od, json.OrderedDict(p)) + self.assertEqual(type(od), json.OrderedDict) + # the object_pairs_hook takes priority over the object_hook + self.assertEqual(json.loads(s, + object_pairs_hook=json.OrderedDict, + object_hook=lambda x: None), + json.OrderedDict(p)) + + + def test_default_encoding(self): + self.assertEquals(json.loads(u'{"a": "\xe9"}'.encode('utf-8')), + {'a': u'\xe9'}) + + def test_unicode_preservation(self): + self.assertEquals(type(json.loads(u'""')), unicode) + self.assertEquals(type(json.loads(u'"a"')), unicode) + self.assertEquals(type(json.loads(u'["a"]')[0]), unicode) + + def test_ensure_ascii_false_returns_unicode(self): + # http://code.google.com/p/simplejson/issues/detail?id=48 + self.assertEquals(type(json.dumps([], ensure_ascii=False)), unicode) + self.assertEquals(type(json.dumps(0, ensure_ascii=False)), unicode) + self.assertEquals(type(json.dumps({}, ensure_ascii=False)), unicode) + self.assertEquals(type(json.dumps("", ensure_ascii=False)), unicode) + + def test_ensure_ascii_false_bytestring_encoding(self): + # http://code.google.com/p/simplejson/issues/detail?id=48 + doc1 = {u'quux': 'Arr\xc3\xaat sur images'} + doc2 = {u'quux': u'Arr\xeat sur images'} + doc_ascii = '{"quux": "Arr\\u00eat sur images"}' + doc_unicode = u'{"quux": "Arr\xeat sur images"}' + self.assertEquals(json.dumps(doc1), doc_ascii) + self.assertEquals(json.dumps(doc2), doc_ascii) + self.assertEquals(json.dumps(doc1, ensure_ascii=False), doc_unicode) + self.assertEquals(json.dumps(doc2, ensure_ascii=False), doc_unicode) diff --git a/mail/test/resources/simplejson-2.1.6/simplejson/tool.py b/mail/test/resources/simplejson-2.1.6/simplejson/tool.py new file mode 100644 index 0000000000..73370db55e --- /dev/null +++ b/mail/test/resources/simplejson-2.1.6/simplejson/tool.py @@ -0,0 +1,39 @@ +r"""Command-line tool to validate and pretty-print JSON + +Usage:: + + $ echo '{"json":"obj"}' | python -m simplejson.tool + { + "json": "obj" + } + $ echo '{ 1.2:3.4}' | python -m simplejson.tool + Expecting property name: line 1 column 2 (char 2) + +""" +import sys +import simplejson as json + +def main(): + if len(sys.argv) == 1: + infile = sys.stdin + outfile = sys.stdout + elif len(sys.argv) == 2: + infile = open(sys.argv[1], 'rb') + outfile = sys.stdout + elif len(sys.argv) == 3: + infile = open(sys.argv[1], 'rb') + outfile = open(sys.argv[2], 'wb') + else: + raise SystemExit(sys.argv[0] + " [infile [outfile]]") + try: + obj = json.load(infile, + object_pairs_hook=json.OrderedDict, + use_decimal=True) + except ValueError, e: + raise SystemExit(e) + json.dump(obj, outfile, sort_keys=True, indent=' ', use_decimal=True) + outfile.write('\n') + + +if __name__ == '__main__': + main() diff --git a/mail/test/resources/virtualenv/AUTHORS.txt b/mail/test/resources/virtualenv/AUTHORS.txt new file mode 100644 index 0000000000..13fb1df134 --- /dev/null +++ b/mail/test/resources/virtualenv/AUTHORS.txt @@ -0,0 +1,29 @@ +Author +------ + +Ian Bicking + +Maintainers +----------- + +Brian Rosner +Carl Meyer +Jannis Leidel + +Contributors +------------ + +Antonio Cuni +Armin Ronacher +Christopher Nilsson +Curt Micol +Douglas Creager +Jeff Hammel +Jorge Vargas +Josh Bronson +Kumar McMillan +Lars Francke +Philip Jenvey +Ronny Pfannschmidt +Tarek Ziadé +Vinay Sajip diff --git a/mail/test/resources/virtualenv/LICENSE.txt b/mail/test/resources/virtualenv/LICENSE.txt new file mode 100644 index 0000000000..2628d93685 --- /dev/null +++ b/mail/test/resources/virtualenv/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2007 Ian Bicking and Contributors +Copyright (c) 2009 Ian Bicking, The Open Planning Project +Copyright (c) 2011 The virtualenv developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mail/test/resources/virtualenv/MANIFEST.in b/mail/test/resources/virtualenv/MANIFEST.in new file mode 100644 index 0000000000..cce54a630a --- /dev/null +++ b/mail/test/resources/virtualenv/MANIFEST.in @@ -0,0 +1,9 @@ +recursive-include docs *.txt +recursive-include scripts * +recursive-include virtualenv_support *.egg *.tar.gz +recursive-exclude virtualenv_support *.py +recursive-exclude docs/_templates *.* +include virtualenv_support/__init__.py +include *.py +include AUTHORS.txt +include LICENSE.txt \ No newline at end of file diff --git a/mail/test/resources/virtualenv/PKG-INFO b/mail/test/resources/virtualenv/PKG-INFO new file mode 100644 index 0000000000..678e0fdc4d --- /dev/null +++ b/mail/test/resources/virtualenv/PKG-INFO @@ -0,0 +1,798 @@ +Metadata-Version: 1.0 +Name: virtualenv +Version: 1.6.1 +Summary: Virtual Python Environment builder +Home-page: http://www.virtualenv.org +Author: Jannis Leidel, Carl Meyer and Brian Rosner +Author-email: python-virtualenv@groups.google.com +License: MIT +Description: + + Status and License + ------------------ + + ``virtualenv`` is a successor to `workingenv + `_, and an extension + of `virtual-python + `_. + + It was written by Ian Bicking, sponsored by the `Open Planning + Project `_ and is now maintained by a + `group of developers `_. + It is licensed under an + `MIT-style permissive license `_. + + You can install it with ``easy_install virtualenv``, or from the `git + repository `_ or from a `tarball + `_ + ``easy_install virtualenv==dev``. + + What It Does + ------------ + + ``virtualenv`` is a tool to create isolated Python environments. + + The basic problem being addressed is one of dependencies and versions, + and indirectly permissions. Imagine you have an application that + needs version 1 of LibFoo, but another application requires version + 2. How can you use both these applications? If you install + everything into ``/usr/lib/python2.7/site-packages`` (or whatever your + platform's standard location is), it's easy to end up in a situation + where you unintentionally upgrade an application that shouldn't be + upgraded. + + Or more generally, what if you want to install an application *and + leave it be*? If an application works, any change in its libraries or + the versions of those libraries can break the application. + + Also, what if you can't install packages into the global + ``site-packages`` directory? For instance, on a shared host. + + In all these cases, ``virtualenv`` can help you. It creates an + environment that has its own installation directories, that doesn't + share libraries with other virtualenv environments (and optionally + doesn't access the globally installed libraries either). + + The basic usage is:: + + $ python virtualenv.py ENV + + If you install it you can also just do ``virtualenv ENV``. + + This creates ``ENV/lib/pythonX.X/site-packages``, where any libraries you + install will go. It also creates ``ENV/bin/python``, which is a Python + interpreter that uses this environment. Anytime you use that interpreter + (including when a script has ``#!/path/to/ENV/bin/python`` in it) the libraries + in that environment will be used. + + It also installs either `Setuptools + `_ or `distribute + `_ into the environment. To use + Distribute instead of setuptools, just call virtualenv like this:: + + $ python virtualenv.py --distribute ENV + + You can also set the environment variable VIRTUALENV_USE_DISTRIBUTE. + + A new virtualenv also includes the `pip `_ + installer, so you can use `ENV/bin/pip`` to install additional packages into + the environment. + + Windows Notes + ~~~~~~~~~~~~~ + + Some paths within the virtualenv are slightly different on Windows: scripts and + executables on Windows go in ``ENV\Scripts\`` instead of ``ENV/bin/`` and + libraries go in ``ENV\Lib\`` rather than ``ENV/lib/``. + + To create a virtualenv under a path with spaces in it on Windows, you'll need + the `win32api `_ library installed. + + PyPy Support + ~~~~~~~~~~~~ + + Beginning with virtualenv version 1.5 there is experimental `PyPy + `_ support. Currently only PyPy trunk is supported. + + Creating Your Own Bootstrap Scripts + ----------------------------------- + + While this creates an environment, it doesn't put anything into the + environment. Developers may find it useful to distribute a script + that sets up a particular environment, for example a script that + installs a particular web application. + + To create a script like this, call + ``virtualenv.create_bootstrap_script(extra_text)``, and write the + result to your new bootstrapping script. Here's the documentation + from the docstring: + + Creates a bootstrap script, which is like this script but with + extend_parser, adjust_options, and after_install hooks. + + This returns a string that (written to disk of course) can be used + as a bootstrap script with your own customizations. The script + will be the standard virtualenv.py script, with your extra text + added (your extra text should be Python code). + + If you include these functions, they will be called: + + ``extend_parser(optparse_parser)``: + You can add or remove options from the parser here. + + ``adjust_options(options, args)``: + You can change options here, or change the args (if you accept + different kinds of arguments, be sure you modify ``args`` so it is + only ``[DEST_DIR]``). + + ``after_install(options, home_dir)``: + + After everything is installed, this function is called. This + is probably the function you are most likely to use. An + example would be:: + + def after_install(options, home_dir): + if sys.platform == 'win32': + bin = 'Scripts' + else: + bin = 'bin' + subprocess.call([join(home_dir, bin, 'easy_install'), + 'MyPackage']) + subprocess.call([join(home_dir, bin, 'my-package-script'), + 'setup', home_dir]) + + This example immediately installs a package, and runs a setup + script from that package. + + Bootstrap Example + ~~~~~~~~~~~~~~~~~ + + Here's a more concrete example of how you could use this:: + + import virtualenv, textwrap + output = virtualenv.create_bootstrap_script(textwrap.dedent(""" + import os, subprocess + def after_install(options, home_dir): + etc = join(home_dir, 'etc') + if not os.path.exists(etc): + os.makedirs(etc) + subprocess.call([join(home_dir, 'bin', 'easy_install'), + 'BlogApplication']) + subprocess.call([join(home_dir, 'bin', 'paster'), + 'make-config', 'BlogApplication', + join(etc, 'blog.ini')]) + subprocess.call([join(home_dir, 'bin', 'paster'), + 'setup-app', join(etc, 'blog.ini')]) + """)) + f = open('blog-bootstrap.py', 'w').write(output) + + Another example is available `here + `_. + + activate script + ~~~~~~~~~~~~~~~ + + In a newly created virtualenv there will be a ``bin/activate`` shell + script, or a ``Scripts/activate.bat`` batch file on Windows. + + On Posix systems you can do:: + + $ source bin/activate + + This will change your ``$PATH`` to point to the virtualenv's ``bin/`` + directory. (You have to use ``source`` because it changes your shell + environment in-place.) This is all it does; it's purely a convenience. If + you directly run a script or the python interpreter from the virtualenv's + ``bin/`` directory (e.g. ``path/to/env/bin/pip`` or + ``/path/to/env/bin/python script.py``) there's no need for activation. + + After activating an environment you can use the function ``deactivate`` to + undo the changes to your ``$PATH``. + + The ``activate`` script will also modify your shell prompt to indicate + which environment is currently active. You can disable this behavior, + which can be useful if you have your own custom prompt that already + displays the active environment name. To do so, set the + ``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable to any non-empty + value before running the ``activate`` script. + + On Windows you just do:: + + > \path\to\env\bin\activate.bat + + And use ``deactivate.bat`` to undo the changes. + + The ``--no-site-packages`` Option + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + If you build with ``virtualenv --no-site-packages ENV`` it will *not* + inherit any packages from ``/usr/lib/python2.5/site-packages`` (or + wherever your global site-packages directory is). This can be used if + you don't have control over site-packages and don't want to depend on + the packages there, or you just want more isolation from the global + system. + + Using Virtualenv without ``bin/python`` + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Sometimes you can't or don't want to use the Python interpreter + created by the virtualenv. For instance, in a `mod_python + `_ or `mod_wsgi `_ + environment, there is only one interpreter. + + Luckily, it's easy. You must use the custom Python interpreter to + *install* libraries. But to *use* libraries, you just have to be sure + the path is correct. A script is available to correct the path. You + can setup the environment like:: + + activate_this = '/path/to/env/bin/activate_this.py' + execfile(activate_this, dict(__file__=activate_this)) + + This will change ``sys.path`` and even change ``sys.prefix``, but also allow + you to use an existing interpreter. Items in your environment will show up + first on ``sys.path``, before global items. However, global items will + always be accessible -- this technique does not support the + ``--no-site-packages`` flag. Also, this cannot undo the activation of other + environments, or modules that have been imported. You shouldn't try to, for + instance, activate an environment before a web request; you should activate + *one* environment as early as possible, and not do it again in that process. + + Making Environments Relocatable + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Note: this option is somewhat experimental, and there are probably + caveats that have not yet been identified. Also this does not + currently work on Windows. + + Normally environments are tied to a specific path. That means that + you cannot move an environment around or copy it to another computer. + You can fix up an environment to make it relocatable with the + command:: + + $ virtualenv --relocatable ENV + + This will make some of the files created by setuptools or distribute + use relative paths, and will change all the scripts to use ``activate_this.py`` + instead of using the location of the Python interpreter to select the + environment. + + **Note:** you must run this after you've installed *any* packages into + the environment. If you make an environment relocatable, then + install a new package, you must run ``virtualenv --relocatable`` + again. + + Also, this **does not make your packages cross-platform**. You can + move the directory around, but it can only be used on other similar + computers. Some known environmental differences that can cause + incompatibilities: a different version of Python, when one platform + uses UCS2 for its internal unicode representation and another uses + UCS4 (a compile-time option), obvious platform changes like Windows + vs. Linux, or Intel vs. ARM, and if you have libraries that bind to C + libraries on the system, if those C libraries are located somewhere + different (either different versions, or a different filesystem + layout). + + Currently the ``--no-site-packages`` option will not be honored if you + use this on an environment. + + The ``--extra-search-dir`` Option + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + When it creates a new environment, virtualenv installs either + setuptools or distribute, and pip. In normal operation, the latest + releases of these packages are fetched from the `Python Package Index + `_ (PyPI). In some circumstances, this + behavior may not be wanted, for example if you are using virtualenv + during a deployment and do not want to depend on Internet access and + PyPI availability. + + As an alternative, you can provide your own versions of setuptools, + distribute and/or pip on the filesystem, and tell virtualenv to use + those distributions instead of downloading them from the Internet. To + use this feature, pass one or more ``--extra-search-dir`` options to + virtualenv like this:: + + $ virtualenv --extra-search-dir=/path/to/distributions ENV + + The ``/path/to/distributions`` path should point to a directory that + contains setuptools, distribute and/or pip distributions. Setuptools + distributions must be ``.egg`` files; distribute and pip distributions + should be `.tar.gz` source distributions. + + Virtualenv will still download these packages if no satisfactory local + distributions are found. + + If you are really concerned about virtualenv fetching these packages + from the Internet and want to ensure that it never will, you can also + provide an option ``--never-download`` like so:: + + $ virtualenv --extra-search-dir=/path/to/distributions --never-download ENV + + If this option is provided, virtualenv will never try to download + setuptools/distribute or pip. Instead, it will exit with status code 1 + if it fails to find local distributions for any of these required + packages. + + Compare & Contrast with Alternatives + ------------------------------------ + + There are several alternatives that create isolated environments: + + * ``workingenv`` (which I do not suggest you use anymore) is the + predecessor to this library. It used the main Python interpreter, + but relied on setting ``$PYTHONPATH`` to activate the environment. + This causes problems when running Python scripts that aren't part of + the environment (e.g., a globally installed ``hg`` or ``bzr``). It + also conflicted a lot with Setuptools. + + * `virtual-python + `_ + is also a predecessor to this library. It uses only symlinks, so it + couldn't work on Windows. It also symlinks over the *entire* + standard library and global ``site-packages``. As a result, it + won't see new additions to the global ``site-packages``. + + This script only symlinks a small portion of the standard library + into the environment, and so on Windows it is feasible to simply + copy these files over. Also, it creates a new/empty + ``site-packages`` and also adds the global ``site-packages`` to the + path, so updates are tracked separately. This script also installs + Setuptools automatically, saving a step and avoiding the need for + network access. + + * `zc.buildout `_ doesn't + create an isolated Python environment in the same style, but + achieves similar results through a declarative config file that sets + up scripts with very particular packages. As a declarative system, + it is somewhat easier to repeat and manage, but more difficult to + experiment with. ``zc.buildout`` includes the ability to setup + non-Python systems (e.g., a database server or an Apache instance). + + I *strongly* recommend anyone doing application development or + deployment use one of these tools. + + Contributing + ------------ + + Refer to the `contributing to pip`_ documentation - it applies equally to + virtualenv. + + Virtualenv's release schedule is tied to pip's -- each time there's a new pip + release, there will be a new virtualenv release that bundles the new version of + pip. + + .. _contributing to pip: http://www.pip-installer.org/en/latest/how-to-contribute.html + + Running the tests + ~~~~~~~~~~~~~~~~~ + + Virtualenv's test suite is small and not yet at all comprehensive, but we aim + to grow it. + + The easy way to run tests (handles test dependencies automatically):: + + $ python setup.py test + + If you want to run only a selection of the tests, you'll need to run them + directly with nose instead. Create a virtualenv, and install required + packages:: + + $ pip install nose mock + + Run nosetests:: + + $ nosetests + + Or select just a single test file to run:: + + $ nosetests tests.test_virtualenv + + + Other Documentation and Links + ----------------------------- + + * James Gardner has written a tutorial on using `virtualenv with + Pylons + `_. + + * `Blog announcement + `_. + + * Doug Hellmann wrote a description of his `command-line work flow + using virtualenv (virtualenvwrapper) + `_ + including some handy scripts to make working with multiple + environments easier. He also wrote `an example of using virtualenv + to try IPython + `_. + + * Chris Perkins created a `showmedo video including virtualenv + `_. + + * `Using virtualenv with mod_wsgi + `_. + + * `virtualenv commands + `_ for some more + workflow-related tools around virtualenv. + + Changes & News + -------------- + + Next release (1.7) schedule + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Beta release mid-July 2011, final release early August. + + + 1.6.1 (2011-04-30) + ~~~~~~~~~~~~~~~~~~ + + * Start to use git-flow. + + * Added support for PyPy 1.5 + + * Fixed #121 -- added sanity-checking of the -p argument. Thanks Paul Nasrat. + + * Added progress meter for pip installation as well as setuptools. Thanks Ethan + Jucovy. + + * Added --never-download and --search-dir options. Thanks Ethan Jucovy. + + 1.6 + ~~~ + + * Added Python 3 support! Huge thanks to Vinay Sajip and Vitaly Babiy. + + * Fixed creation of virtualenvs on Mac OS X when standard library modules + (readline) are installed outside the standard library. + + * Updated bundled pip to 1.0. + + 1.5.2 + ~~~~~ + + * Moved main repository to Github: https://github.com/pypa/virtualenv + + * Transferred primary maintenance from Ian to Jannis Leidel, Carl Meyer and Brian Rosner + + * Fixed a few more pypy related bugs. + + * Updated bundled pip to 0.8.2. + + * Handed project over to new team of maintainers. + + * Moved virtualenv to Github at https://github.com/pypa/virtualenv + + 1.5.1 + ~~~~~ + + * Added ``_weakrefset`` requirement for Python 2.7.1. + + * Fixed Windows regression in 1.5 + + 1.5 + ~~~ + + * Include pip 0.8.1. + + * Add support for PyPy. + + * Uses a proper temporary dir when installing environment requirements. + + * Add ``--prompt`` option to be able to override the default prompt prefix. + + * Fix an issue with ``--relocatable`` on Windows. + + * Fix issue with installing the wrong version of distribute. + + * Add fish and csh activate scripts. + + 1.4.9 + ~~~~~ + + * Include pip 0.7.2 + + 1.4.8 + ~~~~~ + + * Fix for Mac OS X Framework builds that use + ``--universal-archs=intel`` + + * Fix ``activate_this.py`` on Windows. + + * Allow ``$PYTHONHOME`` to be set, so long as you use ``source + bin/activate`` it will get unset; if you leave it set and do not + activate the environment it will still break the environment. + + * Include pip 0.7.1 + + 1.4.7 + ~~~~~ + + * Include pip 0.7 + + 1.4.6 + ~~~~~ + + * Allow ``activate.sh`` to skip updating the prompt (by setting + ``$VIRTUAL_ENV_DISABLE_PROMPT``). + + 1.4.5 + ~~~~~ + + * Include pip 0.6.3 + + * Fix ``activate.bat`` and ``deactivate.bat`` under Windows when + ``PATH`` contained a parenthesis + + 1.4.4 + ~~~~~ + + * Include pip 0.6.2 and Distribute 0.6.10 + + * Create the ``virtualenv`` script even when Setuptools isn't + installed + + * Fix problem with ``virtualenv --relocate`` when ``bin/`` has + subdirectories (e.g., ``bin/.svn/``); from Alan Franzoni. + + * If you set ``$VIRTUALENV_USE_DISTRIBUTE`` then virtualenv will use + Distribute by default (so you don't have to remember to use + ``--distribute``). + + 1.4.3 + ~~~~~ + + * Include pip 0.6.1 + + 1.4.2 + ~~~~~ + + * Fix pip installation on Windows + + * Fix use of stand-alone ``virtualenv.py`` (and boot scripts) + + * Exclude ~/.local (user site-packages) from environments when using + ``--no-site-packages`` + + 1.4.1 + ~~~~~ + + * Include pip 0.6 + + 1.4 + ~~~ + + * Updated setuptools to 0.6c11 + + * Added the --distribute option + + * Fixed packaging problem of support-files + + 1.3.4 + ~~~~~ + + * Virtualenv now copies the actual embedded Python binary on + Mac OS X to fix a hang on Snow Leopard (10.6). + + * Fail more gracefully on Windows when ``win32api`` is not installed. + + * Fix site-packages taking precedent over Jython's ``__classpath__`` + and also specially handle the new ``__pyclasspath__`` entry in + ``sys.path``. + + * Now copies Jython's ``registry`` file to the virtualenv if it exists. + + * Better find libraries when compiling extensions on Windows. + + * Create ``Scripts\pythonw.exe`` on Windows. + + * Added support for the Debian/Ubuntu + ``/usr/lib/pythonX.Y/dist-packages`` directory. + + * Set ``distutils.sysconfig.get_config_vars()['LIBDIR']`` (based on + ``sys.real_prefix``) which is reported to help building on Windows. + + * Make ``deactivate`` work on ksh + + * Fixes for ``--python``: make it work with ``--relocatable`` and the + symlink created to the exact Python version. + + 1.3.3 + ~~~~~ + + * Use Windows newlines in ``activate.bat``, which has been reported to help + when using non-ASCII directory names. + + * Fixed compatibility with Jython 2.5b1. + + * Added a function ``virtualenv.install_python`` for more fine-grained + access to what ``virtualenv.create_environment`` does. + + * Fix `a problem `_ + with Windows and paths that contain spaces. + + * If ``/path/to/env/.pydistutils.cfg`` exists (or + ``/path/to/env/pydistutils.cfg`` on Windows systems) then ignore + ``~/.pydistutils.cfg`` and use that other file instead. + + * Fix ` a problem + `_ picking up + some ``.so`` libraries in ``/usr/local``. + + 1.3.2 + ~~~~~ + + * Remove the ``[install] prefix = ...`` setting from the virtualenv + ``distutils.cfg`` -- this has been causing problems for a lot of + people, in rather obscure ways. + + * If you use a `boot script <./index.html#boot-script>`_ it will attempt to import ``virtualenv`` + and find a pre-downloaded Setuptools egg using that. + + * Added platform-specific paths, like ``/usr/lib/pythonX.Y/plat-linux2`` + + 1.3.1 + ~~~~~ + + * Real Python 2.6 compatibility. Backported the Python 2.6 updates to + ``site.py``, including `user directories + `_ + (this means older versions of Python will support user directories, + whether intended or not). + + * Always set ``[install] prefix`` in ``distutils.cfg`` -- previously + on some platforms where a system-wide ``distutils.cfg`` was present + with a ``prefix`` setting, packages would be installed globally + (usually in ``/usr/local/lib/pythonX.Y/site-packages``). + + * Sometimes Cygwin seems to leave ``.exe`` off ``sys.executable``; a + workaround is added. + + * Fix ``--python`` option. + + * Fixed handling of Jython environments that use a + jython-complete.jar. + + 1.3 + ~~~ + + * Update to Setuptools 0.6c9 + * Added an option ``virtualenv --relocatable EXISTING_ENV``, which + will make an existing environment "relocatable" -- the paths will + not be absolute in scripts, ``.egg-info`` and ``.pth`` files. This + may assist in building environments that can be moved and copied. + You have to run this *after* any new packages installed. + * Added ``bin/activate_this.py``, a file you can use like + ``execfile("path_to/activate_this.py", + dict(__file__="path_to/activate_this.py"))`` -- this will activate + the environment in place, similar to what `the mod_wsgi example + does `_. + * For Mac framework builds of Python, the site-packages directory + ``/Library/Python/X.Y/site-packages`` is added to ``sys.path``, from + Andrea Rech. + * Some platform-specific modules in Macs are added to the path now + (``plat-darwin/``, ``plat-mac/``, ``plat-mac/lib-scriptpackages``), + from Andrea Rech. + * Fixed a small Bashism in the ``bin/activate`` shell script. + * Added ``__future__`` to the list of required modules, for Python + 2.3. You'll still need to backport your own ``subprocess`` module. + * Fixed the ``__classpath__`` entry in Jython's ``sys.path`` taking + precedent over virtualenv's libs. + + 1.2 + ~~~ + + * Added a ``--python`` option to select the Python interpreter. + * Add ``warnings`` to the modules copied over, for Python 2.6 support. + * Add ``sets`` to the module copied over for Python 2.3 (though Python + 2.3 still probably doesn't work). + + 1.1.1 + ~~~~~ + + * Added support for Jython 2.5. + + 1.1 + ~~~ + + * Added support for Python 2.6. + * Fix a problem with missing ``DLLs/zlib.pyd`` on Windows. Create + * ``bin/python`` (or ``bin/python.exe``) even when you run virtualenv + with an interpreter named, e.g., ``python2.4`` + * Fix MacPorts Python + * Added --unzip-setuptools option + * Update to Setuptools 0.6c8 + * If the current directory is not writable, run ez_setup.py in ``/tmp`` + * Copy or symlink over the ``include`` directory so that packages will + more consistently compile. + + 1.0 + ~~~ + + * Fix build on systems that use ``/usr/lib64``, distinct from + ``/usr/lib`` (specifically CentOS x64). + * Fixed bug in ``--clear``. + * Fixed typos in ``deactivate.bat``. + * Preserve ``$PYTHONPATH`` when calling subprocesses. + + 0.9.2 + ~~~~~ + + * Fix include dir copying on Windows (makes compiling possible). + * Include the main ``lib-tk`` in the path. + * Patch ``distutils.sysconfig``: ``get_python_inc`` and + ``get_python_lib`` to point to the global locations. + * Install ``distutils.cfg`` before Setuptools, so that system + customizations of ``distutils.cfg`` won't effect the installation. + * Add ``bin/pythonX.Y`` to the virtualenv (in addition to + ``bin/python``). + * Fixed an issue with Mac Framework Python builds, and absolute paths + (from Ronald Oussoren). + + 0.9.1 + ~~~~~ + + * Improve ability to create a virtualenv from inside a virtualenv. + * Fix a little bug in ``bin/activate``. + * Actually get ``distutils.cfg`` to work reliably. + + 0.9 + ~~~ + + * Added ``lib-dynload`` and ``config`` to things that need to be + copied over in an environment. + * Copy over or symlink the ``include`` directory, so that you can + build packages that need the C headers. + * Include a ``distutils`` package, so you can locally update + ``distutils.cfg`` (in ``lib/pythonX.Y/distutils/distutils.cfg``). + * Better avoid downloading Setuptools, and hitting PyPI on environment + creation. + * Fix a problem creating a ``lib64/`` directory. + * Should work on MacOSX Framework builds (the default Python + installations on Mac). Thanks to Ronald Oussoren. + + 0.8.4 + ~~~~~ + + * Windows installs would sometimes give errors about ``sys.prefix`` that + were inaccurate. + * Slightly prettier output. + + 0.8.3 + ~~~~~ + + * Added support for Windows. + + 0.8.2 + ~~~~~ + + * Give a better warning if you are on an unsupported platform (Mac + Framework Pythons, and Windows). + * Give error about running while inside a workingenv. + * Give better error message about Python 2.3. + + 0.8.1 + ~~~~~ + + Fixed packaging of the library. + + 0.8 + ~~~ + + Initial release. Everything is changed and new! + +Keywords: setuptools deployment installation distutils +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 diff --git a/mail/test/resources/virtualenv/docs/index.txt b/mail/test/resources/virtualenv/docs/index.txt new file mode 100644 index 0000000000..1bc7867aeb --- /dev/null +++ b/mail/test/resources/virtualenv/docs/index.txt @@ -0,0 +1,422 @@ +virtualenv +========== + +* `Discussion list `_ +* `Bugs `_ + +.. contents:: + +.. toctree:: + :maxdepth: 1 + + news + +.. comment: split here + +Status and License +------------------ + +``virtualenv`` is a successor to `workingenv +`_, and an extension +of `virtual-python +`_. + +It was written by Ian Bicking, sponsored by the `Open Planning +Project `_ and is now maintained by a +`group of developers `_. +It is licensed under an +`MIT-style permissive license `_. + +You can install it with ``easy_install virtualenv``, or from the `git +repository `_ or from a `tarball +`_ +``easy_install virtualenv==dev``. + +What It Does +------------ + +``virtualenv`` is a tool to create isolated Python environments. + +The basic problem being addressed is one of dependencies and versions, +and indirectly permissions. Imagine you have an application that +needs version 1 of LibFoo, but another application requires version +2. How can you use both these applications? If you install +everything into ``/usr/lib/python2.7/site-packages`` (or whatever your +platform's standard location is), it's easy to end up in a situation +where you unintentionally upgrade an application that shouldn't be +upgraded. + +Or more generally, what if you want to install an application *and +leave it be*? If an application works, any change in its libraries or +the versions of those libraries can break the application. + +Also, what if you can't install packages into the global +``site-packages`` directory? For instance, on a shared host. + +In all these cases, ``virtualenv`` can help you. It creates an +environment that has its own installation directories, that doesn't +share libraries with other virtualenv environments (and optionally +doesn't access the globally installed libraries either). + +The basic usage is:: + + $ python virtualenv.py ENV + +If you install it you can also just do ``virtualenv ENV``. + +This creates ``ENV/lib/pythonX.X/site-packages``, where any libraries you +install will go. It also creates ``ENV/bin/python``, which is a Python +interpreter that uses this environment. Anytime you use that interpreter +(including when a script has ``#!/path/to/ENV/bin/python`` in it) the libraries +in that environment will be used. + +It also installs either `Setuptools +`_ or `distribute +`_ into the environment. To use +Distribute instead of setuptools, just call virtualenv like this:: + + $ python virtualenv.py --distribute ENV + +You can also set the environment variable VIRTUALENV_USE_DISTRIBUTE. + +A new virtualenv also includes the `pip `_ +installer, so you can use `ENV/bin/pip`` to install additional packages into +the environment. + +Windows Notes +~~~~~~~~~~~~~ + +Some paths within the virtualenv are slightly different on Windows: scripts and +executables on Windows go in ``ENV\Scripts\`` instead of ``ENV/bin/`` and +libraries go in ``ENV\Lib\`` rather than ``ENV/lib/``. + +To create a virtualenv under a path with spaces in it on Windows, you'll need +the `win32api `_ library installed. + +PyPy Support +~~~~~~~~~~~~ + +Beginning with virtualenv version 1.5 there is experimental `PyPy +`_ support. Currently only PyPy trunk is supported. + +Creating Your Own Bootstrap Scripts +----------------------------------- + +While this creates an environment, it doesn't put anything into the +environment. Developers may find it useful to distribute a script +that sets up a particular environment, for example a script that +installs a particular web application. + +To create a script like this, call +``virtualenv.create_bootstrap_script(extra_text)``, and write the +result to your new bootstrapping script. Here's the documentation +from the docstring: + +Creates a bootstrap script, which is like this script but with +extend_parser, adjust_options, and after_install hooks. + +This returns a string that (written to disk of course) can be used +as a bootstrap script with your own customizations. The script +will be the standard virtualenv.py script, with your extra text +added (your extra text should be Python code). + +If you include these functions, they will be called: + +``extend_parser(optparse_parser)``: + You can add or remove options from the parser here. + +``adjust_options(options, args)``: + You can change options here, or change the args (if you accept + different kinds of arguments, be sure you modify ``args`` so it is + only ``[DEST_DIR]``). + +``after_install(options, home_dir)``: + + After everything is installed, this function is called. This + is probably the function you are most likely to use. An + example would be:: + + def after_install(options, home_dir): + if sys.platform == 'win32': + bin = 'Scripts' + else: + bin = 'bin' + subprocess.call([join(home_dir, bin, 'easy_install'), + 'MyPackage']) + subprocess.call([join(home_dir, bin, 'my-package-script'), + 'setup', home_dir]) + + This example immediately installs a package, and runs a setup + script from that package. + +Bootstrap Example +~~~~~~~~~~~~~~~~~ + +Here's a more concrete example of how you could use this:: + + import virtualenv, textwrap + output = virtualenv.create_bootstrap_script(textwrap.dedent(""" + import os, subprocess + def after_install(options, home_dir): + etc = join(home_dir, 'etc') + if not os.path.exists(etc): + os.makedirs(etc) + subprocess.call([join(home_dir, 'bin', 'easy_install'), + 'BlogApplication']) + subprocess.call([join(home_dir, 'bin', 'paster'), + 'make-config', 'BlogApplication', + join(etc, 'blog.ini')]) + subprocess.call([join(home_dir, 'bin', 'paster'), + 'setup-app', join(etc, 'blog.ini')]) + """)) + f = open('blog-bootstrap.py', 'w').write(output) + +Another example is available `here +`_. + +activate script +~~~~~~~~~~~~~~~ + +In a newly created virtualenv there will be a ``bin/activate`` shell +script, or a ``Scripts/activate.bat`` batch file on Windows. + +On Posix systems you can do:: + + $ source bin/activate + +This will change your ``$PATH`` to point to the virtualenv's ``bin/`` +directory. (You have to use ``source`` because it changes your shell +environment in-place.) This is all it does; it's purely a convenience. If +you directly run a script or the python interpreter from the virtualenv's +``bin/`` directory (e.g. ``path/to/env/bin/pip`` or +``/path/to/env/bin/python script.py``) there's no need for activation. + +After activating an environment you can use the function ``deactivate`` to +undo the changes to your ``$PATH``. + +The ``activate`` script will also modify your shell prompt to indicate +which environment is currently active. You can disable this behavior, +which can be useful if you have your own custom prompt that already +displays the active environment name. To do so, set the +``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable to any non-empty +value before running the ``activate`` script. + +On Windows you just do:: + + > \path\to\env\bin\activate.bat + +And use ``deactivate.bat`` to undo the changes. + +The ``--no-site-packages`` Option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you build with ``virtualenv --no-site-packages ENV`` it will *not* +inherit any packages from ``/usr/lib/python2.5/site-packages`` (or +wherever your global site-packages directory is). This can be used if +you don't have control over site-packages and don't want to depend on +the packages there, or you just want more isolation from the global +system. + +Using Virtualenv without ``bin/python`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you can't or don't want to use the Python interpreter +created by the virtualenv. For instance, in a `mod_python +`_ or `mod_wsgi `_ +environment, there is only one interpreter. + +Luckily, it's easy. You must use the custom Python interpreter to +*install* libraries. But to *use* libraries, you just have to be sure +the path is correct. A script is available to correct the path. You +can setup the environment like:: + + activate_this = '/path/to/env/bin/activate_this.py' + execfile(activate_this, dict(__file__=activate_this)) + +This will change ``sys.path`` and even change ``sys.prefix``, but also allow +you to use an existing interpreter. Items in your environment will show up +first on ``sys.path``, before global items. However, global items will +always be accessible -- this technique does not support the +``--no-site-packages`` flag. Also, this cannot undo the activation of other +environments, or modules that have been imported. You shouldn't try to, for +instance, activate an environment before a web request; you should activate +*one* environment as early as possible, and not do it again in that process. + +Making Environments Relocatable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Note: this option is somewhat experimental, and there are probably +caveats that have not yet been identified. Also this does not +currently work on Windows. + +Normally environments are tied to a specific path. That means that +you cannot move an environment around or copy it to another computer. +You can fix up an environment to make it relocatable with the +command:: + + $ virtualenv --relocatable ENV + +This will make some of the files created by setuptools or distribute +use relative paths, and will change all the scripts to use ``activate_this.py`` +instead of using the location of the Python interpreter to select the +environment. + +**Note:** you must run this after you've installed *any* packages into +the environment. If you make an environment relocatable, then +install a new package, you must run ``virtualenv --relocatable`` +again. + +Also, this **does not make your packages cross-platform**. You can +move the directory around, but it can only be used on other similar +computers. Some known environmental differences that can cause +incompatibilities: a different version of Python, when one platform +uses UCS2 for its internal unicode representation and another uses +UCS4 (a compile-time option), obvious platform changes like Windows +vs. Linux, or Intel vs. ARM, and if you have libraries that bind to C +libraries on the system, if those C libraries are located somewhere +different (either different versions, or a different filesystem +layout). + +Currently the ``--no-site-packages`` option will not be honored if you +use this on an environment. + +The ``--extra-search-dir`` Option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When it creates a new environment, virtualenv installs either +setuptools or distribute, and pip. In normal operation, the latest +releases of these packages are fetched from the `Python Package Index +`_ (PyPI). In some circumstances, this +behavior may not be wanted, for example if you are using virtualenv +during a deployment and do not want to depend on Internet access and +PyPI availability. + +As an alternative, you can provide your own versions of setuptools, +distribute and/or pip on the filesystem, and tell virtualenv to use +those distributions instead of downloading them from the Internet. To +use this feature, pass one or more ``--extra-search-dir`` options to +virtualenv like this:: + + $ virtualenv --extra-search-dir=/path/to/distributions ENV + +The ``/path/to/distributions`` path should point to a directory that +contains setuptools, distribute and/or pip distributions. Setuptools +distributions must be ``.egg`` files; distribute and pip distributions +should be `.tar.gz` source distributions. + +Virtualenv will still download these packages if no satisfactory local +distributions are found. + +If you are really concerned about virtualenv fetching these packages +from the Internet and want to ensure that it never will, you can also +provide an option ``--never-download`` like so:: + + $ virtualenv --extra-search-dir=/path/to/distributions --never-download ENV + +If this option is provided, virtualenv will never try to download +setuptools/distribute or pip. Instead, it will exit with status code 1 +if it fails to find local distributions for any of these required +packages. + +Compare & Contrast with Alternatives +------------------------------------ + +There are several alternatives that create isolated environments: + +* ``workingenv`` (which I do not suggest you use anymore) is the + predecessor to this library. It used the main Python interpreter, + but relied on setting ``$PYTHONPATH`` to activate the environment. + This causes problems when running Python scripts that aren't part of + the environment (e.g., a globally installed ``hg`` or ``bzr``). It + also conflicted a lot with Setuptools. + +* `virtual-python + `_ + is also a predecessor to this library. It uses only symlinks, so it + couldn't work on Windows. It also symlinks over the *entire* + standard library and global ``site-packages``. As a result, it + won't see new additions to the global ``site-packages``. + + This script only symlinks a small portion of the standard library + into the environment, and so on Windows it is feasible to simply + copy these files over. Also, it creates a new/empty + ``site-packages`` and also adds the global ``site-packages`` to the + path, so updates are tracked separately. This script also installs + Setuptools automatically, saving a step and avoiding the need for + network access. + +* `zc.buildout `_ doesn't + create an isolated Python environment in the same style, but + achieves similar results through a declarative config file that sets + up scripts with very particular packages. As a declarative system, + it is somewhat easier to repeat and manage, but more difficult to + experiment with. ``zc.buildout`` includes the ability to setup + non-Python systems (e.g., a database server or an Apache instance). + +I *strongly* recommend anyone doing application development or +deployment use one of these tools. + +Contributing +------------ + +Refer to the `contributing to pip`_ documentation - it applies equally to +virtualenv. + +Virtualenv's release schedule is tied to pip's -- each time there's a new pip +release, there will be a new virtualenv release that bundles the new version of +pip. + +.. _contributing to pip: http://www.pip-installer.org/en/latest/how-to-contribute.html + +Running the tests +~~~~~~~~~~~~~~~~~ + +Virtualenv's test suite is small and not yet at all comprehensive, but we aim +to grow it. + +The easy way to run tests (handles test dependencies automatically):: + + $ python setup.py test + +If you want to run only a selection of the tests, you'll need to run them +directly with nose instead. Create a virtualenv, and install required +packages:: + + $ pip install nose mock + +Run nosetests:: + + $ nosetests + +Or select just a single test file to run:: + + $ nosetests tests.test_virtualenv + + +Other Documentation and Links +----------------------------- + +* James Gardner has written a tutorial on using `virtualenv with + Pylons + `_. + +* `Blog announcement + `_. + +* Doug Hellmann wrote a description of his `command-line work flow + using virtualenv (virtualenvwrapper) + `_ + including some handy scripts to make working with multiple + environments easier. He also wrote `an example of using virtualenv + to try IPython + `_. + +* Chris Perkins created a `showmedo video including virtualenv + `_. + +* `Using virtualenv with mod_wsgi + `_. + +* `virtualenv commands + `_ for some more + workflow-related tools around virtualenv. diff --git a/mail/test/resources/virtualenv/docs/news.txt b/mail/test/resources/virtualenv/docs/news.txt new file mode 100644 index 0000000000..9d0530fb4d --- /dev/null +++ b/mail/test/resources/virtualenv/docs/news.txt @@ -0,0 +1,366 @@ +Changes & News +-------------- + +Next release (1.7) schedule +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Beta release mid-July 2011, final release early August. + + +1.6.1 (2011-04-30) +~~~~~~~~~~~~~~~~~~ + +* Start to use git-flow. + +* Added support for PyPy 1.5 + +* Fixed #121 -- added sanity-checking of the -p argument. Thanks Paul Nasrat. + +* Added progress meter for pip installation as well as setuptools. Thanks Ethan + Jucovy. + +* Added --never-download and --search-dir options. Thanks Ethan Jucovy. + +1.6 +~~~ + +* Added Python 3 support! Huge thanks to Vinay Sajip and Vitaly Babiy. + +* Fixed creation of virtualenvs on Mac OS X when standard library modules + (readline) are installed outside the standard library. + +* Updated bundled pip to 1.0. + +1.5.2 +~~~~~ + +* Moved main repository to Github: https://github.com/pypa/virtualenv + +* Transferred primary maintenance from Ian to Jannis Leidel, Carl Meyer and Brian Rosner + +* Fixed a few more pypy related bugs. + +* Updated bundled pip to 0.8.2. + +* Handed project over to new team of maintainers. + +* Moved virtualenv to Github at https://github.com/pypa/virtualenv + +1.5.1 +~~~~~ + +* Added ``_weakrefset`` requirement for Python 2.7.1. + +* Fixed Windows regression in 1.5 + +1.5 +~~~ + +* Include pip 0.8.1. + +* Add support for PyPy. + +* Uses a proper temporary dir when installing environment requirements. + +* Add ``--prompt`` option to be able to override the default prompt prefix. + +* Fix an issue with ``--relocatable`` on Windows. + +* Fix issue with installing the wrong version of distribute. + +* Add fish and csh activate scripts. + +1.4.9 +~~~~~ + +* Include pip 0.7.2 + +1.4.8 +~~~~~ + +* Fix for Mac OS X Framework builds that use + ``--universal-archs=intel`` + +* Fix ``activate_this.py`` on Windows. + +* Allow ``$PYTHONHOME`` to be set, so long as you use ``source + bin/activate`` it will get unset; if you leave it set and do not + activate the environment it will still break the environment. + +* Include pip 0.7.1 + +1.4.7 +~~~~~ + +* Include pip 0.7 + +1.4.6 +~~~~~ + +* Allow ``activate.sh`` to skip updating the prompt (by setting + ``$VIRTUAL_ENV_DISABLE_PROMPT``). + +1.4.5 +~~~~~ + +* Include pip 0.6.3 + +* Fix ``activate.bat`` and ``deactivate.bat`` under Windows when + ``PATH`` contained a parenthesis + +1.4.4 +~~~~~ + +* Include pip 0.6.2 and Distribute 0.6.10 + +* Create the ``virtualenv`` script even when Setuptools isn't + installed + +* Fix problem with ``virtualenv --relocate`` when ``bin/`` has + subdirectories (e.g., ``bin/.svn/``); from Alan Franzoni. + +* If you set ``$VIRTUALENV_USE_DISTRIBUTE`` then virtualenv will use + Distribute by default (so you don't have to remember to use + ``--distribute``). + +1.4.3 +~~~~~ + +* Include pip 0.6.1 + +1.4.2 +~~~~~ + +* Fix pip installation on Windows + +* Fix use of stand-alone ``virtualenv.py`` (and boot scripts) + +* Exclude ~/.local (user site-packages) from environments when using + ``--no-site-packages`` + +1.4.1 +~~~~~ + +* Include pip 0.6 + +1.4 +~~~ + +* Updated setuptools to 0.6c11 + +* Added the --distribute option + +* Fixed packaging problem of support-files + +1.3.4 +~~~~~ + +* Virtualenv now copies the actual embedded Python binary on + Mac OS X to fix a hang on Snow Leopard (10.6). + +* Fail more gracefully on Windows when ``win32api`` is not installed. + +* Fix site-packages taking precedent over Jython's ``__classpath__`` + and also specially handle the new ``__pyclasspath__`` entry in + ``sys.path``. + +* Now copies Jython's ``registry`` file to the virtualenv if it exists. + +* Better find libraries when compiling extensions on Windows. + +* Create ``Scripts\pythonw.exe`` on Windows. + +* Added support for the Debian/Ubuntu + ``/usr/lib/pythonX.Y/dist-packages`` directory. + +* Set ``distutils.sysconfig.get_config_vars()['LIBDIR']`` (based on + ``sys.real_prefix``) which is reported to help building on Windows. + +* Make ``deactivate`` work on ksh + +* Fixes for ``--python``: make it work with ``--relocatable`` and the + symlink created to the exact Python version. + +1.3.3 +~~~~~ + +* Use Windows newlines in ``activate.bat``, which has been reported to help + when using non-ASCII directory names. + +* Fixed compatibility with Jython 2.5b1. + +* Added a function ``virtualenv.install_python`` for more fine-grained + access to what ``virtualenv.create_environment`` does. + +* Fix `a problem `_ + with Windows and paths that contain spaces. + +* If ``/path/to/env/.pydistutils.cfg`` exists (or + ``/path/to/env/pydistutils.cfg`` on Windows systems) then ignore + ``~/.pydistutils.cfg`` and use that other file instead. + +* Fix ` a problem + `_ picking up + some ``.so`` libraries in ``/usr/local``. + +1.3.2 +~~~~~ + +* Remove the ``[install] prefix = ...`` setting from the virtualenv + ``distutils.cfg`` -- this has been causing problems for a lot of + people, in rather obscure ways. + +* If you use a `boot script <./index.html#boot-script>`_ it will attempt to import ``virtualenv`` + and find a pre-downloaded Setuptools egg using that. + +* Added platform-specific paths, like ``/usr/lib/pythonX.Y/plat-linux2`` + +1.3.1 +~~~~~ + +* Real Python 2.6 compatibility. Backported the Python 2.6 updates to + ``site.py``, including `user directories + `_ + (this means older versions of Python will support user directories, + whether intended or not). + +* Always set ``[install] prefix`` in ``distutils.cfg`` -- previously + on some platforms where a system-wide ``distutils.cfg`` was present + with a ``prefix`` setting, packages would be installed globally + (usually in ``/usr/local/lib/pythonX.Y/site-packages``). + +* Sometimes Cygwin seems to leave ``.exe`` off ``sys.executable``; a + workaround is added. + +* Fix ``--python`` option. + +* Fixed handling of Jython environments that use a + jython-complete.jar. + +1.3 +~~~ + +* Update to Setuptools 0.6c9 +* Added an option ``virtualenv --relocatable EXISTING_ENV``, which + will make an existing environment "relocatable" -- the paths will + not be absolute in scripts, ``.egg-info`` and ``.pth`` files. This + may assist in building environments that can be moved and copied. + You have to run this *after* any new packages installed. +* Added ``bin/activate_this.py``, a file you can use like + ``execfile("path_to/activate_this.py", + dict(__file__="path_to/activate_this.py"))`` -- this will activate + the environment in place, similar to what `the mod_wsgi example + does `_. +* For Mac framework builds of Python, the site-packages directory + ``/Library/Python/X.Y/site-packages`` is added to ``sys.path``, from + Andrea Rech. +* Some platform-specific modules in Macs are added to the path now + (``plat-darwin/``, ``plat-mac/``, ``plat-mac/lib-scriptpackages``), + from Andrea Rech. +* Fixed a small Bashism in the ``bin/activate`` shell script. +* Added ``__future__`` to the list of required modules, for Python + 2.3. You'll still need to backport your own ``subprocess`` module. +* Fixed the ``__classpath__`` entry in Jython's ``sys.path`` taking + precedent over virtualenv's libs. + +1.2 +~~~ + +* Added a ``--python`` option to select the Python interpreter. +* Add ``warnings`` to the modules copied over, for Python 2.6 support. +* Add ``sets`` to the module copied over for Python 2.3 (though Python + 2.3 still probably doesn't work). + +1.1.1 +~~~~~ + +* Added support for Jython 2.5. + +1.1 +~~~ + +* Added support for Python 2.6. +* Fix a problem with missing ``DLLs/zlib.pyd`` on Windows. Create +* ``bin/python`` (or ``bin/python.exe``) even when you run virtualenv + with an interpreter named, e.g., ``python2.4`` +* Fix MacPorts Python +* Added --unzip-setuptools option +* Update to Setuptools 0.6c8 +* If the current directory is not writable, run ez_setup.py in ``/tmp`` +* Copy or symlink over the ``include`` directory so that packages will + more consistently compile. + +1.0 +~~~ + +* Fix build on systems that use ``/usr/lib64``, distinct from + ``/usr/lib`` (specifically CentOS x64). +* Fixed bug in ``--clear``. +* Fixed typos in ``deactivate.bat``. +* Preserve ``$PYTHONPATH`` when calling subprocesses. + +0.9.2 +~~~~~ + +* Fix include dir copying on Windows (makes compiling possible). +* Include the main ``lib-tk`` in the path. +* Patch ``distutils.sysconfig``: ``get_python_inc`` and + ``get_python_lib`` to point to the global locations. +* Install ``distutils.cfg`` before Setuptools, so that system + customizations of ``distutils.cfg`` won't effect the installation. +* Add ``bin/pythonX.Y`` to the virtualenv (in addition to + ``bin/python``). +* Fixed an issue with Mac Framework Python builds, and absolute paths + (from Ronald Oussoren). + +0.9.1 +~~~~~ + +* Improve ability to create a virtualenv from inside a virtualenv. +* Fix a little bug in ``bin/activate``. +* Actually get ``distutils.cfg`` to work reliably. + +0.9 +~~~ + +* Added ``lib-dynload`` and ``config`` to things that need to be + copied over in an environment. +* Copy over or symlink the ``include`` directory, so that you can + build packages that need the C headers. +* Include a ``distutils`` package, so you can locally update + ``distutils.cfg`` (in ``lib/pythonX.Y/distutils/distutils.cfg``). +* Better avoid downloading Setuptools, and hitting PyPI on environment + creation. +* Fix a problem creating a ``lib64/`` directory. +* Should work on MacOSX Framework builds (the default Python + installations on Mac). Thanks to Ronald Oussoren. + +0.8.4 +~~~~~ + +* Windows installs would sometimes give errors about ``sys.prefix`` that + were inaccurate. +* Slightly prettier output. + +0.8.3 +~~~~~ + +* Added support for Windows. + +0.8.2 +~~~~~ + +* Give a better warning if you are on an unsupported platform (Mac + Framework Pythons, and Windows). +* Give error about running while inside a workingenv. +* Give better error message about Python 2.3. + +0.8.1 +~~~~~ + +Fixed packaging of the library. + +0.8 +~~~ + +Initial release. Everything is changed and new! diff --git a/mail/test/resources/virtualenv/scripts/virtualenv b/mail/test/resources/virtualenv/scripts/virtualenv new file mode 100644 index 0000000000..c961dd7db5 --- /dev/null +++ b/mail/test/resources/virtualenv/scripts/virtualenv @@ -0,0 +1,3 @@ +#!/usr/bin/env python +import virtualenv +virtualenv.main() diff --git a/mail/test/resources/virtualenv/setup.cfg b/mail/test/resources/virtualenv/setup.cfg new file mode 100644 index 0000000000..861a9f5542 --- /dev/null +++ b/mail/test/resources/virtualenv/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/mail/test/resources/virtualenv/setup.py b/mail/test/resources/virtualenv/setup.py new file mode 100644 index 0000000000..89316db931 --- /dev/null +++ b/mail/test/resources/virtualenv/setup.py @@ -0,0 +1,58 @@ +import sys, os +try: + from setuptools import setup + kw = {'entry_points': + """[console_scripts]\nvirtualenv = virtualenv:main\n""", + 'zip_safe': False} +except ImportError: + from distutils.core import setup + if sys.platform == 'win32': + print('Note: without Setuptools installed you will have to use "python -m virtualenv ENV"') + kw = {} + else: + kw = {'scripts': ['scripts/virtualenv']} + +here = os.path.dirname(os.path.abspath(__file__)) + +## Get long_description from index.txt: +f = open(os.path.join(here, 'docs', 'index.txt')) +long_description = f.read().strip() +long_description = long_description.split('split here', 1)[1] +f.close() +f = open(os.path.join(here, 'docs', 'news.txt')) +long_description += "\n\n" + f.read() +f.close() + +setup(name='virtualenv', + # If you change the version here, change it in virtualenv.py and + # docs/conf.py as well + version="1.6.1", + description="Virtual Python Environment builder", + long_description=long_description, + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.4', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.2', + ], + keywords='setuptools deployment installation distutils', + author='Ian Bicking', + author_email='ianb@colorstudy.com', + maintainer='Jannis Leidel, Carl Meyer and Brian Rosner', + maintainer_email='python-virtualenv@groups.google.com', + url='http://www.virtualenv.org', + license='MIT', + py_modules=['virtualenv'], + packages=['virtualenv_support'], + package_data={'virtualenv_support': ['*-py%s.egg' % sys.version[:3], '*.tar.gz']}, + test_suite='nose.collector', + tests_require=['nose', 'Mock'], + **kw + ) diff --git a/mail/test/resources/virtualenv/virtualenv.py b/mail/test/resources/virtualenv/virtualenv.py new file mode 100644 index 0000000000..db21ecdab0 --- /dev/null +++ b/mail/test/resources/virtualenv/virtualenv.py @@ -0,0 +1,1969 @@ +#!/usr/bin/env python +"""Create a "virtual" Python installation +""" + +# If you change the version here, change it in setup.py +# and docs/conf.py as well. +virtualenv_version = "1.6.1" + +import base64 +import sys +import os +import optparse +import re +import shutil +import logging +import tempfile +import zlib +import errno +import distutils.sysconfig +try: + import subprocess +except ImportError: + if sys.version_info <= (2, 3): + print('ERROR: %s' % sys.exc_info()[1]) + print('ERROR: this script requires Python 2.4 or greater; or at least the subprocess module.') + print('If you copy subprocess.py from a newer version of Python this script will probably work') + sys.exit(101) + else: + raise +try: + set +except NameError: + from sets import Set as set +try: + basestring +except NameError: + basestring = str + +join = os.path.join +py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) + +is_jython = sys.platform.startswith('java') +is_pypy = hasattr(sys, 'pypy_version_info') +abiflags = getattr(sys, 'abiflags', '') + +if is_pypy: + expected_exe = 'pypy' +elif is_jython: + expected_exe = 'jython' +else: + expected_exe = 'python' + + +REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', + 'fnmatch', 'locale', 'encodings', 'codecs', + 'stat', 'UserDict', 'readline', 'copy_reg', 'types', + 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', + 'zlib'] + +REQUIRED_FILES = ['lib-dynload', 'config'] + +majver, minver = sys.version_info[:2] +if majver == 2: + if minver >= 6: + REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc']) + if minver >= 7: + REQUIRED_MODULES.extend(['_weakrefset']) + if minver <= 3: + REQUIRED_MODULES.extend(['sets', '__future__']) +elif majver == 3: + # Some extra modules are needed for Python 3, but different ones + # for different versions. + REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io', + '_weakrefset', 'copyreg', 'tempfile', 'random', + '__future__', 'collections', 'keyword', 'tarfile', + 'shutil', 'struct', 'copy']) + if minver >= 2: + REQUIRED_FILES[-1] = 'config-%s' % majver + if minver == 3: + # The whole list of 3.3 modules is reproduced below - the current + # uncommented ones are required for 3.3 as of now, but more may be + # added as 3.3 development continues. + REQUIRED_MODULES.extend([ + #"aifc", + #"antigravity", + #"argparse", + #"ast", + #"asynchat", + #"asyncore", + "base64", + #"bdb", + #"binhex", + "bisect", + #"calendar", + #"cgi", + #"cgitb", + #"chunk", + #"cmd", + #"codeop", + #"code", + #"colorsys", + #"_compat_pickle", + #"compileall", + #"concurrent", + #"configparser", + #"contextlib", + #"cProfile", + #"crypt", + #"csv", + #"ctypes", + #"curses", + #"datetime", + #"dbm", + #"decimal", + #"difflib", + #"dis", + #"doctest", + #"dummy_threading", + #"_dummy_thread", + #"email", + #"filecmp", + #"fileinput", + #"formatter", + #"fractions", + #"ftplib", + #"functools", + #"getopt", + #"getpass", + #"gettext", + #"glob", + #"gzip", + "hashlib", + "heapq", + "hmac", + #"html", + #"http", + #"idlelib", + #"imaplib", + #"imghdr", + #"importlib", + #"inspect", + #"json", + #"lib2to3", + #"logging", + #"macpath", + #"macurl2path", + #"mailbox", + #"mailcap", + #"_markupbase", + #"mimetypes", + #"modulefinder", + #"multiprocessing", + #"netrc", + #"nntplib", + #"nturl2path", + #"numbers", + #"opcode", + #"optparse", + #"os2emxpath", + #"pdb", + #"pickle", + #"pickletools", + #"pipes", + #"pkgutil", + #"platform", + #"plat-linux2", + #"plistlib", + #"poplib", + #"pprint", + #"profile", + #"pstats", + #"pty", + #"pyclbr", + #"py_compile", + #"pydoc_data", + #"pydoc", + #"_pyio", + #"queue", + #"quopri", + "reprlib", + "rlcompleter", + #"runpy", + #"sched", + #"shelve", + #"shlex", + #"smtpd", + #"smtplib", + #"sndhdr", + #"socket", + #"socketserver", + #"sqlite3", + #"ssl", + #"stringprep", + #"string", + #"_strptime", + #"subprocess", + #"sunau", + #"symbol", + #"symtable", + #"sysconfig", + #"tabnanny", + #"telnetlib", + #"test", + #"textwrap", + #"this", + #"_threading_local", + #"threading", + #"timeit", + #"tkinter", + #"tokenize", + #"token", + #"traceback", + #"trace", + #"tty", + #"turtledemo", + #"turtle", + #"unittest", + #"urllib", + #"uuid", + #"uu", + #"wave", + "weakref", + #"webbrowser", + #"wsgiref", + #"xdrlib", + #"xml", + #"xmlrpc", + #"zipfile", + ]) + +if is_pypy: + # these are needed to correctly display the exceptions that may happen + # during the bootstrap + REQUIRED_MODULES.extend(['traceback', 'linecache']) + +class Logger(object): + + """ + Logging object for use in command-line script. Allows ranges of + levels, to avoid some redundancy of displayed information. + """ + + DEBUG = logging.DEBUG + INFO = logging.INFO + NOTIFY = (logging.INFO+logging.WARN)/2 + WARN = WARNING = logging.WARN + ERROR = logging.ERROR + FATAL = logging.FATAL + + LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] + + def __init__(self, consumers): + self.consumers = consumers + self.indent = 0 + self.in_progress = None + self.in_progress_hanging = False + + def debug(self, msg, *args, **kw): + self.log(self.DEBUG, msg, *args, **kw) + def info(self, msg, *args, **kw): + self.log(self.INFO, msg, *args, **kw) + def notify(self, msg, *args, **kw): + self.log(self.NOTIFY, msg, *args, **kw) + def warn(self, msg, *args, **kw): + self.log(self.WARN, msg, *args, **kw) + def error(self, msg, *args, **kw): + self.log(self.WARN, msg, *args, **kw) + def fatal(self, msg, *args, **kw): + self.log(self.FATAL, msg, *args, **kw) + def log(self, level, msg, *args, **kw): + if args: + if kw: + raise TypeError( + "You may give positional or keyword arguments, not both") + args = args or kw + rendered = None + for consumer_level, consumer in self.consumers: + if self.level_matches(level, consumer_level): + if (self.in_progress_hanging + and consumer in (sys.stdout, sys.stderr)): + self.in_progress_hanging = False + sys.stdout.write('\n') + sys.stdout.flush() + if rendered is None: + if args: + rendered = msg % args + else: + rendered = msg + rendered = ' '*self.indent + rendered + if hasattr(consumer, 'write'): + consumer.write(rendered+'\n') + else: + consumer(rendered) + + def start_progress(self, msg): + assert not self.in_progress, ( + "Tried to start_progress(%r) while in_progress %r" + % (msg, self.in_progress)) + if self.level_matches(self.NOTIFY, self._stdout_level()): + sys.stdout.write(msg) + sys.stdout.flush() + self.in_progress_hanging = True + else: + self.in_progress_hanging = False + self.in_progress = msg + + def end_progress(self, msg='done.'): + assert self.in_progress, ( + "Tried to end_progress without start_progress") + if self.stdout_level_matches(self.NOTIFY): + if not self.in_progress_hanging: + # Some message has been printed out since start_progress + sys.stdout.write('...' + self.in_progress + msg + '\n') + sys.stdout.flush() + else: + sys.stdout.write(msg + '\n') + sys.stdout.flush() + self.in_progress = None + self.in_progress_hanging = False + + def show_progress(self): + """If we are in a progress scope, and no log messages have been + shown, write out another '.'""" + if self.in_progress_hanging: + sys.stdout.write('.') + sys.stdout.flush() + + def stdout_level_matches(self, level): + """Returns true if a message at this level will go to stdout""" + return self.level_matches(level, self._stdout_level()) + + def _stdout_level(self): + """Returns the level that stdout runs at""" + for level, consumer in self.consumers: + if consumer is sys.stdout: + return level + return self.FATAL + + def level_matches(self, level, consumer_level): + """ + >>> l = Logger() + >>> l.level_matches(3, 4) + False + >>> l.level_matches(3, 2) + True + >>> l.level_matches(slice(None, 3), 3) + False + >>> l.level_matches(slice(None, 3), 2) + True + >>> l.level_matches(slice(1, 3), 1) + True + >>> l.level_matches(slice(2, 3), 1) + False + """ + if isinstance(level, slice): + start, stop = level.start, level.stop + if start is not None and start > consumer_level: + return False + if stop is not None or stop <= consumer_level: + return False + return True + else: + return level >= consumer_level + + #@classmethod + def level_for_integer(cls, level): + levels = cls.LEVELS + if level < 0: + return levels[0] + if level >= len(levels): + return levels[-1] + return levels[level] + + level_for_integer = classmethod(level_for_integer) + +# create a silent logger just to prevent this from being undefined +# will be overridden with requested verbosity main() is called. +logger = Logger([(Logger.LEVELS[-1], sys.stdout)]) + +def mkdir(path): + if not os.path.exists(path): + logger.info('Creating %s', path) + os.makedirs(path) + else: + logger.info('Directory %s already exists', path) + +def copyfileordir(src, dest): + if os.path.isdir(src): + shutil.copytree(src, dest, True) + else: + shutil.copy2(src, dest) + +def copyfile(src, dest, symlink=True): + if not os.path.exists(src): + # Some bad symlink in the src + logger.warn('Cannot find file %s (bad symlink)', src) + return + if os.path.exists(dest): + logger.debug('File %s already exists', dest) + return + if not os.path.exists(os.path.dirname(dest)): + logger.info('Creating parent directories for %s' % os.path.dirname(dest)) + os.makedirs(os.path.dirname(dest)) + if not os.path.islink(src): + srcpath = os.path.abspath(src) + else: + srcpath = os.readlink(src) + if symlink and hasattr(os, 'symlink'): + logger.info('Symlinking %s', dest) + try: + os.symlink(srcpath, dest) + except (OSError, NotImplementedError): + logger.info('Symlinking failed, copying to %s', dest) + copyfileordir(src, dest) + else: + logger.info('Copying to %s', dest) + copyfileordir(src, dest) + +def writefile(dest, content, overwrite=True): + if not os.path.exists(dest): + logger.info('Writing %s', dest) + f = open(dest, 'wb') + f.write(content.encode('utf-8')) + f.close() + return + else: + f = open(dest, 'rb') + c = f.read() + f.close() + if c != content: + if not overwrite: + logger.notify('File %s exists with different content; not overwriting', dest) + return + logger.notify('Overwriting %s with new content', dest) + f = open(dest, 'wb') + f.write(content.encode('utf-8')) + f.close() + else: + logger.info('Content %s already in place', dest) + +def rmtree(dir): + if os.path.exists(dir): + logger.notify('Deleting tree %s', dir) + shutil.rmtree(dir) + else: + logger.info('Do not need to delete %s; already gone', dir) + +def make_exe(fn): + if hasattr(os, 'chmod'): + oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777 + newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777 + os.chmod(fn, newmode) + logger.info('Changed mode of %s to %s', fn, oct(newmode)) + +def _find_file(filename, dirs): + for dir in dirs: + if os.path.exists(join(dir, filename)): + return join(dir, filename) + return filename + +def _install_req(py_executable, unzip=False, distribute=False, + search_dirs=None, never_download=False): + + if search_dirs is None: + search_dirs = file_search_dirs() + + if not distribute: + setup_fn = 'setuptools-0.6c11-py%s.egg' % sys.version[:3] + project_name = 'setuptools' + bootstrap_script = EZ_SETUP_PY + source = None + else: + setup_fn = None + source = 'distribute-0.6.16.tar.gz' + project_name = 'distribute' + bootstrap_script = DISTRIBUTE_SETUP_PY + try: + # check if the global Python has distribute installed or plain + # setuptools + import pkg_resources + if not hasattr(pkg_resources, '_distribute'): + location = os.path.dirname(pkg_resources.__file__) + logger.notify("A globally installed setuptools was found (in %s)" % location) + logger.notify("Use the --no-site-packages option to use distribute in " + "the virtualenv.") + except ImportError: + pass + + if setup_fn is not None: + setup_fn = _find_file(setup_fn, search_dirs) + + if source is not None: + source = _find_file(source, search_dirs) + + if is_jython and os._name == 'nt': + # Jython's .bat sys.executable can't handle a command line + # argument with newlines + fd, ez_setup = tempfile.mkstemp('.py') + os.write(fd, bootstrap_script) + os.close(fd) + cmd = [py_executable, ez_setup] + else: + cmd = [py_executable, '-c', bootstrap_script] + if unzip: + cmd.append('--always-unzip') + env = {} + remove_from_env = [] + if logger.stdout_level_matches(logger.DEBUG): + cmd.append('-v') + + old_chdir = os.getcwd() + if setup_fn is not None and os.path.exists(setup_fn): + logger.info('Using existing %s egg: %s' % (project_name, setup_fn)) + cmd.append(setup_fn) + if os.environ.get('PYTHONPATH'): + env['PYTHONPATH'] = setup_fn + os.path.pathsep + os.environ['PYTHONPATH'] + else: + env['PYTHONPATH'] = setup_fn + else: + # the source is found, let's chdir + if source is not None and os.path.exists(source): + logger.info('Using existing %s egg: %s' % (project_name, source)) + os.chdir(os.path.dirname(source)) + # in this case, we want to be sure that PYTHONPATH is unset (not + # just empty, really unset), else CPython tries to import the + # site.py that it's in virtualenv_support + remove_from_env.append('PYTHONPATH') + else: + if never_download: + logger.fatal("Can't find any local distributions of %s to install " + "and --never-download is set. Either re-run virtualenv " + "without the --never-download option, or place a %s " + "distribution (%s) in one of these " + "locations: %r" % (project_name, project_name, + setup_fn or source, + search_dirs)) + sys.exit(1) + + logger.info('No %s egg found; downloading' % project_name) + cmd.extend(['--always-copy', '-U', project_name]) + logger.start_progress('Installing %s...' % project_name) + logger.indent += 2 + cwd = None + if project_name == 'distribute': + env['DONT_PATCH_SETUPTOOLS'] = 'true' + + def _filter_ez_setup(line): + return filter_ez_setup(line, project_name) + + if not os.access(os.getcwd(), os.W_OK): + cwd = tempfile.mkdtemp() + if source is not None and os.path.exists(source): + # the current working dir is hostile, let's copy the + # tarball to a temp dir + target = os.path.join(cwd, os.path.split(source)[-1]) + shutil.copy(source, target) + try: + call_subprocess(cmd, show_stdout=False, + filter_stdout=_filter_ez_setup, + extra_env=env, + remove_from_env=remove_from_env, + cwd=cwd) + finally: + logger.indent -= 2 + logger.end_progress() + if os.getcwd() != old_chdir: + os.chdir(old_chdir) + if is_jython and os._name == 'nt': + os.remove(ez_setup) + +def file_search_dirs(): + here = os.path.dirname(os.path.abspath(__file__)) + dirs = ['.', here, + join(here, 'virtualenv_support')] + if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': + # Probably some boot script; just in case virtualenv is installed... + try: + import virtualenv + except ImportError: + pass + else: + dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) + return [d for d in dirs if os.path.isdir(d)] + +def install_setuptools(py_executable, unzip=False, + search_dirs=None, never_download=False): + _install_req(py_executable, unzip, + search_dirs=search_dirs, never_download=never_download) + +def install_distribute(py_executable, unzip=False, + search_dirs=None, never_download=False): + _install_req(py_executable, unzip, distribute=True, + search_dirs=search_dirs, never_download=never_download) + +_pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) +def install_pip(py_executable, search_dirs=None, never_download=False): + if search_dirs is None: + search_dirs = file_search_dirs() + + filenames = [] + for dir in search_dirs: + filenames.extend([join(dir, fn) for fn in os.listdir(dir) + if _pip_re.search(fn)]) + filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)] + filenames.sort() + filenames = [filename for basename, i, filename in filenames] + if not filenames: + filename = 'pip' + else: + filename = filenames[-1] + easy_install_script = 'easy_install' + if sys.platform == 'win32': + easy_install_script = 'easy_install-script.py' + cmd = [py_executable, join(os.path.dirname(py_executable), easy_install_script), filename] + if filename == 'pip': + if never_download: + logger.fatal("Can't find any local distributions of pip to install " + "and --never-download is set. Either re-run virtualenv " + "without the --never-download option, or place a pip " + "source distribution (zip/tar.gz/tar.bz2) in one of these " + "locations: %r" % search_dirs) + sys.exit(1) + logger.info('Installing pip from network...') + else: + logger.info('Installing existing %s distribution: %s' % ( + os.path.basename(filename), filename)) + logger.start_progress('Installing pip...') + logger.indent += 2 + def _filter_setup(line): + return filter_ez_setup(line, 'pip') + try: + call_subprocess(cmd, show_stdout=False, + filter_stdout=_filter_setup) + finally: + logger.indent -= 2 + logger.end_progress() + +def filter_ez_setup(line, project_name='setuptools'): + if not line.strip(): + return Logger.DEBUG + if project_name == 'distribute': + for prefix in ('Extracting', 'Now working', 'Installing', 'Before', + 'Scanning', 'Setuptools', 'Egg', 'Already', + 'running', 'writing', 'reading', 'installing', + 'creating', 'copying', 'byte-compiling', 'removing', + 'Processing'): + if line.startswith(prefix): + return Logger.DEBUG + return Logger.DEBUG + for prefix in ['Reading ', 'Best match', 'Processing setuptools', + 'Copying setuptools', 'Adding setuptools', + 'Installing ', 'Installed ']: + if line.startswith(prefix): + return Logger.DEBUG + return Logger.INFO + +def main(): + parser = optparse.OptionParser( + version=virtualenv_version, + usage="%prog [OPTIONS] DEST_DIR") + + parser.add_option( + '-v', '--verbose', + action='count', + dest='verbose', + default=0, + help="Increase verbosity") + + parser.add_option( + '-q', '--quiet', + action='count', + dest='quiet', + default=0, + help='Decrease verbosity') + + parser.add_option( + '-p', '--python', + dest='python', + metavar='PYTHON_EXE', + help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 ' + 'interpreter to create the new environment. The default is the interpreter that ' + 'virtualenv was installed with (%s)' % sys.executable) + + parser.add_option( + '--clear', + dest='clear', + action='store_true', + help="Clear out the non-root install and start from scratch") + + parser.add_option( + '--no-site-packages', + dest='no_site_packages', + action='store_true', + help="Don't give access to the global site-packages dir to the " + "virtual environment") + + parser.add_option( + '--unzip-setuptools', + dest='unzip_setuptools', + action='store_true', + help="Unzip Setuptools or Distribute when installing it") + + parser.add_option( + '--relocatable', + dest='relocatable', + action='store_true', + help='Make an EXISTING virtualenv environment relocatable. ' + 'This fixes up scripts and makes all .pth files relative') + + parser.add_option( + '--distribute', + dest='use_distribute', + action='store_true', + help='Use Distribute instead of Setuptools. Set environ variable ' + 'VIRTUALENV_USE_DISTRIBUTE to make it the default ') + + default_search_dirs = file_search_dirs() + parser.add_option( + '--extra-search-dir', + dest="search_dirs", + action="append", + default=default_search_dirs, + help="Directory to look for setuptools/distribute/pip distributions in. " + "You can add any number of additional --extra-search-dir paths.") + + parser.add_option( + '--never-download', + dest="never_download", + action="store_true", + help="Never download anything from the network. Instead, virtualenv will fail " + "if local distributions of setuptools/distribute/pip are not present.") + + parser.add_option( + '--prompt=', + dest='prompt', + help='Provides an alternative prompt prefix for this environment') + + if 'extend_parser' in globals(): + extend_parser(parser) + + options, args = parser.parse_args() + + global logger + + if 'adjust_options' in globals(): + adjust_options(options, args) + + verbosity = options.verbose - options.quiet + logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)]) + + if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): + env = os.environ.copy() + interpreter = resolve_interpreter(options.python) + if interpreter == sys.executable: + logger.warn('Already using interpreter %s' % interpreter) + else: + logger.notify('Running virtualenv with interpreter %s' % interpreter) + env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' + file = __file__ + if file.endswith('.pyc'): + file = file[:-1] + popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) + raise SystemExit(popen.wait()) + + if not args: + print('You must provide a DEST_DIR') + parser.print_help() + sys.exit(2) + if len(args) > 1: + print('There must be only one argument: DEST_DIR (you gave %s)' % ( + ' '.join(args))) + parser.print_help() + sys.exit(2) + + home_dir = args[0] + + if os.environ.get('WORKING_ENV'): + logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') + logger.fatal('Please deactivate your workingenv, then re-run this script') + sys.exit(3) + + if 'PYTHONHOME' in os.environ: + logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') + del os.environ['PYTHONHOME'] + + if options.relocatable: + make_environment_relocatable(home_dir) + return + + create_environment(home_dir, site_packages=not options.no_site_packages, clear=options.clear, + unzip_setuptools=options.unzip_setuptools, + use_distribute=options.use_distribute or majver > 2, + prompt=options.prompt, + search_dirs=options.search_dirs, + never_download=options.never_download) + if 'after_install' in globals(): + after_install(options, home_dir) + +def call_subprocess(cmd, show_stdout=True, + filter_stdout=None, cwd=None, + raise_on_returncode=True, extra_env=None, + remove_from_env=None): + cmd_parts = [] + for part in cmd: + if len(part) > 45: + part = part[:20]+"..."+part[-20:] + if ' ' in part or '\n' in part or '"' in part or "'" in part: + part = '"%s"' % part.replace('"', '\\"') + cmd_parts.append(part) + cmd_desc = ' '.join(cmd_parts) + if show_stdout: + stdout = None + else: + stdout = subprocess.PIPE + logger.debug("Running command %s" % cmd_desc) + if extra_env or remove_from_env: + env = os.environ.copy() + if extra_env: + env.update(extra_env) + if remove_from_env: + for varname in remove_from_env: + env.pop(varname, None) + else: + env = None + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, + cwd=cwd, env=env) + except Exception: + e = sys.exc_info()[1] + logger.fatal( + "Error %s while executing command %s" % (e, cmd_desc)) + raise + all_output = [] + if stdout is not None: + stdout = proc.stdout + encoding = sys.getdefaultencoding() + while 1: + line = stdout.readline().decode(encoding) + if not line: + break + line = line.rstrip() + all_output.append(line) + if filter_stdout: + level = filter_stdout(line) + if isinstance(level, tuple): + level, line = level + logger.log(level, line) + if not logger.stdout_level_matches(level): + logger.show_progress() + else: + logger.info(line) + else: + proc.communicate() + proc.wait() + if proc.returncode: + if raise_on_returncode: + if all_output: + logger.notify('Complete output from command %s:' % cmd_desc) + logger.notify('\n'.join(all_output) + '\n----------------------------------------') + raise OSError( + "Command %s failed with error code %s" + % (cmd_desc, proc.returncode)) + else: + logger.warn( + "Command %s had error code %s" + % (cmd_desc, proc.returncode)) + + +def create_environment(home_dir, site_packages=True, clear=False, + unzip_setuptools=False, use_distribute=False, + prompt=None, search_dirs=None, never_download=False): + """ + Creates a new environment in ``home_dir``. + + If ``site_packages`` is true (the default) then the global + ``site-packages/`` directory will be on the path. + + If ``clear`` is true (default False) then the environment will + first be cleared. + """ + home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) + + py_executable = os.path.abspath(install_python( + home_dir, lib_dir, inc_dir, bin_dir, + site_packages=site_packages, clear=clear)) + + install_distutils(home_dir) + + if use_distribute or os.environ.get('VIRTUALENV_USE_DISTRIBUTE'): + install_distribute(py_executable, unzip=unzip_setuptools, + search_dirs=search_dirs, never_download=never_download) + else: + install_setuptools(py_executable, unzip=unzip_setuptools, + search_dirs=search_dirs, never_download=never_download) + + install_pip(py_executable, search_dirs=search_dirs, never_download=never_download) + + install_activate(home_dir, bin_dir, prompt) + +def path_locations(home_dir): + """Return the path locations for the environment (where libraries are, + where scripts go, etc)""" + # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its + # prefix arg is broken: http://bugs.python.org/issue3386 + if sys.platform == 'win32': + # Windows has lots of problems with executables with spaces in + # the name; this function will remove them (using the ~1 + # format): + mkdir(home_dir) + if ' ' in home_dir: + try: + import win32api + except ImportError: + print('Error: the path "%s" has a space in it' % home_dir) + print('To handle these kinds of paths, the win32api module must be installed:') + print(' http://sourceforge.net/projects/pywin32/') + sys.exit(3) + home_dir = win32api.GetShortPathName(home_dir) + lib_dir = join(home_dir, 'Lib') + inc_dir = join(home_dir, 'Include') + bin_dir = join(home_dir, 'Scripts') + elif is_jython: + lib_dir = join(home_dir, 'Lib') + inc_dir = join(home_dir, 'Include') + bin_dir = join(home_dir, 'bin') + elif is_pypy: + lib_dir = home_dir + inc_dir = join(home_dir, 'include') + bin_dir = join(home_dir, 'bin') + else: + lib_dir = join(home_dir, 'lib', py_version) + inc_dir = join(home_dir, 'include', py_version + abiflags) + bin_dir = join(home_dir, 'bin') + return home_dir, lib_dir, inc_dir, bin_dir + + +def change_prefix(filename, dst_prefix): + prefixes = [sys.prefix] + + if sys.platform == "darwin": + prefixes.extend(( + os.path.join("/Library/Python", sys.version[:3], "site-packages"), + os.path.join(sys.prefix, "Extras", "lib", "python"), + os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"))) + + if hasattr(sys, 'real_prefix'): + prefixes.append(sys.real_prefix) + prefixes = list(map(os.path.abspath, prefixes)) + filename = os.path.abspath(filename) + for src_prefix in prefixes: + if filename.startswith(src_prefix): + _, relpath = filename.split(src_prefix, 1) + assert relpath[0] == os.sep + relpath = relpath[1:] + return join(dst_prefix, relpath) + assert False, "Filename %s does not start with any of these prefixes: %s" % \ + (filename, prefixes) + +def copy_required_modules(dst_prefix): + import imp + for modname in REQUIRED_MODULES: + if modname in sys.builtin_module_names: + logger.info("Ignoring built-in bootstrap module: %s" % modname) + continue + try: + f, filename, _ = imp.find_module(modname) + except ImportError: + logger.info("Cannot import bootstrap module: %s" % modname) + else: + if f is not None: + f.close() + dst_filename = change_prefix(filename, dst_prefix) + copyfile(filename, dst_filename) + if filename.endswith('.pyc'): + pyfile = filename[:-1] + if os.path.exists(pyfile): + copyfile(pyfile, dst_filename[:-1]) + + +def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): + """Install just the base environment, no distutils patches etc""" + if sys.executable.startswith(bin_dir): + print('Please use the *system* python to run this script') + return + + if clear: + rmtree(lib_dir) + ## FIXME: why not delete it? + ## Maybe it should delete everything with #!/path/to/venv/python in it + logger.notify('Not deleting %s', bin_dir) + + if hasattr(sys, 'real_prefix'): + logger.notify('Using real prefix %r' % sys.real_prefix) + prefix = sys.real_prefix + else: + prefix = sys.prefix + mkdir(lib_dir) + fix_lib64(lib_dir) + stdlib_dirs = [os.path.dirname(os.__file__)] + if sys.platform == 'win32': + stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) + elif sys.platform == 'darwin': + stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) + if hasattr(os, 'symlink'): + logger.info('Symlinking Python bootstrap modules') + else: + logger.info('Copying Python bootstrap modules') + logger.indent += 2 + try: + # copy required files... + for stdlib_dir in stdlib_dirs: + if not os.path.isdir(stdlib_dir): + continue + for fn in os.listdir(stdlib_dir): + bn = os.path.splitext(fn)[0] + if fn != 'site-packages' and bn in REQUIRED_FILES: + copyfile(join(stdlib_dir, fn), join(lib_dir, fn)) + # ...and modules + copy_required_modules(home_dir) + finally: + logger.indent -= 2 + mkdir(join(lib_dir, 'site-packages')) + import site + site_filename = site.__file__ + if site_filename.endswith('.pyc'): + site_filename = site_filename[:-1] + elif site_filename.endswith('$py.class'): + site_filename = site_filename.replace('$py.class', '.py') + site_filename_dst = change_prefix(site_filename, home_dir) + site_dir = os.path.dirname(site_filename_dst) + writefile(site_filename_dst, SITE_PY) + writefile(join(site_dir, 'orig-prefix.txt'), prefix) + site_packages_filename = join(site_dir, 'no-global-site-packages.txt') + if not site_packages: + writefile(site_packages_filename, '') + else: + if os.path.exists(site_packages_filename): + logger.info('Deleting %s' % site_packages_filename) + os.unlink(site_packages_filename) + + if is_pypy: + stdinc_dir = join(prefix, 'include') + else: + stdinc_dir = join(prefix, 'include', py_version + abiflags) + if os.path.exists(stdinc_dir): + copyfile(stdinc_dir, inc_dir) + else: + logger.debug('No include dir %s' % stdinc_dir) + + # pypy never uses exec_prefix, just ignore it + if sys.exec_prefix != prefix and not is_pypy: + if sys.platform == 'win32': + exec_dir = join(sys.exec_prefix, 'lib') + elif is_jython: + exec_dir = join(sys.exec_prefix, 'Lib') + else: + exec_dir = join(sys.exec_prefix, 'lib', py_version) + for fn in os.listdir(exec_dir): + copyfile(join(exec_dir, fn), join(lib_dir, fn)) + + if is_jython: + # Jython has either jython-dev.jar and javalib/ dir, or just + # jython.jar + for name in 'jython-dev.jar', 'javalib', 'jython.jar': + src = join(prefix, name) + if os.path.exists(src): + copyfile(src, join(home_dir, name)) + # XXX: registry should always exist after Jython 2.5rc1 + src = join(prefix, 'registry') + if os.path.exists(src): + copyfile(src, join(home_dir, 'registry'), symlink=False) + copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), + symlink=False) + + mkdir(bin_dir) + py_executable = join(bin_dir, os.path.basename(sys.executable)) + if 'Python.framework' in prefix: + if re.search(r'/Python(?:-32|-64)*$', py_executable): + # The name of the python executable is not quite what + # we want, rename it. + py_executable = os.path.join( + os.path.dirname(py_executable), 'python') + + logger.notify('New %s executable in %s', expected_exe, py_executable) + if sys.executable != py_executable: + ## FIXME: could I just hard link? + executable = sys.executable + if sys.platform == 'cygwin' and os.path.exists(executable + '.exe'): + # Cygwin misreports sys.executable sometimes + executable += '.exe' + py_executable += '.exe' + logger.info('Executable actually exists in %s' % executable) + shutil.copyfile(executable, py_executable) + make_exe(py_executable) + if sys.platform == 'win32' or sys.platform == 'cygwin': + pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') + if os.path.exists(pythonw): + logger.info('Also created pythonw.exe') + shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) + if is_pypy: + # make a symlink python --> pypy-c + python_executable = os.path.join(os.path.dirname(py_executable), 'python') + logger.info('Also created executable %s' % python_executable) + copyfile(py_executable, python_executable) + + if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: + secondary_exe = os.path.join(os.path.dirname(py_executable), + expected_exe) + py_executable_ext = os.path.splitext(py_executable)[1] + if py_executable_ext == '.exe': + # python2.4 gives an extension of '.4' :P + secondary_exe += py_executable_ext + if os.path.exists(secondary_exe): + logger.warn('Not overwriting existing %s script %s (you must use %s)' + % (expected_exe, secondary_exe, py_executable)) + else: + logger.notify('Also creating executable in %s' % secondary_exe) + shutil.copyfile(sys.executable, secondary_exe) + make_exe(secondary_exe) + + if 'Python.framework' in prefix: + logger.debug('MacOSX Python framework detected') + + # Make sure we use the the embedded interpreter inside + # the framework, even if sys.executable points to + # the stub executable in ${sys.prefix}/bin + # See http://groups.google.com/group/python-virtualenv/ + # browse_thread/thread/17cab2f85da75951 + original_python = os.path.join( + prefix, 'Resources/Python.app/Contents/MacOS/Python') + shutil.copy(original_python, py_executable) + + # Copy the framework's dylib into the virtual + # environment + virtual_lib = os.path.join(home_dir, '.Python') + + if os.path.exists(virtual_lib): + os.unlink(virtual_lib) + copyfile( + os.path.join(prefix, 'Python'), + virtual_lib) + + # And then change the install_name of the copied python executable + try: + call_subprocess( + ["install_name_tool", "-change", + os.path.join(prefix, 'Python'), + '@executable_path/../.Python', + py_executable]) + except: + logger.fatal( + "Could not call install_name_tool -- you must have Apple's development tools installed") + raise + + # Some tools depend on pythonX.Y being present + py_executable_version = '%s.%s' % ( + sys.version_info[0], sys.version_info[1]) + if not py_executable.endswith(py_executable_version): + # symlinking pythonX.Y > python + pth = py_executable + '%s.%s' % ( + sys.version_info[0], sys.version_info[1]) + if os.path.exists(pth): + os.unlink(pth) + os.symlink('python', pth) + else: + # reverse symlinking python -> pythonX.Y (with --python) + pth = join(bin_dir, 'python') + if os.path.exists(pth): + os.unlink(pth) + os.symlink(os.path.basename(py_executable), pth) + + if sys.platform == 'win32' and ' ' in py_executable: + # There's a bug with subprocess on Windows when using a first + # argument that has a space in it. Instead we have to quote + # the value: + py_executable = '"%s"' % py_executable + cmd = [py_executable, '-c', 'import sys; print(sys.prefix)'] + logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) + try: + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE) + proc_stdout, proc_stderr = proc.communicate() + except OSError: + e = sys.exc_info()[1] + if e.errno == errno.EACCES: + logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e)) + sys.exit(100) + else: + raise e + + proc_stdout = proc_stdout.strip().decode(sys.getdefaultencoding()) + proc_stdout = os.path.normcase(os.path.abspath(proc_stdout)) + if proc_stdout != os.path.normcase(os.path.abspath(home_dir)): + logger.fatal( + 'ERROR: The executable %s is not functioning' % py_executable) + logger.fatal( + 'ERROR: It thinks sys.prefix is %r (should be %r)' + % (proc_stdout, os.path.normcase(os.path.abspath(home_dir)))) + logger.fatal( + 'ERROR: virtualenv is not compatible with this system or executable') + if sys.platform == 'win32': + logger.fatal( + 'Note: some Windows users have reported this error when they installed Python for "Only this user". The problem may be resolvable if you install Python "For all users". (See https://bugs.launchpad.net/virtualenv/+bug/352844)') + sys.exit(100) + else: + logger.info('Got sys.prefix result: %r' % proc_stdout) + + pydistutils = os.path.expanduser('~/.pydistutils.cfg') + if os.path.exists(pydistutils): + logger.notify('Please make sure you remove any previous custom paths from ' + 'your %s file.' % pydistutils) + ## FIXME: really this should be calculated earlier + return py_executable + +def install_activate(home_dir, bin_dir, prompt=None): + if sys.platform == 'win32' or is_jython and os._name == 'nt': + files = {'activate.bat': ACTIVATE_BAT, + 'deactivate.bat': DEACTIVATE_BAT} + if os.environ.get('OS') == 'Windows_NT' and os.environ.get('OSTYPE') == 'cygwin': + files['activate'] = ACTIVATE_SH + else: + files = {'activate': ACTIVATE_SH} + + # suppling activate.fish in addition to, not instead of, the + # bash script support. + files['activate.fish'] = ACTIVATE_FISH + + # same for csh/tcsh support... + files['activate.csh'] = ACTIVATE_CSH + + + + files['activate_this.py'] = ACTIVATE_THIS + vname = os.path.basename(os.path.abspath(home_dir)) + for name, content in files.items(): + content = content.replace('__VIRTUAL_PROMPT__', prompt or '') + content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname) + content = content.replace('__VIRTUAL_ENV__', os.path.abspath(home_dir)) + content = content.replace('__VIRTUAL_NAME__', vname) + content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) + writefile(os.path.join(bin_dir, name), content) + +def install_distutils(home_dir): + distutils_path = change_prefix(distutils.__path__[0], home_dir) + mkdir(distutils_path) + ## FIXME: maybe this prefix setting should only be put in place if + ## there's a local distutils.cfg with a prefix setting? + home_dir = os.path.abspath(home_dir) + ## FIXME: this is breaking things, removing for now: + #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir + writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) + writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) + +def fix_lib64(lib_dir): + """ + Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y + instead of lib/pythonX.Y. If this is such a platform we'll just create a + symlink so lib64 points to lib + """ + if [p for p in distutils.sysconfig.get_config_vars().values() + if isinstance(p, basestring) and 'lib64' in p]: + logger.debug('This system uses lib64; symlinking lib64 to lib') + assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( + "Unexpected python lib dir: %r" % lib_dir) + lib_parent = os.path.dirname(lib_dir) + assert os.path.basename(lib_parent) == 'lib', ( + "Unexpected parent dir: %r" % lib_parent) + copyfile(lib_parent, os.path.join(os.path.dirname(lib_parent), 'lib64')) + +def resolve_interpreter(exe): + """ + If the executable given isn't an absolute path, search $PATH for the interpreter + """ + if os.path.abspath(exe) != exe: + paths = os.environ.get('PATH', '').split(os.pathsep) + for path in paths: + if os.path.exists(os.path.join(path, exe)): + exe = os.path.join(path, exe) + break + if not os.path.exists(exe): + logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe)) + raise SystemExit(3) + if not is_executable(exe): + logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe)) + raise SystemExit(3) + return exe + +def is_executable(exe): + """Checks a file is executable""" + return os.access(exe, os.X_OK) + +############################################################ +## Relocating the environment: + +def make_environment_relocatable(home_dir): + """ + Makes the already-existing environment use relative paths, and takes out + the #!-based environment selection in scripts. + """ + home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) + activate_this = os.path.join(bin_dir, 'activate_this.py') + if not os.path.exists(activate_this): + logger.fatal( + 'The environment doesn\'t have a file %s -- please re-run virtualenv ' + 'on this environment to update it' % activate_this) + fixup_scripts(home_dir) + fixup_pth_and_egg_link(home_dir) + ## FIXME: need to fix up distutils.cfg + +OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], + 'activate', 'activate.bat', 'activate_this.py'] + +def fixup_scripts(home_dir): + # This is what we expect at the top of scripts: + shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) + # This is what we'll put: + new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] + activate = "import os; activate_this=os.path.join(os.path.dirname(__file__), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" + if sys.platform == 'win32': + bin_suffix = 'Scripts' + else: + bin_suffix = 'bin' + bin_dir = os.path.join(home_dir, bin_suffix) + home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) + for filename in os.listdir(bin_dir): + filename = os.path.join(bin_dir, filename) + if not os.path.isfile(filename): + # ignore subdirs, e.g. .svn ones. + continue + f = open(filename, 'rb') + lines = f.readlines() + f.close() + if not lines: + logger.warn('Script %s is an empty file' % filename) + continue + if not lines[0].strip().startswith(shebang): + if os.path.basename(filename) in OK_ABS_SCRIPTS: + logger.debug('Cannot make script %s relative' % filename) + elif lines[0].strip() == new_shebang: + logger.info('Script %s has already been made relative' % filename) + else: + logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' + % (filename, shebang)) + continue + logger.notify('Making script %s relative' % filename) + lines = [new_shebang+'\n', activate+'\n'] + lines[1:] + f = open(filename, 'wb') + f.writelines(lines) + f.close() + +def fixup_pth_and_egg_link(home_dir, sys_path=None): + """Makes .pth and .egg-link files use relative paths""" + home_dir = os.path.normcase(os.path.abspath(home_dir)) + if sys_path is None: + sys_path = sys.path + for path in sys_path: + if not path: + path = '.' + if not os.path.isdir(path): + continue + path = os.path.normcase(os.path.abspath(path)) + if not path.startswith(home_dir): + logger.debug('Skipping system (non-environment) directory %s' % path) + continue + for filename in os.listdir(path): + filename = os.path.join(path, filename) + if filename.endswith('.pth'): + if not os.access(filename, os.W_OK): + logger.warn('Cannot write .pth file %s, skipping' % filename) + else: + fixup_pth_file(filename) + if filename.endswith('.egg-link'): + if not os.access(filename, os.W_OK): + logger.warn('Cannot write .egg-link file %s, skipping' % filename) + else: + fixup_egg_link(filename) + +def fixup_pth_file(filename): + lines = [] + prev_lines = [] + f = open(filename) + prev_lines = f.readlines() + f.close() + for line in prev_lines: + line = line.strip() + if (not line or line.startswith('#') or line.startswith('import ') + or os.path.abspath(line) != line): + lines.append(line) + else: + new_value = make_relative_path(filename, line) + if line != new_value: + logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) + lines.append(new_value) + if lines == prev_lines: + logger.info('No changes to .pth file %s' % filename) + return + logger.notify('Making paths in .pth file %s relative' % filename) + f = open(filename, 'w') + f.write('\n'.join(lines) + '\n') + f.close() + +def fixup_egg_link(filename): + f = open(filename) + link = f.read().strip() + f.close() + if os.path.abspath(link) != link: + logger.debug('Link in %s already relative' % filename) + return + new_link = make_relative_path(filename, link) + logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) + f = open(filename, 'w') + f.write(new_link) + f.close() + +def make_relative_path(source, dest, dest_is_directory=True): + """ + Make a filename relative, where the filename is dest, and it is + being referred to from the filename source. + + >>> make_relative_path('/usr/share/something/a-file.pth', + ... '/usr/share/another-place/src/Directory') + '../another-place/src/Directory' + >>> make_relative_path('/usr/share/something/a-file.pth', + ... '/home/user/src/Directory') + '../../../home/user/src/Directory' + >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') + './' + """ + source = os.path.dirname(source) + if not dest_is_directory: + dest_filename = os.path.basename(dest) + dest = os.path.dirname(dest) + dest = os.path.normpath(os.path.abspath(dest)) + source = os.path.normpath(os.path.abspath(source)) + dest_parts = dest.strip(os.path.sep).split(os.path.sep) + source_parts = source.strip(os.path.sep).split(os.path.sep) + while dest_parts and source_parts and dest_parts[0] == source_parts[0]: + dest_parts.pop(0) + source_parts.pop(0) + full_parts = ['..']*len(source_parts) + dest_parts + if not dest_is_directory: + full_parts.append(dest_filename) + if not full_parts: + # Special case for the current directory (otherwise it'd be '') + return './' + return os.path.sep.join(full_parts) + + + +############################################################ +## Bootstrap script creation: + +def create_bootstrap_script(extra_text, python_version=''): + """ + Creates a bootstrap script, which is like this script but with + extend_parser, adjust_options, and after_install hooks. + + This returns a string that (written to disk of course) can be used + as a bootstrap script with your own customizations. The script + will be the standard virtualenv.py script, with your extra text + added (your extra text should be Python code). + + If you include these functions, they will be called: + + ``extend_parser(optparse_parser)``: + You can add or remove options from the parser here. + + ``adjust_options(options, args)``: + You can change options here, or change the args (if you accept + different kinds of arguments, be sure you modify ``args`` so it is + only ``[DEST_DIR]``). + + ``after_install(options, home_dir)``: + + After everything is installed, this function is called. This + is probably the function you are most likely to use. An + example would be:: + + def after_install(options, home_dir): + subprocess.call([join(home_dir, 'bin', 'easy_install'), + 'MyPackage']) + subprocess.call([join(home_dir, 'bin', 'my-package-script'), + 'setup', home_dir]) + + This example immediately installs a package, and runs a setup + script from that package. + + If you provide something like ``python_version='2.4'`` then the + script will start with ``#!/usr/bin/env python2.4`` instead of + ``#!/usr/bin/env python``. You can use this when the script must + be run with a particular Python version. + """ + filename = __file__ + if filename.endswith('.pyc'): + filename = filename[:-1] + f = open(filename, 'rb') + content = f.read() + f.close() + py_exe = 'python%s' % python_version + content = (('#!/usr/bin/env %s\n' % py_exe) + + '## WARNING: This file is generated\n' + + content) + return content.replace('##EXT' 'END##', extra_text) + +##EXTEND## + +def convert(s): + b = base64.b64decode(s.encode('ascii')) + return zlib.decompress(b).decode('utf-8') + +##file site.py +SITE_PY = convert(""" +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 +""") + + + + + +##file ez_setup.py +EZ_SETUP_PY = convert(""" +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 +BDaonX65d/fwEjNqlDjLVIvM9X+XVxF7 +""") + + + + + +##file distribute_setup.py +DISTRIBUTE_SETUP_PY = convert(""" +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 +""") + + + + + +##file activate.sh +ACTIVATE_SH = convert(""" +eJytVU1v4jAQPW9+xTT0ANVS1GsrDlRFAqmFqmG72m0rY5IJsRRslDiktNr/vuMQ8tFQpNU2B4I9 +H36eeW/SglkgYvBFiLBKYg0LhCRGD1KhA7BjlUQuwkLIHne12HCNNpz5kVrBgsfBmdWCrUrA5VIq +DVEiQWjwRISuDreW5eE+CtodeLeAnhZEGKMGFXqAciMiJVcoNWx4JPgixDjzEj48QVeCfcqmtzfs +cfww+zG4ZfeD2ciGF7gCHaDMPM1jtvuHXAsPfF2rSGeOxV4iDY5GUGb3xVEYv2aj6WQ0vRseAlMY +G5DKsAawwnQUXt2LQOYlzZoYByqhonqoqfxZf4BLD97i4DukgXADCPgGgdOLTK5arYxZB1xnrc9T +EQFcHoZEAa1gSQioo/TPV5FZrDlxJA+NzwF+Ek1UonOzFnKZp6k5mgLBqSkuuAGXS4whJb5xz/xs +wXCHjiVerAk5eh9Kfz1wqOldtVv9dkbscfjgjKeTA8XPrtaNauX5rInOxaHuOReNtpFjo1/OxdFG +5eY9hJ3L3jqcPJbATggXAemDLZX0MNZRYjSDH7C1wMHQh73DyYfTu8a0F9v+6D8W6XNnF1GEIXW/ +JrSKPOtnW1YFat9mrLJkzLbyIlTvYzV0RGXcaTBfVLx7jF2PJ2wyuBsydpm7VSVa4C4Zb6pFO2TR +huypCEPwuQjNftUrNl6GsYZzuFrrLdC9iJjQ3omAPBbcI2lsU77tUD43kw1NPZhTrnZWzuQKLomx +Rd4OXM1ByExVVkmoTwfBJ7Lt10Iq1Kgo23Bmd8Ib1KrGbsbO4Pp2yO4fpnf3s6MnZiwuiJuls1/L +Pu4yUCvhpA+vZaJvWWDTr0yFYYyVnHMqCEq+QniuYX225xmnzRENjbXACF3wkCYNVZ1mBwxoR9Iw +WAo3/36oSOTfgjwEEQKt15e9Xpqm52+oaXxszmnE9GLl65RH2OMmS6+u5acKxDmlPgj2eT5/gQOX +LLK0j1y0Uwbmn438VZkVpqlfNKa/YET/53j+99G8H8tUhr9ZSXs2 +""") + + + + + +##file activate.fish +ACTIVATE_FISH = convert(""" +eJydVm1v4jgQ/s6vmA1wBxUE7X2stJVYlVWR2lK13d6d9laRk0yIr8HmbIe0++tvnIQQB9pbXT5A +Ys/LM55nZtyHx5RrSHiGsMm1gRAh1xhDwU0Kng8hFzMWGb5jBv2E69SDs0TJDdj3MxilxmzPZzP7 +pVPMMl+q9bjXh1eZQ8SEkAZULoAbiLnCyGSvvV6SC7IoBcS4Nw0wjcFbvJDcjiuTswzFDpiIQaHJ +lQAjQUi1YRmUboC2uZJig8J4PaCnT5IaDcgsbm/CjinOwgx1KcUTMEhhTgV4g2B1fRk8Le8fv86v +g7v545UHpZB9rKnp+gXsMhxLunIIpwVQxP/l9c/Hq9Xt1epm4R27bva6AJqN92G4YhbMG2i+LB+u +grv71c3dY7B6WtzfLy9bePbp0taDTXSwJQJszUnnp0y57mvpPcrF7ZODyhswtd59+/jdgw+fwBNS +xLSscksUPIDqwwNmCez3PpxGeyBYg6HE0YdcWBxcKczYzuVJi5Wu915vn5oWePCCoPUZBN5B7IgV +MCi54ZDLG7TUZ0HweXkb3M5vFmSpFm/gthhBx0UrveoPpv9AJ9unIbQYdUoe21bKg2q48sPFGVwu +H+afrxd1qvclaNlRFyh1EQ2sSccEuNAGWQwysfVpz1tPajUqbqJUnEcIJkWo6OXDaodK8ZiLdbmM +L1wb+9H0D+pcyPSrX5u5kgWSygRYXCnJUi/KKcuU4cqsAyTKZBiissLc7NFwizvjxtieKBVCIdWz +fzilzPaYyljZN0cGN1v7NnaIPNCGmVy3GKuJaQ6iVjE1Qfm+36hglErwmnAD8hu0dDy4uICBA8ZV +pQr/q/+O0KFW2kjelu9Dgb9SDBsWV4F4x5CswgS0zBVlk5tDMP5bVtUGpslbm81Lu2sdKq7uNMGh +MVQ4fy9xhogC1lS5guhISa0DlBWv0O8odT6/LP+4WZzDV6FzIkEqC0uolGZSZoMnlpxplmD2euaT +O4hkTpPnbztDccey0bhjDaBIqaWQa0uwEtQEwtyU56i4fq54F9IE3ORR6mKriODM4XOYZwaVYLYz +7SPbKkz4i7VkB6/Ot1upDE3znNqYKpM8raa0Bx8vfvntJ32UENsM4aI6gJL+jJwhxhh3jVIDOcpi +m0r2hmEtS8XXXNBk71QCDXTBNhhPiHX2LtHkrVIlhoEshH/EZgdq53Eirqs5iFKMnkOmqZTtr3Xq +djvPTWZT4S3NT5aVLgurMPUWI07BRVYqkQrmtCKohNY8qu9EdACoT6ki0a66XxVF4f9AQ3W38yO5 +mWmZmIIpnDFrbXakvKWeZhLwhvrbUH8fahhqD0YUcBDJjEBMQwiznE4y5QbHrbhHBOnUAYzb2tVN +jJa65e+eE2Ya30E2GurxUP8ssA6e/wOnvo3V78d3vTcvMB3n7l3iX1JXWqk= +""") + + + + + +##file activate.csh +ACTIVATE_CSH = convert(""" +eJx9U11vmzAUffevOCVRu+UB9pws29Kl0iq1aVWllaZlcgxciiViItsQdb9+xiQp+dh4QOB7Pu49 +XHqY59IgkwVhVRmLmFAZSrGRNkdgykonhFiqSCRW1sJSmJg8wCDT5QrucRCyHn6WFRKhVGmhKwVp +kUpNiS3emup3TY6XIn7DVNQyJUwlrgthJD6n/iCNv72uhCzCpFx9CRkThRQGKe08cWXJ9db/yh/u +pvzl9mn+PLnjj5P5D1yM8QmXlzBkSdXwZ0H/BBc0mEo5FE5qI2jKhclHOOvy9HD/OO/6YO1mX9vx +sY0H/tPIV0dtqel0V7iZvWyNg8XFcBA0ToEqVeqOdNUEQFvN41SumAv32VtJrakQNSmLWmgp4oJM +yDoBHgoydtoEAs47r5wHHnUal5vbJ8oOI+9wI86vb2d8Nrm/4Xy4RZ8R85E4uTZPB5EZPnTaaAGu +E59J8BE2J8XgrkbLeXMlVoQxznEYFYY8uFFdxsKQRx90Giwx9vSueHP1YNaUSFG4vTaErNSYuBOF +lXiVyXa9Sy3JdClEyK1dD6Nos9mEf8iKlOpmqSNTZnYjNEWiUYn2pKNB3ttcLJ3HmYYXy6Un76f7 +r8rRsC1TpTJj7f19m5sUf/V3Ir+x/yjtLu8KjLX/CmN/AcVGUUo= +""") + + + + + +##file activate.bat +ACTIVATE_BAT = convert(""" +eJyFUkEKgzAQvAfyhz0YaL9QEWpRqlSjWGspFPZQTevFHOr/adQaU1GaUzI7Mzu7ZF89XhKkEJS8 +qxaKMMsvboQ+LxxE44VICSW1gEa2UFaibqoS0iyJ0xw2lIA6nX5AHCu1jpRsv5KRjknkac9VLVug +sX9mtzxIeJDE/mg4OGp47qoLo3NHX2jsMB3AiDht5hryAUOEifoTdCXbSh7V0My2NMq/Xbh5MEjU +ZT63gpgNT9lKOJ/CtHsvT99re3pX303kydn4HeyOeAg5cjf2EW1D6HOPkg9NGKhu +""") + + + + + +##file deactivate.bat +DEACTIVATE_BAT = convert(""" +eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgWIgK0q +FlWqXJpcICVYpGzx2BAZ4uHv5+Hv6wq1BWINXBTdKriEKkI1DhW2QAfhttcxxANiFZCBbglQSJUL +i2dASrm4rFz9XLgAwJNbyQ== +""") + + + + + +##file distutils-init.py +DISTUTILS_INIT = convert(""" +eJytV92L4zYQf/dfMU0ottuse7RvC6FQrg8Lxz2Ugz4si9HacqKuIxlJ2ST313dG8odkO9d7aGBB +luZLv/nNjFacOqUtKJMIvzK3cXlhWgp5MDBsqK5SNYftsBAGpLLA4F1oe2Ytl+9wUvW55TswCi4c +KibhbFDSglXQCFmDPXIwtm7FawLRbwtPzg2T9gf4gupKv4GS0N262w7V0NvpbCy8cvTo3eAus6C5 +ETU3ICQZX1hFTw/dzR6V/AW1RCN4/XAtbsVXqIXmlVX6liS4lOzEYY9QFB2zx6LfoSNjz1a0pqT9 +QOIfJWQ2E888NEVZNqLlZZnvIB0NpHkimlFdKn2iRRY7yGG/CCJb6Iz280d34SFXBS2yEYPNF0Q7 +yM7oCjpWvbEDQmnhRwOs6zjThpKE8HogwRAgraqYFZgGZvzmzVh+mgz9vskT3hruwyjdFcqyENJw +bbMPO5jdzonxK68QKT7B57CMRRG5shRSWDTX3dI8LzRndZbnSWL1zfvriUmK4TcGWSnZiEPCrxXv +bM+sP7VW2is2WgWXCO3sAu3Rzysz3FiNCA8WPyM4gb1JAAmCiyTZbhFjWx3h9SzauuRXC9MFoVbc +yNTCm1QXOOIfIn/g1kGMhDUBN72hI5XCBQtIXQw8UEEdma6Jaz4vJIJ51Orc15hzzmu6TdFp3ogr +Aof0c98tsw1SiaiWotHffk3XYCkqdToxWRfTFXqgpg2khcLluOHMVC0zZhLKIomesfSreUNNgbXi +Ky9VRzwzkBneNoGQyyvGjbsFQqOZvpWIjqH281lJ/jireFgR3cPzSyTGWzQpDNIU+03Fs4XKLkhp +/n0uFnuF6VphB44b3uWRneSbBoMSioqE8oeF0JY+qTvYfEK+bPLYdoR4McfYQ7wMZj39q0kfP8q+ +FfsymO0GzNlPh644Jje06ulqHpOEQqdJUfoidI2O4CWx4qOglLye6RrFQirpCRXvhoRqXH3sYdVJ +AItvc+VUsLO2v2hVAWrNIfVGtkG351cUMNncbh/WdowtSPtCdkzYFv6mwYc9o2Jt68ud6wectBr8 +hYAulPSlgzH44YbV3ikjrulEaNJxt+/H3wZ7bXSXje/YY4tfVVrVmUstaDwwOBLMg6iduDB0lMVC +UyzYx7Ab4kjCqdViEJmDcdk/SKbgsjYXgfMznUWcrtS4z4fmJ/XOM1LPk/iIpqass5XwNbdnLb1Y +8h3ERXSWZI6rZJxKs1LBqVH65w0Oy4ra0CBYxEeuOMbDmV5GI6E0Ha/wgVTtkX0+OXvqsD02CKLf +XHbeft85D7tTCMYy2Njp4DJP7gWJr6paVWXZ1+/6YXLv/iE0M90FktiI7yFJD9e7SOLhEkkaMTUO +azq9i2woBNR0/0eoF1HFMf0H8ChxH/jgcB34GZIz3Qn4/vid+VEamQrOVqAPTrOfmD4MPdVh09tb +8dLLjvh/61lEP4yW5vJaH4vHcevG8agXvzPGoOhhXNncpTr99PTHx6e/UvffFLaxUSjuSeP286Dw +gtEMcW1xKr/he4/6IQ6FUXP+0gkioHY5iwC9Eyx3HKO7af0zPPe+XyLn7fAY78k4aiR387bCr5XT +5C4rFgwLGfMvJuAMew== +""") + + + + + +##file distutils.cfg +DISTUTILS_CFG = convert(""" +eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH +xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg +9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= +""") + + + + + +##file activate_this.py +ACTIVATE_THIS = convert(""" +eJyNUlGL2zAMfvevEBlHEujSsXsL9GGDvW1jD3sZpQQ3Ua7aJXawnbT595Ocpe0dO5ghseVP+vRJ +VpIkn2cYPZknwAvWLXWYhRP5Sk4baKgOWRWNqtpdgTyH2Y5wpq5Tug406YAgKEzkwqg7NBPwR86a +Hk0olPopaK0NHJHzYQPnE5rI0o8+yBUwiBfyQcT8mMPJGiAT0A0O+b8BY4MKJ7zPcSSzHaKrSpJE +qeDmUgGvVbPCS41DgO+6xy/OWbfAThMn/OQ9ukDWRCSLiKzk1yrLjWapq6NnvHUoHXQ4bYPdrsVX +4lQMc/q6ZW975nmSK+oH6wL42a9H65U6aha342Mh0UVDzrD87C1bH73s16R5zsStkBZDp0NrXQ+7 +HaRnMo8f06UBnljKoOtn/YT+LtdvSyaT/BtIv9KR60nF9f3qmuYKO4//T9ItJMsjPfgUHqKwCZ3n +xu/Lx8M/UvCLTxW7VULHxB1PRRbrYfvWNY5S8it008jOjcleaMqVBDnUXcWULV2YK9JEQ92OfC96 +1Tv4ZicZZZ7GpuEpZbbeQ7DxquVx5hdqoyFSSmXwfC90f1Dc7hjFs/tK99I0fpkI8zSLy4tSy+sI +3vMWehjQNJmE5VePlZbL61nzX3S93ZcfDqznnkb9AZ3GWJU= +""") + + + + + +if __name__ == '__main__': + main() + +## TODO: +## Copy python.exe.manifest +## Monkeypatch distutils.sysconfig diff --git a/mail/test/resources/virtualenv/virtualenv_support/__init__.py b/mail/test/resources/virtualenv/virtualenv_support/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mail/test/resources/virtualenv/virtualenv_support/distribute-0.6.16.tar.gz b/mail/test/resources/virtualenv/virtualenv_support/distribute-0.6.16.tar.gz new file mode 100644 index 0000000000..f01250a99a Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/distribute-0.6.16.tar.gz differ diff --git a/mail/test/resources/virtualenv/virtualenv_support/pip-1.0.1.tar.gz b/mail/test/resources/virtualenv/virtualenv_support/pip-1.0.1.tar.gz new file mode 100644 index 0000000000..1934ac3590 Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/pip-1.0.1.tar.gz differ diff --git a/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.4.egg b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.4.egg new file mode 100644 index 0000000000..b2c1592410 Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.4.egg differ diff --git a/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.5.egg b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.5.egg new file mode 100644 index 0000000000..b004539db3 Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.5.egg differ diff --git a/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.6.egg b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.6.egg new file mode 100644 index 0000000000..3c72d15b5b Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.6.egg differ diff --git a/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.7.egg b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.7.egg new file mode 100644 index 0000000000..8a51424a41 Binary files /dev/null and b/mail/test/resources/virtualenv/virtualenv_support/setuptools-0.6c11-py2.7.egg differ diff --git a/mail/testsuite-targets.mk b/mail/testsuite-targets.mk index df702f1c8f..3f20fd781e 100644 --- a/mail/testsuite-targets.mk +++ b/mail/testsuite-targets.mk @@ -4,6 +4,12 @@ include $(topsrcdir)/mailnews/testsuite-targets.mk # Instructions below this line are for mail/ specific tests. MOZMILLDIR=$(DEPTH)/mozilla/_tests/mozmill +ifeq ($(OS_ARCH),WINNT) +VIRTUALENV_BIN = $(MOZMILLDIR)/../mozmill-virtualenv/Scripts +else +VIRTUALENV_BIN = $(MOZMILLDIR)/../mozmill-virtualenv/bin +endif +MOZMILLPYTHON = $(call core_abspath,$(VIRTUALENV_BIN)/python$(BIN_SUFFIX)) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) # Mac options @@ -17,15 +23,18 @@ else PROGRAM = ../../../$(DIST)/bin/thunderbird$(BIN_SUFFIX) endif +# PYTHONHOME messes very badly with virtualenv setups, so unset it. mozmill:: - cd $(MOZMILLDIR) && MACOSX_DEPLOYMENT_TARGET= $(PYTHON) \ - runtestlist.py --list=mozmilltests.list --binary=$(PROGRAM) \ + unset PYTHONHOME && cd $(MOZMILLDIR) && MACOSX_DEPLOYMENT_TARGET= \ + $(MOZMILLPYTHON) runtestlist.py --list=mozmilltests.list \ + --binary=$(PROGRAM) \ --dir=$(call core_abspath,$(topsrcdir))/mail/test/mozmill \ --symbols-path=$(call core_abspath,$(DIST)/crashreporter-symbols) \ $(MOZMILL_EXTRA) mozmill-one:: - cd $(MOZMILLDIR) && MACOSX_DEPLOYMENT_TARGET= $(PYTHON) runtest.py \ + unset PYTHONHOME && cd $(MOZMILLDIR) && MACOSX_DEPLOYMENT_TARGET= \ + $(MOZMILLPYTHON) runtest.py \ --test=$(call core_abspath,$(topsrcdir))/mail/test/mozmill/$(SOLO_TEST) \ --binary=$(PROGRAM) \ --symbols-path=$(call core_abspath,$(DIST)/crashreporter-symbols) \ diff --git a/mailnews/test/resources/logHelper.js b/mailnews/test/resources/logHelper.js index da3842204a..7d784dba60 100644 --- a/mailnews/test/resources/logHelper.js +++ b/mailnews/test/resources/logHelper.js @@ -443,6 +443,17 @@ function _normalize_for_json(aObj, aDepthAllowed, aJsonMeNotNeeded) { _stringRep: aObj.message, }; } + else if (aObj instanceof Ci.nsIException) { + return { + type: "error", + message: "nsIException: " + aObj.name, + fileName: aObj.filename, + lineNumber: aObj.lineNumber, + name: aObj.name, + result: aObj.result, + stack: null, + }; + } else if (aObj instanceof Ci.nsIStackFrame) { return { type: "stackFrame",