From reebalazs at codespeak.net Tue May 2 16:10:10 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Tue, 02 May 2006 14:10:10 -0000 Subject: [Kukit-checkins] r26671 - kukit/branch/plugin/tests Message-ID: <20060502141010.70A4010091@code0.codespeak.net> Author: reebalazs Date: Tue May 2 16:10:09 2006 New Revision: 26671 Modified: kukit/branch/plugin/tests/runtests.js kukit/branch/plugin/tests/runtests.sh Log: Merge fschulze's changes from the to-be-replaced trunk and this should actually be revised Modified: kukit/branch/plugin/tests/runtests.js ============================================================================== --- kukit/branch/plugin/tests/runtests.js (original) +++ kukit/branch/plugin/tests/runtests.js Tue May 2 16:10:09 2006 @@ -17,6 +17,30 @@ function runTests() { var reporter = new StdoutReporter; var testsuite = new TestSuite(reporter); + testsuite.registerTest(CssParserTestCase); + testsuite.runSuite(); +}; + +runTests(); +/***************************************************************************** + * + * Copyright (c) 2003-2004 EcmaUnit Contributors. All rights reserved. + * + * This software is distributed under the terms of the EcmaUnit + * License. See LICENSE.txt for license text. For a list of EcmaUnit + * Contributors see CREDITS.txt. + * + *****************************************************************************/ + +// $Id: runtests.js 18718 2005-10-17 14:29:18Z duncan $ + +/* + Test runner for command-line environments, such as spidermonkey +*/ + +function runTests() { + var reporter = new StdoutReporter; + var testsuite = new TestSuite(reporter); testsuite.registerTest(kukit.BsParserTestCase); //testsuite.registerTest(kukit.RuleProcessorTestCase); testsuite.runSuite(); Modified: kukit/branch/plugin/tests/runtests.sh ============================================================================== --- kukit/branch/plugin/tests/runtests.sh (original) +++ kukit/branch/plugin/tests/runtests.sh Tue May 2 16:10:09 2006 @@ -1 +1,2 @@ js -f ecmaunit.js -f testJrsParser.js -f ../kukit/jrs-parser.js runtests.js +js -f ecmaunit.js -f testJrsParser.js -f ../kukit/jrs-parser.js runtests.js From reebalazs at codespeak.net Tue May 2 16:13:26 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Tue, 02 May 2006 14:13:26 -0000 Subject: [Kukit-checkins] r26672 - kukit/trunk Message-ID: <20060502141326.92F37100A0@code0.codespeak.net> Author: reebalazs Date: Tue May 2 16:13:25 2006 New Revision: 26672 Removed: kukit/trunk/ Log: Promote plugin branch From reebalazs at codespeak.net Tue May 2 16:13:48 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Tue, 02 May 2006 14:13:48 -0000 Subject: [Kukit-checkins] r26673 - in kukit: branch/plugin trunk Message-ID: <20060502141347.E1EC2100A7@code0.codespeak.net> Author: reebalazs Date: Tue May 2 16:13:46 2006 New Revision: 26673 Added: kukit/trunk/ - copied from r26672, kukit/branch/plugin/ Removed: kukit/branch/plugin/ Log: Promote plugin branch From reebalazs at codespeak.net Fri May 5 13:05:59 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Fri, 05 May 2006 11:05:59 -0000 Subject: [Kukit-checkins] r26809 - kukit/trunk/tests Message-ID: <20060505110559.68CFF10090@code0.codespeak.net> Author: reebalazs Date: Fri May 5 13:05:57 2006 New Revision: 26809 Modified: kukit/trunk/tests/runtests.js kukit/trunk/tests/runtests.sh Log: Hopefully fixed runtests.js Modified: kukit/trunk/tests/runtests.js ============================================================================== --- kukit/trunk/tests/runtests.js (original) +++ kukit/trunk/tests/runtests.js Fri May 5 13:05:57 2006 @@ -17,30 +17,6 @@ function runTests() { var reporter = new StdoutReporter; var testsuite = new TestSuite(reporter); - testsuite.registerTest(CssParserTestCase); - testsuite.runSuite(); -}; - -runTests(); -/***************************************************************************** - * - * Copyright (c) 2003-2004 EcmaUnit Contributors. All rights reserved. - * - * This software is distributed under the terms of the EcmaUnit - * License. See LICENSE.txt for license text. For a list of EcmaUnit - * Contributors see CREDITS.txt. - * - *****************************************************************************/ - -// $Id: runtests.js 18718 2005-10-17 14:29:18Z duncan $ - -/* - Test runner for command-line environments, such as spidermonkey -*/ - -function runTests() { - var reporter = new StdoutReporter; - var testsuite = new TestSuite(reporter); testsuite.registerTest(kukit.BsParserTestCase); //testsuite.registerTest(kukit.RuleProcessorTestCase); testsuite.runSuite(); Modified: kukit/trunk/tests/runtests.sh ============================================================================== --- kukit/trunk/tests/runtests.sh (original) +++ kukit/trunk/tests/runtests.sh Fri May 5 13:05:57 2006 @@ -1,2 +1,14 @@ -js -f ecmaunit.js -f testJrsParser.js -f ../kukit/jrs-parser.js runtests.js -js -f ecmaunit.js -f testJrsParser.js -f ../kukit/jrs-parser.js runtests.js +js \ + -f ecmaunit.js \ + -f unittestUtilities.js \ + -f testStyleParse.js \ + -f kukit.BsParserTestCase.js \ + -f ../kukit/kukit.Utils.js \ + -f ../kukit/kukit.Parser.js \ + -f ../kukit/kukit.BsSelector.js \ + -f ../kukit/kukit.BsCommentParser.js \ + -f ../kukit/kukit.BsStringParser.js \ + -f ../kukit/kukit.BsMethodParser.js \ + -f ../kukit/kukit.BsPropertyParser.js \ + -f ../kukit/kukit.BsParser.js \ + runtests.js From reebalazs at codespeak.net Sat May 6 16:02:52 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 6 May 2006 16:02:52 +0200 (CEST) Subject: [Kukit-checkins] r26873 - kukit/trunk/kukit Message-ID: <20060506140252.7AEA41007E@code0.codespeak.net> Author: reebalazs Date: Sat May 6 16:02:50 2006 New Revision: 26873 Modified: kukit/trunk/kukit/kukit.Utils.js Log: Utils more robust Modified: kukit/trunk/kukit/kukit.Utils.js ============================================================================== --- kukit/trunk/kukit/kukit.Utils.js (original) +++ kukit/trunk/kukit/kukit.Utils.js Sat May 6 16:02:50 2006 @@ -1,5 +1,7 @@ // Namespace declarations -kukit = {}; +if (typeof(kukit) == "undefined") { + kukit = {}; +} /** * Collection of utils for kukit. @@ -42,4 +44,4 @@ if (!chars) chars='\\s\\n\\r' return str.replace(new RegExp("^["+chars+"]*(.*?)["+chars+"]*$"), "$1"); -} \ No newline at end of file +} From reebalazs at codespeak.net Sat May 6 16:03:10 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 6 May 2006 16:03:10 +0200 (CEST) Subject: [Kukit-checkins] r26874 - kukit/trunk/kukit Message-ID: <20060506140310.4DD8910080@code0.codespeak.net> Author: reebalazs Date: Sat May 6 16:03:09 2006 New Revision: 26874 Modified: kukit/trunk/kukit/kukit.js Log: Prepare for resource syntax separation Modified: kukit/trunk/kukit/kukit.js ============================================================================== --- kukit/trunk/kukit/kukit.js (original) +++ kukit/trunk/kukit/kukit.js Sat May 6 16:03:09 2006 @@ -157,12 +157,24 @@ } /* end of XXX */ +kukit.RuleSheetLink = function(href, res_type) { + this.href = href; + this.res_type = res_type; +} + kukit.getRuleSheetLinks = function() { var nodes = document.getElementsByTagName("link"); - var results = new Array(); + var results = []; for (var i=0; i Author: reebalazs Date: Sun May 7 10:44:08 2006 New Revision: 26920 Added: kukit/trunk/kukit/kukit.KssParser.js kukit/trunk/tests/kukit.KssParserTestCase.js (contents, props changed) Modified: kukit/trunk/kukit/kukit.BsMethodParser.js kukit/trunk/kukit/kukit.BsParser.js kukit/trunk/kukit/kukit.BsPropertyParser.js kukit/trunk/kukit/kukit.BsStringParser.js kukit/trunk/kukit/kukit.Utils.js kukit/trunk/kukit/kukit.js kukit/trunk/tests/kukit.BsParserTestCase.js kukit/trunk/tests/runtests.js kukit/trunk/tests/runtests.sh Log: Progress on kss parser (not yet in function) Modified: kukit/trunk/kukit/kukit.BsMethodParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsMethodParser.js (original) +++ kukit/trunk/kukit/kukit.BsMethodParser.js Sun May 7 10:44:08 2006 @@ -51,4 +51,4 @@ "/*": kukit.BsCommentParser.parse, ",": kukit.BsMethodParser.parsePropertyEnd, ")": kukit.BsMethodParser.parseEnd -} \ No newline at end of file +} Modified: kukit/trunk/kukit/kukit.BsParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsParser.js (original) +++ kukit/trunk/kukit/kukit.BsParser.js Sun May 7 10:44:08 2006 @@ -13,6 +13,10 @@ * to have parser settings that are defined at the end of the file and refer to * certain sub-parsermethods. * + * + * XXX XXX XXX I changed this, please revise the following docs! + * (i.e. where there can be a list there is now always a list etc.) + * *

Example: * {@code BsParser} would transform this css sytax: * @@ -186,6 +190,8 @@ var rest = kukit.Utils.trim(parserresult.src); // Handling if the rest if the type arguments + infos.type.name = null; + infos.type.properties = []; if (rest != "" && !infos.type.name) { infos.type.name = rest; } else { @@ -199,8 +205,6 @@ if (j==0) { infos.type.name = temp[j]; } else { - if (j == 1) - infos.type.properties = []; infos.type.properties.push(temp[j]); } } Modified: kukit/trunk/kukit/kukit.BsPropertyParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsPropertyParser.js (original) +++ kukit/trunk/kukit/kukit.BsPropertyParser.js Sun May 7 10:44:08 2006 @@ -71,7 +71,10 @@ kukit.BsPropertyParser.addProperty = function(options, property) { property = this.checkProperty(property); if (property != "") - options.result.push(property); + // XXX Since we could have an array of properties here, + // for consistence this simple case will be pushed as + // a one-element array as well. + options.result.push([property]); } /** @@ -97,4 +100,4 @@ " ": kukit.BsPropertyParser.parseNextProperty, ";": kukit.BsPropertyParser.parsePropertyEnd, "}": kukit.BsPropertyParser.parsePropertyBlockEnd -} \ No newline at end of file +} Modified: kukit/trunk/kukit/kukit.BsStringParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsStringParser.js (original) +++ kukit/trunk/kukit/kukit.BsStringParser.js Sun May 7 10:44:08 2006 @@ -29,5 +29,11 @@ pos = src.indexOf("\""); } options.src = options.src.substring(0, options.pos)+options.src.substring(options.pos+content.length+2); - options.result.push(kukit.BsPropertyParser.checkProperty(content)); + // XXX Since we could have an array of properties here, + // for consistence this simple case will be pushed as + // a one-element array as well. + // XXX but this is principally broken because this has nothing to + // do with string parsing - only I cannot figure out how to do + // this from an embedding part. + options.result.push([kukit.BsPropertyParser.checkProperty(content)]); } Added: kukit/trunk/kukit/kukit.KssParser.js ============================================================================== --- (empty file) +++ kukit/trunk/kukit/kukit.KssParser.js Sun May 7 10:44:08 2006 @@ -0,0 +1,98 @@ + +/* +* +* Parse the entire rule file from kss format +* +*/ + + +/* +* +* Converters that parse from the format the +* parser gives us as result +* +* We need to have our first class object +* structure EventRule -> Action +* +*/ + +// XXX Note this is principally broken because of +// the possibility of ""-s in kw values, and the =-s +// in string args. +// XXX Parsing must be done entirely by the main parser. +// This is no better than a workaround. + +kukit.ParmConverter = function(parameters) { + // Accept an array of parameters, + // converts them to args and kw. + this.args = [] + this.kw = {} + var found_kw = false; + for (var i=0; i Author: reebalazs Date: Sun May 7 22:26:51 2006 New Revision: 26955 Modified: kukit/trunk/kukit/kukit.KssParser.js kukit/trunk/kukit/kukit.js Log: Put in kss parser; and introduce simple scheme for automatic event name -> type detection, needed for the new kss format Modified: kukit/trunk/kukit/kukit.KssParser.js ============================================================================== --- kukit/trunk/kukit/kukit.KssParser.js (original) +++ kukit/trunk/kukit/kukit.KssParser.js Sun May 7 22:26:51 2006 @@ -67,6 +67,20 @@ this.href = href; } +kukit.KssRuleProcessor.prototype.loadAndParse = function() { + var txt = this.loadRuleSheet(); + this.parseResource(txt); +} + +kukit.KssRuleProcessor.prototype.loadRuleSheet = function() +{ + // Opera does not support getDomDocument.load, so we use XMLHttpRequest + var domDoc = new XMLHttpRequest(); + domDoc.open("GET", this.href, false); + domDoc.send(null); + return domDoc.responseText; +} + kukit.KssRuleProcessor.prototype.parseResource = function(txt) { //Build a parser and parse the text into it @@ -93,6 +107,11 @@ var eventRule = new kukit.EventRule(rule_name, rule_select.selector, rule_parms.args, rule_parms.kw, actions); this.rules.push(eventRule); } - } +// this should be done with inheritence instead: TODO refactor +kukit.KssRuleProcessor.prototype.setupEvents = kukit.RuleProcessor.prototype.setupEvents; +kukit.KssRuleProcessor.prototype.setupEvent = kukit.RuleProcessor.prototype.setupEvent; + +// register this rule processor for type kss +kukit.ruleProcessorClasses['kss'] = kukit.KssRuleProcessor; Modified: kukit/trunk/kukit/kukit.js ============================================================================== --- kukit/trunk/kukit/kukit.js (original) +++ kukit/trunk/kukit/kukit.js Sun May 7 22:26:51 2006 @@ -171,7 +171,7 @@ // Resource syntax is decided on type attribute. if((nodes[i].type == 'text/css') || (nodes[i].type == 'text/kss')) { res_type = 'kss'; - } else { // text/xml or nothing + } else { // text/xml or nothing (to keep back compatibility) res_type = 'xml'; } results[results.length] = new kukit.RuleSheetLink(nodes[i].href, res_type); @@ -203,17 +203,20 @@ kukit.calculateBase(); +// table from res_type to rule processor +// we could make this as a registry but no sense at the moment +kukit.ruleProcessorClasses = {} + kukit.initializeRules = function() { var rulelinks = kukit.getRuleSheetLinks(); - kukit.log("Rule Sheet Links: "+rulelinks); + kukit.log("Nr of Rule Sheet Links: "+rulelinks.length); for (var i=0; i typename } -kukit.EventTypeRegistry.prototype.register = function(name, func, sig) { +kukit.EventTypeRegistry.prototype.register = function(name, func, sig, eventnames) { if (typeof(func) == 'undefined' || typeof(sig) == 'undefined') { throw 'Func and sig are mandatory.'; } + if (typeof(eventnames) == 'undefined') { + eventnames = []; + } if (this.content[name]) { // Do not allow redefinition kukit.logError('Error : redefinition attempt of event setup type ' + name); @@ -604,12 +638,33 @@ var entry = {} entry['func'] = func; entry['sig'] = sig; + entry['type'] = name; this.content[name] = entry; + // do the eventnames + for (var i=0; i Author: reebalazs Date: Sun May 7 22:29:24 2006 New Revision: 26956 Modified: kukit/trunk/kukit/kukit.BsParser.js kukit/trunk/kukit/kukit.BsPropertyParser.js kukit/trunk/kukit/kukit.BsStringParser.js kukit/trunk/kukit/kukit.KssParser.js kukit/trunk/kukit/kukit.Utils.js kukit/trunk/kukit/kukit.js Log: Tabs -> spaces Modified: kukit/trunk/kukit/kukit.BsParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsParser.js (original) +++ kukit/trunk/kukit/kukit.BsParser.js Sun May 7 22:29:24 2006 @@ -190,7 +190,7 @@ var rest = kukit.Utils.trim(parserresult.src); // Handling if the rest if the type arguments - infos.type.name = null; + infos.type.name = null; infos.type.properties = []; if (rest != "" && !infos.type.name) { infos.type.name = rest; Modified: kukit/trunk/kukit/kukit.BsPropertyParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsPropertyParser.js (original) +++ kukit/trunk/kukit/kukit.BsPropertyParser.js Sun May 7 22:29:24 2006 @@ -71,9 +71,9 @@ kukit.BsPropertyParser.addProperty = function(options, property) { property = this.checkProperty(property); if (property != "") - // XXX Since we could have an array of properties here, - // for consistence this simple case will be pushed as - // a one-element array as well. + // XXX Since we could have an array of properties here, + // for consistence this simple case will be pushed as + // a one-element array as well. options.result.push([property]); } Modified: kukit/trunk/kukit/kukit.BsStringParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsStringParser.js (original) +++ kukit/trunk/kukit/kukit.BsStringParser.js Sun May 7 22:29:24 2006 @@ -29,11 +29,11 @@ pos = src.indexOf("\""); } options.src = options.src.substring(0, options.pos)+options.src.substring(options.pos+content.length+2); - // XXX Since we could have an array of properties here, - // for consistence this simple case will be pushed as - // a one-element array as well. - // XXX but this is principally broken because this has nothing to - // do with string parsing - only I cannot figure out how to do - // this from an embedding part. + // XXX Since we could have an array of properties here, + // for consistence this simple case will be pushed as + // a one-element array as well. + // XXX but this is principally broken because this has nothing to + // do with string parsing - only I cannot figure out how to do + // this from an embedding part. options.result.push([kukit.BsPropertyParser.checkProperty(content)]); } Modified: kukit/trunk/kukit/kukit.KssParser.js ============================================================================== --- kukit/trunk/kukit/kukit.KssParser.js (original) +++ kukit/trunk/kukit/kukit.KssParser.js Sun May 7 22:29:24 2006 @@ -51,8 +51,8 @@ kukit.SelectorConverter = function(orig_selector) { // Accept the original selector, // converts it to the part before the : . - // After the :, the method and parameters get ignored. - this.selector = orig_selector.split(':')[0]; + // After the :, the method and parameters get ignored. + this.selector = orig_selector.split(':')[0]; } /* @@ -68,8 +68,8 @@ } kukit.KssRuleProcessor.prototype.loadAndParse = function() { - var txt = this.loadRuleSheet(); - this.parseResource(txt); + var txt = this.loadRuleSheet(); + this.parseResource(txt); } kukit.KssRuleProcessor.prototype.loadRuleSheet = function() @@ -88,24 +88,24 @@ // Format it in the way required by us for (var rulekey in prules) { var prule = prules[rulekey]; - // prepare the event rule attributes + // prepare the event rule attributes var rule_name = prule.type.name; var rule_parms = new kukit.ParmConverter(prule.type.properties); - var rule_select = new kukit.SelectorConverter(rulekey); - // prepare the actions - var actions = []; - for (var actionkey in prule.properties) { - var actiongroup = prule.properties[actionkey]; - for (var i=0; i - if (params.type == 'timeout') { - var p = arg.split(' '); - if (p.length != 2) { - throw "Awaited url and timeout, got: "+arg; - } else { - arg = p[0]; - params.delay = p[1]; - } - } - } else if (property_type != "event") { - throw "Unknown property type ("+property_type+"): must be one of event, kukitevent"; - } - - // Generic action shortcut. If there is no action, a remote - // action is created by default, using the arg. - if (actions.length == 0) { - if (!arg) { - throw 'In case there are no actions, a parameter must be given'; - } - var defparams = {}; - var action = new kukit.Action('remote', [arg], defparams); - actions[actions.length] = action; - } - + + + // Pre-parse parameters for old compatibility. + // All conversion must be done from here. + // There is just one special case: the timeout plugin. + // XXX We want to deprecate kukitevent in the future. + if (property_type == 'kukitevent') { + params.type = params.name; + params.name = null; + // + if (params.type == 'timeout') { + var p = arg.split(' '); + if (p.length != 2) { + throw "Awaited url and timeout, got: "+arg; + } else { + arg = p[0]; + params.delay = p[1]; + } + } + } else if (property_type != "event") { + throw "Unknown property type ("+property_type+"): must be one of event, kukitevent"; + } + + // Generic action shortcut. If there is no action, a remote + // action is created by default, using the arg. + if (actions.length == 0) { + if (!arg) { + throw 'In case there are no actions, a parameter must be given'; + } + var defparams = {}; + var action = new kukit.Action('remote', [arg], defparams); + actions[actions.length] = action; + } + result[result.length] = new kukit.EventRule(params.type, selector, [], params, actions); } @@ -359,27 +359,27 @@ } kukit.RuleProcessor.prototype.parseAction = function(action_node) { - // parameters and type are in attributes - var params = {}; - var action_attributes = action_node.attributes; + // parameters and type are in attributes + var params = {}; + var action_attributes = action_node.attributes; for (var i=0; i {'a': 'x', 'b': 'y', 'c': 'z', 'd': 2} - // sig.f_keywords(['x', 'y', 'z'], {'b': 'y', 'c': 'z'}) ==> ERROR - // - this.f_proto = f_proto - this.f_defaults = f_defaults + // proto = a list of argnames + // default = a dict of default values + // + // e.g. + // sig = kukit.PythonMethodSignature(['a', 'b'], {'c': 1, 'd': 2}); + // sig.f_keywords(['x'], {'b': 'y', 'c': 'z'}) ==> {'a': 'x', 'b': 'y', 'c': 'z', 'd': 2} + // sig.f_keywords(['x', 'y', 'z'], {'b': 'y', 'c': 'z'}) ==> ERROR + // + this.f_proto = f_proto + this.f_defaults = f_defaults } kukit.PythonMethodSignature.prototype.f_keywords = function(args, kw) { - var result = {}; - for (key in this.f_defaults) { - result[key] = this.f_defaults[key]; - } - for (key in kw) { - result[key] = kw[key]; - } - if (args.length < this.f_proto.length) { - for (var i = args.length; i < this.f_proto.length; i++) { - var key = this.f_proto[i]; - if (typeof(kw[key]) == 'undefined') { - throw 'Missing args in python-like method call"' + key + '"'; - } - } - } else if (args.length > this.f_proto.length) { - for (var i = this.f_proto.length; i < args.length; i++) {logDebug(i+'--'+args[i])}; - throw 'Excess args in python-like method call (' + args.length + ' > ' + this.f_proto.length + ')'; - } - for (var i = 0; i < args.length; i++) { - result[this.f_proto[i]] = args[i]; - } - return result; + var result = {}; + for (key in this.f_defaults) { + result[key] = this.f_defaults[key]; + } + for (key in kw) { + result[key] = kw[key]; + } + if (args.length < this.f_proto.length) { + for (var i = args.length; i < this.f_proto.length; i++) { + var key = this.f_proto[i]; + if (typeof(kw[key]) == 'undefined') { + throw 'Missing args in python-like method call"' + key + '"'; + } + } + } else if (args.length > this.f_proto.length) { + for (var i = this.f_proto.length; i < args.length; i++) {logDebug(i+'--'+args[i])}; + throw 'Excess args in python-like method call (' + args.length + ' > ' + this.f_proto.length + ')'; + } + for (var i = 0; i < args.length; i++) { + result[this.f_proto[i]] = args[i]; + } + return result; } /* EventRules and Actions: the objects that build up the rules */ -kukit.EventRuleNr = 0; // just a counter +kukit.EventRuleNr = 0; // just a counter kukit.EventRule = function(type, selector, args, kw, actions) { - this.nr = kukit.EventRuleNr; - kukit.EventRuleNr = this.nr + 1; + this.nr = kukit.EventRuleNr; + kukit.EventRuleNr = this.nr + 1; kukit.logDebug("EventRule #"+this.nr+": "+selector+" "+actions.length); this.type = type; this.selector = selector; - this.args = args; - this.kw = kw; - this.actions = actions; + this.args = args; + this.kw = kw; + this.actions = actions; } kukit.EventRule.prototype.getNr = function() { - return this.nr; + return this.nr; } kukit.EventRule.prototype.bind = function(node) { var entry = kukit.eventTypeRegistry.getReg(this.type); - if (typeof(entry) == 'undefined') { - throw 'Cannot lookup event rule "' + this.type + '"'; - } - // See if the lookup was done rather by name then by type - var args = this.args - if (this.type != entry.type) { - // and add the name on the args if this is so - args = [this.type] - for (var i=0; i 1) { - // XXX suppose not <= 2 though... - value = item[1]; - } - // XXX these are encoded. - // I think we would need to decode them, or else... - query.appendElem('kukitHref_' + key, value); - } - } - } + // XXX Hack to pass query string of link as parameters + // XXX This was fshulze's original hack - but this really should + // be done by registering an event action (ask me how). + if (target.tagName == 'A' || target.tagName == 'a') { + var parts = target.href.split('?'); + if (parts.length > 1) { + var queryparts = parts[1].split['&']; + for (var i=1; i 1) { + // XXX suppose not <= 2 though... + value = item[1]; + } + // XXX these are encoded. + // I think we would need to decode them, or else... + query.appendElem('kukitHref_' + key, value); + } + } + } */ - return query; + return query; } kukit.extractFormData = function(target) { - return kukit.extractFormQuery(target).encode(); + return kukit.extractFormQuery(target).encode(); } /* Server notification */ @@ -935,7 +935,7 @@ // convert params var query = new kukit.FormQuery(); for (var key in params) { - query.appendElem(key, params[key]); + query.appendElem(key, params[key]); } var encoded = query.encode(); // sending form @@ -1074,7 +1074,7 @@ if (nodes.length == 0) { kukit.logError('Command found no nodes'); } else { - func = kukit.commandRegistry.getFunc(command.name); + func = kukit.commandRegistry.getFunc(command.name); } for (var i=0;i < nodes.length;i++) { @@ -1101,9 +1101,9 @@ kukit.selectors._mapping = {}; kukit.selectors.register = function(name, func) { - if (typeof(func) == 'undefined') { - throw 'Func is mandatory.'; - } + if (typeof(func) == 'undefined') { + throw 'Func is mandatory.'; + } kukit.selectors._mapping[name] = func; } @@ -1184,7 +1184,7 @@ kukit.dom.appendChildren = function(nodes, toNode) { var ownerDoc = toNode.nodeType == Node.DOCUMENT_NODE ? toNode : toNode.ownerDocument; var result = new Array(); - if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { + if(ownerDoc.importNode && (!_SARISSA_IS_IE)) { for(var i=0;i < nodes.length;i++) { result[i] = toNode.appendChild(ownerDoc.importNode(nodes[i], true)); }; From reebalazs at codespeak.net Tue May 9 00:26:13 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Tue, 9 May 2006 00:26:13 +0200 (CEST) Subject: [Kukit-checkins] r26985 - in kukit/trunk: kukit tests Message-ID: <20060508222613.75F6F1006F@code0.codespeak.net> Author: reebalazs Date: Tue May 9 00:26:11 2006 New Revision: 26985 Added: kukit/trunk/tests/kukit.PythonMethodSignatureTestCase.js (contents, props changed) Modified: kukit/trunk/kukit/kukit.BsMethodParser.js kukit/trunk/kukit/kukit.js kukit/trunk/tests/kukit.BsParserTestCase.js kukit/trunk/tests/kukit.KssParserTestCase.js kukit/trunk/tests/runtests.js kukit/trunk/tests/runtests.sh Log: Fix method parsing - enhance pythonic method parms conversion - in the kss we allow pure (kw=1, kw2=2) call without a method name (arg) - tests for this Modified: kukit/trunk/kukit/kukit.BsMethodParser.js ============================================================================== --- kukit/trunk/kukit/kukit.BsMethodParser.js (original) +++ kukit/trunk/kukit/kukit.BsMethodParser.js Tue May 9 00:26:11 2006 @@ -15,7 +15,12 @@ */ kukit.BsMethodParser.parse = function(options) { var methodName = options.src.substring(0,options.pos); - var value = [methodName]; + // Ignore an empty method name! + // this is somewhat "off line" but we like this. + var value = []; + if (methodName != '') { + value.push(methodName); + } var properties = options.src.substring(options.pos+1); parseoptions = kukit.Parser.parse(this, properties, value, this.parseSettings); options.result.push(value); Modified: kukit/trunk/kukit/kukit.js ============================================================================== --- kukit/trunk/kukit/kukit.js (original) +++ kukit/trunk/kukit/kukit.js Tue May 9 00:26:11 2006 @@ -440,6 +440,8 @@ // sig = kukit.PythonMethodSignature(['a', 'b'], {'c': 1, 'd': 2}); // sig.f_keywords(['x'], {'b': 'y', 'c': 'z'}) ==> {'a': 'x', 'b': 'y', 'c': 'z', 'd': 2} // sig.f_keywords(['x', 'y', 'z'], {'b': 'y', 'c': 'z'}) ==> ERROR + // sig.f_keywords(['x'], {'d': 3}) ==> ERROR + // sig.f_keywords(['x', 'y'], {'a': '1'}) ==> ERROR // this.f_proto = f_proto this.f_defaults = f_defaults @@ -447,12 +449,7 @@ kukit.PythonMethodSignature.prototype.f_keywords = function(args, kw) { var result = {}; - for (key in this.f_defaults) { - result[key] = this.f_defaults[key]; - } - for (key in kw) { - result[key] = kw[key]; - } + // check if (args.length < this.f_proto.length) { for (var i = args.length; i < this.f_proto.length; i++) { var key = this.f_proto[i]; @@ -461,12 +458,25 @@ } } } else if (args.length > this.f_proto.length) { - for (var i = this.f_proto.length; i < args.length; i++) {logDebug(i+'--'+args[i])}; throw 'Excess args in python-like method call (' + args.length + ' > ' + this.f_proto.length + ')'; } + // set args for (var i = 0; i < args.length; i++) { result[this.f_proto[i]] = args[i]; } + // set keywords + for (key in kw) { + if (typeof(result[key]) != 'undefined') { + throw 'Multiple values for keyword argument "' + key + '"'; + } + result[key] = kw[key]; + } + // set default values + for (key in this.f_defaults) { + if (typeof(result[key]) == 'undefined') { + result[key] = this.f_defaults[key]; + } + } return result; } Modified: kukit/trunk/tests/kukit.BsParserTestCase.js ============================================================================== --- kukit/trunk/tests/kukit.BsParserTestCase.js (original) +++ kukit/trunk/tests/kukit.BsParserTestCase.js Tue May 9 00:26:11 2006 @@ -196,6 +196,9 @@ this.assertEquals(rules["tag"].properties["key"][0], 1); } + // XXX TODO Tests for method parsing? + // these are tested in the Kss parser, but... + this.testTimeoutParsing = function() { var src = "#calendar-previous a:click(cancelSubmit=True) {\n" +" remote: kukitresponse/kukitGetPreviousMonth;\n" Modified: kukit/trunk/tests/kukit.KssParserTestCase.js ============================================================================== --- kukit/trunk/tests/kukit.KssParserTestCase.js (original) +++ kukit/trunk/tests/kukit.KssParserTestCase.js Tue May 9 00:26:11 2006 @@ -47,10 +47,15 @@ +"#calendar-previous a:leave(cancelSubmit=True) {\n" +" remote: kukitresponse/kukitPreviousMonth;\n" +"}\n" - +"button#change:startdrag {\n remote: getDivContent(fgh, foo=bar) getSlowOne(fgd);\n log: \"WE LOG from getDivContent\";}"; + +"button#change:startdrag {\n remote: getDivContent(fgh, foo=bar) getSlowOne(fgd);\n log: \"WE LOG from getDivContent\";}\n" + +"#kukitportlet-recent-click:click {\n" + +" replaceMacro: (selector=#portlet-recent, macropath=portlet_recent/macros/portlet);\n" + +" replaceMacro2: #portlet-recent(macropath=portlet_recent/macros/portlet);\n" + +" replaceMacro3: #portlet-recent(portlet_recent/macros/portlet);\n" + +"}"; var ruleProcessor = new kukit.KssRuleProcessor('http://foo.bar'); ruleProcessor.parseResource(src); - this.assertEquals(ruleProcessor.rules.length, 4); + this.assertEquals(ruleProcessor.rules.length, 5); var rule = null; // rule 0 rule = ruleProcessor.rules[0]; @@ -108,6 +113,25 @@ this.assertEquals(rule.actions[2].type, 'log'); this.assertEquals(rule.actions[2].args, ['WE LOG from getDivContent']); this.assertDictEquals(rule.actions[2].kw, {}); + // rule 4 -- the method name can be omitted. + rule = ruleProcessor.rules[4]; + this.assertEquals(rule.type, 'click'); + this.assertEquals(rule.selector, '#kukitportlet-recent-click'); + this.assertEquals(rule.args, []); + this.assertDictEquals(rule.kw, {}); + this.assertEquals(rule.actions.length, 3); + // + this.assertEquals(rule.actions[0].type, 'replaceMacro'); + this.assertEquals(rule.actions[0].args, []); // this must be nothing. Not [''] ! + this.assertDictEquals(rule.actions[0].kw, {'selector': '#portlet-recent', 'macropath': 'portlet_recent/macros/portlet'}); + // + this.assertEquals(rule.actions[1].type, 'replaceMacro2'); + this.assertEquals(rule.actions[1].args, ['#portlet-recent']); + this.assertDictEquals(rule.actions[1].kw, {'macropath': 'portlet_recent/macros/portlet'}); + // + this.assertEquals(rule.actions[2].type, 'replaceMacro3'); + this.assertEquals(rule.actions[2].args, ['#portlet-recent', 'portlet_recent/macros/portlet']); + this.assertDictEquals(rule.actions[2].kw, {}); } }; Added: kukit/trunk/tests/kukit.PythonMethodSignatureTestCase.js ============================================================================== --- (empty file) +++ kukit/trunk/tests/kukit.PythonMethodSignatureTestCase.js Tue May 9 00:26:11 2006 @@ -0,0 +1,36 @@ +if (typeof(kukit) == "undefined") { + var kukit = {}; +} + +kukit.PythonMethodSignatureTestCase = function() { + this.name = 'kukit.PythonMethodSignatureTestCase'; + + this.setUp = function() { + }; + + this.assertDictEquals = function(a, b) { + for (var key in a) { + this.assertNotEquals(typeof(b[key]), 'undefined', 'key ' + key + ' missing from dict 2'); + this.assertEquals(a[key], b[key], 'mismatch at key ' + key); + } + for (var key in b) { + this.assertNotEquals(typeof(a[key]), 'undefined', 'key ' + key + ' missing from dict 1'); + this.assertEquals(a[key], b[key], 'mismatch at key ' + key); + } + } + + this.testMain = function() { + var sig = new kukit.PythonMethodSignature(['a', 'b'], {'c': 1, 'd': 2}); + // + this.assertDictEquals(sig.f_keywords(['x'], {'b': 'y', 'c': 'z'}), {'a': 'x', 'b': 'y', 'c': 'z', 'd': 2}); + this.assertThrows(sig.f_keywords, 'Excess args in python-like method call (3 > 2)', sig, ['x', 'y', 'z'], {'b': 'y', 'c': 'z'}); + this.assertThrows(sig.f_keywords, 'Missing args in python-like method call"b"', sig, ['x'], {'d': 3}); + this.assertThrows(sig.f_keywords, 'Multiple values for keyword argument "a"', sig, ['x', 'y'], {'a': '1'}); + } +} + +kukit.PythonMethodSignatureTestCase.prototype = new TestCase; + +if (typeof(testcase_registry) != 'undefined') { + testcase_registry.registerTestCase(kukit.PythonMethodSignatureTestCase, 'kukit.PythonMethodSignatureTestCase'); +} Modified: kukit/trunk/tests/runtests.js ============================================================================== --- kukit/trunk/tests/runtests.js (original) +++ kukit/trunk/tests/runtests.js Tue May 9 00:26:11 2006 @@ -19,6 +19,7 @@ var testsuite = new TestSuite(reporter); testsuite.registerTest(kukit.BsParserTestCase); testsuite.registerTest(kukit.KssParserTestCase); + testsuite.registerTest(kukit.PythonMethodSignatureTestCase); //testsuite.registerTest(kukit.RuleProcessorTestCase); testsuite.runSuite(); }; Modified: kukit/trunk/tests/runtests.sh ============================================================================== --- kukit/trunk/tests/runtests.sh (original) +++ kukit/trunk/tests/runtests.sh Tue May 9 00:26:11 2006 @@ -12,6 +12,7 @@ -f ../kukit/kukit.BsPropertyParser.js \ -f ../kukit/kukit.BsParser.js \ -f kukit.KssParserTestCase.js \ + -f kukit.PythonMethodSignatureTestCase.js \ -f ../kukit/kukit.js \ -f ../kukit/kukit.KssParser.js \ runtests.js From reebalazs at codespeak.net Sun May 21 14:53:11 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sun, 21 May 2006 14:53:11 +0200 (CEST) Subject: [Kukit-checkins] r27531 - in kukit: branch kukit.js kukit.js/branch kukit.js/tag kukit.js/trunk tag trunk Message-ID: <20060521125311.1915110077@code0.codespeak.net> Author: reebalazs Date: Sun May 21 14:53:08 2006 New Revision: 27531 Added: kukit/kukit.js/ kukit/kukit.js/branch/ - copied from r27530, kukit/branch/ kukit/kukit.js/tag/ - copied from r27530, kukit/tag/ kukit/kukit.js/trunk/ - copied from r27530, kukit/trunk/ Removed: kukit/branch/ kukit/tag/ kukit/trunk/ Log: directory restructuring in favour or azax From reebalazs at codespeak.net Sun May 21 15:06:57 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sun, 21 May 2006 15:06:57 +0200 (CEST) Subject: [Kukit-checkins] r27533 - in kukit: azax/branch/gotcha-namespaces/demos/azaxdemo azax/branch/snowsprint/demos/azaxdemo azax/trunk/demos/azaxdemo azaxdemo azaxdemo/branch azaxdemo/branch/gotcha-namespaces azaxdemo/branch/snowsprint azaxdemo/tags azaxdemo/trunk Message-ID: <20060521130657.4F2F710077@code0.codespeak.net> Author: reebalazs Date: Sun May 21 15:06:54 2006 New Revision: 27533 Added: kukit/azaxdemo/ kukit/azaxdemo/branch/ kukit/azaxdemo/branch/gotcha-namespaces/ - copied from r27532, kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/ kukit/azaxdemo/branch/snowsprint/ - copied from r27532, kukit/azax/branch/snowsprint/demos/azaxdemo/ kukit/azaxdemo/tags/ kukit/azaxdemo/trunk/ - copied from r27532, kukit/azax/trunk/demos/azaxdemo/ Removed: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/ kukit/azax/branch/snowsprint/demos/azaxdemo/ kukit/azax/trunk/demos/azaxdemo/ Log: Moving azaxdemo to the first directory level From reebalazs at codespeak.net Sun May 21 15:08:13 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sun, 21 May 2006 15:08:13 +0200 (CEST) Subject: [Kukit-checkins] r27534 - in kukit: azax/tag azax/tags azaxdemo/tag azaxdemo/tags Message-ID: <20060521130813.8430910077@code0.codespeak.net> Author: reebalazs Date: Sun May 21 15:08:11 2006 New Revision: 27534 Added: kukit/azax/tag/ - copied from r27532, kukit/azax/tags/ kukit/azaxdemo/tag/ - copied from r27533, kukit/azaxdemo/tags/ Removed: kukit/azax/tags/ kukit/azaxdemo/tags/ Log: dirname correction From reebalazs at codespeak.net Sun May 21 15:10:11 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sun, 21 May 2006 15:10:11 +0200 (CEST) Subject: [Kukit-checkins] r27535 - kukit/azax/trunk/plugins/concatresource Message-ID: <20060521131011.510F710077@code0.codespeak.net> Author: reebalazs Date: Sun May 21 15:10:10 2006 New Revision: 27535 Removed: kukit/azax/trunk/plugins/concatresource/ Log: fix externals From reebalazs at codespeak.net Sun May 21 15:02:51 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sun, 21 May 2006 15:02:51 +0200 (CEST) Subject: [Kukit-checkins] r27532 - in kukit/azax: . branch branch/gotcha-namespaces branch/gotcha-namespaces/demos branch/gotcha-namespaces/demos/azaxdemo branch/gotcha-namespaces/demos/azaxdemo/browser branch/gotcha-namespaces/demos/azaxdemo/tests branch/gotcha-namespaces/demos/azaxdemo/www branch/gotcha-namespaces/tests branch/gotcha-namespaces/tests/js branch/snowsprint branch/snowsprint/browser branch/snowsprint/demos branch/snowsprint/demos/azaxdemo branch/snowsprint/demos/azaxdemo/browser branch/snowsprint/demos/azaxdemo/tests branch/snowsprint/demos/azaxdemo/www branch/snowsprint/dtds branch/snowsprint/tests branch/snowsprint/tests/js tags trunk trunk/browser trunk/demos trunk/demos/azaxdemo trunk/demos/azaxdemo/browser trunk/demos/azaxdemo/tests trunk/demos/azaxdemo/www trunk/dtds trunk/plugins trunk/plugins/concatresource trunk/plugins/concatresource/compression trunk/plugins/concatresource/compression/thirdparty trunk/plugins/concatresource/test trunk/plugins/json trunk/plugins/json/browser trunk/tests trunk/tests/js Message-ID: <20060521130251.D5CB910077@code0.codespeak.net> Author: reebalazs Date: Sun May 21 15:02:20 2006 New Revision: 27532 Added: kukit/azax/ kukit/azax/branch/ kukit/azax/branch/gotcha-namespaces/ kukit/azax/branch/gotcha-namespaces/DEPENDENCIES.txt kukit/azax/branch/gotcha-namespaces/INSTALL.txt kukit/azax/branch/gotcha-namespaces/TODO.txt kukit/azax/branch/gotcha-namespaces/__init__.py kukit/azax/branch/gotcha-namespaces/azaxresponse.py kukit/azax/branch/gotcha-namespaces/config.py kukit/azax/branch/gotcha-namespaces/configure.zcml kukit/azax/branch/gotcha-namespaces/demos/ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/INSTALL.txt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/__init__.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/azaxview.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/__init__.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.azax kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.pt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo_index.pt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.azax kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.pt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.azax kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.pt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.azax kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.pt kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/configure.zcml kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/helpers.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/interfaces.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/simplecontent.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/tests/ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/tests/__init__.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/tests/test_azaxview.py kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/www/ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/www/simpleContentAdd.zpt kukit/azax/branch/gotcha-namespaces/interfaces.py kukit/azax/branch/gotcha-namespaces/tests/ kukit/azax/branch/gotcha-namespaces/tests/__init__.py kukit/azax/branch/gotcha-namespaces/tests/js/ kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit-license.txt kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit.js kukit/azax/branch/gotcha-namespaces/tests/js/runner.html kukit/azax/branch/gotcha-namespaces/tests/js/testStyleParse.js kukit/azax/branch/gotcha-namespaces/tests/js/unittestUtilities.js kukit/azax/branch/gotcha-namespaces/tests/test_azaxresponse.py kukit/azax/branch/snowsprint/ (props changed) kukit/azax/branch/snowsprint/BeautifulSoup.py kukit/azax/branch/snowsprint/DEPENDENCIES.txt kukit/azax/branch/snowsprint/EXTERNALS.TXT kukit/azax/branch/snowsprint/INSTALL.txt kukit/azax/branch/snowsprint/TODO.txt kukit/azax/branch/snowsprint/__init__.py kukit/azax/branch/snowsprint/azaxview.py kukit/azax/branch/snowsprint/browser/ kukit/azax/branch/snowsprint/browser/kukitresponse.pt kukit/azax/branch/snowsprint/config.py kukit/azax/branch/snowsprint/configure.zcml kukit/azax/branch/snowsprint/demos/ kukit/azax/branch/snowsprint/demos/azaxdemo/ kukit/azax/branch/snowsprint/demos/azaxdemo/INSTALL.txt kukit/azax/branch/snowsprint/demos/azaxdemo/__init__.py kukit/azax/branch/snowsprint/demos/azaxdemo/azaxview.py kukit/azax/branch/snowsprint/demos/azaxdemo/browser/ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/__init__.py kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo_index.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/cancel_submit.kukit kukit/azax/branch/snowsprint/demos/azaxdemo/browser/cancel_submit.pt kukit/azax/branch/snowsprint/demos/azaxdemo/browser/header_macros.pt kukit/azax/branch/snowsprint/demos/azaxdemo/configure.zcml kukit/azax/branch/snowsprint/demos/azaxdemo/helpers.py kukit/azax/branch/snowsprint/demos/azaxdemo/interfaces.py kukit/azax/branch/snowsprint/demos/azaxdemo/simplecontent.py kukit/azax/branch/snowsprint/demos/azaxdemo/tests/ kukit/azax/branch/snowsprint/demos/azaxdemo/tests/__init__.py kukit/azax/branch/snowsprint/demos/azaxdemo/tests/test_azaxview.py kukit/azax/branch/snowsprint/demos/azaxdemo/www/ kukit/azax/branch/snowsprint/demos/azaxdemo/www/simpleContentAdd.zpt kukit/azax/branch/snowsprint/dtds/ kukit/azax/branch/snowsprint/dtds/xhtml-lat1.ent kukit/azax/branch/snowsprint/dtds/xhtml-special.ent kukit/azax/branch/snowsprint/dtds/xhtml-symbol.ent kukit/azax/branch/snowsprint/dtds/xhtml1-transitional.dtd kukit/azax/branch/snowsprint/interfaces.py kukit/azax/branch/snowsprint/tests/ kukit/azax/branch/snowsprint/tests/__init__.py kukit/azax/branch/snowsprint/tests/js/ kukit/azax/branch/snowsprint/tests/js/ecmaunit-license.txt kukit/azax/branch/snowsprint/tests/js/ecmaunit.js kukit/azax/branch/snowsprint/tests/js/runner.html kukit/azax/branch/snowsprint/tests/js/testStyleParse.js kukit/azax/branch/snowsprint/tests/js/unittestUtilities.js kukit/azax/branch/snowsprint/tests/kukitresponse_test.pt kukit/azax/branch/snowsprint/tests/test_azaxview.py kukit/azax/branch/snowsprint/unicode_quirks.py kukit/azax/tags/ kukit/azax/trunk/ (props changed) kukit/azax/trunk/BeautifulSoup.py kukit/azax/trunk/DEPENDENCIES.txt kukit/azax/trunk/EXTERNALS.TXT kukit/azax/trunk/INSTALL.txt kukit/azax/trunk/TODO.txt kukit/azax/trunk/__init__.py kukit/azax/trunk/azaxview.py kukit/azax/trunk/browser/ kukit/azax/trunk/browser/kukitresponse.pt kukit/azax/trunk/commands.py kukit/azax/trunk/config.py kukit/azax/trunk/configure.zcml kukit/azax/trunk/demos/ kukit/azax/trunk/demos/azaxdemo/ kukit/azax/trunk/demos/azaxdemo/INSTALL.txt kukit/azax/trunk/demos/azaxdemo/__init__.py kukit/azax/trunk/demos/azaxdemo/azaxview.py kukit/azax/trunk/demos/azaxdemo/browser/ kukit/azax/trunk/demos/azaxdemo/browser/__init__.py kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kss kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kukit kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.pt kukit/azax/trunk/demos/azaxdemo/browser/azax_demo_index.pt kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.kukit kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.pt kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.kukit kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.pt kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.kukit kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.pt kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.kukit kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.pt kukit/azax/trunk/demos/azaxdemo/browser/cancel_submit.kukit kukit/azax/trunk/demos/azaxdemo/browser/cancel_submit.pt kukit/azax/trunk/demos/azaxdemo/browser/header_macros.pt kukit/azax/trunk/demos/azaxdemo/configure.zcml kukit/azax/trunk/demos/azaxdemo/helpers.py kukit/azax/trunk/demos/azaxdemo/interfaces.py kukit/azax/trunk/demos/azaxdemo/simplecontent.py kukit/azax/trunk/demos/azaxdemo/tests/ kukit/azax/trunk/demos/azaxdemo/tests/__init__.py kukit/azax/trunk/demos/azaxdemo/tests/azaxdemo.sel kukit/azax/trunk/demos/azaxdemo/tests/test_azaxview.py kukit/azax/trunk/demos/azaxdemo/www/ kukit/azax/trunk/demos/azaxdemo/www/simpleContentAdd.zpt kukit/azax/trunk/dtds/ kukit/azax/trunk/dtds/xhtml-lat1.ent kukit/azax/trunk/dtds/xhtml-special.ent kukit/azax/trunk/dtds/xhtml-symbol.ent kukit/azax/trunk/dtds/xhtml1-transitional.dtd kukit/azax/trunk/interfaces.py kukit/azax/trunk/meta.zcml kukit/azax/trunk/parsers.py kukit/azax/trunk/plugins/ (props changed) kukit/azax/trunk/plugins/EXTERNALS.TXT kukit/azax/trunk/plugins/__init__.py kukit/azax/trunk/plugins/command.py kukit/azax/trunk/plugins/concatresource/ kukit/azax/trunk/plugins/concatresource/README kukit/azax/trunk/plugins/concatresource/__init__.py kukit/azax/trunk/plugins/concatresource/cachingadapter.py kukit/azax/trunk/plugins/concatresource/compression/ kukit/azax/trunk/plugins/concatresource/compression/__init__.py kukit/azax/trunk/plugins/concatresource/compression/css.py kukit/azax/trunk/plugins/concatresource/compression/javascript.py kukit/azax/trunk/plugins/concatresource/compression/thirdparty/ kukit/azax/trunk/plugins/concatresource/compression/thirdparty/LICENSE kukit/azax/trunk/plugins/concatresource/compression/thirdparty/__init__.py kukit/azax/trunk/plugins/concatresource/compression/thirdparty/packer.py kukit/azax/trunk/plugins/concatresource/concatfileresource.py kukit/azax/trunk/plugins/concatresource/concatresource-configure.zcml kukit/azax/trunk/plugins/concatresource/concatresource-meta.zcml kukit/azax/trunk/plugins/concatresource/concatresource.py kukit/azax/trunk/plugins/concatresource/configure.zcml kukit/azax/trunk/plugins/concatresource/directives.py kukit/azax/trunk/plugins/concatresource/fields.py kukit/azax/trunk/plugins/concatresource/fileresource.py kukit/azax/trunk/plugins/concatresource/interfaces.py kukit/azax/trunk/plugins/concatresource/meta.py kukit/azax/trunk/plugins/concatresource/meta.zcml kukit/azax/trunk/plugins/concatresource/test/ kukit/azax/trunk/plugins/concatresource/test/README kukit/azax/trunk/plugins/concatresource/test/__init__.py kukit/azax/trunk/plugins/concatresource/test/configure.zcml kukit/azax/trunk/plugins/concatresource/test/test1.js kukit/azax/trunk/plugins/concatresource/test/test2.js kukit/azax/trunk/plugins/configure.py kukit/azax/trunk/plugins/configure.zcml kukit/azax/trunk/plugins/directives.py kukit/azax/trunk/plugins/event_action.py kukit/azax/trunk/plugins/event_type.py kukit/azax/trunk/plugins/interfaces.py kukit/azax/trunk/plugins/json/ kukit/azax/trunk/plugins/json/__init__.py kukit/azax/trunk/plugins/json/browser/ kukit/azax/trunk/plugins/json/browser/jsonkukit.js kukit/azax/trunk/plugins/json/config.py kukit/azax/trunk/plugins/json/configure.zcml kukit/azax/trunk/plugins/json/interfaces.py kukit/azax/trunk/plugins/json/utils.py kukit/azax/trunk/plugins/meta.zcml kukit/azax/trunk/plugins/plugin.py kukit/azax/trunk/plugins/registry.py kukit/azax/trunk/selectors.py kukit/azax/trunk/tests/ kukit/azax/trunk/tests/__init__.py kukit/azax/trunk/tests/js/ kukit/azax/trunk/tests/js/ecmaunit-license.txt kukit/azax/trunk/tests/js/ecmaunit.js kukit/azax/trunk/tests/js/runner.html kukit/azax/trunk/tests/js/testStyleParse.js kukit/azax/trunk/tests/js/unittestUtilities.js kukit/azax/trunk/tests/kukitresponse_test.pt kukit/azax/trunk/tests/test_azaxview.py kukit/azax/trunk/unicode_quirks.py Log: Bringing up azax sources Added: kukit/azax/branch/gotcha-namespaces/DEPENDENCIES.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/DEPENDENCIES.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +$Id$ + +- Zope-2.7.3 or greater +- Zope X3.0.0 or greater +- Five 0.3 or greater +- lxml 0.7 or greater + Added: kukit/azax/branch/gotcha-namespaces/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,62 @@ +$Id$ + +INSTALLATION NOTES +================== + +1 - Prerequisites +2 - lxml installation +3 - Zope X3 installation +4 - Products setup +5 - Zope 3 <-> Zope 2 path setup +6 - How to use azax + +---------- + +1- Prerequisites + +To install azax you will need: + +- a working Zope 2.7.x + CPS 3.3.4 or greater +- ZopeX3-3.0.0 : http://www.zope.org/Products/ZopeX3 +- Five 1.0.1 or greater : http://codespeak.net/z3/five/ +- lxml 0.7 : http://codespeak.net/lxml + +2 - lxml installation + +Install the python package lxml on your python installation, ie the one +Zope 2.7 actually use with the usual command:: + + $ python setup.py install + +3 - Zope X3 installation + +(not needed if you have Zope 2.8 or later as it includes Zope X3) + +Please install ZopeX3-3.0.0 on your system (usually in /opt/ZopeX3-3.0.0) with +the python version you use for Zope2.7 and CPS, e.g.:: + + $ ./configure --prefix /opt/ZopeX3-3.0.0 --with-python /usr/bin/python + $ make + $ make install + $ make check + +4 - Products setup + +Put Five, azax in your $INSTANCE/Products + +(not needed if you have Zope 2.8 or later as it includes Five) + +5 - Zope 3 <-> Zope 2 path setup + +(not needed if you have Zope 2.8 or later as it Zope X3 is correctly setup) + +Edit your $INSTANCE/etc/zope.conf to add the following path declarations:: + + path /opt/ZopeX3-3.0.0/lib/python + path $INSTANCE/Products/azax + +6 - How to use azax + +Copy the product azaxdemo that's inside demos in your instance, +and look at its own README.txt + Added: kukit/azax/branch/gotcha-namespaces/TODO.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/TODO.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,12 @@ +* rename AzaxResponse to KukitResponse +* look to TextIndexNG3 howto have it compatible with Z3, Z2 and Plone +* rename the commands to better match the DOM API +* try to fix the namespace problems + + + +

2005-11-16 21:24:05.484000

+ + + Added: kukit/azax/branch/gotcha-namespaces/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,3 @@ +import mimetypes + +mimetypes.types_map['.azax'] = 'text/xml' Added: kukit/azax/branch/gotcha-namespaces/azaxresponse.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/azaxresponse.py Sun May 21 15:02:20 2006 @@ -0,0 +1,121 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +from StringIO import StringIO + +from zope.interface import implements + +from interfaces import IAzaxResponse +import config + +class AzaxCommand: + def __init__(self, commands_ob, name, selector): + etree = config.etree + self.ob = etree.SubElement(commands_ob, 'kukit:command') + self.ob.set('selector', selector) + self.ob.set('name', name) + + def addData(self, name): + etree = config.etree + param = etree.SubElement(self.ob, 'kukit:param') + param.set('name', name) + return param + +HTML_DTD = '' + +HTML_PREFIX = HTML_DTD + '%s' + +class AzaxResponse(object): + """ azax command Response + """ + implements(IAzaxResponse) + + def __init__(self, response=None): + etree = config.etree + self._commands = etree.fromstring(HTML_PREFIX % '') + self._body = self._commands[0] + self._commands.set('xmlns:kukit', 'http://www.kukit.org/commands/1.0') + self._response = response + + def addCommand(self, name, selector): + return AzaxCommand(self._body, name, selector) + + def __str__(self): + """ renders the xml """ + etree = config.etree + result = etree.tostring(self._commands) + return result + + def __call__(self): + """ set response content type, if given """ + if self._response is not None: + self._response.setHeader('Content-Type', 'text/xml') + return str(self) + + def wrapInHtmlNamespace(self, new_value, data): + etree = config.etree + new_value = HTML_PREFIX % new_value + valuetree = etree.fromstring(new_value) + body = valuetree[0] + body.text = valuetree.text + for elem in body.getchildren(): + data.append(elem) + + # commands + def setHtmlAsChild(self, selector, new_value): + """ see interfaces.py """ + command = self.addCommand('setHtmlAsChild', selector) + data = command.addData('html') + self.wrapInHtmlNamespace(new_value, data) + + def addAfter(self, selector, new_value): + """ see interfaces.py """ + command = self.addCommand('addAfter', selector) + data = command.addData('html') + self.wrapInHtmlNamespace(new_value, data) + + def clearChildren(self, selector): + """ see interfaces.py """ + command = self.addCommand('clearChildren', selector) + data = command.addData('none') + + def copyChildrenFrom(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenFrom', selector) + data = command.addData('html_id') + data.text = id + + def copyChildrenTo(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenTo', selector) + data = command.addData('html_id') + data.text = id + + def moveChildrenTo(self, selector, id): + """ see interfaces.py """ + self.copyChildrenTo(selector, id) + self.clearChildren(selector) + + def executeCode(self, selector, code): + command = self.addCommand('executeCode', selector) + data = command.addData('code') + data.text = code Added: kukit/azax/branch/gotcha-namespaces/config.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/config.py Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +# we use lxml for all the XML stuff for speed, but you can also use +# ElemenTree or maybe even cElementTree + +try: + from lxml import etree +except ImportError: + try: + import cElementTree as etree + except ImportError: + from elementtree import ElementTree as etree Added: kukit/azax/branch/gotcha-namespaces/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +$Id$ + +INSTALLATION NOTES +================== + +1/ Drop azaxdemo into your product dir +2/ In the zmi, add a "Azax Simple content" anywhere + let's call it "test" +3/ Get into "test" page, you should have a menu with available demos + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ +import simplecontent + +def initialize(context): + + context.registerClass( + simplecontent.SimpleContent, + constructors = (simplecontent.manage_addSimpleContentForm, + simplecontent.manage_addSimpleContent), + ) Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,101 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +from Products.Five import BrowserView + +from Products.azax.azaxresponse import AzaxResponse +from datetime import datetime + +class AzaxView(BrowserView): + + def __init__(self, context, request): + BrowserView.__init__(self, context, request) + + def clearDivContent(self): + """ clear div content """ + return_object = AzaxResponse(self.request.response) + return_object.clearChildren('div#demo') + return return_object() + + def copyFromDivContent(self): + """ copy div content """ + return_object = AzaxResponse(self.request.response) + return_object.copyChildrenFrom('div#copy', 'demo') + return return_object() + + def copyToDivContent(self): + """ copy div content """ + return_object = AzaxResponse(self.request.response) + return_object.copyChildrenTo('div#copy', 'demo') + return return_object() + + def moveToDivContent(self): + """ copy div content """ + return_object = AzaxResponse(self.request.response) + return_object.moveChildrenTo('div#copy', 'demo') + return return_object() + + def getDivContent(self): + """ returns div content """ + return_object = AzaxResponse(self.request.response) + return_object.setHtmlAsChild('div#demo', '

it worked

') + return_object.setHtmlAsChild('div#demo', '

it worked again

') + return return_object() + + def getCorrespondingSelect(self, value): + """ returns select content """ + mapping = {} + mapping['']=[] + mapping['animals']=['dog', 'cat', 'cow'] + mapping['machines']=['computer', 'car', 'airplane'] + return_object = AzaxResponse(self.request.response) + result = ['' % item for item in mapping[value]] + + return_object.setHtmlAsChild('select#second', ' '.join(result)) + return return_object() + + def getAutoupdateMarkup(self): + """ returns the current time """ + return_object = AzaxResponse(self.request.response) + return_object.setHtmlAsChild('div#update-wrapper', '
') + return return_object() + + def getCurrentTime(self): + """ returns the current time """ + return_object = AzaxResponse(self.request.response) + return_object.setHtmlAsChild('div#update-area', "

%s

" % str(datetime.now())) + return return_object() + + def getInputField(self, value): + """ returns the current time """ + return_object = AzaxResponse(self.request.response) + return_object.setHtmlAsChild('div#text', + '' \ + '' + ) + return return_object() + + def saveText(self, value): + """ returns the current time """ + return_object = AzaxResponse(self.request.response) + return_object.setHtmlAsChild('div#text', value+'') + return return_object() + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.azax ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.azax Sun May 21 15:02:20 2006 @@ -0,0 +1,18 @@ + + + + copyFromDivContent + + + copyToDivContent + + + moveToDivContent + + + clearDivContent + + + getDivContent + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,131 @@ + + + + + + + + + +

All demos

+

Change tag content

+

Top div

+
+ Azax +
+ +

Bottom div

+
+ copy here +
+

Javascript Styling

+

This page defines a link in the header which rel attribute points + to a .azax server file : + + + see the kukit file here.

+

+ This .azax file declares CSS selectors associated with events and server URLs called when the event occur. +

+

A javascript engine processes the XML content : it inserts javascript + events in the DOM.

+

+ For instance, the click event of button with the id change will call asynchronously the getDivContent URL. + +

+

Server Asynchronous Call

+

+ The events associated with Javascript just call the server asynchronously. +

+

+ The response is a XML file. + The XML contains CSS selectors associated with commands. +

+

+ The Javascript engine selects the DOM nodes to which it applies each command. +

+

+ For instance, let's look at the moveToDivContent response. +

+
+<selectors>
+  <selector>
+    <value>div#copy</value>
+    <commands>
+      <command>
+	<name>copyChildrenTo</name>
+	<data name="html_id">demo</data>
+      </command>
+      <command>
+	<name>clearChildren</name>
+	<data name="none"/>
+      </command>
+    </commands>
+  </selector>
+</selectors>
+   
+

+ The XML specifies two commands to apply to the div#copy node : +

+

+ copyChildrenTo copies the children of the DOM node to the node with id demo. +

+

+ clearChildren removes the children of the DOM node. +

+ + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo_index.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_demo_index.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,12 @@ + + + +

Azax demos

+ + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.azax ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.azax Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ + + + + getInputField + + + saveText + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_instant_edit.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,33 @@ + + + + + + + + + +

All demos

+ +

Instant edit Demo

+
+
+ click me! + +
+
+ + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.azax ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.azax Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ + + + + getCurrentTime + + + getAutoupdateMarkup + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_three_autoupdate.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,50 @@ + + + + + + + + + + + +

All demos

+

Three autoupdate

+ +

Demo

+
+
+ + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.azax ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.azax Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + getCorrespondingSelect + + Added: kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/demos/azaxdemo/browser/azax_two_select.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,62 @@ + + + + + + + + + +

All demos

+

Two Selects

+ + +

Demo

+
+ + + + + + +
+ Title +
+ + + + + + + + + +
+ +
+ + + +
+ +

Footer

Added: kukit/azax/branch/gotcha-namespaces/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,40 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +""" IAzaxResponse provide apis to be able to generate + the xml response used by the javascript +""" +from zope.interface import Interface + +class IAzaxResponse(Interface): + + def updateTag(selector, new_value): + """ updates a tag on the page """ + + + + + + + + + + Added: kukit/azax/branch/gotcha-namespaces/tests/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit-license.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit-license.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,31 @@ +Copyright (c) 2003-2004, EcmaUnit Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of EcmaUnit nor the names of its contributors may + be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Added: kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit.js ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/js/ecmaunit.js Sun May 21 15:02:20 2006 @@ -0,0 +1,461 @@ +/***************************************************************************** + * + * Copyright (c) 2003-2004 EcmaUnit Contributors. All rights reserved. + * + * This software is distributed under the terms of the EcmaUnit + * License. See LICENSE.txt for license text. For a list of EcmaUnit + * Contributors see CREDITS.txt. + * + *****************************************************************************/ + +// $Id: ecmaunit.js 10153 2005-03-29 09:50:05Z guido $ + +/* + Object-oriented prototype-based unit test suite +*/ + +function TestCase() { + /* a single test case */ + this.name = 'TestCase'; + + this.initialize = function(reporter) { + // this array's contents will be displayed when done (if it + // contains anything) + this._exceptions = new Array(); + this._reporter = reporter; + }; + + this.setUp = function() { + /* this will be called on before each test method that is ran */ + }; + + this.tearDown = function() { + /* this will be called after each test method that has been ran */ + }; + + this.assertEquals = function(var1, var2, message) { + /* assert whether 2 vars have the same value */ + if (!message) { + message = ''; + } else { + message = "'" + message + "' "; + } + if (var1 && var1.toSource && var2 && var2.toSource) { + if (var1.toSource() != var2.toSource()) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' != ' + var2); + }; + } else { + if (var1 != var2) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' != ' + var2); + }; + }; + }; + + this.assertNotEquals = function(var1, var2, message) { + /* assert whether 2 vars have different values */ + if (!message) { + message = ''; + } else { + message = "'" + message + "' "; + } + if (var1 && var1.toSource && var2 && var2.toSource) { + if (var1.toSource() == var2.toSource()) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' == ' + var2); + }; + } else { + if (var1 == var2) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' == ' + var2); + }; + }; + }; + + this.debug = function(msg) { + this._reporter.debug(msg); + } + this.assert = function(statement, message) { + /* assert whether a variable resolves to true */ + if (!statement) { + if (!message) message = (statement && statement.toString) ? + statement.toString() : statement; + this._throwException('Assertion \'' + message + '\' failed'); + }; + }; + + this.assertTrue = this.assert; + + this.assertFalse = function(statement, message) { + /* assert whether a variable resolves to false */ + if (statement) { + if (!message) message = statement.toString ? + statement.toString() : statement; + this._throwException('AssertFalse \'' + message + '\' failed'); + }; + }; + + this.assertThrows = function(func, exception, context) { + /* assert whether a certain exception is raised */ + if (!context) { + context = null; + }; + var exception_thrown = false; + // remove the first three args, they're the function's normal args + var args = []; + for (var i=3; i < arguments.length; i++) { + args.push(arguments[i]); + }; + try { + func.apply(context, args); + } catch(e) { + // allow catching undefined exceptions too + if (exception === undefined) { + } else if (exception) { + if (exception.toSource && e.toSource) { + exception = exception.toSource(); + e = e.toSource(); + }; + if (exception.toString && e.toString) { + exception = exception.toString(); + e = e.toString(); + }; + if (e != exception) { + this._throwException('Function threw the wrong ' + + 'exception ' + e.toString() + + ', while expecting ' + exception.toString()); + }; + }; + exception_thrown = true; + }; + if (!exception_thrown) { + if (exception) { + this._throwException("function didn\'t raise exception \'" + + exception.toString() + "'"); + } else { + this._throwException('function didn\'t raise exception'); + }; + }; + }; + + this.runTests = function() { + /* find all methods of which the name starts with 'test' + and call them */ + var ret = this._runHelper(); + this._reporter.summarize(ret[0], ret[1], this._exceptions); + }; + + this._runHelper = function() { + /* this actually runs the tests + return value is an array [total tests ran, total time spent (ms)] + */ + var now = new Date(); + var starttime = now.getTime(); + var numtests = 0; + for (var attr in this) { + if (attr.substr(0, 4) == 'test') { + this.setUp(); + try { + this[attr](); + this._reporter.reportSuccess(this.name, attr); + } catch(e) { + var raw = e; + if (e.name && e.message) { // Microsoft + e = e.name + ': ' + e.message; + } + this._reporter.reportError(this.name, attr, e, raw); + this._exceptions.push(new Array(this.name, attr, e, raw)); + }; + this.tearDown(); + numtests++; + }; + }; + var now = new Date(); + var totaltime = now.getTime() - starttime; + return new Array(numtests, totaltime); + }; + + this._throwException = function(message) { + var lineno = this._getLineNo(); + if (lineno) { + message = 'line ' + lineno + ' - ' + message; + }; + throw(message); + }; + + this._getLineNo = function() { + /* tries to get the line no in Moz */ + var stack = undefined; + try {notdefined()} catch(e) {stack = e.stack}; + if (stack) { + stack = stack.toString().split('\n'); + for (var i=0; i < stack.length; i++) { + var line = stack[i].split('@')[1]; + if (line.indexOf('ecmaunit') == -1) { + // return the first line after we get out of ecmaunit + var chunks = line.split(':'); + var lineno = chunks[chunks.length - 1]; + if (lineno != '0') { + return lineno; + }; + }; + }; + } else { + return false; + }; + }; +}; + +function TestSuite(reporter) { + /* run a suite of tests */ + this._reporter = reporter; + this._tests = new Array(); + this._exceptions = new Array(); + + this.registerTest = function(test) { + /* register a test */ + if (!test) { + throw('TestSuite.registerTest() requires a testcase as argument'); + }; + this._tests.push(test); + }; + + this.runSuite = function() { + /* run the suite */ + var now = new Date(); + var starttime = now.getTime(); + var testsran = 0; + for (var i=0; i < this._tests.length; i++) { + var test = new this._tests[i](); + test.initialize(this._reporter); + testsran += test._runHelper()[0]; + // the TestCase class handles output of dots and Fs, but we + // should take care of the exceptions + if (test._exceptions.length) { + for (var j=0; j < test._exceptions.length; j++) { + // attr, exc in the org array, so here it becomes + // name, attr, exc + var excinfo = test._exceptions[j]; + this._exceptions.push(excinfo); + }; + }; + }; + var now = new Date(); + var totaltime = now.getTime() - starttime; + this._reporter.summarize(testsran, totaltime, this._exceptions); + }; +}; + +function StdoutReporter(verbose) { + this.verbose = verbose; + this.debug = function(text) { + print(text+"\n"); + } + + this.reportSuccess = function(testcase, attr) { + /* report a test success */ + if (this.verbose) { + print(testcase + '.' + attr + '(): OK'); + } else { + print('.'); + }; + }; + + this.reportError = function(testcase, attr, exception, raw) { + /* report a test failure */ + if (this.verbose) { + print(testcase + '.' + attr + '(): FAILED!'); + } else { + print('F'); + }; + }; + + this.summarize = function(numtests, time, exceptions) { + print('\n' + numtests + ' tests ran in ' + time / 1000.0 + + ' seconds\n'); + if (exceptions.length) { + for (var i=0; i < exceptions.length; i++) { + var testcase = exceptions[i][0]; + var attr = exceptions[i][1]; + var exception = exceptions[i][2]; + var raw = exceptions[i][3]; + print(testcase + '.' + attr + ', exception: ' + exception); + if (verbose) { + this._printStackTrace(raw); + }; + }; + print('NOT OK!'); + } else { + print('OK!'); + }; + }; + + this._printStackTrace = function(exc) { + if (!exc.stack) { + print('no stacktrace available'); + return; + }; + var lines = exc.stack.toString().split('\n'); + var toprint = []; + for (var i=0; i < lines.length; i++) { + var line = lines[i]; + if (line.indexOf('ecmaunit.js') > -1) { + // remove useless bit of traceback + break; + }; + if (line.charAt(0) == '(') { + line = 'function' + line; + }; + var chunks = line.split('@'); + toprint.push(chunks); + }; + toprint.reverse(); + for (var i=0; i < toprint.length; i++) { + print(' ' + toprint[i][1]); + print(' ' + toprint[i][0]); + }; + print(); + }; +}; + +function HTMLReporter(outputelement, verbose) { + this.outputelement = outputelement; + this.document = outputelement.ownerDocument; + this.verbose = verbose; //XXX verbose not yet supported + + this.debug = function(text) { + var msg = this.document.createTextNode(text); + var div = this.document.createElement('div'); + div.appendChild(msg); + this.outputelement.appendChild(div); + } + this.reportSuccess = function(testcase, attr) { + /* report a test success */ + // a single dot looks rather small + var dot = this.document.createTextNode('+'); + this.outputelement.appendChild(dot); + }; + + this.reportError = function(testcase, attr, exception, raw) { + /* report a test failure */ + var f = this.document.createTextNode('F'); + this.outputelement.appendChild(f); + if (this.verbose) { + }; + }; + + this.summarize = function(numtests, time, exceptions) { + /* write the result output to the html node */ + var p = this.document.createElement('p'); + var text = this.document.createTextNode(numtests + ' tests ran in ' + + time / 1000.0 + ' seconds'); + p.appendChild(text); + this.outputelement.appendChild(p); + if (exceptions.length) { + for (var i=0; i < exceptions.length; i++) { + var testcase = exceptions[i][0]; + var attr = exceptions[i][1]; + var exception = exceptions[i][2]; + var raw = exceptions[i][3]; + var div = this.document.createElement('div'); + var lines = exception.split('\n'); + var text = this.document.createTextNode( + testcase + '.' + attr + ', exception '); + div.appendChild(text); + // add some formatting for Opera: this browser displays nice + // tracebacks... + for (var j=0; j < lines.length; j++) { + var text = lines[j]; + if (j > 0) { + text = '\xa0\xa0\xa0\xa0' + text; + }; + div.appendChild(this.document.createTextNode(text)); + div.appendChild(this.document.createElement('br')); + }; + div.style.color = 'red'; + this.outputelement.appendChild(div); + if (this.verbose) { + // display stack trace on Moz + this._displayStackTrace(raw); + }; + }; + var div = this.document.createElement('div'); + var text = this.document.createTextNode('NOT OK!'); + div.appendChild(text); + div.style.backgroundColor = 'red'; + div.style.color = 'black'; + div.style.fontWeight = 'bold'; + div.style.textAlign = 'center'; + div.style.marginTop = '1em'; + this.outputelement.appendChild(div); + } else { + var div = this.document.createElement('div'); + var text = this.document.createTextNode('OK!'); + div.appendChild(text); + div.style.backgroundColor = 'lightgreen'; + div.style.color = 'black'; + div.style.fontWeight = 'bold'; + div.style.textAlign = 'center'; + div.style.marginTop = '1em'; + this.outputelement.appendChild(div); + }; + }; + + this._displayStackTrace = function(exc) { + /* + if (arguments.caller) { + // IE + var caller = arguments; + toprint = []; + while (caller) { + var callee = caller.callee.toString(); + callee = callee.replace('\n', '').replace(/\s+/g, ' '); + var funcsig = /(.*?)\s*\{/.exec(callee)[1]; + var args = caller.callee.arguments; + var displayargs = []; + for (var i=0; i < args.length; i++) { + displayargs.push(args[i].toString()); + }; + toprint.push((funcsig + ' - (' + displayargs + ')')); + caller = caller.caller; + }; + toprint.reverse(); + var pre = this.document.createElement('pre'); + for (var i=0; i < toprint.length; i++) { + pre.appendChild(document.createTextNode(toprint[i])); + pre.appendChild(document.createElement('br')); + }; + this.outputelement.appendChild(pre); + }; + */ + if (exc.stack) { + // Moz (sometimes) + var lines = exc.stack.toString().split('\n'); + var toprint = []; // need to reverse this before outputting + for (var i=0; i < lines.length; i++) { + var line = lines[i]; + if (line.indexOf('ecmaunit.js') > -1) { + // remove useless bit of traceback + break; + }; + if (line[0] == '(') { + line = 'function' + line; + }; + line = line.split('@'); + toprint.push(line); + }; + toprint.reverse(); + var pre = this.document.createElement('pre'); + for (var i=0; i < toprint.length; i++) { + pre.appendChild( + this.document.createTextNode( + ' ' + toprint[i][1] + '\n ' + toprint[i][0] + '\n' + ) + ); + }; + pre.appendChild(document.createTextNode('\n')); + this.outputelement.appendChild(pre); + }; + }; +}; Added: kukit/azax/branch/gotcha-namespaces/tests/js/runner.html ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/js/runner.html Sun May 21 15:02:20 2006 @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + +

+ Plone ECMAScript Unit Tests +

+ +

+ This page is the entry to the ECMAScript Unit Tests. +

+ + +
+ Suite filter: + Test filter: +
+
+
    +
    +
    + + + Added: kukit/azax/branch/gotcha-namespaces/tests/js/testStyleParse.js ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/js/testStyleParse.js Sun May 21 15:02:20 2006 @@ -0,0 +1,131 @@ +function RuleParseTestCase() { + this.name = 'RuleParseTestCase'; +} + +RuleParseTestCase.prototype = new TestCase; +Class = RuleParseTestCase.prototype; + +Class.setUp = function() { + this.preprocessor = new kukit.RuleProcessor(); + this.doc = new DOMParser(); +}; + +Class.tearDown = function() { +}; + + +Class.testOneRule = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 1); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'getDivContent'); +} + +Class.testOneWrongSelector = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testOneWrongEvent = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testOneWrongAction = function() { + input = ''; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testCorrectRules = function() { + input = 'getDivContentSecond'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 2); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'getDivContent'); + rule = rules[1]; + this.assertEquals(rule.selector, 'button.second'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'Second'); +} + +Class.xxx_testCorrectRulesPlusOneWrong = function() { + // this should throw an error, diabled till this is implemented + input = '' + + '' + + '' + + 'getDivContent' + + '' + + '' + + 'Second' + + '' + + '' + + 'getDivContent' + + 'getDivContent' + + '' + + ''; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 2); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'getDivContent'); + rule = rules[1]; + this.assertEquals(rule.selector, 'button.second'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'Second'); +} + +testcase_registry.registerTestCase(RuleParseTestCase, 'ruleparse'); + +function CommandsParseTestCase() { + this.name = 'CommandsParseTestCase'; +} + +CommandsParseTestCase.prototype = new TestCase; +Class = CommandsParseTestCase.prototype; + +Class.setUp = function() { + this.preprocessor = new kukit.RuleProcessor(); + this.doc = new DOMParser(); +}; + +Class.tearDown = function() { +}; + + +Class.testOneCommand = function() { + /*input = 'button.buttonclickgetDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 1); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'getDivContent');*/ +} + +testcase_registry.registerTestCase(CommandsParseTestCase, 'commandparse'); Added: kukit/azax/branch/gotcha-namespaces/tests/js/unittestUtilities.js ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/js/unittestUtilities.js Sun May 21 15:02:20 2006 @@ -0,0 +1,160 @@ + +function TestCaseRegistry() { + this._testcases = new Object(); + + this.registerTestCase = function(testcase, suite_name) { + if (!testcase) { + throw('TestCaseRegistry.registerTestCase() requires a testcase as argument'); + } + testcase = new testcase(); + if (!suite_name) { + suite_name = 'default'; + } + if (!this._testcases[suite_name]) { + this._testcases[suite_name] = new Array(); + } + this._testcases[suite_name].push(testcase); + } + + this.setTestSuiteFilter = function(filter) { + if (filter) { + this.suite_filter = new RegExp(filter, "i"); + } else { + this.suite_filter = null; + } + } + + this.setTestFilter = function(filter) { + if (filter) { + this.test_filter = new RegExp(filter, "i"); + } else { + this.test_filter = null; + } + } + + this.getFilteredTestCases = function() { + var testcases = new Array(); + + var suites = this.getFilteredTestSuitNames(); + for (suite_index in suites) { + var suite = this._testcases[suites[suite_index]]; + for (var test_index in suite) { + var testcase = suite[test_index]; + if (this.test_filter) { + if (!this.test_filter.test(testcase.name)) { + continue; + } + } + testcases.push(testcase); + } + } + + return testcases; + } + + this.getFilteredTestSuitNames = function() { + var names = new Array(); + + for (var suite_name in this._testcases) { + if (this.suite_filter) { + if (!this.suite_filter.test(suite_name)) { + continue; + } + } + names.push(suite_name); + } + + return names; + } + + this.getFilteredTestNames = function() { + var names = new Array(); + + var testcases = this.getFilteredTestCases(); + for (var testcase_index in testcases) { + names.push(testcases[testcase_index].name); + } + + return names; + } +} +testcase_registry = new TestCaseRegistry(); + +function runTestCase(testCase) { + // append TOC entry + var name = testCase.name; + var toc = document.getElementById("testResultsToc"); + var results_box = document.getElementById("testResultsPlaceHolder"); + + // create toc element + var toc_item = document.createElement("li"); + toc_item.appendChild(createLink("../++resource++azaxtestrunner.html#"+name, name+" Results", false)); + toc.appendChild(toc_item); + + // append testcase section + var placeHolder = document.createElement("div"); + placeHolder.className = "placeholder"; + var link = createLink(name, name+" Results", true); + var header = document.createElement("h3"); + header.appendChild(link); + placeHolder.appendChild(header); + results_box.appendChild(placeHolder); + testCase.initialize(new HTMLReporter(placeHolder)); + testCase.runTests(); +}; + +function runTestCases() { + var suite_filter = document.getElementById('suite-filter').value; + var test_filter = document.getElementById('test-filter').value; + testcase_registry.setTestSuiteFilter(suite_filter); + testcase_registry.setTestFilter(test_filter); + var testcases = testcase_registry.getFilteredTestCases(); + for (var testcase_index in testcases) { + runTestCase(testcases[testcase_index]); + } +} + +function clearOutput() { + clearChildNodes(document.getElementById("testResultsToc")); + clearChildNodes(document.getElementById("testResultsPlaceHolder")); + clearChildNodes(document.getElementById("testSandbox")); +} + +function showFilteredTests() { + var suite_filter = document.getElementById('suite-filter').value; + var test_filter = document.getElementById('test-filter').value; + testcase_registry.setTestSuiteFilter(suite_filter); + testcase_registry.setTestFilter(test_filter); + putTextInPlaceHolder(testcase_registry.getFilteredTestSuitNames() + + testcase_registry.getFilteredTestNames()); +} + +function showMarkup() { + var text = document.getElementById('testResultsPlaceHolder').innerHTML + var msg = this.document.createTextNode(text); + var sandbox = document.getElementById("testSandbox"); + clearChildNodes(sandbox); + sandbox.appendChild(msg); +} + +function putTextInPlaceHolder(text) { + var msg = this.document.createTextNode(text); + var placeholder = document.getElementById("testResultsPlaceHolder"); + clearChildNodes(placeholder); + placeholder.appendChild(msg); +} + +clearChildNodes = function(oNode) { + while(oNode.hasChildNodes()) { + oNode.removeChild(oNode.firstChild); + } +} + +function createLink(link, desc, bName) { + var a = document.createElement("a"); + + a.setAttribute((bName?"name":"href"), link); + if(desc) + a.appendChild(document.createTextNode(desc)); + return a; +} Added: kukit/azax/branch/gotcha-namespaces/tests/test_azaxresponse.py ============================================================================== --- (empty file) +++ kukit/azax/branch/gotcha-namespaces/tests/test_azaxresponse.py Sun May 21 15:02:20 2006 @@ -0,0 +1,159 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +import unittest, os +from zope.testing import doctest +from Testing.ZopeTestCase import ZopeTestCase + +from Products.azax.azaxresponse import AzaxResponse +from Products.azax.azaxresponse import HTML_DTD +from Products.azax import config + +class FakeResponse: + _stuff = {} + + def setHeader(self, name, value): + self._stuff[name] = value + +class AzaxResponseTestCase(ZopeTestCase): + + def cleanupXMLForTesting(self, xml): + return xml.replace(' />', '/>') + + def test_instanciation(self): + ob = AzaxResponse() + self.assertNotEquals(ob, None) + + def test_str_(self): + ob = AzaxResponse() + self.assertEquals(self.cleanupXMLForTesting(str(ob)), '') + + def assertXMLEquals(self, a, b): + etree = config.etree + a = etree.fromstring(a) + a = etree.tostring(a) + b = etree.fromstring(b) + b = etree.tostring(b) + self.assertEquals(a, b) + + def xtest_addCommand(self): + ob = AzaxResponse() + command = ob.addCommand('name', 'selector') + self.assertXMLEquals(self.cleanupXMLForTesting( + config.etree.tostring(command.ob)), + '') + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), + '') + + def test_setHtmlAsChildTextPlusEntity(self): + ob = AzaxResponse() + ob.setHtmlAsChild('div.class', ' ') + result = HTML_DTD + '' \ + '' \ + ' ' \ + '' + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), result) + + def xtest_setHtmlAsChildTextOnly(self): + ob = AzaxResponse() + ob.setHtmlAsChild('div.class', 'new_content') + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), + '' \ + 'new_content') + + def xtest_setHtmlAsChildTagOnly(self): + ob = AzaxResponse() + ob.setHtmlAsChild('div.class', '

    new_content

    ') + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), + '' \ + '

    ' \ + 'new_content

    ') + + def xtest_setHtmlAsChildTagPlusText(self): + ob = AzaxResponse() + ob.setHtmlAsChild('div.class', '

    new_content

    after') + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), + '' \ + '

    ' \ + 'new_content

    after
    ') + + def xtest_setHtmlAsChildTextTagPlusText(self): + ob = AzaxResponse() + ob.setHtmlAsChild('div.class', 'before

    new_content

    after') + self.assertXMLEquals(self.cleanupXMLForTesting(str(ob)), + '' \ + 'before

    ' \ + 'new_content

    after
    ') + + def test___call__(self): + response = FakeResponse() + ob = AzaxResponse(response) + ob.setHtmlAsChild('div.class', 'new_content') + ob() + self.assertEquals(response._stuff['Content-Type'], 'text/xml') + +class ElementTreeTestCase(AzaxResponseTestCase): + def afterSetUp(self): + from elementtree import ElementTree as etree + config.etree = etree + + def test_etree(self): + self.assertEquals(config.etree.__name__, 'elementtree.ElementTree') + +class CElementTreeTestCase(AzaxResponseTestCase): + def afterSetUp(self): + import cElementTree as etree + config.etree = etree + + def test_etree(self): + self.assertEquals(config.etree.__name__, 'cElementTree') + +class LxmlTestCase(AzaxResponseTestCase): + def afterSetUp(self): + from lxml import etree + config.etree = etree + + def test_etree(self): + self.assertEquals(config.etree.__name__, 'lxml.etree') + +def test_suite(): + suites = [] + try: + from lxml import etree + except ImportError: + pass + else: + suites.append(unittest.makeSuite(LxmlTestCase)) + try: + from elementtree import ElementTree as etree + except ImportError: + pass + #else: + # suites.append(unittest.makeSuite(ElementTreeTestCase)) + try: + import cElementTree as etree + except ImportError: + pass + #else: + # suites.append(unittest.makeSuite(CElementTreeTestCase)) + suites.append(doctest.DocTestSuite('Products.azax.azaxresponse')) + return unittest.TestSuite(suites) + Added: kukit/azax/branch/snowsprint/BeautifulSoup.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/BeautifulSoup.py Sun May 21 15:02:20 2006 @@ -0,0 +1,1080 @@ +"""Beautiful Soup +Elixir and Tonic +"The Screen-Scraper's Friend" +v2.1.1 +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup parses arbitrarily invalid XML- or HTML-like substance +into a tree representation. It provides methods and Pythonic idioms +that make it easy to search and modify the tree. + +A well-formed XML/HTML document will yield a well-formed data +structure. An ill-formed XML/HTML document will yield a +correspondingly ill-formed data structure. If your document is only +locally well-formed, you can use this library to find and process the +well-formed part of it. The BeautifulSoup class has heuristics for +obtaining a sensible parse tree in the face of common HTML errors. + +Beautiful Soup has no external dependencies. It works with Python 2.2 +and up. + +Beautiful Soup defines classes for four different parsing strategies: + + * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific + language that kind of looks like XML. + + * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid + or invalid. + + * ICantBelieveItsBeautifulSoup, for parsing valid but bizarre HTML + that trips up BeautifulSoup. + + * BeautifulSOAP, for making it easier to parse XML documents that use + lots of subelements containing a single string, where you'd prefer + they put that string into an attribute (such as SOAP messages). + +You can subclass BeautifulStoneSoup or BeautifulSoup to create a +parsing strategy specific to an XML schema or a particular bizarre +HTML document. Typically your subclass would just override +SELF_CLOSING_TAGS and/or NESTABLE_TAGS. +""" +from __future__ import generators + +__author__ = "Leonard Richardson (leonardr at segfault.org)" +__version__ = "2.1.1" +__date__ = "$Date: 2004/10/18 00:14:20 $" +__copyright__ = "Copyright (c) 2004-2005 Leonard Richardson" +__license__ = "PSF" + +from sgmllib import SGMLParser, SGMLParseError +import types +import re +import sgmllib + +#This code makes Beautiful Soup able to parse XML with namespaces +sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') + +class NullType(object): + + """Similar to NoneType with a corresponding singleton instance + 'Null' that, unlike None, accepts any message and returns itself. + + Examples: + >>> Null("send", "a", "message")("and one more", + ... "and what you get still") is Null + True + """ + + def __new__(cls): return Null + def __call__(self, *args, **kwargs): return Null +## def __getstate__(self, *args): return Null + def __getattr__(self, attr): return Null + def __getitem__(self, item): return Null + def __setattr__(self, attr, value): pass + def __setitem__(self, item, value): pass + def __len__(self): return 0 + # FIXME: is this a python bug? otherwise ``for x in Null: pass`` + # never terminates... + def __iter__(self): return iter([]) + def __contains__(self, item): return False + def __repr__(self): return "Null" +Null = object.__new__(NullType) + +class PageElement: + """Contains the navigational information for some part of the page + (either a tag or a piece of text)""" + + def setup(self, parent=Null, previous=Null): + """Sets up the initial relations between this element and + other elements.""" + self.parent = parent + self.previous = previous + self.next = Null + self.previousSibling = Null + self.nextSibling = Null + if self.parent and self.parent.contents: + self.previousSibling = self.parent.contents[-1] + self.previousSibling.nextSibling = self + + def findNext(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears after this Tag in the document.""" + return self._first(self.fetchNext, name, attrs, text) + firstNext = findNext + + def fetchNext(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before after Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextGenerator) + + def findNextSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears after this Tag in the document.""" + return self._first(self.fetchNextSiblings, name, attrs, text) + firstNextSibling = findNextSibling + + def fetchNextSiblings(self, name=None, attrs={}, text=None, limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear after this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextSiblingGenerator) + + def findPrevious(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears before this Tag in the document.""" + return self._first(self.fetchPrevious, name, attrs, text) + + def fetchPrevious(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.previousGenerator) + firstPrevious = findPrevious + + def findPreviousSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears before this Tag in the document.""" + return self._first(self.fetchPreviousSiblings, name, attrs, text) + firstPreviousSibling = findPreviousSibling + + def fetchPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, + self.previousSiblingGenerator) + + def findParent(self, name=None, attrs={}): + """Returns the closest parent of this Tag that matches the given + criteria.""" + r = Null + l = self.fetchParents(name, attrs, 1) + if l: + r = l[0] + return r + firstParent = findParent + + def fetchParents(self, name=None, attrs={}, limit=None): + """Returns the parents of this Tag that match the given + criteria.""" + return self._fetch(name, attrs, None, limit, self.parentGenerator) + + #These methods do the real heavy lifting. + + def _first(self, method, name, attrs, text): + r = Null + l = method(name, attrs, text, 1) + if l: + r = l[0] + return r + + def _fetch(self, name, attrs, text, limit, generator): + "Iterates over a generator looking for things that match." + if not hasattr(attrs, 'items'): + attrs = {'class' : attrs} + + results = [] + g = generator() + while True: + try: + i = g.next() + except StopIteration: + break + found = None + if isinstance(i, Tag): + if not text: + if not name or self._matches(i, name): + match = True + for attr, matchAgainst in attrs.items(): + check = i.get(attr) + if not self._matches(check, matchAgainst): + match = False + break + if match: + found = i + elif text: + if self._matches(i, text): + found = i + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #Generators that can be used to navigate starting from both + #NavigableTexts and Tags. + def nextGenerator(self): + i = self + while i: + i = i.next + yield i + + def nextSiblingGenerator(self): + i = self + while i: + i = i.nextSibling + yield i + + def previousGenerator(self): + i = self + while i: + i = i.previous + yield i + + def previousSiblingGenerator(self): + i = self + while i: + i = i.previousSibling + yield i + + def parentGenerator(self): + i = self + while i: + i = i.parent + yield i + + def _matches(self, chunk, howToMatch): + #print 'looking for %s in %s' % (howToMatch, chunk) + # + # If given a list of items, return true if the list contains a + # text element that matches. + if isList(chunk) and not isinstance(chunk, Tag): + for tag in chunk: + if isinstance(tag, NavigableText) and self._matches(tag, howToMatch): + return True + return False + if callable(howToMatch): + return howToMatch(chunk) + if isinstance(chunk, Tag): + #Custom match methods take the tag as an argument, but all other + #ways of matching match the tag name as a string + chunk = chunk.name + #Now we know that chunk is a string + if not isinstance(chunk, basestring): + chunk = str(chunk) + if hasattr(howToMatch, 'match'): + # It's a regexp object. + return howToMatch.search(chunk) + if isList(howToMatch): + return chunk in howToMatch + if hasattr(howToMatch, 'items'): + return howToMatch.has_key(chunk) + #It's just a string + return str(howToMatch) == chunk + +class NavigableText(PageElement): + + def __getattr__(self, attr): + "For backwards compatibility, text.string gives you text" + if attr == 'string': + return self + else: + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + +class NavigableString(str, NavigableText): + pass + +class NavigableUnicodeString(unicode, NavigableText): + pass + +class Tag(PageElement): + + """Represents a found HTML tag with its attributes and contents.""" + + def __init__(self, name, attrs=None, parent=Null, previous=Null): + "Basic constructor." + self.name = name + if attrs == None: + attrs = [] + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self._getAttrMap().get(key, default) + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the tag, + and throws an exception if it's not there.""" + return self._getAttrMap()[key] + + def __iter__(self): + "Iterating over a tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __nonzero__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self._getAttrMap() + self.attrMap[key] = value + found = False + for i in range(0, len(self.attrs)): + if self.attrs[i][0] == key: + self.attrs[i] = (key, value) + found = True + if not found: + self.attrs.append((key, value)) + self._getAttrMap()[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + for item in self.attrs: + if item[0] == key: + self.attrs.remove(item) + #We don't break because bad HTML can define the same + #attribute multiple times. + self._getAttrMap() + if self.attrMap.has_key(key): + del self.attrMap[key] + + def __call__(self, *args, **kwargs): + """Calling a tag like a function is the same as calling its + fetch() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return apply(self.fetch, args, kwargs) + + def __getattr__(self, tag): + if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: + return self.first(tag[:-3]) + elif tag.find('__') != 0: + return self.first(tag) + + def __eq__(self, other): + """Returns true iff this tag has the same name, the same attributes, + and the same contents (recursively) as the given tag. + + NOTE: right now this will return false if two tags have the + same attributes in a different order. Should this be fixed?""" + if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other): + return False + for i in range(0, len(self.contents)): + if self.contents[i] != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this tag is not identical to the other tag, + as defined in __eq__.""" + return not self == other + + def __repr__(self): + """Renders this tag as a string.""" + return str(self) + + def __unicode__(self): + return self.__str__(1) + + def __str__(self, needUnicode=None, showStructureIndent=None): + """Returns a string or Unicode representation of this tag and + its contents. + + NOTE: since Python's HTML parser consumes whitespace, this + method is not certain to reproduce the whitespace present in + the original string.""" + + attrs = [] + if self.attrs: + for key, val in self.attrs: + attrs.append('%s="%s"' % (key, val)) + close = '' + closeTag = '' + if self.isSelfClosing(): + close = ' /' + else: + closeTag = '' % self.name + indentIncrement = None + if showStructureIndent != None: + indentIncrement = showStructureIndent + if not self.hidden: + indentIncrement += 1 + contents = self.renderContents(indentIncrement, needUnicode=needUnicode) + if showStructureIndent: + space = '\n%s' % (' ' * showStructureIndent) + if self.hidden: + s = contents + else: + s = [] + attributeString = '' + if attrs: + attributeString = ' ' + ' '.join(attrs) + if showStructureIndent: + s.append(space) + s.append('<%s%s%s>' % (self.name, attributeString, close)) + s.append(contents) + if closeTag and showStructureIndent != None: + s.append(space) + s.append(closeTag) + s = ''.join(s) + isUnicode = type(s) == types.UnicodeType + if needUnicode and not isUnicode: + s = unicode(s) + elif isUnicode and needUnicode==False: + s = str(s) + return s + + def prettify(self, needUnicode=None): + return self.__str__(needUnicode, showStructureIndent=True) + + def renderContents(self, showStructureIndent=None, needUnicode=None): + """Renders the contents of this tag as a (possibly Unicode) + string.""" + s=[] + for c in self: + text = None + if isinstance(c, NavigableUnicodeString) or type(c) == types.UnicodeType: + text = unicode(c) + elif isinstance(c, Tag): + s.append(c.__str__(needUnicode, showStructureIndent)) + elif needUnicode: + text = unicode(c) + else: + text = str(c) + if text: + if showStructureIndent != None: + if text[-1] == '\n': + text = text[:-1] + s.append(text) + return ''.join(s) + + #Soup methods + + def firstText(self, text, recursive=True): + """Convenience method to retrieve the first piece of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.first(recursive=recursive, text=text) + + def fetchText(self, text, recursive=True, limit=None): + """Convenience method to retrieve all pieces of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.fetch(recursive=recursive, text=text, limit=limit) + + def first(self, name=None, attrs={}, recursive=True, text=None): + """Return only the first child of this + Tag matching the given criteria.""" + r = Null + l = self.fetch(name, attrs, recursive, text, 1) + if l: + r = l[0] + return r + findChild = first + + def fetch(self, name=None, attrs={}, recursive=True, text=None, + limit=None): + """Extracts a list of Tag objects that match the given + criteria. You can specify the name of the Tag and any + attributes you want the Tag to have. + + The value of a key-value pair in the 'attrs' map can be a + string, a list of strings, a regular expression object, or a + callable that takes a string and returns whether or not the + string matches for some custom definition of 'matches'. The + same is true of the tag name.""" + generator = self.recursiveChildGenerator + if not recursive: + generator = self.childGenerator + return self._fetch(name, attrs, text, limit, generator) + fetchChildren = fetch + + #Utility methods + + def isSelfClosing(self): + """Returns true iff this is a self-closing tag as defined in the HTML + standard. + + TODO: This is specific to BeautifulSoup and its subclasses, but it's + used by __str__""" + return self.name in BeautifulSoup.SELF_CLOSING_TAGS + + def append(self, tag): + """Appends the given tag to the contents of this tag.""" + self.contents.append(tag) + + #Private methods + + def _getAttrMap(self): + """Initializes a map representation of this tag's attributes, + if not already initialized.""" + if not getattr(self, 'attrMap'): + self.attrMap = {} + for (key, value) in self.attrs: + self.attrMap[key] = value + return self.attrMap + + #Generator methods + def childGenerator(self): + for i in range(0, len(self.contents)): + yield self.contents[i] + raise StopIteration + + def recursiveChildGenerator(self): + stack = [(self, 0)] + while stack: + tag, start = stack.pop() + if isinstance(tag, Tag): + for i in range(start, len(tag.contents)): + a = tag.contents[i] + yield a + if isinstance(a, Tag) and tag.contents: + if i < len(tag.contents) - 1: + stack.append((tag, i+1)) + stack.append((a, 0)) + break + raise StopIteration + + +def isList(l): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is listlike.""" + return hasattr(l, '__iter__') \ + or (type(l) in (types.ListType, types.TupleType)) + +def buildTagMap(default, *args): + """Turns a list of maps, lists, or scalars into a single map. + Used to build the SELF_CLOSING_TAGS and NESTABLE_TAGS maps out + of lists and partial maps.""" + built = {} + for portion in args: + if hasattr(portion, 'items'): + #It's a map. Merge it. + for k,v in portion.items(): + built[k] = v + elif isList(portion): + #It's a list. Map each item to the default. + for k in portion: + built[k] = default + else: + #It's a scalar. Map it to the default. + built[portion] = default + return built + +class BeautifulStoneSoup(Tag, SGMLParser): + + """This class contains the basic parser and fetch code. It defines + a parser that knows nothing about tag behavior except for the + following: + + You can't close a tag without closing all the tags it encloses. + That is, "" actually means + "". + + [Another possible explanation is "", but since + this class defines no SELF_CLOSING_TAGS, it will never use that + explanation.] + + This class is useful for parsing XML or made-up markup languages, + or when BeautifulSoup makes an assumption counter to what you were + expecting.""" + + SELF_CLOSING_TAGS = {} + NESTABLE_TAGS = {} + RESET_NESTING_TAGS = {} + QUOTE_TAGS = {} + + #As a public service we will by default silently replace MS smart quotes + #and similar characters with their HTML or ASCII equivalents. + MS_CHARS = { '\x80' : '€', + '\x81' : ' ', + '\x82' : '‚', + '\x83' : 'ƒ', + '\x84' : '„', + '\x85' : '…', + '\x86' : '†', + '\x87' : '‡', + '\x88' : '⁁', + '\x89' : '%', + '\x8A' : 'Š', + '\x8B' : '<', + '\x8C' : 'Œ', + '\x8D' : '?', + '\x8E' : 'Z', + '\x8F' : '?', + '\x90' : '?', + '\x91' : '‘', + '\x92' : '’', + '\x93' : '“', + '\x94' : '”', + '\x95' : '•', + '\x96' : '–', + '\x97' : '—', + '\x98' : '˜', + '\x99' : '™', + '\x9a' : 'š', + '\x9b' : '>', + '\x9c' : 'œ', + '\x9d' : '?', + '\x9e' : 'z', + '\x9f' : 'Ÿ',} + + PARSER_MASSAGE = [(re.compile('(<[^<>]*)/>'), + lambda(x):x.group(1) + ' />'), + (re.compile(']*)>'), + lambda(x):''), + (re.compile("([\x80-\x9f])"), + lambda(x): BeautifulStoneSoup.MS_CHARS.get(x.group(1))) + ] + + ROOT_TAG_NAME = '[document]' + + def __init__(self, text=None, avoidParserProblems=True, + initialTextIsEverything=True): + """Initialize this as the 'root tag' and feed in any text to + the parser. + + NOTE about avoidParserProblems: sgmllib will process most bad + HTML, and BeautifulSoup has tricks for dealing with some HTML + that kills sgmllib, but Beautiful Soup can nonetheless choke + or lose data if your data uses self-closing tags or + declarations incorrectly. By default, Beautiful Soup sanitizes + its input to avoid the vast majority of these problems. The + problems are relatively rare, even in bad HTML, so feel free + to pass in False to avoidParserProblems if they don't apply to + you, and you'll get better performance. The only reason I have + this turned on by default is so I don't get so many tech + support questions. + + The two most common instances of invalid HTML that will choke + sgmllib are fixed by the default parser massage techniques: + +
    (No space between name of closing tag and tag close) + (Extraneous whitespace in declaration) + + You can pass in a custom list of (RE object, replace method) + tuples to get Beautiful Soup to scrub your input the way you + want.""" + Tag.__init__(self, self.ROOT_TAG_NAME) + if avoidParserProblems \ + and not isList(avoidParserProblems): + avoidParserProblems = self.PARSER_MASSAGE + self.avoidParserProblems = avoidParserProblems + SGMLParser.__init__(self) + self.quoteStack = [] + self.hidden = 1 + self.reset() + if hasattr(text, 'read'): + #It's a file-type object. + text = text.read() + if text: + self.feed(text) + if initialTextIsEverything: + self.done() + + def __getattr__(self, methodName): + """This method routes method call requests to either the SGMLParser + superclass or the Tag superclass, depending on the method name.""" + if methodName.find('start_') == 0 or methodName.find('end_') == 0 \ + or methodName.find('do_') == 0: + return SGMLParser.__getattr__(self, methodName) + elif methodName.find('__') != 0: + return Tag.__getattr__(self, methodName) + else: + raise AttributeError + + def feed(self, text): + if self.avoidParserProblems: + for fix, m in self.avoidParserProblems: + text = fix.sub(m, text) + SGMLParser.feed(self, text) + + def done(self): + """Called when you're done parsing, so that the unclosed tags can be + correctly processed.""" + self.endData() #NEW + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def reset(self): + SGMLParser.reset(self) + self.currentData = [] + self.currentTag = None + self.tagStack = [] + self.pushTag(self) + + def popTag(self): + tag = self.tagStack.pop() + # Tags with just one string-owning child get the child as a + # 'string' property, so that soup.tag.string is shorthand for + # soup.tag.contents[0] + if len(self.currentTag.contents) == 1 and \ + isinstance(self.currentTag.contents[0], NavigableText): + self.currentTag.string = self.currentTag.contents[0] + + #print "Pop", tag.name + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + #print "Push", tag.name + if self.currentTag: + self.currentTag.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + + def endData(self): + currentData = ''.join(self.currentData) + if currentData: + if not currentData.strip(): + if '\n' in currentData: + currentData = '\n' + else: + currentData = ' ' + c = NavigableString + if type(currentData) == types.UnicodeType: + c = NavigableUnicodeString + o = c(currentData) + o.setup(self.currentTag, self.previous) + if self.previous: + self.previous.next = o + self.previous = o + self.currentTag.contents.append(o) + self.currentData = [] + + def _popToTag(self, name, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. If inclusivePop is false, pops the tag + stack up to but *not* including the most recent instqance of + the given tag.""" + if name == self.ROOT_TAG_NAME: + return + + numPops = 0 + mostRecentTag = None + for i in range(len(self.tagStack)-1, 0, -1): + if name == self.tagStack[i].name: + numPops = len(self.tagStack)-i + break + if not inclusivePop: + numPops = numPops - 1 + + for i in range(0, numPops): + mostRecentTag = self.popTag() + return mostRecentTag + + def _smartPop(self, name): + + """We need to pop up to the previous tag of this type, unless + one of this tag's nesting reset triggers comes between this + tag and the previous tag of this type, OR unless this tag is a + generic nesting trigger and another generic nesting trigger + comes between this tag and the previous tag of this type. + + Examples: +

    FooBar

    should pop to 'p', not 'b'. +

    FooBar

    should pop to 'table', not 'p'. +

    Foo

    Bar

    should pop to 'tr', not 'p'. +

    FooBar

    should pop to 'p', not 'b'. + +

    • *
    • * should pop to 'ul', not the first 'li'. +
  • ** should pop to 'table', not the first 'tr' + tag should + implicitly close the previous tag within the same
    ** should pop to 'tr', not the first 'td' + """ + + nestingResetTriggers = self.NESTABLE_TAGS.get(name) + isNestable = nestingResetTriggers != None + isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + popTo = None + inclusive = True + for i in range(len(self.tagStack)-1, 0, -1): + p = self.tagStack[i] + if (not p or p.name == name) and not isNestable: + #Non-nestable tags get popped to the top or to their + #last occurance. + popTo = name + break + if (nestingResetTriggers != None + and p.name in nestingResetTriggers) \ + or (nestingResetTriggers == None and isResetNesting + and self.RESET_NESTING_TAGS.has_key(p.name)): + + #If we encounter one of the nesting reset triggers + #peculiar to this tag, or we encounter another tag + #that causes nesting to reset, pop up to but not + #including that tag. + + popTo = p.name + inclusive = False + break + p = p.parent + if popTo: + self._popToTag(popTo, inclusive) + + def unknown_starttag(self, name, attrs, selfClosing=0): + #print "Start tag %s" % name + if self.quoteStack: + #This is not a real tag. + #print "<%s> is not real!" % name + attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs)) + self.handle_data('<%s%s>' % (name, attrs)) + return + self.endData() + if not name in self.SELF_CLOSING_TAGS and not selfClosing: + self._smartPop(name) + tag = Tag(name, attrs, self.currentTag, self.previous) + if self.previous: + self.previous.next = tag + self.previous = tag + self.pushTag(tag) + if selfClosing or name in self.SELF_CLOSING_TAGS: + self.popTag() + if name in self.QUOTE_TAGS: + #print "Beginning quote (%s)" % name + self.quoteStack.append(name) + self.literal = 1 + + def unknown_endtag(self, name): + if self.quoteStack and self.quoteStack[-1] != name: + #This is not a real end tag. + #print " is not real!" % name + self.handle_data('' % name) + return + self.endData() + self._popToTag(name) + if self.quoteStack and self.quoteStack[-1] == name: + self.quoteStack.pop() + self.literal = (len(self.quoteStack) > 0) + + def handle_data(self, data): + self.currentData.append(data) + + def handle_pi(self, text): + "Propagate processing instructions right through." + self.handle_data("" % text) + + def handle_comment(self, text): + "Propagate comments right through." + self.handle_data("" % text) + + def handle_charref(self, ref): + "Propagate char refs right through." + self.handle_data('&#%s;' % ref) + + def handle_entityref(self, ref): + "Propagate entity refs right through." + self.handle_data('&%s;' % ref) + + def handle_decl(self, data): + "Propagate DOCTYPEs and the like right through." + self.handle_data('' % data) + + def parse_declaration(self, i): + """Treat a bogus SGML declaration as raw data. Treat a CDATA + declaration as regular data.""" + j = None + if self.rawdata[i:i+9] == '', i) + if k == -1: + k = len(self.rawdata) + self.handle_data(self.rawdata[i+9:k]) + j = k+3 + else: + try: + j = SGMLParser.parse_declaration(self, i) + except SGMLParseError: + toHandle = self.rawdata[i:] + self.handle_data(toHandle) + j = i + len(toHandle) + return j + +class BeautifulSoup(BeautifulStoneSoup): + + """This parser knows the following facts about HTML: + + * Some tags have no closing tag and should be interpreted as being + closed as soon as they are encountered. + + * The text inside some tags (ie. 'script') may contain tags which + are not really part of the document and which should be parsed + as text, not tags. If you want to parse the text as tags, you can + always fetch it and parse it explicitly. + + * Tag nesting rules: + + Most tags can't be nested at all. For instance, the occurance of + a

    tag should implicitly close the previous

    tag. + +

    Para1

    Para2 + should be transformed into: +

    Para1

    Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a

    tag should _not_ implicitly close the previous +
    tag. + + Alice said:
    Bob said:
    Blah + should NOT be transformed into: + Alice said:
    Bob said:
    Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a
    , + but not close a tag in another table. + +
    BlahBlah + should be transformed into: +
    BlahBlah + but, + Blah
    Blah + should NOT be transformed into + Blah
    Blah + + Differing assumptions about tag nesting rules are a major source + of problems with the BeautifulSoup class. If BeautifulSoup is not + treating as nestable a tag your page author treats as nestable, + try ICantBelieveItsBeautifulSoup before writing your own + subclass.""" + + SELF_CLOSING_TAGS = buildTagMap(None, ['br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base']) + + QUOTE_TAGS = {'script': None} + + #According to the HTML standard, each of these inline tags can + #contain another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', + 'center'] + + #According to the HTML standard, these block tags can contain + #another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del'] + + #Lists can contain other lists, but there are restrictions. + NESTABLE_LIST_TAGS = { 'ol' : [], + 'ul' : [], + 'li' : ['ul', 'ol'], + 'dl' : [], + 'dd' : ['dl'], + 'dt' : ['dl'] } + + #Tables can contain other tables, but there are restrictions. + NESTABLE_TABLE_TAGS = {'table' : [], + 'tr' : ['table', 'tbody', 'tfoot', 'thead'], + 'td' : ['tr'], + 'th' : ['tr'], + } + + NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] + + #If one of these tags is encountered, all tags up to the next tag of + #this type are popped. + RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', + NON_NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, + NESTABLE_TABLE_TAGS) + + NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) + +class ICantBelieveItsBeautifulSoup(BeautifulSoup): + + """The BeautifulSoup class is oriented towards skipping over + common HTML errors like unclosed tags. However, sometimes it makes + errors of its own. For instance, consider this fragment: + + FooBar + + This is perfectly valid (if bizarre) HTML. However, the + BeautifulSoup class will implicitly close the first b tag when it + encounters the second 'b'. It will think the author wrote + "FooBar", and didn't close the first 'b' tag, because + there's no real-world reason to bold something that's already + bold. When it encounters '' it will close two more 'b' + tags, for a grand total of three tags closed instead of two. This + can throw off the rest of your document structure. The same is + true of a number of other tags, listed below. + + It's much more common for someone to forget to close (eg.) a 'b' + tag than to actually use nested 'b' tags, and the BeautifulSoup + class handles the common case. This class handles the + not-co-common case: where you can't believe someone wrote what + they did, but it's valid HTML and BeautifulSoup screwed up by + assuming it wouldn't be. + + If this doesn't do what you need, try subclassing this class or + BeautifulSoup, and providing your own list of NESTABLE_TAGS.""" + + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ + ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', + 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', + 'big'] + + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript'] + + NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) + +class BeautifulSOAP(BeautifulStoneSoup): + """This class will push a tag with only a single string child into + the tag's parent as an attribute. The attribute's name is the tag + name, and the value is the string child. An example should give + the flavor of the change: + + baz + => + baz + + You can then access fooTag['bar'] instead of fooTag.barTag.string. + + This is, of course, useful for scraping structures that tend to + use subelements instead of attributes, such as SOAP messages. Note + that it modifies its input, so don't print the modified version + out. + + I'm not sure how many people really want to use this class; let me + know if you do. Mainly I like the name.""" + + def popTag(self): + if len(self.tagStack) > 1: + tag = self.tagStack[-1] + parent = self.tagStack[-2] + parent._getAttrMap() + if (isinstance(tag, Tag) and len(tag.contents) == 1 and + isinstance(tag.contents[0], NavigableText) and + not parent.attrMap.has_key(tag.name)): + parent[tag.name] = tag.contents[0] + BeautifulStoneSoup.popTag(self) + +#Enterprise class names! It has come to our attention that some people +#think the names of the Beautiful Soup parser classes are too silly +#and "unprofessional" for use in enterprise screen-scraping. We feel +#your pain! For such-minded folk, the Beautiful Soup Consortium And +#All-Night Kosher Bakery recommends renaming this file to +#"RobustParser.py" (or, in cases of extreme enterprisitude, +#"RobustParserBeanInterface.class") and using the following +#enterprise-friendly class aliases: +class RobustXMLParser(BeautifulStoneSoup): + pass +class RobustHTMLParser(BeautifulSoup): + pass +class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup): + pass +class SimplifyingSOAPParser(BeautifulSOAP): + pass + +### + + +#By default, act as an HTML pretty-printer. +if __name__ == '__main__': + import sys + soup = BeautifulStoneSoup(sys.stdin.read()) + print soup.prettify() Added: kukit/azax/branch/snowsprint/DEPENDENCIES.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/DEPENDENCIES.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +$Id$ + +- Zope-2.7.3 or greater +- Zope X3.0.0 or greater +- Five 0.3 or greater +- lxml 0.7 or greater + Added: kukit/azax/branch/snowsprint/EXTERNALS.TXT ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/EXTERNALS.TXT Sun May 21 15:02:20 2006 @@ -0,0 +1,8 @@ +# +# results of svn propget svn:externals +# https://svn.z3lab.org/z3lab/azax/trunk +# +# created by: svn propset svn:externals -F EXTERNALS.TXT . +# +kukit http://codespeak.net/svn/kukit/kukit.js/branch/snowsprint + Added: kukit/azax/branch/snowsprint/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,62 @@ +$Id$ + +INSTALLATION NOTES +================== + +1 - Prerequisites +2 - lxml installation +3 - Zope X3 installation +4 - Products setup +5 - Zope 3 <-> Zope 2 path setup +6 - How to use azax + +---------- + +1- Prerequisites + +To install azax you will need: + +- a working Zope 2.7.x + CPS 3.3.4 or greater +- ZopeX3-3.0.0 : http://www.zope.org/Products/ZopeX3 +- Five 1.0.1 or greater : http://codespeak.net/z3/five/ +- lxml 0.7 : http://codespeak.net/lxml + +2 - lxml installation + +Install the python package lxml on your python installation, ie the one +Zope 2.7 actually use with the usual command:: + + $ python setup.py install + +3 - Zope X3 installation + +(not needed if you have Zope 2.8 or later as it includes Zope X3) + +Please install ZopeX3-3.0.0 on your system (usually in /opt/ZopeX3-3.0.0) with +the python version you use for Zope2.7 and CPS, e.g.:: + + $ ./configure --prefix /opt/ZopeX3-3.0.0 --with-python /usr/bin/python + $ make + $ make install + $ make check + +4 - Products setup + +Put Five, azax in your $INSTANCE/Products + +(not needed if you have Zope 2.8 or later as it includes Five) + +5 - Zope 3 <-> Zope 2 path setup + +(not needed if you have Zope 2.8 or later as it Zope X3 is correctly setup) + +Edit your $INSTANCE/etc/zope.conf to add the following path declarations:: + + path /opt/ZopeX3-3.0.0/lib/python + path $INSTANCE/Products/azax + +6 - How to use azax + +Copy the product azaxdemo that's inside demos in your instance, +and look at its own README.txt + Added: kukit/azax/branch/snowsprint/TODO.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/TODO.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,14 @@ +* rename AzaxResponse to KukitResponse +* look to TextIndexNG3 howto have it compatible with Z3, Z2 and Plone +* rename the commands to better match the DOM API +* try to fix the namespace problems - see https://svn.z3lab.org/z3lab/azax/branches/gotcha-namespaces + + + +

    2005-11-16 21:24:05.484000

    +
    +
    + +* add more demos, we need radio buttons, addAfter and much more. There are bugs + in IE which we have to test and document. Added: kukit/azax/branch/snowsprint/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +import mimetypes + +mimetypes.types_map['.kkt'] = 'text/xml' # XXX legacy! +mimetypes.types_map['.kukit'] = 'text/xml' + +from azaxview import AzaxBaseView +from unicode_quirks import force_unicode, AzaxUnicodeError Added: kukit/azax/branch/snowsprint/azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,269 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005-2006 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# Bal?zs Re? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from zope.interface import implements +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile +from Products.Five import BrowserView +from unicode_quirks import force_unicode + +import htmlentitydefs +import re +_entity_regexp = re.compile(r"&([a-zA-Z]+);") + +# -- +# Parser implementations +# +# These assure that output is valid XML or HTML and fix the code or +# raise an exception. +# +# The wrapping makes it possible to change the parser transparently +# if necessary. +# -- + +class XmlParser(object): + '''Custom XML parser + + wraps the parser implementation + ''' + + from BeautifulSoup import BeautifulStoneSoup + + def __init__(self, value): + value = force_unicode(value) + self.soup = self.BeautifulStoneSoup(value) + + def __call__(self): + return unicode(self.soup) + +class HtmlParser(object): + '''Custom HTML parser + + wraps the parser implementation + ''' + + from BeautifulSoup import BeautifulSoup + + def __init__(self, value): + value = force_unicode(value) + self.soup = self.BeautifulSoup(value) + for tag in self.soup.fetch(recursive=False): + tag['xmlns'] = "http://www.w3.org/1999/xhtml" + + def __call__(self): + return unicode(self.soup) + +# -- +# Marshal objects +# +# These build up the response and get marshalled to the client +# in the defined format +# -- + +class AzaxParam: + + def __init__(self, name, content=''): + self.name = name + # Content must be str with ascii encoding, or unicode! + self.content = force_unicode(content) + + def getName(self): + return self.name + + def getContent(self): + return self.content + +class AzaxCommand: + + def __init__(self, name, selector): + if isinstance(selector, basestring): + self.selector = selector + self.selectorType = '' + else: + self.selector = selector.value + self.selectorType = selector.type + self.name = name + self.params = [] + + def addParam(self, name, content=''): + param = AzaxParam(name, content) + self.params.append(param) + return param + + def getName(self): + return self.name + + def getSelector(self): + return self.selector + + def getSelectorType(self): + return self.selectorType + + def getParams(self): + return self.params + +XPATH_SELECTOR = 'xpath' +CSS_SELECTOR = 'css' +HTMLID_SELECTOR = 'htmlid' + +class XpathSelector: + + type = XPATH_SELECTOR + + def __init__(self, selector): + self.value = selector + +class CssSelector: + + type = CSS_SELECTOR + + def __init__(self, selector): + self.value = selector + +class HtmlIdSelector: + + type = HTMLID_SELECTOR + + def __init__(self, selector): + self.value = selector +# -- +# Base view class +# +# All the implementations should create a specialization +# of this class when building their browser views +# -- + +class AzaxBaseView(BrowserView): + """ Base azax view + + this allows setting up the content of the response, and then + generate it out. + """ + + # XML output gets rendered via a page template + _render = ViewPageTemplateFile('browser/kukitresponse.pt', content_type='text/xml;charset=utf-8') + + # replace named entities to fix a bug in IE + def render(self): + name2codepoint = htmlentitydefs.name2codepoint + def entity_replacer(m): + value = name2codepoint.get(m.group(1)) + if value is None: + return m.group(0) + return "&#%i;" % value + + result = self._render() + result = _entity_regexp.sub(entity_replacer, result) + return result + + def __init__(self, context, request): + BrowserView.__init__(self, context, request) + self.commands = [] + + def addCommand(self, name, selector): + command = AzaxCommand(name, selector) + self.commands.append(command) + return command + + def getCommands(self): + return self.commands + + + def getXpathSelector(self, selector): + return XpathSelector(selector) + + def getCssSelector(self, selector): + return CssSelector(selector) + + def getHtmlIdSelector(self, selector): + return HtmlIdSelector(selector) + + # -- + # commands + # -- + + def setHtmlAsChild(self, selector, new_value): + """ see interfaces.py """ + new_value = HtmlParser(new_value)() + command = self.addCommand('setHtmlAsChild', selector) + data = command.addParam('html', new_value) + + def setAttribute(self, selector, name, value): + """ see interfaces.py """ + command = self.addCommand('setAttribute', selector) + data = command.addParam('name', name) + data = command.addParam('value', value) + + def addAfter(self, selector, new_value): + """ see interfaces.py """ + new_value = HtmlParser(new_value)() + command = self.addCommand('addAfter', selector) + data = command.addParam('html', new_value) + + def clearChildren(self, selector): + """ see interfaces.py """ + command = self.addCommand('clearChildren', selector) + data = command.addParam('none') + + def removeNode(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNode', selector) + data = command.addParam('none') + + def removeNode(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNode', selector) + data = command.addParam('none') + + def removeNextSibling(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNextSibling', selector) + data = command.addParam('none') + + def removePreviousSibling(self, selector): + """ see interfaces.py """ + command = self.addCommand('removePreviousSibling', selector) + data = command.addParam('none') + + def copyChildrenFrom(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenFrom', selector) + data = command.addParam('html_id', id) + + def moveNodeAfter(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('moveNodeAfter', selector) + data = command.addParam('html_id', id) + + def copyChildrenTo(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenTo', selector) + data = command.addParam('html_id', id) + + def moveChildrenTo(self, selector, id): + """ see interfaces.py """ + self.copyChildrenTo(selector, id) + self.clearChildren(selector) + + def executeCode(self, selector, code): + code = XmlParser(code)() + command = self.addCommand('executeCode', selector) + data = command.addParam('code', code) Added: kukit/azax/branch/snowsprint/browser/kukitresponse.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/browser/kukitresponse.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,19 @@ + + + + +

    it worked

    +
    +
    + Added: kukit/azax/branch/snowsprint/config.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/config.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/snowsprint/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +$Id$ + +INSTALLATION NOTES +================== + +1/ Drop azaxdemo into your product dir +2/ In the zmi, add a "Azax Simple content" anywhere + let's call it "test" +3/ Get into "test" page, you should have a menu with available demos + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ +import simplecontent + +def initialize(context): + + context.registerClass( + simplecontent.SimpleContent, + constructors = (simplecontent.manage_addSimpleContentForm, + simplecontent.manage_addSimpleContent), + ) Added: kukit/azax/branch/snowsprint/demos/azaxdemo/azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,103 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from Products.azax import AzaxBaseView +from datetime import datetime + +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile + +class AzaxView(AzaxBaseView): + + #header_macros = ViewPageTemplateFile('browser/header_macros.pt') + + def clearDivContent(self): + """ clear div content """ + self.clearChildren('div#demo') + return self.render() + + def copyFromDivContent(self): + """ copy div content """ + self.copyChildrenFrom('div#copy', 'demo') + return self.render() + + def copyToDivContent(self): + """ copy div content """ + self.copyChildrenTo('div#copy', 'demo') + return self.render() + + def moveToDivContent(self): + """ copy div content """ + self.moveChildrenTo('div#copy', 'demo') + return self.render() + + def getDivContent(self): + """ returns div content """ + self.setHtmlAsChild('div#demo', '

    it worked

    ') + self.setHtmlAsChild('div#demo', '

    it worked again

    ') + return self.render() + + def getCorrespondingSelect(self, value): + """ returns select content """ + mapping = {} + mapping['']=[] + mapping['animals']=['dog', 'cat', 'cow'] + mapping['machines']=['computer', 'car', 'airplane'] + result = ['' % item for item in mapping[value]] + self.setHtmlAsChild('select#second', ' '.join(result)) + return self.render() + + def getAutoupdateMarkup(self): + """ returns the current time """ + self.setHtmlAsChild('div#update-wrapper', '
    ') + return self.render() + + def getCurrentTime(self): + """ returns the current time """ + self.setHtmlAsChild('div#update-area', "

    %s

    " % str(datetime.now())) + return self.render() + + def getInputField(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', + '
    ' \ + '' + ) + return self.render() + + def saveText(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', value+'') + return self.render() + + #we'll need parameter passing first do this in a sane way + def getSubTree(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', 'works') + return self.render() + + def cancelSubmitSave(self, text_save): + self.setHtmlAsChild('div#async', 'Async saved %s' % text_save) + return self.render() + + def removeNodeXpath(self): + sel = self.getXpathSelector("//P[@id='xpath']/following-sibling::*[position()=1]") + self.removeNode(sel) + return self.render() Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.kukit ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,18 @@ + + + + copyFromDivContent + + + copyToDivContent + + + moveToDivContent + + + clearDivContent + + + getDivContent + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,117 @@ + + + + + + + +

    All demos

    +

    Change tag content

    +

    Top div

    +
    + Azax +
    + +

    Bottom div

    +
    + copy here +
    +

    Javascript Styling

    +

    This page defines a link in the header which rel attribute points + to a .kukit server file : + + + see the kukit file here.

    +

    + This .kukit file declares CSS selectors associated with events and server URLs called when the event occur. +

    +

    A javascript engine processes the XML content : it inserts javascript + events in the DOM.

    +

    + For instance, the click event of button with the id change will call asynchronously the getDivContent URL. + +

    +

    Server Asynchronous Call

    +

    + The events associated with Javascript just call the server asynchronously. +

    +

    + The response is a XML file. + The XML contains CSS selectors associated with commands. +

    +

    + The Javascript engine selects the DOM nodes to which it applies each command. +

    +

    + For instance, let's look at the moveToDivContent response. +

    +
    +<selectors>
    +  <selector>
    +    <value>div#copy</value>
    +    <commands>
    +      <command>
    +	<name>copyChildrenTo</name>
    +	<data name="html_id">demo</data>
    +      </command>
    +      <command>
    +	<name>clearChildren</name>
    +	<data name="none"/>
    +      </command>
    +    </commands>
    +  </selector>
    +</selectors>
    +   
    +

    + The XML specifies two commands to apply to the div#copy node : +

    +

    + copyChildrenTo copies the children of the DOM node to the node with id demo. +

    +

    + clearChildren removes the children of the DOM node. +

    + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo_index.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_demo_index.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,15 @@ + + + +

    Azax demos

    + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.kukit ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ + + + + getInputField + + + saveText + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_instant_edit.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,51 @@ + + + + + + +

    All demos

    + +

    Instant edit Demo

    +
    +
    +
    + click me! + +
    + +
    + + +

    ab

    + + + +

    Remove Node with XPath

    +

    XPath

    +

    next 1

    +

    next 2

    +

    next 3

    + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.kukit ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ + + + + getCurrentTime 2000 + + + getAutoupdateMarkup + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_three_autoupdate.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,35 @@ + + + + + + + + +

    All demos

    +

    Three autoupdate

    + +

    Demo

    +
    +
    + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.kukit ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + getSubTree + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_tree.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,39 @@ + + + + + + + +

    All demos

    + + +

    Instant edit Demo

    +
    +
      +
    • 1
    • +
    • +
        +
      • 2.1
      • +
      • 2.2
      • +
      +
    • +
        +
      • 3.1
      • +
      • 3.2
      • +
      +
    + + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.kukit ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + getCorrespondingSelect + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/azax_two_select.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,20 @@ + + + + + + +

    All demos

    +

    Two Selects

    + +
    + + + + + + +
    +

    Kukit response

    + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/browser/header_macros.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/browser/header_macros.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,41 @@ + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/helpers.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/helpers.py Sun May 21 15:02:20 2006 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2004, 2005 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Test helpers + +$Id: helpers.py 12915 2005-05-31 10:23:19Z philikon $ +""" +import urllib + +def add_and_edit(self, id, REQUEST): + """Helper function to point to the object's management screen if + 'Add and Edit' button is pressed. + id -- id of the object we just added + """ + if REQUEST is None: + return + try: + u = self.DestinationURL() + except: + u = REQUEST['URL1'] + if REQUEST.has_key('submit_edit'): + u = "%s/%s" % (u, urllib.quote(id)) + REQUEST.RESPONSE.redirect(u+'/manage_main') + + +from OFS.Folder import Folder + +class NoVerifyPasteFolder(Folder): + """Folder that does not perform paste verification. + Used by test_events + """ + def _verifyObjectPaste(self, object, validate_src=1): + pass + +def manage_addNoVerifyPasteFolder(container, id, title=''): + container._setObject(id, NoVerifyPasteFolder()) + folder = container[id] + folder.id = id + folder.title = title + +class FiveTraversableFolder(Folder): + """Folder that is declared Five traversable, see configure.zcml + """ + pass + +def manage_addFiveTraversableFolder(container, id, title=''): + container._setObject(id, FiveTraversableFolder()) + folder = container[id] + folder.id = id + folder.title = title + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,4 @@ +from zope.interface import Interface + +class ISimpleContent(Interface): + pass \ No newline at end of file Added: kukit/azax/branch/snowsprint/demos/azaxdemo/simplecontent.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/simplecontent.py Sun May 21 15:02:20 2006 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2004, 2005 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Simple content implementationm + +$Id: simplecontent.py 12915 2005-05-31 10:23:19Z philikon $ +""" +from OFS.SimpleItem import SimpleItem +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo +from helpers import add_and_edit +from Products.PageTemplates.PageTemplateFile import PageTemplateFile +from zope.interface import implements +from interfaces import ISimpleContent + +class SimpleContent(SimpleItem): + implements(ISimpleContent) + + meta_type = 'AzaxDemo SimpleContent' + security = ClassSecurityInfo() + + def __init__(self, id, title): + self.id = id + self.title = title + + security.declarePublic('direct') + def direct(self): + """Should be able to traverse directly to this as there is no view. + """ + return "Direct traversal worked" + +InitializeClass(SimpleContent) + +manage_addSimpleContentForm = PageTemplateFile( + "www/simpleContentAdd", globals(), + __name__ = 'manage_addSimpleContentForm') + +def manage_addSimpleContent(self, id, title, REQUEST=None): + """Add the simple content.""" + id = self._setObject(id, SimpleContent(id, title)) + add_and_edit(self, id, REQUEST) + return '' Added: kukit/azax/branch/snowsprint/demos/azaxdemo/tests/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/tests/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/snowsprint/demos/azaxdemo/tests/test_azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/tests/test_azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,55 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +import unittest, os +from zope.testing import doctest +from Testing.ZopeTestCase import ZopeTestCase + +from Products.azaxdemo.azaxview import AzaxView + +class FakeResponse: + _stuff = {} + + def setHeader(self, name, value): + self._stuff[name] = value + +class FakeRequest: + response = FakeResponse() + +class AzaxViewTestCase(ZopeTestCase): + + def test_instanciation(self): + ob = AzaxView(None, None) + self.assertNotEquals(ob, None) + + def test_getDivContent(self): + req = FakeRequest() + + ob = AzaxView(None, req) + + res = ob.getDivContent() + self.assertEquals(res, 'div.clickableinnerHTML<h1>it worked</h1>') + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(AzaxViewTestCase), + doctest.DocTestSuite('Products.azaxdemo.azaxview'), + )) Added: kukit/azax/branch/snowsprint/demos/azaxdemo/www/simpleContentAdd.zpt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/demos/azaxdemo/www/simpleContentAdd.zpt Sun May 21 15:02:20 2006 @@ -0,0 +1,45 @@ +

    Header

    + +

    Form Title

    + +

    +Add Simple Content +

    + +
    +
    + + + + + + + + + + + + +
    +
    + Id +
    +
    + +
    +
    + Title +
    +
    + +
    + +
    + +
    +
    + + +

    Footer

    Added: kukit/azax/branch/snowsprint/dtds/xhtml-lat1.ent ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/dtds/xhtml-lat1.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/dtds/xhtml-special.ent ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/dtds/xhtml-special.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/dtds/xhtml-symbol.ent ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/dtds/xhtml-symbol.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/dtds/xhtml1-transitional.dtd ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/dtds/xhtml1-transitional.dtd Sun May 21 15:02:20 2006 @@ -0,0 +1,1201 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/branch/snowsprint/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,23 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from zope.interface import Interface + Added: kukit/azax/branch/snowsprint/tests/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/branch/snowsprint/tests/js/ecmaunit-license.txt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/js/ecmaunit-license.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,31 @@ +Copyright (c) 2003-2004, EcmaUnit Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of EcmaUnit nor the names of its contributors may + be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Added: kukit/azax/branch/snowsprint/tests/js/ecmaunit.js ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/js/ecmaunit.js Sun May 21 15:02:20 2006 @@ -0,0 +1,461 @@ +/***************************************************************************** + * + * Copyright (c) 2003-2004 EcmaUnit Contributors. All rights reserved. + * + * This software is distributed under the terms of the EcmaUnit + * License. See LICENSE.txt for license text. For a list of EcmaUnit + * Contributors see CREDITS.txt. + * + *****************************************************************************/ + +// $Id: ecmaunit.js 10153 2005-03-29 09:50:05Z guido $ + +/* + Object-oriented prototype-based unit test suite +*/ + +function TestCase() { + /* a single test case */ + this.name = 'TestCase'; + + this.initialize = function(reporter) { + // this array's contents will be displayed when done (if it + // contains anything) + this._exceptions = new Array(); + this._reporter = reporter; + }; + + this.setUp = function() { + /* this will be called on before each test method that is ran */ + }; + + this.tearDown = function() { + /* this will be called after each test method that has been ran */ + }; + + this.assertEquals = function(var1, var2, message) { + /* assert whether 2 vars have the same value */ + if (!message) { + message = ''; + } else { + message = "'" + message + "' "; + } + if (var1 && var1.toSource && var2 && var2.toSource) { + if (var1.toSource() != var2.toSource()) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' != ' + var2); + }; + } else { + if (var1 != var2) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' != ' + var2); + }; + }; + }; + + this.assertNotEquals = function(var1, var2, message) { + /* assert whether 2 vars have different values */ + if (!message) { + message = ''; + } else { + message = "'" + message + "' "; + } + if (var1 && var1.toSource && var2 && var2.toSource) { + if (var1.toSource() == var2.toSource()) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' == ' + var2); + }; + } else { + if (var1 == var2) { + this._throwException('Assertion ' + message + 'failed: ' + + var1 + ' == ' + var2); + }; + }; + }; + + this.debug = function(msg) { + this._reporter.debug(msg); + } + this.assert = function(statement, message) { + /* assert whether a variable resolves to true */ + if (!statement) { + if (!message) message = (statement && statement.toString) ? + statement.toString() : statement; + this._throwException('Assertion \'' + message + '\' failed'); + }; + }; + + this.assertTrue = this.assert; + + this.assertFalse = function(statement, message) { + /* assert whether a variable resolves to false */ + if (statement) { + if (!message) message = statement.toString ? + statement.toString() : statement; + this._throwException('AssertFalse \'' + message + '\' failed'); + }; + }; + + this.assertThrows = function(func, exception, context) { + /* assert whether a certain exception is raised */ + if (!context) { + context = null; + }; + var exception_thrown = false; + // remove the first three args, they're the function's normal args + var args = []; + for (var i=3; i < arguments.length; i++) { + args.push(arguments[i]); + }; + try { + func.apply(context, args); + } catch(e) { + // allow catching undefined exceptions too + if (exception === undefined) { + } else if (exception) { + if (exception.toSource && e.toSource) { + exception = exception.toSource(); + e = e.toSource(); + }; + if (exception.toString && e.toString) { + exception = exception.toString(); + e = e.toString(); + }; + if (e != exception) { + this._throwException('Function threw the wrong ' + + 'exception ' + e.toString() + + ', while expecting ' + exception.toString()); + }; + }; + exception_thrown = true; + }; + if (!exception_thrown) { + if (exception) { + this._throwException("function didn\'t raise exception \'" + + exception.toString() + "'"); + } else { + this._throwException('function didn\'t raise exception'); + }; + }; + }; + + this.runTests = function() { + /* find all methods of which the name starts with 'test' + and call them */ + var ret = this._runHelper(); + this._reporter.summarize(ret[0], ret[1], this._exceptions); + }; + + this._runHelper = function() { + /* this actually runs the tests + return value is an array [total tests ran, total time spent (ms)] + */ + var now = new Date(); + var starttime = now.getTime(); + var numtests = 0; + for (var attr in this) { + if (attr.substr(0, 4) == 'test') { + this.setUp(); + try { + this[attr](); + this._reporter.reportSuccess(this.name, attr); + } catch(e) { + var raw = e; + if (e.name && e.message) { // Microsoft + e = e.name + ': ' + e.message; + } + this._reporter.reportError(this.name, attr, e, raw); + this._exceptions.push(new Array(this.name, attr, e, raw)); + }; + this.tearDown(); + numtests++; + }; + }; + var now = new Date(); + var totaltime = now.getTime() - starttime; + return new Array(numtests, totaltime); + }; + + this._throwException = function(message) { + var lineno = this._getLineNo(); + if (lineno) { + message = 'line ' + lineno + ' - ' + message; + }; + throw(message); + }; + + this._getLineNo = function() { + /* tries to get the line no in Moz */ + var stack = undefined; + try {notdefined()} catch(e) {stack = e.stack}; + if (stack) { + stack = stack.toString().split('\n'); + for (var i=0; i < stack.length; i++) { + var line = stack[i].split('@')[1]; + if (line.indexOf('ecmaunit') == -1) { + // return the first line after we get out of ecmaunit + var chunks = line.split(':'); + var lineno = chunks[chunks.length - 1]; + if (lineno != '0') { + return lineno; + }; + }; + }; + } else { + return false; + }; + }; +}; + +function TestSuite(reporter) { + /* run a suite of tests */ + this._reporter = reporter; + this._tests = new Array(); + this._exceptions = new Array(); + + this.registerTest = function(test) { + /* register a test */ + if (!test) { + throw('TestSuite.registerTest() requires a testcase as argument'); + }; + this._tests.push(test); + }; + + this.runSuite = function() { + /* run the suite */ + var now = new Date(); + var starttime = now.getTime(); + var testsran = 0; + for (var i=0; i < this._tests.length; i++) { + var test = new this._tests[i](); + test.initialize(this._reporter); + testsran += test._runHelper()[0]; + // the TestCase class handles output of dots and Fs, but we + // should take care of the exceptions + if (test._exceptions.length) { + for (var j=0; j < test._exceptions.length; j++) { + // attr, exc in the org array, so here it becomes + // name, attr, exc + var excinfo = test._exceptions[j]; + this._exceptions.push(excinfo); + }; + }; + }; + var now = new Date(); + var totaltime = now.getTime() - starttime; + this._reporter.summarize(testsran, totaltime, this._exceptions); + }; +}; + +function StdoutReporter(verbose) { + this.verbose = verbose; + this.debug = function(text) { + print(text+"\n"); + } + + this.reportSuccess = function(testcase, attr) { + /* report a test success */ + if (this.verbose) { + print(testcase + '.' + attr + '(): OK'); + } else { + print('.'); + }; + }; + + this.reportError = function(testcase, attr, exception, raw) { + /* report a test failure */ + if (this.verbose) { + print(testcase + '.' + attr + '(): FAILED!'); + } else { + print('F'); + }; + }; + + this.summarize = function(numtests, time, exceptions) { + print('\n' + numtests + ' tests ran in ' + time / 1000.0 + + ' seconds\n'); + if (exceptions.length) { + for (var i=0; i < exceptions.length; i++) { + var testcase = exceptions[i][0]; + var attr = exceptions[i][1]; + var exception = exceptions[i][2]; + var raw = exceptions[i][3]; + print(testcase + '.' + attr + ', exception: ' + exception); + if (verbose) { + this._printStackTrace(raw); + }; + }; + print('NOT OK!'); + } else { + print('OK!'); + }; + }; + + this._printStackTrace = function(exc) { + if (!exc.stack) { + print('no stacktrace available'); + return; + }; + var lines = exc.stack.toString().split('\n'); + var toprint = []; + for (var i=0; i < lines.length; i++) { + var line = lines[i]; + if (line.indexOf('ecmaunit.js') > -1) { + // remove useless bit of traceback + break; + }; + if (line.charAt(0) == '(') { + line = 'function' + line; + }; + var chunks = line.split('@'); + toprint.push(chunks); + }; + toprint.reverse(); + for (var i=0; i < toprint.length; i++) { + print(' ' + toprint[i][1]); + print(' ' + toprint[i][0]); + }; + print(); + }; +}; + +function HTMLReporter(outputelement, verbose) { + this.outputelement = outputelement; + this.document = outputelement.ownerDocument; + this.verbose = verbose; //XXX verbose not yet supported + + this.debug = function(text) { + var msg = this.document.createTextNode(text); + var div = this.document.createElement('div'); + div.appendChild(msg); + this.outputelement.appendChild(div); + } + this.reportSuccess = function(testcase, attr) { + /* report a test success */ + // a single dot looks rather small + var dot = this.document.createTextNode('+'); + this.outputelement.appendChild(dot); + }; + + this.reportError = function(testcase, attr, exception, raw) { + /* report a test failure */ + var f = this.document.createTextNode('F'); + this.outputelement.appendChild(f); + if (this.verbose) { + }; + }; + + this.summarize = function(numtests, time, exceptions) { + /* write the result output to the html node */ + var p = this.document.createElement('p'); + var text = this.document.createTextNode(numtests + ' tests ran in ' + + time / 1000.0 + ' seconds'); + p.appendChild(text); + this.outputelement.appendChild(p); + if (exceptions.length) { + for (var i=0; i < exceptions.length; i++) { + var testcase = exceptions[i][0]; + var attr = exceptions[i][1]; + var exception = exceptions[i][2]; + var raw = exceptions[i][3]; + var div = this.document.createElement('div'); + var lines = exception.split('\n'); + var text = this.document.createTextNode( + testcase + '.' + attr + ', exception '); + div.appendChild(text); + // add some formatting for Opera: this browser displays nice + // tracebacks... + for (var j=0; j < lines.length; j++) { + var text = lines[j]; + if (j > 0) { + text = '\xa0\xa0\xa0\xa0' + text; + }; + div.appendChild(this.document.createTextNode(text)); + div.appendChild(this.document.createElement('br')); + }; + div.style.color = 'red'; + this.outputelement.appendChild(div); + if (this.verbose) { + // display stack trace on Moz + this._displayStackTrace(raw); + }; + }; + var div = this.document.createElement('div'); + var text = this.document.createTextNode('NOT OK!'); + div.appendChild(text); + div.style.backgroundColor = 'red'; + div.style.color = 'black'; + div.style.fontWeight = 'bold'; + div.style.textAlign = 'center'; + div.style.marginTop = '1em'; + this.outputelement.appendChild(div); + } else { + var div = this.document.createElement('div'); + var text = this.document.createTextNode('OK!'); + div.appendChild(text); + div.style.backgroundColor = 'lightgreen'; + div.style.color = 'black'; + div.style.fontWeight = 'bold'; + div.style.textAlign = 'center'; + div.style.marginTop = '1em'; + this.outputelement.appendChild(div); + }; + }; + + this._displayStackTrace = function(exc) { + /* + if (arguments.caller) { + // IE + var caller = arguments; + toprint = []; + while (caller) { + var callee = caller.callee.toString(); + callee = callee.replace('\n', '').replace(/\s+/g, ' '); + var funcsig = /(.*?)\s*\{/.exec(callee)[1]; + var args = caller.callee.arguments; + var displayargs = []; + for (var i=0; i < args.length; i++) { + displayargs.push(args[i].toString()); + }; + toprint.push((funcsig + ' - (' + displayargs + ')')); + caller = caller.caller; + }; + toprint.reverse(); + var pre = this.document.createElement('pre'); + for (var i=0; i < toprint.length; i++) { + pre.appendChild(document.createTextNode(toprint[i])); + pre.appendChild(document.createElement('br')); + }; + this.outputelement.appendChild(pre); + }; + */ + if (exc.stack) { + // Moz (sometimes) + var lines = exc.stack.toString().split('\n'); + var toprint = []; // need to reverse this before outputting + for (var i=0; i < lines.length; i++) { + var line = lines[i]; + if (line.indexOf('ecmaunit.js') > -1) { + // remove useless bit of traceback + break; + }; + if (line[0] == '(') { + line = 'function' + line; + }; + line = line.split('@'); + toprint.push(line); + }; + toprint.reverse(); + var pre = this.document.createElement('pre'); + for (var i=0; i < toprint.length; i++) { + pre.appendChild( + this.document.createTextNode( + ' ' + toprint[i][1] + '\n ' + toprint[i][0] + '\n' + ) + ); + }; + pre.appendChild(document.createTextNode('\n')); + this.outputelement.appendChild(pre); + }; + }; +}; Added: kukit/azax/branch/snowsprint/tests/js/runner.html ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/js/runner.html Sun May 21 15:02:20 2006 @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + +

    + Plone ECMAScript Unit Tests +

    + +

    + This page is the entry to the ECMAScript Unit Tests. +

    + + +
    + Suite filter: + Test filter: +
    +
    +
      +
      +
      + + + Added: kukit/azax/branch/snowsprint/tests/js/testStyleParse.js ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/js/testStyleParse.js Sun May 21 15:02:20 2006 @@ -0,0 +1,131 @@ +function RuleParseTestCase() { + this.name = 'RuleParseTestCase'; +} + +RuleParseTestCase.prototype = new TestCase; +Class = RuleParseTestCase.prototype; + +Class.setUp = function() { + this.preprocessor = new kukit.RuleProcessor(); + this.doc = new DOMParser(); +}; + +Class.tearDown = function() { +}; + + +Class.testOneRule = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 1); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'getDivContent'); +} + +Class.testOneWrongSelector = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testOneWrongEvent = function() { + input = 'getDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testOneWrongAction = function() { + input = ''; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 0); +} + +Class.testCorrectRules = function() { + input = 'getDivContentSecond'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 2); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'getDivContent'); + rule = rules[1]; + this.assertEquals(rule.selector, 'button.second'); + this.assertEquals(rule.property_type, 'event'); + this.assertEquals(rule.name, 'click'); + this.assertEquals(rule.action, 'Second'); +} + +Class.xxx_testCorrectRulesPlusOneWrong = function() { + // this should throw an error, diabled till this is implemented + input = '' + + '' + + '' + + 'getDivContent' + + '' + + '' + + 'Second' + + '' + + '' + + 'getDivContent' + + 'getDivContent' + + '' + + ''; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 2); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'getDivContent'); + rule = rules[1]; + this.assertEquals(rule.selector, 'button.second'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'Second'); +} + +testcase_registry.registerTestCase(RuleParseTestCase, 'ruleparse'); + +function CommandsParseTestCase() { + this.name = 'CommandsParseTestCase'; +} + +CommandsParseTestCase.prototype = new TestCase; +Class = CommandsParseTestCase.prototype; + +Class.setUp = function() { + this.preprocessor = new kukit.RuleProcessor(); + this.doc = new DOMParser(); +}; + +Class.tearDown = function() { +}; + + +Class.testOneCommand = function() { + /*input = 'button.buttonclickgetDivContent'; + var dom = this.doc.parseFromString(input, 'text/xml') + this.preprocessor.parseRuleDom(dom); + rules = this.preprocessor.rules; + this.assertEquals(rules.length, 1); + rule = rules[0]; + this.assertEquals(rule.selector, 'button.button'); + this.assertEquals(rule.event, 'click'); + this.assertEquals(rule.action, 'getDivContent');*/ +} + +testcase_registry.registerTestCase(CommandsParseTestCase, 'commandparse'); Added: kukit/azax/branch/snowsprint/tests/js/unittestUtilities.js ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/js/unittestUtilities.js Sun May 21 15:02:20 2006 @@ -0,0 +1,160 @@ + +function TestCaseRegistry() { + this._testcases = new Object(); + + this.registerTestCase = function(testcase, suite_name) { + if (!testcase) { + throw('TestCaseRegistry.registerTestCase() requires a testcase as argument'); + } + testcase = new testcase(); + if (!suite_name) { + suite_name = 'default'; + } + if (!this._testcases[suite_name]) { + this._testcases[suite_name] = new Array(); + } + this._testcases[suite_name].push(testcase); + } + + this.setTestSuiteFilter = function(filter) { + if (filter) { + this.suite_filter = new RegExp(filter, "i"); + } else { + this.suite_filter = null; + } + } + + this.setTestFilter = function(filter) { + if (filter) { + this.test_filter = new RegExp(filter, "i"); + } else { + this.test_filter = null; + } + } + + this.getFilteredTestCases = function() { + var testcases = new Array(); + + var suites = this.getFilteredTestSuitNames(); + for (suite_index in suites) { + var suite = this._testcases[suites[suite_index]]; + for (var test_index in suite) { + var testcase = suite[test_index]; + if (this.test_filter) { + if (!this.test_filter.test(testcase.name)) { + continue; + } + } + testcases.push(testcase); + } + } + + return testcases; + } + + this.getFilteredTestSuitNames = function() { + var names = new Array(); + + for (var suite_name in this._testcases) { + if (this.suite_filter) { + if (!this.suite_filter.test(suite_name)) { + continue; + } + } + names.push(suite_name); + } + + return names; + } + + this.getFilteredTestNames = function() { + var names = new Array(); + + var testcases = this.getFilteredTestCases(); + for (var testcase_index in testcases) { + names.push(testcases[testcase_index].name); + } + + return names; + } +} +testcase_registry = new TestCaseRegistry(); + +function runTestCase(testCase) { + // append TOC entry + var name = testCase.name; + var toc = document.getElementById("testResultsToc"); + var results_box = document.getElementById("testResultsPlaceHolder"); + + // create toc element + var toc_item = document.createElement("li"); + toc_item.appendChild(createLink("../++resource++azaxtestrunner.html#"+name, name+" Results", false)); + toc.appendChild(toc_item); + + // append testcase section + var placeHolder = document.createElement("div"); + placeHolder.className = "placeholder"; + var link = createLink(name, name+" Results", true); + var header = document.createElement("h3"); + header.appendChild(link); + placeHolder.appendChild(header); + results_box.appendChild(placeHolder); + testCase.initialize(new HTMLReporter(placeHolder)); + testCase.runTests(); +}; + +function runTestCases() { + var suite_filter = document.getElementById('suite-filter').value; + var test_filter = document.getElementById('test-filter').value; + testcase_registry.setTestSuiteFilter(suite_filter); + testcase_registry.setTestFilter(test_filter); + var testcases = testcase_registry.getFilteredTestCases(); + for (var testcase_index in testcases) { + runTestCase(testcases[testcase_index]); + } +} + +function clearOutput() { + clearChildNodes(document.getElementById("testResultsToc")); + clearChildNodes(document.getElementById("testResultsPlaceHolder")); + clearChildNodes(document.getElementById("testSandbox")); +} + +function showFilteredTests() { + var suite_filter = document.getElementById('suite-filter').value; + var test_filter = document.getElementById('test-filter').value; + testcase_registry.setTestSuiteFilter(suite_filter); + testcase_registry.setTestFilter(test_filter); + putTextInPlaceHolder(testcase_registry.getFilteredTestSuitNames() + + testcase_registry.getFilteredTestNames()); +} + +function showMarkup() { + var text = document.getElementById('testResultsPlaceHolder').innerHTML + var msg = this.document.createTextNode(text); + var sandbox = document.getElementById("testSandbox"); + clearChildNodes(sandbox); + sandbox.appendChild(msg); +} + +function putTextInPlaceHolder(text) { + var msg = this.document.createTextNode(text); + var placeholder = document.getElementById("testResultsPlaceHolder"); + clearChildNodes(placeholder); + placeholder.appendChild(msg); +} + +clearChildNodes = function(oNode) { + while(oNode.hasChildNodes()) { + oNode.removeChild(oNode.firstChild); + } +} + +function createLink(link, desc, bName) { + var a = document.createElement("a"); + + a.setAttribute((bName?"name":"href"), link); + if(desc) + a.appendChild(document.createTextNode(desc)); + return a; +} Added: kukit/azax/branch/snowsprint/tests/kukitresponse_test.pt ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/kukitresponse_test.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,16 @@ + + + + + +

      it worked

      +
      +
      + + +

      it worked again (test)

      +
      +
      + + Added: kukit/azax/branch/snowsprint/tests/test_azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/tests/test_azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,181 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005-2006 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# Bal?zs Re? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +import unittest, os +from zope.testing import doctest +from Testing.ZopeTestCase import ZopeTestCase +from Products.azax import AzaxBaseView, AzaxUnicodeError +from Products.azax import config +from ZPublisher.HTTPRequest import HTTPRequest +from cStringIO import StringIO +from Globals import InitializeClass +from OFS.SimpleItem import SimpleItem +from zope.publisher.browser import TestRequest +from textwrap import dedent + +# Fake content + +class FakeContent(SimpleItem, object): + pass + +#InitializeClass(FakeContent) + +class AzaxViewTestCase(ZopeTestCase): + + def afterSetUp(self): + from zope.app.tests.placelesssetup import setUp + setUp() + # fake content + fakecontent = FakeContent() + self.folder._setObject('ob', fakecontent) + fakecontent = fakecontent.__of__(self.folder) + # Set up a fake view (with no content) + fakerequest = TestRequest() + self.view = AzaxBaseView(fakecontent, fakerequest) + # Allow traversing + try: + import Products.Five + except ImportError: + # probably zope 3 + pass + else: + from Products.Five.zcml import load_string, load_config + load_config('meta.zcml', package=Products.Five) + load_string(dedent('''\ + + + + + ''')) + +class TestAzaxView(AzaxViewTestCase): + + def test_empty(self): + view = self.view + commands = view.getCommands() + self.assertEqual(len(commands), 0) + + def test_addCommand(self): + view = self.view + command = view.addCommand('name', 'selector') + commands = view.getCommands() + self.assertEqual(len(commands), 1) + self.assertEqual(command.getName(), 'name') + self.assertEqual(command.getSelector(), 'selector') + params = command.getParams() + self.assertEqual(len(params), 0) + + # XXX since lxml is gone, the next cases are no problem anymore + # Nevertheless, we test all these cases + + def _checkSetHtmlResult(self, content): + view = self.view + view.setHtmlAsChild('div.class', content) + commands = view.getCommands() + self.assertEqual(len(commands), 1) + command = commands[0] + self.assertEqual(command.getName(), 'setHtmlAsChild') + self.assertEqual(command.getSelector(), 'div.class') + params = command.getParams() + self.assertEqual(len(params), 1) + self.assertEqual(params[0].getName(), 'html') + self.assertEqual(params[0].getContent(), content) + + def test_setHtmlAsChildTextPlusEntity(self): + 'See if non breaking space entity works' + self._checkSetHtmlResult(' ') + + def test_setHtmlAsChildTextPlusEntityOthers(self): + 'See if the other HTML entities work as well' + self._checkSetHtmlResult('

      »Hello world!«

      ') + + def test_setHtmlAsChildTextOnly(self): + self._checkSetHtmlResult('new content') + + def test_setHtmlAsChildTagOnly(self): + self._checkSetHtmlResult('

      new_content

      ') + + def test_setHtmlAsChildTagPlusText(self): + self._checkSetHtmlResult('

      new_content

      after') + + def test_setHtmlAsChildTextTagPlusText(self): + self._checkSetHtmlResult('before

      new_content

      after') + + def test_setHtmlAcceptsUnicode(self): + 'Test that it accepts unicode' + self._checkSetHtmlResult(u'abc?') + + def test_setHtmlChecksForNonUnicode(self): + 'Test that it does not accept non unicode (unless pure ascii)' + self.assertRaises(AzaxUnicodeError, self._checkSetHtmlResult, 'abc?') + +class FTestAzaxView(AzaxViewTestCase): + 'Functional tests' + + def _wrapped_commands(self, inline): + header = dedent('''\ + + + ''') + footer = dedent('''\ + + ''') + return header + inline + footer + + def assertXMLEquals(self, a, b): + self.assertEqual(a, b) + + def assertCommandsEqual(self, a, b): + self.assertXMLEquals(a, self._wrapped_commands(b)) + + def test_empty(self): + view = self.view + result = view.render() + self.assertEquals(view.request.response.getHeader('Content-Type'), 'text/xml;charset=utf-8') + self.assertCommandsEqual(result, '') + + def test_setHtmlAsChild(self): + view = self.view + view.setHtmlAsChild('div.class', 'new content') + result = view.render() + self.assertCommandsEqual(result, '''\ + +\tnew content + +''') + +def test_suite(): + suites = [] + suites.append(unittest.makeSuite(TestAzaxView)) + suites.append(unittest.makeSuite(FTestAzaxView)) + suites.append(doctest.DocTestSuite('Products.azax.azaxview')) + return unittest.TestSuite(suites) Added: kukit/azax/branch/snowsprint/unicode_quirks.py ============================================================================== --- (empty file) +++ kukit/azax/branch/snowsprint/unicode_quirks.py Sun May 21 15:02:20 2006 @@ -0,0 +1,14 @@ + +class AzaxUnicodeError(RuntimeError): + pass + +def force_unicode(value, encoding='ascii'): + 'Force value to be unicode - allow also value in a specific encoding (by default, ascii).' + if isinstance(value, str): + try: + value = unicode(value, encoding) + except UnicodeDecodeError, exc: + raise AzaxUnicodeError, 'Content must be unicode or ascii string, original exception: %s' % (exc, ) + else: + assert isinstance(value, unicode) + return value Added: kukit/azax/trunk/BeautifulSoup.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/BeautifulSoup.py Sun May 21 15:02:20 2006 @@ -0,0 +1,1080 @@ +"""Beautiful Soup +Elixir and Tonic +"The Screen-Scraper's Friend" +v2.1.1 +http://www.crummy.com/software/BeautifulSoup/ + +Beautiful Soup parses arbitrarily invalid XML- or HTML-like substance +into a tree representation. It provides methods and Pythonic idioms +that make it easy to search and modify the tree. + +A well-formed XML/HTML document will yield a well-formed data +structure. An ill-formed XML/HTML document will yield a +correspondingly ill-formed data structure. If your document is only +locally well-formed, you can use this library to find and process the +well-formed part of it. The BeautifulSoup class has heuristics for +obtaining a sensible parse tree in the face of common HTML errors. + +Beautiful Soup has no external dependencies. It works with Python 2.2 +and up. + +Beautiful Soup defines classes for four different parsing strategies: + + * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific + language that kind of looks like XML. + + * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid + or invalid. + + * ICantBelieveItsBeautifulSoup, for parsing valid but bizarre HTML + that trips up BeautifulSoup. + + * BeautifulSOAP, for making it easier to parse XML documents that use + lots of subelements containing a single string, where you'd prefer + they put that string into an attribute (such as SOAP messages). + +You can subclass BeautifulStoneSoup or BeautifulSoup to create a +parsing strategy specific to an XML schema or a particular bizarre +HTML document. Typically your subclass would just override +SELF_CLOSING_TAGS and/or NESTABLE_TAGS. +""" +from __future__ import generators + +__author__ = "Leonard Richardson (leonardr at segfault.org)" +__version__ = "2.1.1" +__date__ = "$Date: 2004/10/18 00:14:20 $" +__copyright__ = "Copyright (c) 2004-2005 Leonard Richardson" +__license__ = "PSF" + +from sgmllib import SGMLParser, SGMLParseError +import types +import re +import sgmllib + +#This code makes Beautiful Soup able to parse XML with namespaces +sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') + +class NullType(object): + + """Similar to NoneType with a corresponding singleton instance + 'Null' that, unlike None, accepts any message and returns itself. + + Examples: + >>> Null("send", "a", "message")("and one more", + ... "and what you get still") is Null + True + """ + + def __new__(cls): return Null + def __call__(self, *args, **kwargs): return Null +## def __getstate__(self, *args): return Null + def __getattr__(self, attr): return Null + def __getitem__(self, item): return Null + def __setattr__(self, attr, value): pass + def __setitem__(self, item, value): pass + def __len__(self): return 0 + # FIXME: is this a python bug? otherwise ``for x in Null: pass`` + # never terminates... + def __iter__(self): return iter([]) + def __contains__(self, item): return False + def __repr__(self): return "Null" +Null = object.__new__(NullType) + +class PageElement: + """Contains the navigational information for some part of the page + (either a tag or a piece of text)""" + + def setup(self, parent=Null, previous=Null): + """Sets up the initial relations between this element and + other elements.""" + self.parent = parent + self.previous = previous + self.next = Null + self.previousSibling = Null + self.nextSibling = Null + if self.parent and self.parent.contents: + self.previousSibling = self.parent.contents[-1] + self.previousSibling.nextSibling = self + + def findNext(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears after this Tag in the document.""" + return self._first(self.fetchNext, name, attrs, text) + firstNext = findNext + + def fetchNext(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before after Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextGenerator) + + def findNextSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears after this Tag in the document.""" + return self._first(self.fetchNextSiblings, name, attrs, text) + firstNextSibling = findNextSibling + + def fetchNextSiblings(self, name=None, attrs={}, text=None, limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear after this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.nextSiblingGenerator) + + def findPrevious(self, name=None, attrs={}, text=None): + """Returns the first item that matches the given criteria and + appears before this Tag in the document.""" + return self._first(self.fetchPrevious, name, attrs, text) + + def fetchPrevious(self, name=None, attrs={}, text=None, limit=None): + """Returns all items that match the given criteria and appear + before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, self.previousGenerator) + firstPrevious = findPrevious + + def findPreviousSibling(self, name=None, attrs={}, text=None): + """Returns the closest sibling to this Tag that matches the + given criteria and appears before this Tag in the document.""" + return self._first(self.fetchPreviousSiblings, name, attrs, text) + firstPreviousSibling = findPreviousSibling + + def fetchPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None): + """Returns the siblings of this Tag that match the given + criteria and appear before this Tag in the document.""" + return self._fetch(name, attrs, text, limit, + self.previousSiblingGenerator) + + def findParent(self, name=None, attrs={}): + """Returns the closest parent of this Tag that matches the given + criteria.""" + r = Null + l = self.fetchParents(name, attrs, 1) + if l: + r = l[0] + return r + firstParent = findParent + + def fetchParents(self, name=None, attrs={}, limit=None): + """Returns the parents of this Tag that match the given + criteria.""" + return self._fetch(name, attrs, None, limit, self.parentGenerator) + + #These methods do the real heavy lifting. + + def _first(self, method, name, attrs, text): + r = Null + l = method(name, attrs, text, 1) + if l: + r = l[0] + return r + + def _fetch(self, name, attrs, text, limit, generator): + "Iterates over a generator looking for things that match." + if not hasattr(attrs, 'items'): + attrs = {'class' : attrs} + + results = [] + g = generator() + while True: + try: + i = g.next() + except StopIteration: + break + found = None + if isinstance(i, Tag): + if not text: + if not name or self._matches(i, name): + match = True + for attr, matchAgainst in attrs.items(): + check = i.get(attr) + if not self._matches(check, matchAgainst): + match = False + break + if match: + found = i + elif text: + if self._matches(i, text): + found = i + if found: + results.append(found) + if limit and len(results) >= limit: + break + return results + + #Generators that can be used to navigate starting from both + #NavigableTexts and Tags. + def nextGenerator(self): + i = self + while i: + i = i.next + yield i + + def nextSiblingGenerator(self): + i = self + while i: + i = i.nextSibling + yield i + + def previousGenerator(self): + i = self + while i: + i = i.previous + yield i + + def previousSiblingGenerator(self): + i = self + while i: + i = i.previousSibling + yield i + + def parentGenerator(self): + i = self + while i: + i = i.parent + yield i + + def _matches(self, chunk, howToMatch): + #print 'looking for %s in %s' % (howToMatch, chunk) + # + # If given a list of items, return true if the list contains a + # text element that matches. + if isList(chunk) and not isinstance(chunk, Tag): + for tag in chunk: + if isinstance(tag, NavigableText) and self._matches(tag, howToMatch): + return True + return False + if callable(howToMatch): + return howToMatch(chunk) + if isinstance(chunk, Tag): + #Custom match methods take the tag as an argument, but all other + #ways of matching match the tag name as a string + chunk = chunk.name + #Now we know that chunk is a string + if not isinstance(chunk, basestring): + chunk = str(chunk) + if hasattr(howToMatch, 'match'): + # It's a regexp object. + return howToMatch.search(chunk) + if isList(howToMatch): + return chunk in howToMatch + if hasattr(howToMatch, 'items'): + return howToMatch.has_key(chunk) + #It's just a string + return str(howToMatch) == chunk + +class NavigableText(PageElement): + + def __getattr__(self, attr): + "For backwards compatibility, text.string gives you text" + if attr == 'string': + return self + else: + raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + +class NavigableString(str, NavigableText): + pass + +class NavigableUnicodeString(unicode, NavigableText): + pass + +class Tag(PageElement): + + """Represents a found HTML tag with its attributes and contents.""" + + def __init__(self, name, attrs=None, parent=Null, previous=Null): + "Basic constructor." + self.name = name + if attrs == None: + attrs = [] + self.attrs = attrs + self.contents = [] + self.setup(parent, previous) + self.hidden = False + + def get(self, key, default=None): + """Returns the value of the 'key' attribute for the tag, or + the value given for 'default' if it doesn't have that + attribute.""" + return self._getAttrMap().get(key, default) + + def __getitem__(self, key): + """tag[key] returns the value of the 'key' attribute for the tag, + and throws an exception if it's not there.""" + return self._getAttrMap()[key] + + def __iter__(self): + "Iterating over a tag iterates over its contents." + return iter(self.contents) + + def __len__(self): + "The length of a tag is the length of its list of contents." + return len(self.contents) + + def __contains__(self, x): + return x in self.contents + + def __nonzero__(self): + "A tag is non-None even if it has no contents." + return True + + def __setitem__(self, key, value): + """Setting tag[key] sets the value of the 'key' attribute for the + tag.""" + self._getAttrMap() + self.attrMap[key] = value + found = False + for i in range(0, len(self.attrs)): + if self.attrs[i][0] == key: + self.attrs[i] = (key, value) + found = True + if not found: + self.attrs.append((key, value)) + self._getAttrMap()[key] = value + + def __delitem__(self, key): + "Deleting tag[key] deletes all 'key' attributes for the tag." + for item in self.attrs: + if item[0] == key: + self.attrs.remove(item) + #We don't break because bad HTML can define the same + #attribute multiple times. + self._getAttrMap() + if self.attrMap.has_key(key): + del self.attrMap[key] + + def __call__(self, *args, **kwargs): + """Calling a tag like a function is the same as calling its + fetch() method. Eg. tag('a') returns a list of all the A tags + found within this tag.""" + return apply(self.fetch, args, kwargs) + + def __getattr__(self, tag): + if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: + return self.first(tag[:-3]) + elif tag.find('__') != 0: + return self.first(tag) + + def __eq__(self, other): + """Returns true iff this tag has the same name, the same attributes, + and the same contents (recursively) as the given tag. + + NOTE: right now this will return false if two tags have the + same attributes in a different order. Should this be fixed?""" + if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other): + return False + for i in range(0, len(self.contents)): + if self.contents[i] != other.contents[i]: + return False + return True + + def __ne__(self, other): + """Returns true iff this tag is not identical to the other tag, + as defined in __eq__.""" + return not self == other + + def __repr__(self): + """Renders this tag as a string.""" + return str(self) + + def __unicode__(self): + return self.__str__(1) + + def __str__(self, needUnicode=None, showStructureIndent=None): + """Returns a string or Unicode representation of this tag and + its contents. + + NOTE: since Python's HTML parser consumes whitespace, this + method is not certain to reproduce the whitespace present in + the original string.""" + + attrs = [] + if self.attrs: + for key, val in self.attrs: + attrs.append('%s="%s"' % (key, val)) + close = '' + closeTag = '' + if self.isSelfClosing(): + close = ' /' + else: + closeTag = '' % self.name + indentIncrement = None + if showStructureIndent != None: + indentIncrement = showStructureIndent + if not self.hidden: + indentIncrement += 1 + contents = self.renderContents(indentIncrement, needUnicode=needUnicode) + if showStructureIndent: + space = '\n%s' % (' ' * showStructureIndent) + if self.hidden: + s = contents + else: + s = [] + attributeString = '' + if attrs: + attributeString = ' ' + ' '.join(attrs) + if showStructureIndent: + s.append(space) + s.append('<%s%s%s>' % (self.name, attributeString, close)) + s.append(contents) + if closeTag and showStructureIndent != None: + s.append(space) + s.append(closeTag) + s = ''.join(s) + isUnicode = type(s) == types.UnicodeType + if needUnicode and not isUnicode: + s = unicode(s) + elif isUnicode and needUnicode==False: + s = str(s) + return s + + def prettify(self, needUnicode=None): + return self.__str__(needUnicode, showStructureIndent=True) + + def renderContents(self, showStructureIndent=None, needUnicode=None): + """Renders the contents of this tag as a (possibly Unicode) + string.""" + s=[] + for c in self: + text = None + if isinstance(c, NavigableUnicodeString) or type(c) == types.UnicodeType: + text = unicode(c) + elif isinstance(c, Tag): + s.append(c.__str__(needUnicode, showStructureIndent)) + elif needUnicode: + text = unicode(c) + else: + text = str(c) + if text: + if showStructureIndent != None: + if text[-1] == '\n': + text = text[:-1] + s.append(text) + return ''.join(s) + + #Soup methods + + def firstText(self, text, recursive=True): + """Convenience method to retrieve the first piece of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.first(recursive=recursive, text=text) + + def fetchText(self, text, recursive=True, limit=None): + """Convenience method to retrieve all pieces of text matching the + given criteria. 'text' can be a string, a regular expression object, + a callable that takes a string and returns whether or not the + string 'matches', etc.""" + return self.fetch(recursive=recursive, text=text, limit=limit) + + def first(self, name=None, attrs={}, recursive=True, text=None): + """Return only the first child of this + Tag matching the given criteria.""" + r = Null + l = self.fetch(name, attrs, recursive, text, 1) + if l: + r = l[0] + return r + findChild = first + + def fetch(self, name=None, attrs={}, recursive=True, text=None, + limit=None): + """Extracts a list of Tag objects that match the given + criteria. You can specify the name of the Tag and any + attributes you want the Tag to have. + + The value of a key-value pair in the 'attrs' map can be a + string, a list of strings, a regular expression object, or a + callable that takes a string and returns whether or not the + string matches for some custom definition of 'matches'. The + same is true of the tag name.""" + generator = self.recursiveChildGenerator + if not recursive: + generator = self.childGenerator + return self._fetch(name, attrs, text, limit, generator) + fetchChildren = fetch + + #Utility methods + + def isSelfClosing(self): + """Returns true iff this is a self-closing tag as defined in the HTML + standard. + + TODO: This is specific to BeautifulSoup and its subclasses, but it's + used by __str__""" + return self.name in BeautifulSoup.SELF_CLOSING_TAGS + + def append(self, tag): + """Appends the given tag to the contents of this tag.""" + self.contents.append(tag) + + #Private methods + + def _getAttrMap(self): + """Initializes a map representation of this tag's attributes, + if not already initialized.""" + if not getattr(self, 'attrMap'): + self.attrMap = {} + for (key, value) in self.attrs: + self.attrMap[key] = value + return self.attrMap + + #Generator methods + def childGenerator(self): + for i in range(0, len(self.contents)): + yield self.contents[i] + raise StopIteration + + def recursiveChildGenerator(self): + stack = [(self, 0)] + while stack: + tag, start = stack.pop() + if isinstance(tag, Tag): + for i in range(start, len(tag.contents)): + a = tag.contents[i] + yield a + if isinstance(a, Tag) and tag.contents: + if i < len(tag.contents) - 1: + stack.append((tag, i+1)) + stack.append((a, 0)) + break + raise StopIteration + + +def isList(l): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is listlike.""" + return hasattr(l, '__iter__') \ + or (type(l) in (types.ListType, types.TupleType)) + +def buildTagMap(default, *args): + """Turns a list of maps, lists, or scalars into a single map. + Used to build the SELF_CLOSING_TAGS and NESTABLE_TAGS maps out + of lists and partial maps.""" + built = {} + for portion in args: + if hasattr(portion, 'items'): + #It's a map. Merge it. + for k,v in portion.items(): + built[k] = v + elif isList(portion): + #It's a list. Map each item to the default. + for k in portion: + built[k] = default + else: + #It's a scalar. Map it to the default. + built[portion] = default + return built + +class BeautifulStoneSoup(Tag, SGMLParser): + + """This class contains the basic parser and fetch code. It defines + a parser that knows nothing about tag behavior except for the + following: + + You can't close a tag without closing all the tags it encloses. + That is, "" actually means + "". + + [Another possible explanation is "", but since + this class defines no SELF_CLOSING_TAGS, it will never use that + explanation.] + + This class is useful for parsing XML or made-up markup languages, + or when BeautifulSoup makes an assumption counter to what you were + expecting.""" + + SELF_CLOSING_TAGS = {} + NESTABLE_TAGS = {} + RESET_NESTING_TAGS = {} + QUOTE_TAGS = {} + + #As a public service we will by default silently replace MS smart quotes + #and similar characters with their HTML or ASCII equivalents. + MS_CHARS = { '\x80' : '€', + '\x81' : ' ', + '\x82' : '‚', + '\x83' : 'ƒ', + '\x84' : '„', + '\x85' : '…', + '\x86' : '†', + '\x87' : '‡', + '\x88' : '⁁', + '\x89' : '%', + '\x8A' : 'Š', + '\x8B' : '<', + '\x8C' : 'Œ', + '\x8D' : '?', + '\x8E' : 'Z', + '\x8F' : '?', + '\x90' : '?', + '\x91' : '‘', + '\x92' : '’', + '\x93' : '“', + '\x94' : '”', + '\x95' : '•', + '\x96' : '–', + '\x97' : '—', + '\x98' : '˜', + '\x99' : '™', + '\x9a' : 'š', + '\x9b' : '>', + '\x9c' : 'œ', + '\x9d' : '?', + '\x9e' : 'z', + '\x9f' : 'Ÿ',} + + PARSER_MASSAGE = [(re.compile('(<[^<>]*)/>'), + lambda(x):x.group(1) + ' />'), + (re.compile(']*)>'), + lambda(x):''), + (re.compile("([\x80-\x9f])"), + lambda(x): BeautifulStoneSoup.MS_CHARS.get(x.group(1))) + ] + + ROOT_TAG_NAME = '[document]' + + def __init__(self, text=None, avoidParserProblems=True, + initialTextIsEverything=True): + """Initialize this as the 'root tag' and feed in any text to + the parser. + + NOTE about avoidParserProblems: sgmllib will process most bad + HTML, and BeautifulSoup has tricks for dealing with some HTML + that kills sgmllib, but Beautiful Soup can nonetheless choke + or lose data if your data uses self-closing tags or + declarations incorrectly. By default, Beautiful Soup sanitizes + its input to avoid the vast majority of these problems. The + problems are relatively rare, even in bad HTML, so feel free + to pass in False to avoidParserProblems if they don't apply to + you, and you'll get better performance. The only reason I have + this turned on by default is so I don't get so many tech + support questions. + + The two most common instances of invalid HTML that will choke + sgmllib are fixed by the default parser massage techniques: + +
      (No space between name of closing tag and tag close) + (Extraneous whitespace in declaration) + + You can pass in a custom list of (RE object, replace method) + tuples to get Beautiful Soup to scrub your input the way you + want.""" + Tag.__init__(self, self.ROOT_TAG_NAME) + if avoidParserProblems \ + and not isList(avoidParserProblems): + avoidParserProblems = self.PARSER_MASSAGE + self.avoidParserProblems = avoidParserProblems + SGMLParser.__init__(self) + self.quoteStack = [] + self.hidden = 1 + self.reset() + if hasattr(text, 'read'): + #It's a file-type object. + text = text.read() + if text: + self.feed(text) + if initialTextIsEverything: + self.done() + + def __getattr__(self, methodName): + """This method routes method call requests to either the SGMLParser + superclass or the Tag superclass, depending on the method name.""" + if methodName.find('start_') == 0 or methodName.find('end_') == 0 \ + or methodName.find('do_') == 0: + return SGMLParser.__getattr__(self, methodName) + elif methodName.find('__') != 0: + return Tag.__getattr__(self, methodName) + else: + raise AttributeError + + def feed(self, text): + if self.avoidParserProblems: + for fix, m in self.avoidParserProblems: + text = fix.sub(m, text) + SGMLParser.feed(self, text) + + def done(self): + """Called when you're done parsing, so that the unclosed tags can be + correctly processed.""" + self.endData() #NEW + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() + + def reset(self): + SGMLParser.reset(self) + self.currentData = [] + self.currentTag = None + self.tagStack = [] + self.pushTag(self) + + def popTag(self): + tag = self.tagStack.pop() + # Tags with just one string-owning child get the child as a + # 'string' property, so that soup.tag.string is shorthand for + # soup.tag.contents[0] + if len(self.currentTag.contents) == 1 and \ + isinstance(self.currentTag.contents[0], NavigableText): + self.currentTag.string = self.currentTag.contents[0] + + #print "Pop", tag.name + if self.tagStack: + self.currentTag = self.tagStack[-1] + return self.currentTag + + def pushTag(self, tag): + #print "Push", tag.name + if self.currentTag: + self.currentTag.append(tag) + self.tagStack.append(tag) + self.currentTag = self.tagStack[-1] + + def endData(self): + currentData = ''.join(self.currentData) + if currentData: + if not currentData.strip(): + if '\n' in currentData: + currentData = '\n' + else: + currentData = ' ' + c = NavigableString + if type(currentData) == types.UnicodeType: + c = NavigableUnicodeString + o = c(currentData) + o.setup(self.currentTag, self.previous) + if self.previous: + self.previous.next = o + self.previous = o + self.currentTag.contents.append(o) + self.currentData = [] + + def _popToTag(self, name, inclusivePop=True): + """Pops the tag stack up to and including the most recent + instance of the given tag. If inclusivePop is false, pops the tag + stack up to but *not* including the most recent instqance of + the given tag.""" + if name == self.ROOT_TAG_NAME: + return + + numPops = 0 + mostRecentTag = None + for i in range(len(self.tagStack)-1, 0, -1): + if name == self.tagStack[i].name: + numPops = len(self.tagStack)-i + break + if not inclusivePop: + numPops = numPops - 1 + + for i in range(0, numPops): + mostRecentTag = self.popTag() + return mostRecentTag + + def _smartPop(self, name): + + """We need to pop up to the previous tag of this type, unless + one of this tag's nesting reset triggers comes between this + tag and the previous tag of this type, OR unless this tag is a + generic nesting trigger and another generic nesting trigger + comes between this tag and the previous tag of this type. + + Examples: +

      FooBar

      should pop to 'p', not 'b'. +

      FooBar

      should pop to 'table', not 'p'. +

      Foo

      Bar

      should pop to 'tr', not 'p'. +

      FooBar

      should pop to 'p', not 'b'. + +

      • *
      • * should pop to 'ul', not the first 'li'. +
    • ** should pop to 'table', not the first 'tr' + tag should + implicitly close the previous tag within the same
      ** should pop to 'tr', not the first 'td' + """ + + nestingResetTriggers = self.NESTABLE_TAGS.get(name) + isNestable = nestingResetTriggers != None + isResetNesting = self.RESET_NESTING_TAGS.has_key(name) + popTo = None + inclusive = True + for i in range(len(self.tagStack)-1, 0, -1): + p = self.tagStack[i] + if (not p or p.name == name) and not isNestable: + #Non-nestable tags get popped to the top or to their + #last occurance. + popTo = name + break + if (nestingResetTriggers != None + and p.name in nestingResetTriggers) \ + or (nestingResetTriggers == None and isResetNesting + and self.RESET_NESTING_TAGS.has_key(p.name)): + + #If we encounter one of the nesting reset triggers + #peculiar to this tag, or we encounter another tag + #that causes nesting to reset, pop up to but not + #including that tag. + + popTo = p.name + inclusive = False + break + p = p.parent + if popTo: + self._popToTag(popTo, inclusive) + + def unknown_starttag(self, name, attrs, selfClosing=0): + #print "Start tag %s" % name + if self.quoteStack: + #This is not a real tag. + #print "<%s> is not real!" % name + attrs = ''.join(map(lambda(x, y): ' %s="%s"' % (x, y), attrs)) + self.handle_data('<%s%s>' % (name, attrs)) + return + self.endData() + if not name in self.SELF_CLOSING_TAGS and not selfClosing: + self._smartPop(name) + tag = Tag(name, attrs, self.currentTag, self.previous) + if self.previous: + self.previous.next = tag + self.previous = tag + self.pushTag(tag) + if selfClosing or name in self.SELF_CLOSING_TAGS: + self.popTag() + if name in self.QUOTE_TAGS: + #print "Beginning quote (%s)" % name + self.quoteStack.append(name) + self.literal = 1 + + def unknown_endtag(self, name): + if self.quoteStack and self.quoteStack[-1] != name: + #This is not a real end tag. + #print " is not real!" % name + self.handle_data('' % name) + return + self.endData() + self._popToTag(name) + if self.quoteStack and self.quoteStack[-1] == name: + self.quoteStack.pop() + self.literal = (len(self.quoteStack) > 0) + + def handle_data(self, data): + self.currentData.append(data) + + def handle_pi(self, text): + "Propagate processing instructions right through." + self.handle_data("" % text) + + def handle_comment(self, text): + "Propagate comments right through." + self.handle_data("" % text) + + def handle_charref(self, ref): + "Propagate char refs right through." + self.handle_data('&#%s;' % ref) + + def handle_entityref(self, ref): + "Propagate entity refs right through." + self.handle_data('&%s;' % ref) + + def handle_decl(self, data): + "Propagate DOCTYPEs and the like right through." + self.handle_data('' % data) + + def parse_declaration(self, i): + """Treat a bogus SGML declaration as raw data. Treat a CDATA + declaration as regular data.""" + j = None + if self.rawdata[i:i+9] == '', i) + if k == -1: + k = len(self.rawdata) + self.handle_data(self.rawdata[i+9:k]) + j = k+3 + else: + try: + j = SGMLParser.parse_declaration(self, i) + except SGMLParseError: + toHandle = self.rawdata[i:] + self.handle_data(toHandle) + j = i + len(toHandle) + return j + +class BeautifulSoup(BeautifulStoneSoup): + + """This parser knows the following facts about HTML: + + * Some tags have no closing tag and should be interpreted as being + closed as soon as they are encountered. + + * The text inside some tags (ie. 'script') may contain tags which + are not really part of the document and which should be parsed + as text, not tags. If you want to parse the text as tags, you can + always fetch it and parse it explicitly. + + * Tag nesting rules: + + Most tags can't be nested at all. For instance, the occurance of + a

      tag should implicitly close the previous

      tag. + +

      Para1

      Para2 + should be transformed into: +

      Para1

      Para2 + + Some tags can be nested arbitrarily. For instance, the occurance + of a

      tag should _not_ implicitly close the previous +
      tag. + + Alice said:
      Bob said:
      Blah + should NOT be transformed into: + Alice said:
      Bob said:
      Blah + + Some tags can be nested, but the nesting is reset by the + interposition of other tags. For instance, a
      , + but not close a tag in another table. + +
      BlahBlah + should be transformed into: +
      BlahBlah + but, + Blah
      Blah + should NOT be transformed into + Blah
      Blah + + Differing assumptions about tag nesting rules are a major source + of problems with the BeautifulSoup class. If BeautifulSoup is not + treating as nestable a tag your page author treats as nestable, + try ICantBelieveItsBeautifulSoup before writing your own + subclass.""" + + SELF_CLOSING_TAGS = buildTagMap(None, ['br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base']) + + QUOTE_TAGS = {'script': None} + + #According to the HTML standard, each of these inline tags can + #contain another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup', + 'center'] + + #According to the HTML standard, these block tags can contain + #another tag of the same type. Furthermore, it's common + #to actually use these tags this way. + NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del'] + + #Lists can contain other lists, but there are restrictions. + NESTABLE_LIST_TAGS = { 'ol' : [], + 'ul' : [], + 'li' : ['ul', 'ol'], + 'dl' : [], + 'dd' : ['dl'], + 'dt' : ['dl'] } + + #Tables can contain other tables, but there are restrictions. + NESTABLE_TABLE_TAGS = {'table' : [], + 'tr' : ['table', 'tbody', 'tfoot', 'thead'], + 'td' : ['tr'], + 'th' : ['tr'], + } + + NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] + + #If one of these tags is encountered, all tags up to the next tag of + #this type are popped. + RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', + NON_NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, + NESTABLE_TABLE_TAGS) + + NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, + NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) + +class ICantBelieveItsBeautifulSoup(BeautifulSoup): + + """The BeautifulSoup class is oriented towards skipping over + common HTML errors like unclosed tags. However, sometimes it makes + errors of its own. For instance, consider this fragment: + + FooBar + + This is perfectly valid (if bizarre) HTML. However, the + BeautifulSoup class will implicitly close the first b tag when it + encounters the second 'b'. It will think the author wrote + "FooBar", and didn't close the first 'b' tag, because + there's no real-world reason to bold something that's already + bold. When it encounters '' it will close two more 'b' + tags, for a grand total of three tags closed instead of two. This + can throw off the rest of your document structure. The same is + true of a number of other tags, listed below. + + It's much more common for someone to forget to close (eg.) a 'b' + tag than to actually use nested 'b' tags, and the BeautifulSoup + class handles the common case. This class handles the + not-co-common case: where you can't believe someone wrote what + they did, but it's valid HTML and BeautifulSoup screwed up by + assuming it wouldn't be. + + If this doesn't do what you need, try subclassing this class or + BeautifulSoup, and providing your own list of NESTABLE_TAGS.""" + + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ + ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', + 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b', + 'big'] + + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript'] + + NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, + I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) + +class BeautifulSOAP(BeautifulStoneSoup): + """This class will push a tag with only a single string child into + the tag's parent as an attribute. The attribute's name is the tag + name, and the value is the string child. An example should give + the flavor of the change: + + baz + => + baz + + You can then access fooTag['bar'] instead of fooTag.barTag.string. + + This is, of course, useful for scraping structures that tend to + use subelements instead of attributes, such as SOAP messages. Note + that it modifies its input, so don't print the modified version + out. + + I'm not sure how many people really want to use this class; let me + know if you do. Mainly I like the name.""" + + def popTag(self): + if len(self.tagStack) > 1: + tag = self.tagStack[-1] + parent = self.tagStack[-2] + parent._getAttrMap() + if (isinstance(tag, Tag) and len(tag.contents) == 1 and + isinstance(tag.contents[0], NavigableText) and + not parent.attrMap.has_key(tag.name)): + parent[tag.name] = tag.contents[0] + BeautifulStoneSoup.popTag(self) + +#Enterprise class names! It has come to our attention that some people +#think the names of the Beautiful Soup parser classes are too silly +#and "unprofessional" for use in enterprise screen-scraping. We feel +#your pain! For such-minded folk, the Beautiful Soup Consortium And +#All-Night Kosher Bakery recommends renaming this file to +#"RobustParser.py" (or, in cases of extreme enterprisitude, +#"RobustParserBeanInterface.class") and using the following +#enterprise-friendly class aliases: +class RobustXMLParser(BeautifulStoneSoup): + pass +class RobustHTMLParser(BeautifulSoup): + pass +class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup): + pass +class SimplifyingSOAPParser(BeautifulSOAP): + pass + +### + + +#By default, act as an HTML pretty-printer. +if __name__ == '__main__': + import sys + soup = BeautifulStoneSoup(sys.stdin.read()) + print soup.prettify() Added: kukit/azax/trunk/DEPENDENCIES.txt ============================================================================== --- (empty file) +++ kukit/azax/trunk/DEPENDENCIES.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +$Id$ + +- Zope-2.7.3 or greater +- Zope X3.0.0 or greater +- Five 0.3 or greater +- lxml 0.7 or greater + Added: kukit/azax/trunk/EXTERNALS.TXT ============================================================================== --- (empty file) +++ kukit/azax/trunk/EXTERNALS.TXT Sun May 21 15:02:20 2006 @@ -0,0 +1,8 @@ +# +# results of svn propget svn:externals +# https://svn.z3lab.org/z3lab/azax/trunk +# +# You can update your working dir by: +# svn propset svn:externals -F EXTERNALS.TXT . +# +kukit http://codespeak.net/svn/kukit/kukit.js/trunk Added: kukit/azax/trunk/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/trunk/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,63 @@ +$Id$ + +INSTALLATION NOTES +================== + +1 - Prerequisites +2 - lxml installation +3 - Zope X3 installation +4 - Products setup +5 - Zope 3 <-> Zope 2 path setup +6 - How to use azax + +---------- + +1- Prerequisites + +To install azax you will need: + +- a working Zope 2.7.x + CPS 3.3.4 or greater +- ZopeX3-3.0.0 : http://www.zope.org/Products/ZopeX3 +- Five 1.0.1 or greater : http://codespeak.net/z3/five/ +- lxml 0.7 : http://codespeak.net/lxml + +2 - lxml installation + +Install the python package lxml on your python installation, ie the one +Zope 2.7 actually use with the usual command:: + + $ python setup.py install + +3 - Zope X3 installation + +(not needed if you have Zope 2.8 or later as it includes Zope X3) + +Please install ZopeX3-3.0.0 on your system (usually in /opt/ZopeX3-3.0.0) with +the python version you use for Zope2.7 and CPS, e.g.:: + + $ ./configure --prefix /opt/ZopeX3-3.0.0 --with-python /usr/bin/python + $ make + $ make install + $ make check + +4 - Products setup + +Put Five, azax in your $INSTANCE/Products + +(not needed if you have Zope 2.8 or later as it includes Five) + +5 - Zope 3 <-> Zope 2 path setup + +(not needed if you have Zope 2.8 or later as it Zope X3 is correctly setup) + +Edit your $INSTANCE/etc/zope.conf to add the following path declarations:: + + path /opt/ZopeX3-3.0.0/lib/python + path $INSTANCE/Products/azax + +6 - How to use azax + +Copy the product azaxdemo that's inside demos in your instance, +and look at its own README.txt + + Added: kukit/azax/trunk/TODO.txt ============================================================================== --- (empty file) +++ kukit/azax/trunk/TODO.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,14 @@ +* rename AzaxResponse to KukitResponse +* look to TextIndexNG3 howto have it compatible with Z3, Z2 and Plone +* rename the commands to better match the DOM API +* try to fix the namespace problems - see https://svn.z3lab.org/z3lab/azax/branches/gotcha-namespaces + + + +

      2005-11-16 21:24:05.484000

      +
      +
      + +* add more demos, we need radio buttons, addAfter and much more. There are bugs + in IE which we have to test and document. Added: kukit/azax/trunk/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +import mimetypes + +mimetypes.types_map['.kkt'] = 'text/xml' # XXX legacy! +mimetypes.types_map['.kukit'] = 'text/xml' + +from azaxview import AzaxBaseView +from unicode_quirks import force_unicode, AzaxUnicodeError Added: kukit/azax/trunk/azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,146 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005-2006 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# Bal?zs Re? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +'''\ +AZAX Base view class + +All the implementations should create a specialization +of this class when building their browser views. + +The policy is that a method should build up a command +set on available methods and then return self.render(). + +The default command set is implemented in the base class +as well. +''' + +from Products.Five import BrowserView +from commands import AzaxCommands, AzaxCommand +from selectors import XpathSelector, CssSelector, HtmlIdSelector +from parsers import XmlParser, HtmlParser +import zope.component +from zope.interface import Interface + +class AzaxBaseView(BrowserView): + """ Base azax view + + this allows setting up the content of the response, and then + generate it out. + """ + + def render(self): + '''All methods must use this to return their command set + ''' + adapter = zope.component.getMultiAdapter((self.getCommands(), self.request), Interface, 'render') + return adapter.render() + + def __init__(self, context, request): + BrowserView.__init__(self, context, request) + self.commands = AzaxCommands() + + def addCommand(self, name, selector): + command = AzaxCommand(name, selector) + self.commands.append(command) + return command + + def getCommands(self): + return self.commands + + def getXpathSelector(self, selector): + return XpathSelector(selector) + + def getCssSelector(self, selector): + return CssSelector(selector) + + def getHtmlIdSelector(self, selector): + return HtmlIdSelector(selector) + + # -- + # default set of commands + # -- + + def setHtmlAsChild(self, selector, new_value): + """ see interfaces.py """ + new_value = HtmlParser(new_value)() + command = self.addCommand('setHtmlAsChild', selector) + data = command.addParam('html', new_value) + + def setAttribute(self, selector, name, value): + """ see interfaces.py """ + command = self.addCommand('setAttribute', selector) + data = command.addParam('name', name) + data = command.addParam('value', value) + + def addAfter(self, selector, new_value): + """ see interfaces.py """ + new_value = HtmlParser(new_value)() + command = self.addCommand('addAfter', selector) + data = command.addParam('html', new_value) + + def clearChildren(self, selector): + """ see interfaces.py """ + command = self.addCommand('clearChildren', selector) + data = command.addParam('none') + + def removeNode(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNode', selector) + data = command.addParam('none') + + def removeNode(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNode', selector) + data = command.addParam('none') + + def removeNextSibling(self, selector): + """ see interfaces.py """ + command = self.addCommand('removeNextSibling', selector) + data = command.addParam('none') + + def removePreviousSibling(self, selector): + """ see interfaces.py """ + command = self.addCommand('removePreviousSibling', selector) + data = command.addParam('none') + + def copyChildrenFrom(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenFrom', selector) + data = command.addParam('html_id', id) + + def moveNodeAfter(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('moveNodeAfter', selector) + data = command.addParam('html_id', id) + + def copyChildrenTo(self, selector, id): + """ see interfaces.py """ + command = self.addCommand('copyChildrenTo', selector) + data = command.addParam('html_id', id) + + def moveChildrenTo(self, selector, id): + """ see interfaces.py """ + self.copyChildrenTo(selector, id) + self.clearChildren(selector) + + def executeCode(self, selector, code): + code = XmlParser(code)() + command = self.addCommand('executeCode', selector) + data = command.addParam('code', code) Added: kukit/azax/trunk/browser/kukitresponse.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/browser/kukitresponse.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,19 @@ + + + + +

      it worked

      +
      +
      + Added: kukit/azax/trunk/commands.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/commands.py Sun May 21 15:02:20 2006 @@ -0,0 +1,119 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2006 +# Authors: +# Godefroid Chapelle +# Bal?zs Re? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +'''\ +Marshal objects + +These build up the response and get marshalled to the client +in the defined format +''' + +from zope.interface import implements +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile +from interfaces import IAzaxCommands, IAzaxCommand, IAzaxParam +from plugins import Command +from unicode_quirks import force_unicode + +class AzaxCommands(list): + implements(IAzaxCommands) + +class AzaxParam: + implements(IAzaxParam) + + def __init__(self, name, content=''): + self.name = name + self.content = content + + def force_content_unicode(self): + # Content must be str with ascii encoding, or unicode! + self.content = force_unicode(self.content) + + def getName(self): + return self.name + + def getContent(self): + return self.content + +class AzaxCommand: + implements(IAzaxCommand) + + def __init__(self, name, selector): + Command.checkRegistered(name) + if isinstance(selector, basestring): + self.selector = selector + self.selectorType = '' + else: + self.selector = selector.value + self.selectorType = selector.type + self.name = name + self.params = [] + + def addParam(self, name, content=''): + param = AzaxParam(name, content) + self.params.append(param) + return param + + def getName(self): + return self.name + + def getSelector(self): + return self.selector + + def getSelectorType(self): + return self.selectorType + + def getParams(self): + return self.params + +class CommandView(object): + '''View of a command. + + The render method does actual marshalling + of the commands to be sent to the client. + ''' + + def __init__(self, context, request): + self.context = context + self.request = request + # XXX From Zope2.9 we need this. + # Note: We don't use proper views for Five. As our context object + # is not even a proper Zope content. There would be a way to: + # + # - use ZopeTwoPageTemplateFile + # - and, make the object to be proper zope content. + # + # This would be two much ado for nothing, so we just use barefoot + # rendering but as a consequence no path expression, only python: + # is available from the page template. + if not hasattr(self.request, 'debug'): + self.request.debug = None + + # Force parameters content to be unicode + for command in context: + for param in command.getParams(): + param.force_content_unicode() + + # XML output gets rendered via a page template + # XXX note: barefoot rendering, use python: only after zope2.9 + render = ViewPageTemplateFile('browser/kukitresponse.pt', content_type='text/xml;charset=utf-8') + +# def render(self): +# result = self._render() +# return result Added: kukit/azax/trunk/config.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/config.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/trunk/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/demos/azaxdemo/INSTALL.txt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/INSTALL.txt Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +$Id$ + +INSTALLATION NOTES +================== + +1/ Drop azaxdemo into your product dir +2/ In the zmi, add a "Azax Simple content" anywhere + let's call it "test" +3/ Get into "test" page, you should have a menu with available demos + Added: kukit/azax/trunk/demos/azaxdemo/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ +import simplecontent + +def initialize(context): + + context.registerClass( + simplecontent.SimpleContent, + constructors = (simplecontent.manage_addSimpleContentForm, + simplecontent.manage_addSimpleContent), + ) Added: kukit/azax/trunk/demos/azaxdemo/azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,103 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from Products.azax import AzaxBaseView +from datetime import datetime + +from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile + +class AzaxView(AzaxBaseView): + + #header_macros = ViewPageTemplateFile('browser/header_macros.pt') + + def clearDivContent(self): + """ clear div content """ + self.clearChildren('div#demo') + return self.render() + + def copyFromDivContent(self): + """ copy div content """ + self.copyChildrenFrom('div#copy', 'demo') + return self.render() + + def copyToDivContent(self): + """ copy div content """ + self.copyChildrenTo('div#copy', 'demo') + return self.render() + + def moveToDivContent(self): + """ copy div content """ + self.moveChildrenTo('div#copy', 'demo') + return self.render() + + def getDivContent(self): + """ returns div content """ + self.setHtmlAsChild('div#demo', '

      it worked

      ') + self.setHtmlAsChild('div#demo', '

      it worked again

      ') + return self.render() + + def getCorrespondingSelect(self, value): + """ returns select content """ + mapping = {} + mapping['']=[] + mapping['animals']=['dog', 'cat', 'cow'] + mapping['machines']=['computer', 'car', 'airplane'] + result = ['' % item for item in mapping[value]] + self.setHtmlAsChild('select#second', ' '.join(result)) + return self.render() + + def getAutoupdateMarkup(self): + """ returns the current time """ + self.setHtmlAsChild('div#update-wrapper', '
      ') + return self.render() + + def getCurrentTime(self): + """ returns the current time """ + self.setHtmlAsChild('div#update-area', "

      %s

      " % str(datetime.now())) + return self.render() + + def getInputField(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', + '
      ' \ + '' + ) + return self.render() + + def saveText(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', value+'') + return self.render() + + #we'll need parameter passing first do this in a sane way + def getSubTree(self, value): + """ returns the current time """ + self.setHtmlAsChild('div#text', 'works') + return self.render() + + def cancelSubmitSave(self, text_save): + self.setHtmlAsChild('div#async', 'Async saved %s' % text_save) + return self.render() + + def removeNodeXpath(self): + sel = self.getXpathSelector("//P[@id='xpath']/following-sibling::*[position()=1]") + self.removeNode(sel) + return self.render() Added: kukit/azax/trunk/demos/azaxdemo/browser/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kss ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kss Sun May 21 15:02:20 2006 @@ -0,0 +1,23 @@ + +button#copyFrom:click { + remote: copyFromDivContent; +} + +button#copyTo:click { + remote: copyToDivContent; +} + +button#moveTo:click { + remote: moveToDivContent; +} + +button#clear:click { + remote: clearDivContent; +} + +button#change:click { + remote: getDivContent; + log: "WE LOG from getDivContent"; +/* alert: "WE LOG from getDivContent"; */ +} + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kukit ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,26 @@ + + + + copyFromDivContent + + + copyToDivContent + + + moveToDivContent + + + + + clearDivContent + + + + + + getDivContent + + + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_demo.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,119 @@ + + + + + + + + +

      All demos

      +

      Change tag content

      +

      Top div

      +
      + Azax +
      + +

      Bottom div

      +
      + copy here +
      +

      Javascript Styling

      +

      This page defines a link in the header which rel attribute points + to a .kukit server file : + + + see the kukit file here.

      +

      + This .kukit file declares CSS selectors associated with events and server URLs called when the event occur. +

      +

      A javascript engine processes the XML content : it inserts javascript + events in the DOM.

      +

      + For instance, the click event of button with the id change will call asynchronously the getDivContent URL. + +

      +

      Server Asynchronous Call

      +

      + The events associated with Javascript just call the server asynchronously. +

      +

      + The response is a XML file. + The XML contains CSS selectors associated with commands. +

      +

      + The Javascript engine selects the DOM nodes to which it applies each command. +

      +

      + For instance, let's look at the moveToDivContent response. +

      +
      +<selectors>
      +  <selector>
      +    <value>div#copy</value>
      +    <commands>
      +      <command>
      +	<name>copyChildrenTo</name>
      +	<data name="html_id">demo</data>
      +      </command>
      +      <command>
      +	<name>clearChildren</name>
      +	<data name="none"/>
      +      </command>
      +    </commands>
      +  </selector>
      +</selectors>
      +   
      +

      + The XML specifies two commands to apply to the div#copy node : +

      +

      + copyChildrenTo copies the children of the DOM node to the node with id demo. +

      +

      + clearChildren removes the children of the DOM node. +

      + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_demo_index.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_demo_index.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,15 @@ + + + +

      Azax demos

      + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.kukit ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ + + + + getInputField + + + saveText + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_instant_edit.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,51 @@ + + + + + + +

      All demos

      + +

      Instant edit Demo

      +
      +
      +
      + click me! + +
      + +
      + + +

      ab

      + + + +

      Remove Node with XPath

      +

      XPath

      +

      next 1

      +

      next 2

      +

      next 3

      + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.kukit ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,15 @@ + + + + + + getCurrentTime + + + + + + getAutoupdateMarkup + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_three_autoupdate.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,35 @@ + + + + + + + + +

      All demos

      +

      Three autoupdate

      + +

      Demo

      +
      +
      + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.kukit ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + getSubTree + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_tree.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,39 @@ + + + + + + + +

      All demos

      + + +

      Instant edit Demo

      +
      +
        +
      • 1
      • +
      • +
          +
        • 2.1
        • +
        • 2.2
        • +
        +
      • +
          +
        • 3.1
        • +
        • 3.2
        • +
        +
      + + + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.kukit ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.kukit Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + getCorrespondingSelect + + Added: kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/azax_two_select.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,20 @@ + + + + + + +

      All demos

      +

      Two Selects

      + +
      + + + + + + +
      +

      Kukit response

      + + Added: kukit/azax/trunk/demos/azaxdemo/browser/header_macros.pt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/browser/header_macros.pt Sun May 21 15:02:20 2006 @@ -0,0 +1,41 @@ + + + + + + + + + + + + + Added: kukit/azax/trunk/demos/azaxdemo/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/demos/azaxdemo/helpers.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/helpers.py Sun May 21 15:02:20 2006 @@ -0,0 +1,61 @@ +############################################################################## +# +# Copyright (c) 2004, 2005 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Test helpers + +$Id: helpers.py 12915 2005-05-31 10:23:19Z philikon $ +""" +import urllib + +def add_and_edit(self, id, REQUEST): + """Helper function to point to the object's management screen if + 'Add and Edit' button is pressed. + id -- id of the object we just added + """ + if REQUEST is None: + return + try: + u = self.DestinationURL() + except: + u = REQUEST['URL1'] + if REQUEST.has_key('submit_edit'): + u = "%s/%s" % (u, urllib.quote(id)) + REQUEST.RESPONSE.redirect(u+'/manage_main') + + +from OFS.Folder import Folder + +class NoVerifyPasteFolder(Folder): + """Folder that does not perform paste verification. + Used by test_events + """ + def _verifyObjectPaste(self, object, validate_src=1): + pass + +def manage_addNoVerifyPasteFolder(container, id, title=''): + container._setObject(id, NoVerifyPasteFolder()) + folder = container[id] + folder.id = id + folder.title = title + +class FiveTraversableFolder(Folder): + """Folder that is declared Five traversable, see configure.zcml + """ + pass + +def manage_addFiveTraversableFolder(container, id, title=''): + container._setObject(id, FiveTraversableFolder()) + folder = container[id] + folder.id = id + folder.title = title + Added: kukit/azax/trunk/demos/azaxdemo/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,4 @@ +from zope.interface import Interface + +class ISimpleContent(Interface): + pass \ No newline at end of file Added: kukit/azax/trunk/demos/azaxdemo/simplecontent.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/simplecontent.py Sun May 21 15:02:20 2006 @@ -0,0 +1,52 @@ +############################################################################## +# +# Copyright (c) 2004, 2005 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Simple content implementationm + +$Id: simplecontent.py 12915 2005-05-31 10:23:19Z philikon $ +""" +from OFS.SimpleItem import SimpleItem +from Globals import InitializeClass +from AccessControl import ClassSecurityInfo +from helpers import add_and_edit +from Products.PageTemplates.PageTemplateFile import PageTemplateFile +from zope.interface import implements +from interfaces import ISimpleContent + +class SimpleContent(SimpleItem): + implements(ISimpleContent) + + meta_type = 'AzaxDemo SimpleContent' + security = ClassSecurityInfo() + + def __init__(self, id, title): + self.id = id + self.title = title + + security.declarePublic('direct') + def direct(self): + """Should be able to traverse directly to this as there is no view. + """ + return "Direct traversal worked" + +InitializeClass(SimpleContent) + +manage_addSimpleContentForm = PageTemplateFile( + "www/simpleContentAdd", globals(), + __name__ = 'manage_addSimpleContentForm') + +def manage_addSimpleContent(self, id, title, REQUEST=None): + """Add the simple content.""" + id = self._setObject(id, SimpleContent(id, title)) + add_and_edit(self, id, REQUEST) + return '' Added: kukit/azax/trunk/demos/azaxdemo/tests/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/tests/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1 @@ + Added: kukit/azax/trunk/demos/azaxdemo/tests/azaxdemo.sel ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/tests/azaxdemo.sel Sun May 21 15:02:20 2006 @@ -0,0 +1,143 @@ + + + +azaxdemo + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      azaxdemo
      assertTitle
      clickAndWaitlink=Change tag content
      assertTitle
      clickchange
      assertTextPresentit worked again
      clickclear
      clicklink=All demos
      assertTitle
      clickAndWaitlink=Two selects
      assertTitle
      selectfirstlabel=animals
      assertValueseconddog
      selectfirstlabel=machines
      assertValuesecondcomputer
      clicklink=All demos
      assertTitle
      clickAndWaitlink=Three autoupdate
      assertTitle
      clickstart-update
      clicklink=All demos
      assertTitle
      clickAndWaitlink=Cancel Submit Click
      assertTitle
      typetext_saveaaa
      clicksubmit
      assertTextPresentAsync saved aaa
      + + Added: kukit/azax/trunk/demos/azaxdemo/tests/test_azaxview.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/tests/test_azaxview.py Sun May 21 15:02:20 2006 @@ -0,0 +1,55 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +import unittest, os +from zope.testing import doctest +from Testing.ZopeTestCase import ZopeTestCase + +from Products.azaxdemo.azaxview import AzaxView + +class FakeResponse: + _stuff = {} + + def setHeader(self, name, value): + self._stuff[name] = value + +class FakeRequest: + response = FakeResponse() + +class AzaxViewTestCase(ZopeTestCase): + + def test_instanciation(self): + ob = AzaxView(None, None) + self.assertNotEquals(ob, None) + + def test_getDivContent(self): + req = FakeRequest() + + ob = AzaxView(None, req) + + res = ob.getDivContent() + self.assertEquals(res, 'div.clickableinnerHTML<h1>it worked</h1>') + + +def test_suite(): + return unittest.TestSuite(( + unittest.makeSuite(AzaxViewTestCase), + doctest.DocTestSuite('Products.azaxdemo.azaxview'), + )) Added: kukit/azax/trunk/demos/azaxdemo/www/simpleContentAdd.zpt ============================================================================== --- (empty file) +++ kukit/azax/trunk/demos/azaxdemo/www/simpleContentAdd.zpt Sun May 21 15:02:20 2006 @@ -0,0 +1,45 @@ +

      Header

      + +

      Form Title

      + +

      +Add Simple Content +

      + +
      + + + + + + + + + + + + + +
      +
      + Id +
      +
      + +
      +
      + Title +
      +
      + +
      + +
      + +
      +
      +
      + +

      Footer

      Added: kukit/azax/trunk/dtds/xhtml-lat1.ent ============================================================================== --- (empty file) +++ kukit/azax/trunk/dtds/xhtml-lat1.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/dtds/xhtml-special.ent ============================================================================== --- (empty file) +++ kukit/azax/trunk/dtds/xhtml-special.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/dtds/xhtml-symbol.ent ============================================================================== --- (empty file) +++ kukit/azax/trunk/dtds/xhtml-symbol.ent Sun May 21 15:02:20 2006 @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/dtds/xhtml1-transitional.dtd ============================================================================== --- (empty file) +++ kukit/azax/trunk/dtds/xhtml1-transitional.dtd Sun May 21 15:02:20 2006 @@ -0,0 +1,1201 @@ + + + + + +%HTMLlat1; + + +%HTMLsymbol; + + +%HTMLspecial; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,31 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2005 +# Authors: +# Godefroid Chapelle +# Tarek Ziad? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# + +from zope.interface import Interface + +class IAzaxCommands(Interface): + 'Azax commands' + +class IAzaxCommand(Interface): + 'An Azax command' + +class IAzaxParam(Interface): + 'An Azax parameter' Added: kukit/azax/trunk/meta.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/meta.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,6 @@ + + + + + Added: kukit/azax/trunk/parsers.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/parsers.py Sun May 21 15:02:20 2006 @@ -0,0 +1,92 @@ +# -*- coding: ISO-8859-15 -*- +# Copyright (c) 2006 +# Authors: +# Godefroid Chapelle +# Bal?zs Re? +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +'''\ +Parser implementations + +These assure that output is valid XML or HTML and fix the code or +raise an exception. + +The wrapping makes it possible to change the parser transparently +if necessary. +''' + +from unicode_quirks import force_unicode +import re, htmlentitydefs + +class replace_html_named_entities(object): + + _entity_regexp = re.compile(r'&([A-Za-z]+);') + + def _entity_replacer(m): + value = htmlentitydefs.name2codepoint.get(m.group(1)) + if value is None: + return m.group(0) + return "&#%i;" % value + _entity_replacer = staticmethod(_entity_replacer) + + def _replace(cls, value): + return cls._entity_regexp.sub(cls._entity_replacer, value) + _replace = classmethod(_replace) + + def __new__(cls, value): + return cls._replace(value) + +class XmlParser(object): + '''Custom XML parser + + wraps the parser implementation + ''' + + from BeautifulSoup import BeautifulStoneSoup + + def __init__(self, value): + value = force_unicode(value) + self.soup = self.BeautifulStoneSoup(value) + + def __call__(self): + return unicode(self.soup) + +class HtmlParser(object): + '''Custom HTML parser + + wraps the parser implementation + ''' + + from BeautifulSoup import BeautifulSoup + + def __init__(self, value): + value = force_unicode(value) + self.soup = self.BeautifulSoup(value) + # + # XXX ree: I think these are not needed any more. See + # kukit patches r25865, r25866 that IMO fix this on IE. + # + #for tag in self.soup.fetch(recursive=False): + # tag['xmlns'] = "http://www.w3.org/1999/xhtml" + + def __call__(self): + value = unicode(self.soup) + # Replace named HTML entitied in each case. + # This is necessary for two reasons: + # 1. Fixes an IE bug. + # 2. Needed for the alternate transport mechanism to work. + value = replace_html_named_entities(value) + return value Added: kukit/azax/trunk/plugins/EXTERNALS.TXT ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/EXTERNALS.TXT Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +# +# results of svn propget svn:externals +# https://svn.z3lab.org/z3lab/azax/trunk/plugins +# +# You can update your working dir by: +# svn propset svn:externals -F EXTERNALS.TXT . +# + +# concatresource is now included like as batteries +concatresource http://codespeak.net/svn/z3/jsonserver/branch/merge/concatresource Added: kukit/azax/trunk/plugins/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,9 @@ +'''\ +Module init +''' + +from registry import AzaxPluginError +from plugin import registerPlugin +from command import Command +from event_type import EventType +from event_action import EventAction Added: kukit/azax/trunk/plugins/command.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/command.py Sun May 21 15:02:20 2006 @@ -0,0 +1,13 @@ + +from plugin import Plugin + +class Command(Plugin): + '''The command plugin + + ''' + + plugintype = 'command' + + def __init__(self, name, jsfile): + Plugin.__init__(self, name, jsfile) + Added: kukit/azax/trunk/plugins/concatresource/README ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/README Sun May 21 15:02:20 2006 @@ -0,0 +1,82 @@ + +Objectives +---------- + +I made this utility for the works in the jsonserver integration and +AZAX/kukit. This was meant to be a temporary, lightweight solution but it +has long term merits too. I needed this because I wanted a solution that +works right now, works on zope3 and zope 2.8 even (so zc.resourcelibraries +was not a choice), does not depend on Plone (so ResourceRegistries is out +too). + +The interesting point of the utility is: instead of building registries to +emit include tags in the HTML, it simply makes one resource that needs to +be imported in a single line:: + + + +Also: + +- it compresses the JS or CSS files with Florian Schulze's compressor + from ResourceRegistries + +- it contains an utility interface that another component can implement + to dynamically extend the list of files (like I did with AZAX). So the + "registry" is not really implemented but can be plugged in. + +- it is tested (although not much) to work with Zope 2.8, 2.9, 3.1, 3.2 + +Meanwhile I implemented the cache headers for the resources correctly +here, the current implementation of caching is broken in Five and I +believe in Z3 resources as well; beware that I considered the original +Zope3 code only and did not look at additional utilities. According to my +observation: in Z3 the original resources attempt to handle the cache +headers correctly but never really check if the file gets changed on the +filesystem (only on restart). If this is really a problem and not just I +believe, then it is fixed in my code. + +More information on this issue: + +http://article.gmane.org/gmane.comp.web.zope.z3base.general/53 + +Compression +----------- + +You can use more levels of compressions with the compress_level attribute. + + + +Some explanation: + +- compression is only implemented for JavaScript and CSS + +- The default level is "safe", this is useable with all scripts + +- The "full" level gives even better compression by taking out all newlines + and mangling private variable names, but some preparation must be made + in the scripts for that (like putting a ; after }-s I think, and + also maybe more). + +- Specifying "none" will leave your resource uncompressed. This + can be useful for debugging. + +Release notes +------------- + +In Zope 2.9.2 there is Five 1.3.3 included. This contains a but that +the resources will never be looked up from the +application root. + +To fix this, you need to update Five to version 1.3.5, or update Zope +to version 2.9.3 (not available of the time of writing this). + + Added: kukit/azax/trunk/plugins/concatresource/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,11 @@ +'''\ +Product init +''' +# alias myself to Products, directly +try: + import sys, Products + if not hasattr(Products, 'concatresource'): + # only 1st import is aliased. + Products.concatresource = sys.modules['Products.concatresource'] = sys.modules[globals()['__name__']] +except ImportError: + pass Added: kukit/azax/trunk/plugins/concatresource/cachingadapter.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/cachingadapter.py Sun May 21 15:02:20 2006 @@ -0,0 +1,61 @@ + +from time import time +from interfaces import ICachedResource +from zope.interface import implements +from zope.app.datetimeutils import rfc1123_date + +class CachedResource(object): + 'Adapts a ContextFile to a cached resource' + implements(ICachedResource) + + # this is a period in secs within what the resource + # modification time is never checked. + # (if 0, always checked) + # XXX actually this should be kept 0 for debugging and like a minute + # XXX for production + lmt_check_period = 60.0 + ##lmt_check_period = 0 + + def __init__(self, context): + self.context = context + self.lmt_last_checked = 0 + + def _fetchdata(self): + try: + result = self._contents + except AttributeError: + result = self._contents = self.context.getContents() + return result + + def _deldata(self): + del self._contents + + def purgeData(self): + 'Force file contents to be reloaded' + ##print "***** PURGE", self.context.__name__ + try: + self._deldata() + except AttributeError: + pass + + # Once fetched, data is cached in the object until + # explicitely deleted. + data = property(lambda self: self._fetchdata()['data'], None, _deldata) + content_type = property(lambda self: self._fetchdata()['content_type'], None, _deldata) + + # Last modified time is calculated on demand + # but never more often then lmt_check_period + def _fetchlm(self): + now = time() + if now - self.lmt_last_checked > self.lmt_check_period: + self.lmt_last_checked = now + lmt = float(self.context.getLastMod()) or now + lmh = rfc1123_date(lmt) + d = self._last_mod = dict(lmt = lmt, lmh = lmh) + ##print "***** LMT reread", d + else: + d = self._last_mod + return d + + lmt = property(lambda self: self._fetchlm()['lmt']) + lmh = property(lambda self: self._fetchlm()['lmh']) Added: kukit/azax/trunk/plugins/concatresource/compression/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,20 @@ +'''\ +Preprocess resource files by applying compression on them +''' + +__all__ = ('compress', ) + +from javascript import compress as compress_javascript +from css import compress as compress_css + +compress_methods = { + 'application/x-javascript': compress_javascript, + 'text/css': compress_css, + } + +default_compress_method = lambda text: text + +def compress(data, content_type, compress_level): + 'Returns compressed text for a given content type' + method = compress_methods.get(content_type, default_compress_method) + return method(data, compress_level) Added: kukit/azax/trunk/plugins/concatresource/compression/css.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/css.py Sun May 21 15:02:20 2006 @@ -0,0 +1,18 @@ +'''\ +The css compressor uses the 3rdparty packer module +that is taken from Plone's ResourceRegistries. +''' + +from thirdparty.packer import CSSPacker + +csspacker_safe = CSSPacker('safe') +csspacker_full = CSSPacker('full') + +def compress(data, compress_level): + if compress_level == "safe": + return csspacker_safe.pack(data) + elif compress_level == "full": + return csspacker_full.pack(data) + else: + # none + return data Added: kukit/azax/trunk/plugins/concatresource/compression/javascript.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/javascript.py Sun May 21 15:02:20 2006 @@ -0,0 +1,17 @@ +'''\ +The javascript compressor uses the 3rdparty packer module +that is taken from Plone's ResourceRegistries.''' + +from thirdparty.packer import JavascriptPacker + +jspacker_safe = JavascriptPacker('safe') +jspacker_full = JavascriptPacker('full') + +def compress(data, compress_level): + if compress_level == "safe": + return jspacker_safe.pack(data) + elif compress_level == "full": + return jspacker_full.pack(data) + else: + # none + return data Added: kukit/azax/trunk/plugins/concatresource/compression/thirdparty/LICENSE ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/thirdparty/LICENSE Sun May 21 15:02:20 2006 @@ -0,0 +1,25 @@ + +packer.py is released under the MIT license. + +# packer.py +# +# Copyright (c) 2006 Florian Schulze +# +# 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. + Added: kukit/azax/trunk/plugins/concatresource/compression/thirdparty/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/thirdparty/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,4 @@ +'''\ +Module init +''' + Added: kukit/azax/trunk/plugins/concatresource/compression/thirdparty/packer.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/compression/thirdparty/packer.py Sun May 21 15:02:20 2006 @@ -0,0 +1,411 @@ +# +# packer.py +# +# Copyright (c) 2006 Florian Schulze +# +# 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. + +import re + + +class KeywordMapper: + def __init__(self, regexp, encoder): + if isinstance(regexp, (str, unicode)): + self.regexp = re.compile(regexp) + else: + self.regexp = regexp + self.encoder = encoder + self.mapping = {} + + def analyseKeywords(self, input): + matches = self.regexp.findall(input) + + protected = {} + keyword_count = {} + index = 0 + for match in matches: + if match not in keyword_count: + keyword_count[match] = 0 + protected[self.encoder(index)] = index + index = index + 1 + keyword_count[match] = keyword_count[match] + 1 + + for match in matches: + if match in protected and keyword_count[match]: + keyword_count[match] = 0 + + protected = {} + for match in keyword_count: + if not keyword_count[match]: + protected[match] = None + + ## sorted_matches = [(c,len(v),v) for v,c in keyword_count.iteritems()] + # the above line implements the original behaviour, the code below + # removes keywords which have not enough weight to be encoded, in total + # this saves some bytes, because the total length of the generated + # codes is a bit smaller. This needs corresponding code in the + # fast_decode javascript function of the decoder, see comment there + sorted_matches = [] + for value, count in keyword_count.iteritems(): + weight = count * len(value) + if len(value) >= weight: + keyword_count[value] = 0 + sorted_matches.append((0, value)) + else: + sorted_matches.append((weight, value)) + sorted_matches.sort() + sorted_matches.reverse() + sorted_matches = [x[-1] for x in sorted_matches] + + index = 0 + mapping = {} + for match in sorted_matches: + if not keyword_count[match]: + if match not in protected: + mapping[match] = (-1, match) + continue + while 1: + encoded = self.encoder(index) + index = index + 1 + if encoded in protected: + mapping[encoded] = (index-1, encoded) + continue + else: + break + mapping[match] = (index-1, encoded) + + return mapping + + def analyse(self, input): + self.mapping = self.analyseKeywords(input) + + def getKeywords(self): + sorted = zip(self.mapping.itervalues(), self.mapping.iterkeys()) + sorted.sort() + keywords = [] + for (index, encoded), value in sorted: + if index >= 0: + if encoded != value: + keywords.append(value) + else: + keywords.append('') + return keywords + + def sub(self, input): + def repl(m): + return self.mapping.get(m.group(0), ('', m.group(0)))[1] + return self.regexp.sub(repl, input) + + +class JavascriptKeywordMapper(KeywordMapper): + def __init__(self, regexp=None, encoder=None): + if regexp is None: + self.regexp = re.compile(r'\w+') + elif isinstance(regexp, (str, unicode)): + self.regexp = re.compile(regexp) + else: + self.regexp = regexp + if encoder is None: + self.encoder = self._encode + else: + self.encoder = encoder + self.mapping = {} + + def _encode(self, charCode, + mapping="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"): + result = [] + quotient = charCode + while quotient or not len(result): + quotient, remainder = divmod(quotient, 62) + result.append(mapping[remainder]) + result.reverse() + return "".join(result) + + def getDecodeFunction(self, fast=True, name=None): + jspacker = JavascriptPacker('full') + + # fast boot function + fast_decoder = r""" + // does the browser support String.replace where the + // replacement value is a function? + if (!''.replace(/^/, String)) { + // decode all the values we need + // we have to add the dollar prefix, because $encoded can be + // any keyword in the decode function below. For example + // 'constructor' is an attribute of any object and it would + // return a false positive match in that case. + while ($count--) $decode["$"+$encode($count)] = $keywords[$count] || $encode($count); + // global replacement function + $keywords = [function($encoded){$result = $decode["$"+$encoded]; return $result!=undefined?$result:$encoded}]; + // generic match + $encode = function(){return'\\w+'}; + // reset the loop counter - we are now doing a global replace + $count = 1; + };""" + + if name is None: + # boot function + decoder = r""" + function($packed, $ascii, $count, $keywords, $encode, $decode) { + $encode = function($charCode) { + return ($charCode < $ascii ? "" : $encode(parseInt($charCode / $ascii))) + + (($charCode = $charCode % $ascii) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); + }; + // fastDecodePlaceholder + while ($count--) + if ($keywords[$count]) + $packed = $packed.replace(new RegExp("\\b" + $encode($count) + "\\b", "g"), $keywords[$count]); + return $packed; + }""" + + if fast: + decoder = decoder.replace('// fastDecodePlaceholder', fast_decoder) + + decoder = jspacker.pack(decoder) + + else: + decoder = r""" + var %s = function($ascii, $count, $keywords, $encode, $decode) { + $encode = function($charCode) { + return ($charCode < $ascii ? "" : $encode(parseInt($charCode / $ascii))) + + (($charCode = $charCode %% $ascii) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36)); + }; + // fastDecodePlaceholder + var decoder = function($packed, $ascii1, $count1, $keywords1, $encode1, $decode1) { + $count1 = $count; + while ($count1--) + if ($keywords[$count1]) + $packed = $packed.replace(new RegExp("\\b" + $encode($count1) + "\\b", "g"), $keywords[$count1]); + return $packed; + }; + return decoder; + }""" % name + + if fast: + decoder = decoder.replace('// fastDecodePlaceholder', fast_decoder) + + decoder = jspacker.pack(decoder) + + keywords = self.getKeywords() + decoder = "%s(62, %i, '%s'.split('|'), 0, {});" % (decoder, len(keywords), "|".join(keywords)) + + return decoder + + def getDecoder(self, input, keyword_var=None, decode_func=None): + if keyword_var is None: + keywords = self.getKeywords() + num_keywords = len(keywords) + keywords = "|".join(keywords) + keywords = "'%s'.split('|')" % keywords + else: + keywords = keyword_var + num_keywords = len(self.getKeywords()) + + if decode_func is None: + decode_func = self.getDecodeFunction() + + escaped_single = input.replace("\\","\\\\").replace("'","\\'").replace('\n','\\n') + escaped_double = input.replace("\\","\\\\").replace('"','\\"').replace('\n','\\n') + if len(escaped_single) < len(escaped_double): + script = "'%s'" % escaped_single + else: + script = '"%s"' % escaped_double + return "eval(%s(%s,62,%i,%s,0,{}))" % (decode_func, script, + num_keywords, + keywords) + + +class Packer: + def __init__(self): + self.patterns = [] + + def copy(self): + result = Packer() + result.patterns = self.patterns[:] + return result + + def _repl(self, match): + # store protected part + self.replacelist.append(match.group(1)) + # return escaped index + return "\x00%i" % len(self.replacelist) + + def pack(self, input): + # list of protected parts + self.replacelist = [] + # escape the escapechar + output = input.replace('\x00','\x00\x00') + for regexp, replacement, keyword_encoder in self.patterns: + if replacement is None: + if keyword_encoder is None: + # protect the matched parts + output = regexp.sub(self._repl, output) + else: + mapper = KeywordMapper(regexp=regexp, + encoder=keyword_encoder) + # get keywords + mapper.analyse(output) + # replace keywords + output = mapper.sub(output) + else: + # substitute + output = regexp.sub(replacement, output) + # restore protected parts + replacelist = list(enumerate(self.replacelist)) + replacelist.reverse() # from back to front, so 1 doesn't break 10 etc. + for index, replacement in replacelist: + # we use lambda in here, so the real string is used and no escaping + # is done on it + before = len(output) + regexp = re.compile('(? n, $$name -> na, $top1 -> t1, $top2 -> t2 + def _dollar_replacement(match): + length = len(match.group(2)) + start = length - max(length - len(match.group(3)), 0) + result = match.group(1)[start:start+length] + match.group(4) + return result + self.sub(r"""((\$+)([a-zA-Z\$_]+))(\d*)\b""", _dollar_replacement) + + self.keywordSub(r"""\b_[A-Za-z\d]\w*""", lambda i: "_%i" % i) + + # protect strings + # this is more correct, but needs more testing + # it has to be more accurate because of the more aggresive packing later + self.protect(r"""(?<=return|..case|.....[=\[|(,?:+])\s*((?P['"])(?:\\(?P=quote)|\\\n|.)*?(?P=quote))""", re.DOTALL) + else: + # protect strings + # these sometimes catch to much, but in safe mode this doesn't hurt + self.protect(r"""('(?:\\'|\\\n|.)*?')""") + self.protect(r'''("(?:\\"|\\\n|.)*?")''') + # protect regular expressions + self.protect(r"""\s+(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)""") + self.protect(r"""([^\w\$\/'"*)\?:]\/[^\/\n\r\*][^\/\n\r]*\/g?i?)""") + # multiline comments + self.sub(r'/\*.*?\*/', '', re.DOTALL) + # one line comments + self.sub(r'\s*//.*$', '', re.MULTILINE) + # strip whitespace at the beginning and end of each line + self.sub(r'^[ \t\r\f\v]*(.*?)[ \t\r\f\v]*$', r'\1', re.MULTILINE) + # whitespace after some special chars but not + # before function declaration + self.sub(r'([{;\[(,=&|\?:<>%!/])\s+(?!function)', r'\1') + # after an equal sign a function definition is ok + self.sub(r'=\s+(?=function)', r'=') + if level == 'full': + # whitespace after some more special chars + self.sub(r'([};\):,])\s+', r'\1') + # whitespace before some special chars + self.sub(r'\s+([={},&|\?:\.()<>%!/\]])', r'\1') + # whitespace before plus chars if no other plus char before it + self.sub(r'(? Added: kukit/azax/trunk/plugins/concatresource/concatresource-meta.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/concatresource-meta.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,2 @@ + + \ No newline at end of file Added: kukit/azax/trunk/plugins/concatresource/concatresource.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/concatresource.py Sun May 21 15:02:20 2006 @@ -0,0 +1,134 @@ +from zope.app.publisher.browser import BrowserView +from zope.publisher.interfaces.browser import IBrowserPublisher +from zope.app.datetimeutils import time as timeFromDateTimeString +from zope.interface import implements +from concatfileresource import ConcatFiles +from interfaces import ICachedResource +import cachingadapter # force adapter registration + +try: + import Products.Five +except ImportError: + __five__ = False + from zope.app.publisher.browser.resource import Resource +else: + __five__ = True + try: + # Zope 2.8 / Five 1.0.2 + from Products.Five.resource import Resource + __five_pre_1_3_ = True + except ImportError: + # Zope 2.9 / Five 1.3 + from Products.Five.browser.resource import Resource + __five_pre_1_3__ = False + +class GenericResource(BrowserView, Resource): + """A publishable resource""" + + if __five__: + #implements(IBrowserPublisher) + + def __browser_default__(self, request): + return self, (request.REQUEST_METHOD,) + + else: + implements(IBrowserPublisher) + + def publishTraverse(self, request, name): + '''See interface IBrowserPublisher''' + raise LookupError(name) + + def browserDefault(self, request): + '''See interface IBrowserPublisher''' + return getattr(self, request.method), () + + # for unit tests + def _testData(self): + return self.context.data + + def chooseContext(self): + """Choose the appropriate context""" + return self.context + + def GET(self): + """Default document""" + + file = self.chooseContext() + request = self.request + response = request.response + + # HTTP If-Modified-Since header handling. This is duplicated + # from OFS.Image.Image - it really should be consolidated + # somewhere... + if __five__: + header = request.get_header('If-Modified-Since') + else: + header = request.getHeader('If-Modified-Since', None) + if header is not None: + header = header.split(';')[0] + # Some proxies seem to send invalid date strings for this + # header. If the date string is not valid, we ignore it + # rather than raise an error to be generally consistent + # with common servers such as Apache (which can usually + # understand the screwy date string as a lucky side effect + # of the way they parse it). + try: mod_since=long(timeFromDateTimeString(header)) + except: mod_since=None + if mod_since is not None: + last_mod = file.lmt + if last_mod > 0 and last_mod <= mod_since: + response.setStatus(304) + return '' + + response.setHeader('Content-Type', file.content_type) + response.setHeader('Last-Modified', file.lmh) + # Cache for one day + response.setHeader('Cache-Control', 'public,max-age=86400') + data = file.data + # force delete file contents + file.purgeData() + + return data + + def HEAD(self): + file = self.chooseContext() + response = self.request.response + response.setHeader('Content-Type', file.content_type) + response.setHeader('Last-Modified', file.lmh) + # Cache for one day + response.setHeader('Cache-Control', 'public,max-age=86400') + return '' + +class ResourceFactory(object): + + factory = None + resource = None + + def __init__(self, path, name, compress_level, resource_factory=None, checker=None): + self.__name = name + self.__path = path + self.__compress_level = compress_level + if resource_factory is not None: + self.resource = resource_factory + # z3 only + self.__checker = checker + + def __call__(self, request): + try: + rsrc = self.__rsrc + except AttributeError: + # Delayed creation. That assures that registry is set up by this time. + rsrc = self.__rsrc = ICachedResource(self.factory(self.__path, self.__name, self.__compress_level)) + resource = self.resource(rsrc, request) + # z3 only + resource.__name__ = self.__name + if self.__checker is not None: + # z3 only + resource.__Security_checker__ = self.__checker + return resource + +class ConcatResourceFactory(ResourceFactory): + """A factory for concat resources""" + + factory = ConcatFiles + resource = GenericResource Added: kukit/azax/trunk/plugins/concatresource/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,12 @@ + + + + + + + + Added: kukit/azax/trunk/plugins/concatresource/directives.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/directives.py Sun May 21 15:02:20 2006 @@ -0,0 +1,38 @@ +from zope.interface import Interface +from zope.configuration.fields import GlobalObject, Tokens, Path, \ + PythonIdentifier, MessageID +from zope.schema import TextLine, Text, Id, Choice +from zope.app.security.fields import Permission +from fields import PathList +from zope.app.component.metadirectives import IBasicViewInformation +from zope.app.publisher.browser.metadirectives import IBasicResourceInformation + +class IConcatResourceDirective(IBasicResourceInformation): + """ + Defines a concatenated browser resource + """ + + name = TextLine( + title=u"The name of the resource", + description=u""" + This is the name used in resource urls. Resource urls are of + the form site/@@/resourcename, where site is the url of + "site", a folder with a service manager. + + We make resource urls site-relative (as opposed to + content-relative) so as not to defeat caches.""", + required=True + ) + + files = PathList( + title=u"Files", + description=u"A space separated list of resource files", + required=True + ) + + compress_level = Choice( + title=u"Compress level", + description=u"Level of compression applied, by default 'safe'.", + values=(u'none', u'safe', u'full'), + required=False, + ) Added: kukit/azax/trunk/plugins/concatresource/fields.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/fields.py Sun May 21 15:02:20 2006 @@ -0,0 +1,19 @@ + +from zope.schema import Text +from zope.schema.interfaces import IFromUnicode +from zope.interface import implements +import os.path + +class PathList(Text): + + implements(IFromUnicode) + + def fromUnicode(self, u): + result = [] + for u in u.split(): + if os.path.isabs(u): + path = os.path.normpath(u) + else: + path = self.context.path(u) + result.append(path) + return result Added: kukit/azax/trunk/plugins/concatresource/fileresource.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/fileresource.py Sun May 21 15:02:20 2006 @@ -0,0 +1,44 @@ +'''\ +this is a fixture of zope app's fileresource + +It correctly handles cache expiration headers and rereads +files when needed only. +''' + +from zope.interface import implements +try: + # XXX ??? What zope version needs this? + from zope.app.contenttypes import guess_content_type +except ImportError: + from zope.app.content_types import guess_content_type +import os +from interfaces import IContextFile + +class File(object): + implements(IContextFile) + + def __init__(self, path, name): + self.path = path + self.__name__ = name + + def getLastMod(self): + return os.path.getmtime(self.path) + + def getContents(self): + ##print "***** READ", self.path + f = open(self.path, 'rb') + data = f.read() + f.close() + content_type, enc = guess_content_type(self.path, data) + return dict(data = data, content_type = content_type) + +class Image(File): + """Image objects stored in external files.""" + + def getContents(self): + d = super(Image, self).getContens() + if d ['content_type'] in (None, 'application/octet-stream'): + ext = os.path.splitext(self.path)[1] + if ext: + d['content_type'] = 'image/%s' % ext[1:] + return d Added: kukit/azax/trunk/plugins/concatresource/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,41 @@ + +from zope.interface import Interface +from zope.schema import Bytes, TextLine, Float + +class IContextFile(Interface): + + def getLastMod(self): + 'Returns last modification time of the file' + + def getContents(): + 'Reads the data and content type of the file' + +class ICachedResource(Interface): + + data = Bytes(title = u'The content data of the file') + + content_type = TextLine(title = u'The mime content type of the file') + + lmt = Float(title = u'Last modification timestamp') + + lmh = Float(title = u'Last modification in human readable form') + + def purgeData(self): + 'Purges the cached data' + +class IConcatResourceAddon(Interface): + '''Utility to register addons + + This can be used to dynamically extend components for a given resource. + We don't provide implementation for this here, but other + components can implement this to provide dynamic add-ons. + + The name of the utility should be the name of the resource. + ''' + + def getAddonFiles(request): + '''Returns a list of addon files. + This will be concatenated to the end of the static list. + ''' + + Added: kukit/azax/trunk/plugins/concatresource/meta.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/meta.py Sun May 21 15:02:20 2006 @@ -0,0 +1,96 @@ + +import os +from zope.security.checker import CheckerPublic, NamesChecker +from zope.configuration.exceptions import ConfigurationError +from zope.publisher.interfaces.browser import IBrowserRequest +from zope.app.component.metaconfigure import handler +from zope.interface import Interface +from concatresource import ConcatResourceFactory + +try: + import Products.Five +except ImportError: + __five__ = False +else: + __five__ = True + from Products.Five.metaclass import makeClass + from Products.Five.security import getSecurityInfo, protectClass, protectName,\ + initializeClass + +try: + from zope.app.servicenames import Presentation + _layer = 'default' + __pre_3_2__ = True +except: + from zope.publisher.interfaces.browser import IDefaultBrowserLayer + _layer = IDefaultBrowserLayer + __pre_3_2__ = False + +# z3 only +allowed_names = ('GET', 'HEAD', 'publishTraverse', 'browserDefault', + 'request', '__call__') + +# We keep this in order to allow an occasional merge to browser:resource +_factory_map = { + 'files': { + 'prefix': 'ConcatResource', + 'count': 0, + 'factory': ConcatResourceFactory + }, + } + +def concatresource(_context, name, files=None, compress_level='safe', layer=_layer, permission='zope.Public'): + + if not files: + raise ConfigurationError( + "Must use a files" + " attribute for concatresource directives, with at least" + " one file contained." + ) + + res = files + res_type = 'files' + factory_info = _factory_map.get(res_type) + factory_info['count'] += 1 + res_factory = factory_info['factory'] + + if __five__: + checker = None + _class_name = '%s%s' % (factory_info['prefix'], factory_info['count']) + new_class = makeClass(_class_name, (res_factory.resource,), {}) + + _context.action( + discriminator = ('five:protectClass', new_class), + callable = protectClass, + args = (new_class, permission) + ) + _context.action( + discriminator = ('five:initialize:class', new_class), + callable = initializeClass, + args = (new_class,) + ) + + else: + new_class = res_factory.resource + + if permission == 'zope.Public': + permission = CheckerPublic + + checker = NamesChecker(allowed_names, permission) + + factory = res_factory(res, name, compress_level, resource_factory=new_class, checker=checker) + + if __pre_3_2__: + _context.action( + discriminator = ('resource', name, IBrowserRequest, layer), + callable = handler, + args = (Presentation, 'provideResource', + name, IBrowserRequest, factory, layer), + ) + else: + _context.action( + discriminator = ('resource', name, IBrowserRequest, layer), + callable = handler, + args = ('provideAdapter', + (layer,), Interface, name, factory, _context.info), + ) Added: kukit/azax/trunk/plugins/concatresource/meta.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/meta.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,15 @@ + + + + + + + + + Added: kukit/azax/trunk/plugins/concatresource/test/README ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/test/README Sun May 21 15:02:20 2006 @@ -0,0 +1,5 @@ + +I yet have to add tests that work transparently with zope3 and five. + +Till then, include the configure.zmcl in this directory from the root. + Added: kukit/azax/trunk/plugins/concatresource/test/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/test/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,4 @@ +'''\ +Module init +''' + Added: kukit/azax/trunk/plugins/concatresource/test/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/test/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,18 @@ + + + + + + + Added: kukit/azax/trunk/plugins/concatresource/test/test1.js ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/test/test1.js Sun May 21 15:02:20 2006 @@ -0,0 +1,13 @@ + +_PythonKw = function(kw) { + this.kw = kw; + } + +_PythonKw.prototype.toJSON = function() { + var pack = {"pythonKwMaRkEr": this.kw}; + return toJSON(pack); + } + +function PythonKw(kw) { + return new _PythonKw(kw); + } Added: kukit/azax/trunk/plugins/concatresource/test/test2.js ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/concatresource/test/test2.js Sun May 21 15:02:20 2006 @@ -0,0 +1,10 @@ +/* A 2nd js file */ + +/* +* +* +* +*/ + +var someContents = "Some content here, too."; +var someMoreContents = "Some more content."; Added: kukit/azax/trunk/plugins/configure.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/configure.py Sun May 21 15:02:20 2006 @@ -0,0 +1,48 @@ + +import os.path +from command import Command +from event_type import EventType +from event_action import EventAction +from plugin import registerPlugin + +#class AzaxConfigurationError(Exception): +# pass + +def registerCommand(_context, name, jsfile=None): + 'Directive that registers a command' + + # check to see if the file exists + if jsfile is not None: + file(jsfile, 'rb').close() + + _context.action( + discriminator = ('registerAzaxCommand', name, jsfile), + callable = registerPlugin, + args = (Command, name, jsfile), + ) + +def registerEventType(_context, name, jsfile=None): + 'Directive that registers an event type' + + # check to see if the file exists + if jsfile is not None: + file(jsfile, 'rb').close() + + _context.action( + discriminator = ('registerAzaxEventType', name, jsfile), + callable = registerPlugin, + args = (EventType, name, jsfile), + ) + +def registerEventAction(_context, name, jsfile=None): + 'Directive that registers an event action' + + # check to see if the file exists + if jsfile is not None: + file(jsfile, 'rb').close() + + _context.action( + discriminator = ('registerAzaxEventAction', name, jsfile), + callable = registerPlugin, + args = (EventAction, name, jsfile), + ) Added: kukit/azax/trunk/plugins/configure.zcml ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/configure.zcml Sun May 21 15:02:20 2006 @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: kukit/azax/trunk/plugins/directives.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/directives.py Sun May 21 15:02:20 2006 @@ -0,0 +1,48 @@ +from zope.interface import Interface +from zope.schema import TextLine +from zope.configuration.fields import Path + +class IRegisterCommandDirective(Interface): + 'Register an AZAX command plugin' + + name = TextLine( + title=u"Name", + description=u"The name of the command plugin.", + required=True, + ) + + jsfile = Path( + title=u"Javascript file", + description=u"The path of the javascript file that defines the plugin", + required=False, + ) + +class IRegisterEventTypeDirective(Interface): + 'Register an AZAX event type' + + name = TextLine( + title=u"Name", + description=u"The name of the event type plugin.", + required=True, + ) + + jsfile = Path( + title=u"Javascript file", + description=u"The path of the javascript file that defines the plugin", + required=False, + ) + +class IRegisterEventActionDirective(Interface): + 'Register an AZAX event action' + + name = TextLine( + title=u"Name", + description=u"The name of the event action plugin.", + required=True, + ) + + jsfile = Path( + title=u"Javascript file", + description=u"The path of the javascript file that defines the plugin", + required=False, + ) Added: kukit/azax/trunk/plugins/event_action.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/event_action.py Sun May 21 15:02:20 2006 @@ -0,0 +1,13 @@ + +from plugin import Plugin + +class EventAction(Plugin): + '''The event action plugin + + ''' + + plugintype = 'event_action' + + def __init__(self, name, jsfile): + Plugin.__init__(self, name, jsfile) + Added: kukit/azax/trunk/plugins/event_type.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/event_type.py Sun May 21 15:02:20 2006 @@ -0,0 +1,13 @@ + +from plugin import Plugin + +class EventType(Plugin): + '''The event type plugin + + ''' + + plugintype = 'event_type' + + def __init__(self, name, jsfile): + Plugin.__init__(self, name, jsfile) + Added: kukit/azax/trunk/plugins/interfaces.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/interfaces.py Sun May 21 15:02:20 2006 @@ -0,0 +1,7 @@ +from zope.interface import Interface + +class IAzaxPluginRegistry(Interface): + '''Utility of registration + + This allows components to register additional AZAX plugins. + ''' Added: kukit/azax/trunk/plugins/json/__init__.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/json/__init__.py Sun May 21 15:02:20 2006 @@ -0,0 +1,5 @@ +'''\ +Module init +''' + +from utils import getJsonAddonFiles Added: kukit/azax/trunk/plugins/json/browser/jsonkukit.js ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/json/browser/jsonkukit.js Sun May 21 15:02:20 2006 @@ -0,0 +1,134 @@ + +/* +* Supplemental script for json +* +* This gets included when json plugins are registered. +* +* This transparently replaces the normal transport with JSON. +* To do this, we overwrite notifyServer and at one point we +* return to the normal execution chain (after results are received). +* (We need to do this because even an old style event plugin may +* invoke new style command plugins as a response.) +* +* To manually call the server from an event plugin, the +* makeJSONKukitMethod function should be used to create a +* proxy method. +*/ + +kukit.makeJSONKukitMethod = function(url, methodName, timeout) { + // url and methodName can be used, + // or methodName can be set to null and url will be used as a full url. + if (typeof(timeout) == 'undefined' || timeout == null) { + // default timeout is 4 sec... a sane choice + timeout = 4000; + } + return new JSONRPCMethod(url, methodName, kukit.jsonCallback, kukit.jsonError, + 4000, null, null, kukit.requestManager); +} + +// OVERWRITE kukit.js +kukit.notifyServer = function(url, target) { + // sending form, with standard form parameters. + var form_data = kukit.extractFormQuery(target).toDict(); + var method = kukit.makeJSONKukitMethod(url, null); + method(form_data); +} + +kukit.jsonCallback = function(result) { + var command_processor = new kukit.CommandProcessor(); + command_processor.parseCommands(result); + command_processor.executeCommands(); +} + +kukit.jsonError = function(result) { + kukit.logError('JSON call failed: ' + result); +} + +/* Command execution */ + +// OVERWRITE kukit.js +kukit.CommandProcessor.prototype.parseCommand = function(command) { + // Add the command. + this.addCommand(command); +} + +kukit.CommandRegistry.prototype.forceJsonRegistry = function() { + if (typeof(this.jsonContent) == 'undefined') { + this.jsonContent = {}; + this.jsonNr = 0; + } +} + +kukit.CommandRegistry.prototype.registerJson = function(name, func) { + this.forceJsonRegistry(); + // Register a new style (json) command. + if (this.jsonContent[name] || this.content[name]) { + // Do not allow redefinition + kukit.logError('Error : redefinition attempt of command ' + name); + return; + } + this.jsonContent[name] = func; + this.jsonNr = this.jsonNr + 1; +} + +// OVERWRITE kukit.js +kukit.CommandRegistry.prototype.getFunc = function(name) { + this.forceJsonRegistry(); + var func = this.content[name]; + var jsonFunc = this.jsonContent[name]; + var stub = null; + if (jsonFunc) { + // json commands receive the parameters "as are" + stub = jsonFunc; + } else if (func) { + // old style commands need all the parameters converted to DOM + stub = function(node, command_data) { + var new_params = {}; + for (var key in command_data) { + var param = command_data[key]; + + // + // now convert to dom + // + + //param = ""; + // ***BROKEN*** param = ""; + //param = ""; + + // This is a good solution since it does not do magic to + // our html - BUT html is parsed as xml + // so we currently preprocess it on the server + // and remove html named entities from it. + // + var root_txt = '
      ' + param + '
      '; + var doc = (new DOMParser()).parseFromString(root_txt, "text/xml"); + var root = doc.getElementsByTagName('div')[0]; + + // This would be the perfect solution: insert our stuff + // into the main document that is, since it is html, + // will parse html in correctly. But the problem is + // that is also sensitive to context which we have not + // at this point yet. So: