From reebalazs at codespeak.net Wed Aug 2 11:31:55 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Wed, 2 Aug 2006 11:31:55 +0200 (CEST) Subject: [Kukit-checkins] r30878 - in kukit/kukit.js/trunk: kukit tests Message-ID: <20060802093155.7331A10083@code0.codespeak.net> Author: reebalazs Date: Wed Aug 2 11:31:51 2006 New Revision: 30878 Added: kukit/kukit.js/trunk/kukit/requestmanager.js kukit/kukit.js/trunk/kukit/utils.js kukit/kukit.js/trunk/tests/test_utils.js Modified: kukit/kukit.js/trunk/kukit/kukit.js kukit/kukit.js/trunk/kukit/tokenizer.js kukit/kukit.js/trunk/tests/runner.html kukit/kukit.js/trunk/tests/runtests.js kukit/kukit.js/trunk/tests/runtests.sh kukit/kukit.js/trunk/tests/test_kssparser.js kukit/kukit.js/trunk/tests/test_tokenizer.js Log: Refactoring request manager Modified: kukit/kukit.js/trunk/kukit/kukit.js ============================================================================== --- kukit/kukit.js/trunk/kukit/kukit.js (original) +++ kukit/kukit.js/trunk/kukit/kukit.js Wed Aug 2 11:31:51 2006 @@ -1,190 +1,3 @@ -/* Create kukit namespace */ - -if (typeof(kukit) == 'undefined') { - var kukit = {}; -} - -/* check whether the logging stuff of MochiKit is available */ -try { - MochiKit.Logging.log('Initializing kukit'); - kukit.log = MochiKit.Logging.log; - kukit.logError = MochiKit.Logging.logError; - kukit.logDebug = MochiKit.Logging.logDebug; - kukit.logFatal = MochiKit.Logging.logFatal; - kukit.logWarning = MochiKit.Logging.logWarning; -} catch(e) { - kukit.log = function(str){}; - kukit.logError = kukit.log; - kukit.logDebug = kukit.log; - kukit.logFatal = kukit.log; - kukit.logWarning = kukit.log; -} - -/* -* Exception factory -* -* Create exception types: -* -* myError = kukit.exceptionFactory("myError", "My special error: "); -* -* Throwing: -* -* throw new myError("There was an error in my program."); -* -* Catching example: -* -* ... -* } catch(e) { -* if (e.name == 'JSONRPCError') { -* ... -* } else { -* throw(e); -* } -* } -* -*/ - -// XXX TODO what about IE or other browsers? -// - on IE6, the error text is not printed. - -kukit.exceptionFactory = function(name) { - var exc = function (arg1, arg2, arg3, arg4, arg5) { - var kw = this.__init__(name, arg1, arg2, arg3, arg4, arg5); - var err = new Error(kw.message); - for (var key in kw) { - err[key] = kw[key]; - } - // number is an IE-only property - if (typeof err.number == 'number') { - // show sensible error on IE - err.toString = function () { - return this.name + ': ' + this.message; - } - } - return err - } - exc.prototype.__init__ = function(name, message) { - var kw = {}; - kw.name = name; - kw.message = message; - return kw; - } - return exc -}; - -/* Request manager */ - -kukit.RequestManager = function (name, maxNr) { - this.waitingQueue = new kukit.FifoQueue(); - this.sentNr = 0; - if (typeof(name) == undefined) { - name = null; - } - this.name = name; - var namestr = ''; - if (name != null) { - namestr = '[' + name + '] ' - } - this.namestr = namestr; - if (typeof(maxNr) != undefined && maxNr != null) { - this.maxNr = maxNr; - } -}; - -// max request number -kukit.RequestManager.prototype.maxNr = 4; - -kukit.RequestManager.prototype.getInfo = function() { - return '(RQ: ' + this.sentNr + ' OUT, ' + this.waitingQueue.size() + ' WAI)'; -}; - -kukit.RequestManager.prototype.log = function(txt) { - kukit.logDebug('RequestManager ' + this.namestr + txt + ' ' + this.getInfo()); -}; - -kukit.RequestManager.prototype.pushWaitingRequest = function(func, url) { - this.waitingQueue.push([func, url]); -}; - -kukit.RequestManager.prototype.popWaitingRequest = function() { - return this.waitingQueue.pop(); -}; - -kukit.RequestManager.prototype.isWaitingRequestQueueEmpty = function() { - return this.waitingQueue.empty(); -}; - -kukit.RequestManager.prototype.pushSentRequest = function(func, url) { - // we do not store the elems, since they are not needed now - this.sentNr = this.sentNr + 1; -}; - -kukit.RequestManager.prototype.popSentRequest = function() { - this.sentNr = this.sentNr - 1; -}; - -kukit.RequestManager.prototype.isSentRequestQueueFull = function() { - return (this.sentNr >= this.maxNr) -}; - -/* request manager notification API */ - -kukit.RequestManager.prototype.notifyServer = function(func, url) { - // func must be a partial (e.g. use Mochikit or wrap up) - // here url is only for the logging - if (! this.isSentRequestQueueFull()) { - // can be sent if we are not over the limit. - this.pushSentRequest(func, url); - this.log('Notify server at ' + url); - func(); - } else { - this.pushWaitingRequest(func, url); - this.log('Queue server notification at ' + url); - } -}; - -kukit.RequestManager.prototype.receivedResult = function() { - // must be called when one result arrived - // Mark that we have one less request out. - this.popSentRequest(); - if (! this.isWaitingRequestQueueEmpty()) { - // see if we can send another request in place of the received one - // request is waiting, send it. - var waiting = this.popWaitingRequest(); - var func = waiting[0]; - var url = waiting[1]; - this.pushSentRequest(func, url); - this.log("Send queued notification to server at " + url); - func(); - } else { - this.log("Request queue empty."); - } -}; - -/* simple FIFO queue */ - -kukit.FifoQueue = function () { - this.reset(); -}; - -kukit.FifoQueue.prototype.reset = function() { - this.elements = new Array(); -}; -kukit.FifoQueue.prototype.push = function(obj) { - this.elements.push(obj); -}; -kukit.FifoQueue.prototype.pop = function() { - return this.elements.shift(); -}; -kukit.FifoQueue.prototype.empty = function() { - return ! this.elements.length; -}; -kukit.FifoQueue.prototype.size = function() { - return this.elements.length; -}; -kukit.FifoQueue.prototype.front = function() { - return this.elements[0]; -}; /* State vars srorage. This can be used from kss via a method. */ @@ -192,7 +5,7 @@ /* instantiate request manager */ -kukit.requestManager = new kukit.RequestManager(); +kukit.requestManager = new kukit.rm.RequestManager(); kukit.RuleSheetLink = function(href, res_type) { this.href = href; @@ -365,33 +178,33 @@ kukit.processResult = function(domDoc, backparms) { var domXml = domDoc.responseXML; - kukit.requestManager.receivedResult(); - if (!domXml) { - kukit.logError('Error : no kukit response'); - return; - } - // Opera <= 8.5 does not have the parseError attribute, so check for it first - if (domXml.parseError && (domXml.parseError != 0)) { - kukit.logError(Sarissa.getParseErrorText(domXml)); - return; - } - if (domXml.getElementsByTagNameNS) { - commands = domXml.getElementsByTagNameNS("http://www.kukit.org/commands/1.0", - "command"); - } else { - //IE does not know DOM2 - commands = domXml.getElementsByTagName("kukit:command"); - } - if (commands.length == 0) { - kukit.logWarning('No commands in kukit response'); - return; - } - var command_processor = new kukit.CommandProcessor(); - command_processor.parseCommands(commands, domDoc); - command_processor.executeCommands(backparms); + if (!domXml) { + // This will happen in case there are errors in the response + kukit.logError('Error : no kukit response'); + return; + } + // Opera <= 8.5 does not have the parseError attribute, so check for it first + if (domXml.parseError && (domXml.parseError != 0)) { + kukit.logError(Sarissa.getParseErrorText(domXml)); + return; + } + if (domXml.getElementsByTagNameNS) { + commands = domXml.getElementsByTagNameNS("http://www.kukit.org/commands/1.0", + "command"); + } else { + //IE does not know DOM2 + commands = domXml.getElementsByTagName("kukit:command"); + } + if (commands.length == 0) { + kukit.logWarning('No commands in kukit response'); + return; + } + var command_processor = new kukit.CommandProcessor(); + command_processor.parseCommands(commands, domDoc); + command_processor.executeCommands(backparms); }; -kukit.CommandExecutionError = kukit.exceptionFactory('CommandExecutionError'); +kukit.CommandExecutionError = kukit.ut.exceptionFactory('CommandExecutionError'); kukit.CommandExecutionError.prototype.__superinit__ = kukit.CommandExecutionError.prototype.__init__; kukit.CommandExecutionError.prototype.__init__ = function(name, e, command) { var kw = this.__superinit__(name, ''); Added: kukit/kukit.js/trunk/kukit/requestmanager.js ============================================================================== --- (empty file) +++ kukit/kukit.js/trunk/kukit/requestmanager.js Wed Aug 2 11:31:51 2006 @@ -0,0 +1,107 @@ + +/* Request manager */ + +kukit.rm = {}; + +kukit.rm.RequestManager = function (name, maxNr) { + this.waitingQueue = new kukit.ut.FifoQueue(); + this.freeSlotQueue = new kukit.ut.FifoQueue(); + this.sentNr = 0; + if (typeof(name) == undefined) { + name = null; + } + this.name = name; + var namestr = ''; + if (name != null) { + namestr = '[' + name + '] ' + } + this.namestr = namestr; + if (typeof(maxNr) != undefined && maxNr != null) { + this.maxNr = maxNr; + } + for (var i=0; i b) return +1; + else return 0; +}; + +kukit.ut.SortedQueue.prototype.reset = function() { + this.elements = new Array(); +}; + +kukit.ut.SortedQueue.prototype.push = function(obj) { + // Find the position of the object. + var i = 0; + while (i < this.elements.length && this.comparefunc(this.elements[i], obj) == -1) { + i ++; + } + // and insert it there + this.elements.splice(i, 0, obj); +}; + +kukit.ut.SortedQueue.prototype.pop = function() { + // takes minimal element + return this.elements.shift(); +}; + +kukit.ut.SortedQueue.prototype.empty = function() { + return ! this.elements.length; +}; + +kukit.ut.SortedQueue.prototype.size = function() { + return this.elements.length; +}; + +kukit.ut.SortedQueue.prototype.front = function() { + return this.elements[0]; +}; + Modified: kukit/kukit.js/trunk/tests/runner.html ============================================================================== --- kukit/kukit.js/trunk/tests/runner.html (original) +++ kukit/kukit.js/trunk/tests/runner.html Wed Aug 2 11:31:51 2006 @@ -15,6 +15,8 @@ + + @@ -26,6 +28,7 @@ + Modified: kukit/kukit.js/trunk/tests/runtests.js ============================================================================== --- kukit/kukit.js/trunk/tests/runtests.js (original) +++ kukit/kukit.js/trunk/tests/runtests.js Wed Aug 2 11:31:51 2006 @@ -17,6 +17,7 @@ function runTests() { var reporter = new StdoutReporter; var testsuite = new TestSuite(reporter); + testsuite.registerTest(kukit.UtilsTestCase); testsuite.registerTest(kukit.TokenizerTestCase); testsuite.registerTest(kukit.KssParserTestCase); testsuite.runSuite(); Modified: kukit/kukit.js/trunk/tests/runtests.sh ============================================================================== --- kukit/kukit.js/trunk/tests/runtests.sh (original) +++ kukit/kukit.js/trunk/tests/runtests.sh Wed Aug 2 11:31:51 2006 @@ -1,6 +1,8 @@ js \ -f ecmaunit.js \ -f unittestUtilities.js \ + -f ../kukit/utils.js \ + -f ../kukit/requestmanager.js \ -f ../kukit/kukit.js \ -f ../kukit/tokenizer.js \ -f ../kukit/resourcedata.js \ @@ -12,6 +14,7 @@ -f ../kukit/selectorreg.js \ -f ../kukit/forms.js \ -f ../kukit/plugin.js \ + -f test_utils.js \ -f test_tokenizer.js \ -f test_kssparser.js \ runtests.js Modified: kukit/kukit.js/trunk/tests/test_kssparser.js ============================================================================== --- kukit/kukit.js/trunk/tests/test_kssparser.js (original) +++ kukit/kukit.js/trunk/tests/test_kssparser.js Wed Aug 2 11:31:51 2006 @@ -8,26 +8,6 @@ this.setUp = function() { }; - this.printDebug = function(parser, prefix) { - if (typeof(prefix) == "undefined") { - prefix = ''; - print('########################'); - } - for (var i=0; i b) return -1; + else return 0; + }; + q = new kukit.ut.SortedQueue(comparefunc); + this.assertEquals(q.empty(), true); + this.assertEquals(q.size(), 0); + q.push(1); + q.push(3); + q.push(2); + q.push(4); + this.assertEquals(q.empty(), false); + this.assertEquals(q.size(), 4); + this.assertEquals(q.front(), 4); + this.assertListEquals(q.elements, [4, 3, 2, 1]); + this.assertEquals(q.pop(), 4); + this.assertEquals(q.pop(), 3); + this.assertEquals(q.pop(), 2); + this.assertEquals(q.pop(), 1); + this.assertEquals(q.empty(), true); + this.assertEquals(q.size(), 0); + + }; + +}; + +kukit.UtilsTestCase.prototype = new kukit.UtilsTestCaseBase; + +if (typeof(testcase_registry) != 'undefined') { + testcase_registry.registerTestCase(kukit.UtilsTestCase, 'kukit.UtilsTestCase'); +} From reebalazs at codespeak.net Wed Aug 2 11:32:11 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Wed, 2 Aug 2006 11:32:11 +0200 (CEST) Subject: [Kukit-checkins] r30879 - kukit/azax/trunk Message-ID: <20060802093211.A602910084@code0.codespeak.net> Author: reebalazs Date: Wed Aug 2 11:32:10 2006 New Revision: 30879 Modified: kukit/azax/trunk/configure.zcml Log: Refactoring request manager Modified: kukit/azax/trunk/configure.zcml ============================================================================== --- kukit/azax/trunk/configure.zcml (original) +++ kukit/azax/trunk/configure.zcml Wed Aug 2 11:32:10 2006 @@ -11,7 +11,9 @@ /> Author: reebalazs Date: Wed Aug 2 11:33:42 2006 New Revision: 30880 Modified: kukit/azax/trunk/configure.zcml Log: Fix Modified: kukit/azax/trunk/configure.zcml ============================================================================== --- kukit/azax/trunk/configure.zcml (original) +++ kukit/azax/trunk/configure.zcml Wed Aug 2 11:33:42 2006 @@ -12,8 +12,8 @@ Author: reebalazs Date: Wed Aug 2 11:57:05 2006 New Revision: 30883 Modified: kukit/azax/trunk/configure.zcml kukit/azax/trunk/plugins/json/browser/jsonkukit.js Log: Add copyright, set js compression Modified: kukit/azax/trunk/configure.zcml ============================================================================== --- kukit/azax/trunk/configure.zcml (original) +++ kukit/azax/trunk/configure.zcml Wed Aug 2 11:57:05 2006 @@ -25,8 +25,11 @@ kukit/kukit/forms.js kukit/kukit/plugin.js" name="kukit.js" - compress_level="none" + compress_level="safe" /> + +* +* 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. +*/ + +/* * Supplemental script for json * * This gets included when json plugins are registered. From reebalazs at codespeak.net Wed Aug 2 11:57:21 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Wed, 2 Aug 2006 11:57:21 +0200 (CEST) Subject: [Kukit-checkins] r30884 - in kukit/kukit.js/trunk: kukit tests Message-ID: <20060802095721.CE7BA10087@code0.codespeak.net> Author: reebalazs Date: Wed Aug 2 11:57:16 2006 New Revision: 30884 Modified: kukit/kukit.js/trunk/kukit/actionreg.js kukit/kukit.js/trunk/kukit/commandreg.js kukit/kukit.js/trunk/kukit/dom.js kukit/kukit.js/trunk/kukit/eventreg.js kukit/kukit.js/trunk/kukit/forms.js kukit/kukit.js/trunk/kukit/kssparser.js kukit/kukit.js/trunk/kukit/kukit.js kukit/kukit.js/trunk/kukit/plugin.js kukit/kukit.js/trunk/kukit/requestmanager.js kukit/kukit.js/trunk/kukit/resourcedata.js kukit/kukit.js/trunk/kukit/selectorreg.js kukit/kukit.js/trunk/kukit/tokenizer.js kukit/kukit.js/trunk/kukit/utils.js kukit/kukit.js/trunk/tests/test_kssparser.js kukit/kukit.js/trunk/tests/test_tokenizer.js kukit/kukit.js/trunk/tests/test_utils.js Log: Add copyright, set js compression Modified: kukit/kukit.js/trunk/kukit/actionreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/actionreg.js (original) +++ kukit/kukit.js/trunk/kukit/actionreg.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Event registration */ Modified: kukit/kukit.js/trunk/kukit/commandreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/commandreg.js (original) +++ kukit/kukit.js/trunk/kukit/commandreg.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Command registration */ Modified: kukit/kukit.js/trunk/kukit/dom.js ============================================================================== --- kukit/kukit.js/trunk/kukit/dom.js (original) +++ kukit/kukit.js/trunk/kukit/dom.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Generic dom helpers */ Modified: kukit/kukit.js/trunk/kukit/eventreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/eventreg.js (original) +++ kukit/kukit.js/trunk/kukit/eventreg.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Event registration */ Modified: kukit/kukit.js/trunk/kukit/forms.js ============================================================================== --- kukit/kukit.js/trunk/kukit/forms.js (original) +++ kukit/kukit.js/trunk/kukit/forms.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Form handling utilities */ Modified: kukit/kukit.js/trunk/kukit/kssparser.js ============================================================================== --- kukit/kukit.js/trunk/kukit/kssparser.js (original) +++ kukit/kukit.js/trunk/kukit/kssparser.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,23 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Martin Heidegger +* 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. +*/ /* Tokens of the kss parser */ Modified: kukit/kukit.js/trunk/kukit/kukit.js ============================================================================== --- kukit/kukit.js/trunk/kukit/kukit.js (original) +++ kukit/kukit.js/trunk/kukit/kukit.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* State vars srorage. This can be used from kss via a method. */ Modified: kukit/kukit.js/trunk/kukit/plugin.js ============================================================================== --- kukit/kukit.js/trunk/kukit/plugin.js (original) +++ kukit/kukit.js/trunk/kukit/plugin.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Core plugins and utilities */ Modified: kukit/kukit.js/trunk/kukit/requestmanager.js ============================================================================== --- kukit/kukit.js/trunk/kukit/requestmanager.js (original) +++ kukit/kukit.js/trunk/kukit/requestmanager.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Request manager */ Modified: kukit/kukit.js/trunk/kukit/resourcedata.js ============================================================================== --- kukit/kukit.js/trunk/kukit/resourcedata.js (original) +++ kukit/kukit.js/trunk/kukit/resourcedata.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Supplemental data that the parser builds up */ Modified: kukit/kukit.js/trunk/kukit/selectorreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/selectorreg.js (original) +++ kukit/kukit.js/trunk/kukit/selectorreg.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ /* Selector registration */ Modified: kukit/kukit.js/trunk/kukit/tokenizer.js ============================================================================== --- kukit/kukit.js/trunk/kukit/tokenizer.js (original) +++ kukit/kukit.js/trunk/kukit/tokenizer.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,23 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Martin Heidegger +* 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. +*/ /* Simple but effective tokenizing parser engine */ Modified: kukit/kukit.js/trunk/kukit/utils.js ============================================================================== --- kukit/kukit.js/trunk/kukit/utils.js (original) +++ kukit/kukit.js/trunk/kukit/utils.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,25 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Godefroid Chapelle +* Florian Schulze +* 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. +*/ + /* Create kukit namespace */ if (typeof(kukit) == 'undefined') { Modified: kukit/kukit.js/trunk/tests/test_kssparser.js ============================================================================== --- kukit/kukit.js/trunk/tests/test_kssparser.js (original) +++ kukit/kukit.js/trunk/tests/test_kssparser.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Martin Heidegger +* 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. +*/ + if (typeof(kukit) == "undefined") { var kukit = {}; } Modified: kukit/kukit.js/trunk/tests/test_tokenizer.js ============================================================================== --- kukit/kukit.js/trunk/tests/test_tokenizer.js (original) +++ kukit/kukit.js/trunk/tests/test_tokenizer.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,24 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* Martin Heidegger +* 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. +*/ + if (typeof(kukit) == "undefined") { var kukit = {}; } Modified: kukit/kukit.js/trunk/tests/test_utils.js ============================================================================== --- kukit/kukit.js/trunk/tests/test_utils.js (original) +++ kukit/kukit.js/trunk/tests/test_utils.js Wed Aug 2 11:57:16 2006 @@ -1,3 +1,23 @@ +/* +* Copyright (c) 2005-2006 +* Authors: +* 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. +*/ + if (typeof(kukit) == "undefined") { var kukit = {}; } From reebalazs at codespeak.net Sat Aug 5 19:47:07 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 5 Aug 2006 19:47:07 +0200 (CEST) Subject: [Kukit-checkins] r31030 - in kukit/kukit.js/trunk: kukit tests Message-ID: <20060805174707.4237710079@code0.codespeak.net> Author: reebalazs Date: Sat Aug 5 19:47:04 2006 New Revision: 31030 Modified: kukit/kukit.js/trunk/kukit/actionreg.js kukit/kukit.js/trunk/kukit/commandreg.js kukit/kukit.js/trunk/kukit/plugin.js kukit/kukit.js/trunk/kukit/resourcedata.js kukit/kukit.js/trunk/tests/test_kssparser.js Log: Action-command unifying, command name changes, kss parameter producers to camelCase Modified: kukit/kukit.js/trunk/kukit/actionreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/actionreg.js (original) +++ kukit/kukit.js/trunk/kukit/actionreg.js Sat Aug 5 19:47:04 2006 @@ -32,11 +32,11 @@ /* Action registry */ -kukit.ar.EventActionRegistry = function () { +kukit.ar.ActionRegistry = function () { this.content = {}; }; -kukit.ar.EventActionRegistry.prototype.register = function(name, func) { +kukit.ar.ActionRegistry.prototype.register = function(name, func) { if (typeof(func) == 'undefined') { throw 'Func is mandatory.'; } @@ -48,12 +48,12 @@ this.content[name] = func; }; -kukit.ar.EventActionRegistry.prototype.exists = function(name) { +kukit.ar.ActionRegistry.prototype.exists = function(name) { var entry = this.content[name]; return (typeof(entry) != 'undefined'); }; -kukit.ar.EventActionRegistry.prototype.get = function(name) { +kukit.ar.ActionRegistry.prototype.get = function(name) { var func = this.content[name]; if (! func) { // not found @@ -63,4 +63,4 @@ return func; }; -kukit.ar.eventActionRegistry = new kukit.ar.EventActionRegistry(); +kukit.ar.actionRegistry = new kukit.ar.ActionRegistry(); Modified: kukit/kukit.js/trunk/kukit/commandreg.js ============================================================================== --- kukit/kukit.js/trunk/kukit/commandreg.js (original) +++ kukit/kukit.js/trunk/kukit/commandreg.js Sat Aug 5 19:47:04 2006 @@ -28,6 +28,28 @@ this.content = {}; }; +// This is the proposed way of registration, as we like all commands to be +// client actions first. +// +// Examples: +// +// kukit.ar.actionRegistry.register('log', f1); +// kukit.cr.commandRegistry.registerFromAction('log', kukit.cr.makeGlobalCommand); +// +// kukit.ar.actionRegistry.register('replaceInnerHTML', f2); +// kukit.cr.commandRegistry.registerFromAction('replaceInnerHTML', kukit.cr.makeSelectorCommand); +// +kukit.cr.CommandRegistry.prototype.registerFromAction = function(srcname, factory, name) { + if (typeof(name) == 'undefined') { + // allows to set a different name as the action name, usable for backward + // compatibility setups + name = srcname; + } + // register a given action as a command, using the given vactor + var f = kukit.ar.actionRegistry.get(srcname); + factory(name, f); +} + kukit.cr.CommandRegistry.prototype.register = function(name, klass) { if (this.content[name]) { // Do not allow redefinition Modified: kukit/kukit.js/trunk/kukit/plugin.js ============================================================================== --- kukit/kukit.js/trunk/kukit/plugin.js (original) +++ kukit/kukit.js/trunk/kukit/plugin.js Sat Aug 5 19:47:04 2006 @@ -266,37 +266,13 @@ /* Core actions * -* The core local actions that can be executed on the client +* The core client actions that can be executed on the client * side. +* +* They also get registered as commands */ -kukit.ar.eventActionRegistry.register("cascade", function (node, parms, eventrule) { - parms = kukit.pl.completeParms(parms, ['name'], {'id': null, 'namespace': null}, 'cascade action'); - var eventinstance; - if (parms.id) { - // by id - eventinstance = kukit.er.eventInstanceRegistry.getEventById(parms.id); - // if namespace is defined too, let's check it! - if (parms.namespace != null && parms.namespace != eventinstance.__event_namespace__) { - throw 'Id and namespace are conflicting in cascade action, rule=#' + eventrule.getNr() + ', node=' + node.nodeName; - } - } else { - // singleton by class - eventinstance = kukit.er.eventInstanceRegistry.getSingletonEventByName(parms.namespace, parms.name); - } - // marshall it, the rest of the parms will be passed - actionparms = {}; - for (var key in parms) { - if (key != 'name' && key != 'id' && key != 'namespace') { - actionparms[key] = parms[key]; - } - } - eventinstance.__trigger_event__(parms.name, actionparms, node); - } -); - - -kukit.ar.eventActionRegistry.register("logDebug", function (node, parms, eventrule) { +kukit.ar.actionRegistry.register("logDebug", function (node, parms, eventrule) { parms = kukit.pl.completeParms(parms, [], {'message': 'Logging from Event'}, 'logDebug action'); var nodeName = ''; if (node != null) { @@ -305,8 +281,9 @@ kukit.logDebug(parms.message + ', rule=#' + eventrule.getNr() + ', node=' + nodeName); } ); +kukit.cr.commandRegistry.registerFromAction('logDebug', kukit.cr.makeGlobalCommand); -kukit.ar.eventActionRegistry.register("log", function (node, parms, eventrule) { +kukit.ar.actionRegistry.register("log", function (node, parms, eventrule) { parms = kukit.pl.completeParms(parms, [], {'message': 'Logging from Event'}, 'log action'); var nodeName = ''; if (node != null) { @@ -315,8 +292,9 @@ kukit.log(parms.message + ', rule=#' + eventrule.getNr() + ', node=' + nodeName); } ); +kukit.cr.commandRegistry.registerFromAction('log', kukit.cr.makeGlobalCommand); -kukit.ar.eventActionRegistry.register("alert", function (node, parms, eventrule) { +kukit.ar.actionRegistry.register("alert", function (node, parms, eventrule) { parms = kukit.pl.completeParms(parms, [], {'message': 'Logging from Event'}, 'alert action'); var nodeName = ''; if (node != null) { @@ -325,13 +303,16 @@ alert(parms.message + ', rule=#' + eventrule.getNr() + ', node=' + nodeName); } ); +kukit.cr.commandRegistry.registerFromAction('alert', kukit.cr.makeGlobalCommand); -/* Core commands */ +/* Core commands +* +* All the commands are also client actions. +*/ -// innerHTML // accepts both string and dom. -kukit.cr.makeSelectorCommand('setHtmlAsChild', function(node) { - parms = kukit.pl.completeParms(this.params, ['html'], {}, 'setHtmlAsChild command'); +kukit.ar.actionRegistry.register('replaceInnerHTML', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html'], {}, 'replaceInnerHTML action'); if (typeof(parms.html) == 'string') { node.innerHTML = parms.html; } else { @@ -341,11 +322,11 @@ } kukit.setupEvents(node); }); +kukit.cr.commandRegistry.registerFromAction('replaceInnerHTML', kukit.cr.makeSelectorCommand); -// outerHTML ? -// This seems to be untested. -kukit.cr.makeSelectorCommand('replaceNode', function(node) { - parms = kukit.pl.completeParms(this.params, ['html'], {'selector': null}, 'replaceNode command'); +// XXX This seems to be untested. +kukit.ar.actionRegistry.register('replaceHTML', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html'], {'selector': null}, 'replaceHTML action'); parms.html = kukit.dom.forceToDom(parms.html); var childNodes = parms.html.childNodes; var parentNode = node.parentNode; @@ -366,14 +347,16 @@ } kukit.setupEvents(parentNode); }); +kukit.cr.commandRegistry.registerFromAction('replaceHTML', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('setAttribute', function(node) { - parms = kukit.pl.completeParms(this.params, ['name', 'value'], {}, 'setAttribute command'); +kukit.ar.actionRegistry.register('setAttribute', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['name', 'value'], {}, 'setAttribute action'); node.setAttribute(parms.name, parms.value); }); +kukit.cr.commandRegistry.registerFromAction('setAttribute', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('addAfter', function(node) { - parms = kukit.pl.completeParms(this.params, ['html'], {}, 'addAfter command'); +kukit.ar.actionRegistry.register('insertHTMLAfter', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html'], {}, 'insertHTMLAfter action'); parms.html = kukit.dom.forceToDom(parms.html); var content = document.importNode(parms.html, true); var parentNode = node.parentNode; @@ -387,38 +370,42 @@ kukit.logDebug("Inserted nodes length: "+inserted.length); kukit.setupEvents(node); }); +kukit.cr.commandRegistry.registerFromAction('insertHTMLAfter', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('removeNextSibling', function(node) { - parms = kukit.pl.completeParms(this.params, [], {}, 'removeNextSibling command'); +kukit.ar.actionRegistry.register('deleteNodeAfter', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, [], {}, 'deleteNodeAfter action'); var parentNode = node.parentNode; var toNode = kukit.dom.getNextSiblingTag(node); if (toNode != null) { parentNode.removeChild(toNode); } }); +kukit.cr.commandRegistry.registerFromAction('deleteNodeAfter', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('removePreviousSibling', function(node) { - parms = kukit.pl.completeParms(this.params, [], {}, 'removePreviousSibling command'); +kukit.ar.actionRegistry.register('deleteNodeBefore', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, [], {}, 'deleteNodeBefore action'); var parentNode = node.parentNode; var toNode = kukit.dom.getPreviousSiblingTag(node); parentNode.removeChild(toNode); }); +kukit.cr.commandRegistry.registerFromAction('deleteNodeBefore', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('removeNode', function(node) { - parms = kukit.pl.completeParms(this.params, [], {}, 'removeNode command'); +kukit.ar.actionRegistry.register('deleteNode', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, [], {}, 'deleteNode action'); var parentNode = node.parentNode; parentNode.removeChild(node); }); +kukit.cr.commandRegistry.registerFromAction('deleteNode', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('clearChildren', function(node) { +kukit.ar.actionRegistry.register('clearChildNodes', function(node, parms, eventrule) { // TODO get rid of none - //parms = kukit.pl.completeParms(this.params, [], {}, 'clearChildren command'); - parms = kukit.pl.completeParms(this.params, [], {'none': false}, 'clearChildren command'); + parms = kukit.pl.completeParms(parms, [], {'none': false}, 'clearChildNodes action'); kukit.dom.clearChildNodes(node); }); +kukit.cr.commandRegistry.registerFromAction('clearChildNodes', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('moveNodeAfter', function(node) { - parms = kukit.pl.completeParms(this.params, ['html_id'], {}, 'moveNodeAfter command'); +kukit.ar.actionRegistry.register('moveNodeAfter', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html_id'], {}, 'moveNodeAfter action'); var parentNode = node.parentNode; parentNode.removeChild(node); @@ -431,33 +418,37 @@ parentNode.insertBefore(node, nextNode); } }); +kukit.cr.commandRegistry.registerFromAction('moveNodeAfter', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('copyChildrenFrom', function(node) { - parms = kukit.pl.completeParms(this.params, ['html_id'], {}, 'copyChildrenFrom command'); +kukit.ar.actionRegistry.register('copyChildNodesFrom', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html_id'], {}, 'copyChildNodesFrom action'); var fromNode = document.getElementById(parms.html_id); Sarissa.copyChildNodes(fromNode, node); }); +kukit.cr.commandRegistry.registerFromAction('copyChildNodesFrom', kukit.cr.makeSelectorCommand); -kukit.cr.makeSelectorCommand('copyChildrenTo', function(node) { - parms = kukit.pl.completeParms(this.params, ['html_id'], {}, 'copyChildrenTo command'); +kukit.ar.actionRegistry.register('copyChildNodesTo', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['html_id'], {}, 'copyChildNodesTo action'); toNode = document.getElementById(parms.html_id); Sarissa.copyChildNodes(node, toNode); }); +kukit.cr.commandRegistry.registerFromAction('copyChildNodesTo', kukit.cr.makeSelectorCommand); -kukit.cr.makeGlobalCommand('setStatevar', function(node) { - parms = kukit.pl.completeParms(this.params, ['varname', 'value'], {}, 'setStatevar command'); +kukit.ar.actionRegistry.register('setStateVar', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['varname', 'value'], {}, 'setStateVar action'); kukit.statevars[parms.varname] = parms.value; }); +kukit.cr.commandRegistry.registerFromAction('setStateVar', kukit.cr.makeGlobalCommand); -kukit.cr.makeGlobalCommand('triggerEvent', function (node, parms, eventrule, eventinstance) { +kukit.ar.actionRegistry.register('triggerEvent', function(node, parms, eventrule, eventinstance) { // allows excess parms in the following check. - parms = kukit.pl.completeParms(parms, ['name'], {'id': null, 'namespace': null}, 'triggerEvent command', true); + parms = kukit.pl.completeParms(parms, ['name'], {'id': null, 'namespace': null}, 'triggerEvent action', true); if (parms.id) { // by id eventinstance = kukit.er.eventInstanceRegistry.getEventById(parms.id); // if namespace is defined too, let's check it! if (parms.namespace != null && parms.namespace != eventinstance.__event_namespace__) { - throw 'Id and namespace are conflicting in cascade action, rule=#' + eventrule.getNr() + ', node=' + node.nodeName; + throw 'Id and namespace are conflicting in triggerEvent action, rule=#' + eventrule.getNr() + ', node=' + node.nodeName; } } else { if (eventinstance == null) { @@ -474,4 +465,20 @@ } eventinstance.__trigger_event__(parms.name, actionparms, node); }); +kukit.cr.commandRegistry.registerFromAction('triggerEvent', kukit.cr.makeSelectorCommand); + +// XXX Compatibility settings for old command names. +// These will be removed as soon as all current use cases are changed. +// XXX XXX XXX Do not use these as your code will break! +// +kukit.cr.commandRegistry.registerFromAction('replaceInnerHTML', kukit.cr.makeSelectorCommand, 'setHtmlAsChild'); +kukit.cr.commandRegistry.registerFromAction('replaceHTML', kukit.cr.makeSelectorCommand, 'replaceNode'); +kukit.cr.commandRegistry.registerFromAction('insertHTMLAfter', kukit.cr.makeSelectorCommand, 'addAfter'); +kukit.cr.commandRegistry.registerFromAction('deleteNodeAfter', kukit.cr.makeSelectorCommand, 'removeNextSibling'); +kukit.cr.commandRegistry.registerFromAction('deleteNodeBefore', kukit.cr.makeSelectorCommand, 'removePreviousSibling'); +kukit.cr.commandRegistry.registerFromAction('deleteNode', kukit.cr.makeSelectorCommand, 'removeNode'); +kukit.cr.commandRegistry.registerFromAction('clearChildNodes', kukit.cr.makeSelectorCommand, 'clearChildren'); +kukit.cr.commandRegistry.registerFromAction('copyChildNodesFrom', kukit.cr.makeSelectorCommand, 'copyChildrenFrom'); +kukit.cr.commandRegistry.registerFromAction('copyChildNodesTo', kukit.cr.makeSelectorCommand, 'copyChildrenTo'); +kukit.cr.commandRegistry.registerFromAction('setStateVar', kukit.cr.makeGlobalCommand, 'setStatevar'); Modified: kukit/kukit.js/trunk/kukit/resourcedata.js ============================================================================== --- kukit/kukit.js/trunk/kukit/resourcedata.js (original) +++ kukit/kukit.js/trunk/kukit/resourcedata.js Sat Aug 5 19:47:04 2006 @@ -106,24 +106,25 @@ kukit.rd.KssMethodValue.prototype.check = function() { // Check syntax switch (this.methodname) { - case 'formvar': { + case 'formVar': { + if (this.methodname == 'formvar') if (this.args.length != 2) { - throw 'formvar method needs 2 arguments (formname, varname)'; + throw 'formVar method needs 2 arguments (formname, varname)'; } } break; - case 'currentformvar': { + case 'currentFormVar': { if (this.args.length != 1) { - throw 'currentformvar method needs 1 argument (varname)'; + throw 'currentFormVar method needs 1 argument (varname)'; } } break; - case 'nodeattr': { + case 'nodeAttr': { if (this.args.length != 1) { - throw 'nodeattr method needs 1 argument (attrname)'; + throw 'nodeAttr method needs 1 argument (attrname)'; } } break; - case 'statevar': { + case 'stateVar': { if (this.args.length != 1) { - throw 'statevar method needs 1 argument (varname)'; + throw 'stateVar method needs 1 argument (varname)'; } } break; case 'pass': { @@ -139,13 +140,13 @@ kukit.rd.KssMethodValue.prototype.evaluate = function(parms, key, node, defaultparms) { // Evaluate into parms. switch (this.methodname) { - case 'formvar': { + case 'formVar': { parms[key] = kukit.fo.getFormVarFromNamedForm(this.args[0], this.args[1]); } break; - case 'currentformvar': { + case 'currentFormVar': { parms[key] = kukit.fo.getFormVarFromCurrentForm(node, this.args[0]); } break; - case 'nodeattr': { + case 'nodeAttr': { var argname = this.args[0]; // XXX this works but not for kukit:xxx args var value = node[argname]; @@ -157,7 +158,7 @@ } parms[key] = value; } break; - case 'statevar': { + case 'stateVar': { var key2 = this.args[0]; var value = kukit.statevars[key2]; if (typeof(value) == 'undefined') { @@ -434,7 +435,7 @@ kukit.rd.executeClientAction = function(name, node, eventrule, parms, eventinstance) { - var func = kukit.ar.eventActionRegistry.get(name); + var func = kukit.ar.actionRegistry.get(name); func(node, parms, eventrule); }; Modified: kukit/kukit.js/trunk/tests/test_kssparser.js ============================================================================== --- kukit/kukit.js/trunk/tests/test_kssparser.js (original) +++ kukit/kukit.js/trunk/tests/test_kssparser.js Sat Aug 5 19:47:04 2006 @@ -175,43 +175,43 @@ this.assertEquals(parser.value.txt, 'apples and oranges'); // params ok - txt= "formvar(x, y) "; + txt= "formVar(x, y) "; src = new kukit.tk.Cursor(txt); parser = new kukit.kssp.propvalue(src, null, true); this.assertEquals(parser.finished, true); this.assertEquals(parser.value.isMethod, true); - this.assertEquals(parser.value.methodname, 'formvar'); + this.assertEquals(parser.value.methodname, 'formVar'); this.assertListEquals(parser.value.args, ['x', 'y']); // params ok - txt= "formvar(x, y)"; + txt= "formVar(x, y)"; src = new kukit.tk.Cursor(txt); parser = new kukit.kssp.propvalue(src, null, true); this.assertEquals(parser.finished, true); this.assertEquals(parser.value.isMethod, true); - this.assertEquals(parser.value.methodname, 'formvar'); + this.assertEquals(parser.value.methodname, 'formVar'); this.assertListEquals(parser.value.args, ['x', 'y']); //ok - txt= " formvar (x, y)"; + txt= " formVar (x, y)"; src = new kukit.tk.Cursor(txt); parser = new kukit.kssp.propvalue(src, null, true); this.assertEquals(parser.finished, true); this.assertEquals(parser.value.isMethod, true); - this.assertEquals(parser.value.methodname, 'formvar'); + this.assertEquals(parser.value.methodname, 'formVar'); this.assertListEquals(parser.value.args, ['x', 'y']); - txt= " a formvar(x, y)"; + txt= " a formVar(x, y)"; src = new kukit.tk.Cursor(txt); this.assertParsingError(kukit.kssp.propvalue, src, null, true, 'Method property value must have a one-word method name', 16); - txt= " 'formvar'(x, y)"; + txt= " 'formVar'(x, y)"; src = new kukit.tk.Cursor(txt); this.assertParsingError(kukit.kssp.propvalue, src, null, true, 'Excess characters after the property value', 16); - txt= "formvar(x, y) xxx"; + txt= "formVar(x, y) xxx"; src = new kukit.tk.Cursor(txt); this.assertParsingError(kukit.kssp.propvalue, src, null, true, 'Excess characters after the property value', 17); @@ -611,14 +611,14 @@ +"}\n" +"#calendar-previous a:click {\n" +" action-server : kukitGetPreviousMonth /* place comment here*/;\n" - +" kukitGetPreviousMonth-member: formvar(edit, 'f_member');\n" + +" kukitGetPreviousMonth-member: formVar(edit, 'f_member');\n" +"}\n" +"#calendar-previous a:dnd-drag(shelve) {\n" +" action-server : whatever\n" +"}\n" +"#button-one:annoyClicker-click(annoy-me) {\n" +" action-server: clickedButton;\n" - +" clickedButton-id: nodeattr(id);\n" + +" clickedButton-id: nodeAttr(id);\n" +"}\n" +"document:annoy(annoyMe) {\n" +" action-client: alert;\n" @@ -705,7 +705,7 @@ // rule 3 // #calendar-previous a:click { // kss-action : 'kukitresponse/kukitGetPreviousMonth' /* place comment here*/; - // member: formvar(edit, 'f_member'); + // member: formVar(edit, 'f_member'); // } rule = parser.eventRules[3]; this.assertDictEquals(rule.parms, {}); @@ -719,7 +719,7 @@ this.assertEquals(action.name, 'kukitGetPreviousMonth'); this.assertEquals(action.error, null); this.assertKssParmEquals(action.parms, { - 'member': new kukit.rd.KssMethodValue('formvar', ['edit', 'f_member']) + 'member': new kukit.rd.KssMethodValue('formVar', ['edit', 'f_member']) }); // rule 4 @@ -743,7 +743,7 @@ // rule 5 //#button-one:annoyClicker-click(annoyMe) { // kss-action: clickedButton; - // id: nodeattr(id); + // id: nodeAttr(id); //} rule = parser.eventRules[5]; this.assertDictEquals(rule.parms, {}); @@ -757,7 +757,7 @@ this.assertEquals(action.name, 'clickedButton'); this.assertEquals(action.error, null); this.assertKssParmEquals(action.parms, { - 'id': new kukit.rd.KssMethodValue('nodeattr', ['id']) + 'id': new kukit.rd.KssMethodValue('nodeAttr', ['id']) }); // rule 6 From reebalazs at codespeak.net Sat Aug 5 19:48:28 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 5 Aug 2006 19:48:28 +0200 (CEST) Subject: [Kukit-checkins] r31031 - in kukit/azax/trunk: . plugins Message-ID: <20060805174828.D740110079@code0.codespeak.net> Author: reebalazs Date: Sat Aug 5 19:48:24 2006 New Revision: 31031 Modified: kukit/azax/trunk/commands.py kukit/azax/trunk/plugins/__init__.py kukit/azax/trunk/plugins/configure.py kukit/azax/trunk/plugins/configure.zcml kukit/azax/trunk/plugins/directives.py kukit/azax/trunk/plugins/meta.zcml kukit/azax/trunk/plugins/plugin.py Log: Action-command unifying, command name changes, server side registration changes Modified: kukit/azax/trunk/commands.py ============================================================================== --- kukit/azax/trunk/commands.py (original) +++ kukit/azax/trunk/commands.py Sat Aug 5 19:48:24 2006 @@ -82,7 +82,15 @@ implements(IAzaxCommand) def __init__(self, name, selector=None): - plugins.Command.checkRegistered(name) + if plugins.Command.isRegistered(name): + # ok. XXX this will be deprecated + # All registerCommand commands are obsolete, by default + import warnings, textwrap + warnings.warn(textwrap.dedent('''\ + The usage of the kss command "%s" is deprecated''' + % (name, )), DeprecationWarning, 2) + else: + plugins.Action.checkRegisteredCommand(name) if isinstance(selector, basestring): self.selector = selector self.selectorType = '' Modified: kukit/azax/trunk/plugins/__init__.py ============================================================================== --- kukit/azax/trunk/plugins/__init__.py (original) +++ kukit/azax/trunk/plugins/__init__.py Sat Aug 5 19:48:24 2006 @@ -6,5 +6,5 @@ from plugin import registerPlugin from command import Command from event_type import EventType -from event_action import EventAction +from action import Action from selector_type import SelectorType Modified: kukit/azax/trunk/plugins/configure.py ============================================================================== --- kukit/azax/trunk/plugins/configure.py (original) +++ kukit/azax/trunk/plugins/configure.py Sat Aug 5 19:48:24 2006 @@ -2,16 +2,27 @@ import os.path from command import Command from event_type import EventType -from event_action import EventAction +from action import Action from selector_type import SelectorType from plugin import registerPlugin #class AzaxConfigurationError(Exception): # pass +# XXX This will go away. def registerCommand(_context, name, jsfile=None): 'Directive that registers a command' + import warnings, textwrap + warnings.warn(textwrap.dedent('''\ + + %s + + The directive azax:registerCommand is deprecated and will be removed, + use azax:registerAction with command_factory="selector" or "global".''' + % _context.info), + DeprecationWarning) + # check to see if the file exists if jsfile is not None: file(jsfile, 'rb').close() @@ -35,17 +46,18 @@ args = (EventType, name, jsfile), ) -def registerEventAction(_context, name, jsfile=None): - 'Directive that registers an event action' +def registerAction(_context, name, jsfile=None, command_factory='none', + params_mandatory=[], params_optional=[], deprecated=None): + 'Directive that registers an action.' # check to see if the file exists if jsfile is not None: file(jsfile, 'rb').close() _context.action( - discriminator = ('registerAzaxEventAction', name, jsfile), + discriminator = ('registerAzaxAction', name, jsfile), callable = registerPlugin, - args = (EventAction, name, jsfile), + args = (Action, name, jsfile, command_factory, params_mandatory, params_optional, deprecated), ) def registerSelectorType(_context, name, jsfile=None): Modified: kukit/azax/trunk/plugins/configure.zcml ============================================================================== --- kukit/azax/trunk/plugins/configure.zcml (original) +++ kukit/azax/trunk/plugins/configure.zcml Sat Aug 5 19:48:24 2006 @@ -10,19 +10,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -146,22 +96,179 @@ name="load" /> - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /> + + + + + + + + Modified: kukit/azax/trunk/plugins/directives.py ============================================================================== --- kukit/azax/trunk/plugins/directives.py (original) +++ kukit/azax/trunk/plugins/directives.py Sat Aug 5 19:48:24 2006 @@ -1,7 +1,8 @@ from zope.interface import Interface -from zope.schema import TextLine -from zope.configuration.fields import Path +from zope.schema import TextLine, Choice +from zope.configuration.fields import Path, Tokens, PythonIdentifier +# XXX this will go away class IRegisterCommandDirective(Interface): 'Register an AZAX command plugin' @@ -32,12 +33,12 @@ required=False, ) -class IRegisterEventActionDirective(Interface): - 'Register an AZAX event action' +class IRegisterActionDirective(Interface): + 'Register an AZAX action' name = TextLine( title=u"Name", - description=u"The name of the event action plugin.", + description=u"The name of the action plugin.", required=True, ) @@ -47,6 +48,33 @@ required=False, ) + command_factory = Choice( + title=u"Command factory type", + description=u"Command factory type, by default 'none'.", + values=(u'none', u'global', u'selector'), + required=False, + ) + + params_mandatory = Tokens( + title=u"Mandatory parameters", + description=u"Space separated list of mandatory parameter names", + value_type=PythonIdentifier(), + required=False, + ) + + params_optional = Tokens( + title=u"Optional parameters", + description=u"Comma separated list of optional parameter names", + value_type=PythonIdentifier(), + required=False, + ) + + deprecated = TextLine( + title=u"Deprecated", + description=u"The hint that we should give as warning about deprecation", + required=False, + ) + class IRegisterSelectorTypeDirective(Interface): 'Register an AZAX selector type' Modified: kukit/azax/trunk/plugins/meta.zcml ============================================================================== --- kukit/azax/trunk/plugins/meta.zcml (original) +++ kukit/azax/trunk/plugins/meta.zcml Sat Aug 5 19:48:24 2006 @@ -6,6 +6,7 @@ + Author: reebalazs Date: Sat Aug 5 19:49:20 2006 New Revision: 31032 Modified: kukit/azaxdemo/trunk/browser/azax_instant_edit.kss kukit/azaxdemo/trunk/browser/azax_tree.kss kukit/azaxdemo/trunk/browser/azax_two_select.kss kukit/azaxdemo/trunk/browser/cancel_submit.kss kukit/azaxdemo/trunk/browser/more_selectors.kss Log: kss parameter producer names changed to camelCase Modified: kukit/azaxdemo/trunk/browser/azax_instant_edit.kss ============================================================================== --- kukit/azaxdemo/trunk/browser/azax_instant_edit.kss (original) +++ kukit/azaxdemo/trunk/browser/azax_instant_edit.kss Sat Aug 5 19:49:20 2006 @@ -1,10 +1,10 @@ div#text:click { action-server: getInputField; - getInputField-value: currentformvar(value); + getInputField-value: currentFormVar(value); } input#save:click { action-server: saveText; - saveText-value: currentformvar(value); + saveText-value: currentFormVar(value); } Modified: kukit/azaxdemo/trunk/browser/azax_tree.kss ============================================================================== --- kukit/azaxdemo/trunk/browser/azax_tree.kss (original) +++ kukit/azaxdemo/trunk/browser/azax_tree.kss Sat Aug 5 19:49:20 2006 @@ -1,9 +1,9 @@ .state-expanded:click { action-server: collapseSubTree; - collapseSubTree-value: nodeattr("id"); + collapseSubTree-value: nodeAttr("id"); } .state-collapsed:click { action-server: expandSubTree; - expandSubTree-value: nodeattr("id"); + expandSubTree-value: nodeAttr("id"); } Modified: kukit/azaxdemo/trunk/browser/azax_two_select.kss ============================================================================== --- kukit/azaxdemo/trunk/browser/azax_two_select.kss (original) +++ kukit/azaxdemo/trunk/browser/azax_two_select.kss Sat Aug 5 19:49:20 2006 @@ -1,4 +1,4 @@ select#first:change { action-server: getCorrespondingSelect; - getCorrespondingSelect-value: currentformvar(value); + getCorrespondingSelect-value: currentFormVar(value); } Modified: kukit/azaxdemo/trunk/browser/cancel_submit.kss ============================================================================== --- kukit/azaxdemo/trunk/browser/cancel_submit.kss (original) +++ kukit/azaxdemo/trunk/browser/cancel_submit.kss Sat Aug 5 19:49:20 2006 @@ -1,5 +1,5 @@ input#submit:click { evt-click-preventdefault: true; action-server: save; - save-text_save: currentformvar(text_save); + save-text_save: currentFormVar(text_save); } Modified: kukit/azaxdemo/trunk/browser/more_selectors.kss ============================================================================== --- kukit/azaxdemo/trunk/browser/more_selectors.kss (original) +++ kukit/azaxdemo/trunk/browser/more_selectors.kss Sat Aug 5 19:49:20 2006 @@ -4,7 +4,7 @@ behaviour:annoyclicker-doit(annoyMe) { action-server: clickedButton; - clickedButton-id: nodeattr(id); + clickedButton-id: nodeAttr(id); action-client: log; log-message: "Was here."; } @@ -20,7 +20,7 @@ behaviour:annoyclicker-doit(annoyYou) { action-server: clickedButton; - clickedButton-id: nodeattr(id); + clickedButton-id: nodeAttr(id); } behaviour:annoyclicker-annoy(annoyYou) { From reebalazs at codespeak.net Sat Aug 5 21:00:15 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 5 Aug 2006 21:00:15 +0200 (CEST) Subject: [Kukit-checkins] r31038 - in kukit/azax/trunk: . plugins Message-ID: <20060805190015.0FA4410083@code0.codespeak.net> Author: reebalazs Date: Sat Aug 5 21:00:12 2006 New Revision: 31038 Added: kukit/azax/trunk/plugins/action.py Removed: kukit/azax/trunk/plugins/event_action.py Modified: kukit/azax/trunk/azaxview.py Log: Fix command method deprecations on azaxview too+ fix renamed file Modified: kukit/azax/trunk/azaxview.py ============================================================================== --- kukit/azax/trunk/azaxview.py (original) +++ kukit/azax/trunk/azaxview.py Sat Aug 5 21:00:12 2006 @@ -35,6 +35,7 @@ from Products.Five import BrowserView from commands import AzaxCommands, AzaxCommand from parsers import XmlParser, HtmlParser +from zope.deprecation import deprecated class AzaxBaseView(BrowserView): """ Base azax view @@ -72,10 +73,12 @@ # default set of commands # -- - def setHtmlAsChild(self, selector, new_value): + # XXX the list is not full: maybe complete them? + + def replaceInnerHTML(self, selector, new_value): """ see interfaces.py """ new_value = HtmlParser(new_value)().encode('ascii', 'xmlcharrefreplace') - command = self.addCommand('setHtmlAsChild', selector) + command = self.addCommand('replaceInnerHTML', selector) data = command.addParam('html', new_value) def setAttribute(self, selector, name, value): @@ -84,31 +87,31 @@ data = command.addParam('name', name) data = command.addParam('value', value) - def addAfter(self, selector, new_value): + def insertHTMLAfter(self, selector, new_value): """ see interfaces.py """ new_value = HtmlParser(new_value)().encode('ascii', 'xmlcharrefreplace') - command = self.addCommand('addAfter', selector) + command = self.addCommand('insertHTMLAfter', selector) data = command.addParam('html', new_value) - def clearChildren(self, selector): + def clearChildNodes(self, selector): """ see interfaces.py """ - command = self.addCommand('clearChildren', selector) - - def removeNode(self, selector): + command = self.addCommand('clearChildNodes', selector) + + def deleteNode(self, selector): """ see interfaces.py """ - command = self.addCommand('removeNode', selector) + command = self.addCommand('deleteNode', selector) - def removeNextSibling(self, selector): + def deleteNodeAfter(self, selector): """ see interfaces.py """ - command = self.addCommand('removeNextSibling', selector) + command = self.addCommand('deleteNodeAfter', selector) - def removePreviousSibling(self, selector): + def deleteNodeBefore(self, selector): """ see interfaces.py """ - command = self.addCommand('removePreviousSibling', selector) + command = self.addCommand('deleteNodeBefore', selector) - def copyChildrenFrom(self, selector, id): + def copyChildNodesFrom(self, selector, id): """ see interfaces.py """ - command = self.addCommand('copyChildrenFrom', selector) + command = self.addCommand('copyChildNodesFrom', selector) data = command.addParam('html_id', id) def moveNodeAfter(self, selector, id): @@ -116,23 +119,38 @@ command = self.addCommand('moveNodeAfter', selector) data = command.addParam('html_id', id) - def copyChildrenTo(self, selector, id): + def copyChildNodesTo(self, selector, id): """ see interfaces.py """ - command = self.addCommand('copyChildrenTo', selector) + command = self.addCommand('copyChildNodesTo', selector) data = command.addParam('html_id', id) - def moveChildrenTo(self, selector, id): + def setStateVar(self, varname, value): """ see interfaces.py """ - self.copyChildrenTo(selector, id) - self.clearChildren(selector) - - def setStatevar(self, varname, value): - command = self.addCommand('setStatevar') + command = self.addCommand('setStateVar') command.addParam('varname', varname) command.addParam('value', value) def triggerEvent(self, name, **kw): + """ see interfaces.py """ command = self.addCommand('triggerEvent') command.addParam('name', name) for key, value in kw.iteritems(): command.addParam(key, value) + + # XXX Deprecated ones + + def moveChildrenTo(self, selector, id): + """ see interfaces.py """ + self.copyChildrenTo(selector, id) + self.clearChildren(selector) + moveChildrenTo = deprecated(moveChildrenTo, 'No more supported, use a sequence of copyChildrenTo and clearChildren') + + setHtmlAsChild = deprecated(replaceInnerHTML, 'use replaceInnerHTML instead') + addAfter = deprecated(insertHTMLAfter, 'use insertHTMLAfter instead') + clearChildren = deprecated(clearChildNodes, 'use clearChildNodes instead') + removeNode = deprecated(deleteNode, 'use deleteNode instead') + removeNextSibling = deprecated(deleteNodeAfter, 'use deleteNodeAfter instead') + removePreviousSibling = deprecated(deleteNodeBefore, 'use deleteNodeBefore instead') + copyChildrenFrom = deprecated(copyChildNodesFrom, 'use copyChildNodesFrom instead') + copyChildrenTo = deprecated(copyChildNodesTo, 'use copyChildNodesTo instead') + setStatevar = deprecated(setStateVar, 'use setStateVar (capital V) instead') Added: kukit/azax/trunk/plugins/action.py ============================================================================== --- (empty file) +++ kukit/azax/trunk/plugins/action.py Sat Aug 5 21:00:12 2006 @@ -0,0 +1,34 @@ + +from plugin import Plugin +from registry import AzaxPluginError + +class Action(Plugin): + '''The action plugin + + ''' + + plugintype = 'action' + + def __init__(self, name, jsfile, command_factory, + params_mandatory, params_optional, deprecated): + Plugin.__init__(self, name, jsfile) + self.command_factory = command_factory + self.params_mandatory = params_mandatory + self.params_optional = params_optional + self.deprecated = deprecated + + def checkRegisteredCommand(cls, name): + command = cls.getRegistered(name) + # check if the action has a valid command factory + if command.command_factory == 'none': + raise AzaxPluginError, '"%s" is not a registered kss command (or missing command_factory?)' % (name, ) + # issue deprecation warning, if necessary + command.check_deprecation() + checkRegisteredCommand = classmethod(checkRegisteredCommand) + + def check_deprecation(self): + if self.deprecated: + import warnings, textwrap + warnings.warn(textwrap.dedent('''\ + The usage of the kss command "%s" is deprecated, + %s''' % (self.name, self.deprecated)), DeprecationWarning, 2) Deleted: /kukit/azax/trunk/plugins/event_action.py ============================================================================== --- /kukit/azax/trunk/plugins/event_action.py Sat Aug 5 21:00:12 2006 +++ (empty file) @@ -1,13 +0,0 @@ - -from plugin import Plugin - -class EventAction(Plugin): - '''The event action plugin - - ''' - - plugintype = 'event_action' - - def __init__(self, name, jsfile): - Plugin.__init__(self, name, jsfile) - From reebalazs at codespeak.net Sat Aug 5 21:11:23 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Sat, 5 Aug 2006 21:11:23 +0200 (CEST) Subject: [Kukit-checkins] r31040 - kukit/azaxdemo/trunk Message-ID: <20060805191123.AFBC510083@code0.codespeak.net> Author: reebalazs Date: Sat Aug 5 21:11:22 2006 New Revision: 31040 Modified: kukit/azaxdemo/trunk/azaxview.py Log: Change to new command names in azaxview Modified: kukit/azaxdemo/trunk/azaxview.py ============================================================================== --- kukit/azaxdemo/trunk/azaxview.py (original) +++ kukit/azaxdemo/trunk/azaxview.py Sat Aug 5 21:11:22 2006 @@ -30,28 +30,29 @@ def clearDivContent(self): """ clear div content """ - self.clearChildren('div#demo') + self.clearChildNodes('div#demo') return self.render() def copyFromDivContent(self): """ copy div content """ - self.copyChildrenFrom('div#copy', 'demo') + self.copyChildNodesFrom('div#copy', 'demo') return self.render() def copyToDivContent(self): """ copy div content """ - self.copyChildrenTo('div#copy', 'demo') + self.copyChildNodesTo('div#copy', 'demo') return self.render() def moveToDivContent(self): """ copy div content """ - self.moveChildrenTo('div#copy', 'demo') + self.copyChildNodesTo('div#copy', 'demo') + self.clearChildNodes('div#copy') return self.render() def getDivContent(self): """ returns div content """ - self.setHtmlAsChild('div#demo', '

it worked

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

it worked again

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

it worked

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

it worked again

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

%s

" % str(datetime.datetime.now())) + self.replaceInnerHTML('div#update-area', "

%s

" % str(datetime.datetime.now())) return self.render() def getInputField(self, value): 'Inserts the value as entered into an input field' # we receive in utf encoding, but the methods receive utf, so we convert. value = unicode(value, 'utf') - self.setHtmlAsChild('div#text', + self.replaceInnerHTML('div#text', '
' \ '' ) @@ -88,30 +89,31 @@ 'Inserts the value to display it on the page' # we receive in utf encoding, but the methods receive utf, so we convert. value = unicode(value, 'utf') - self.setHtmlAsChild('div#text', value+'') + self.relpaceInnerHTML('div#text', value+'') return self.render() def expandSubTree(self, value): 'Expands given subtree' - self.setHtmlAsChild('#text', 'works, expand %s' % value) + self.replaceInnerHTML('#text', 'works, expand %s' % value) return self.render() def collapseSubTree(self, value): 'Collapses given subtree' - self.setHtmlAsChild('#text', 'works, collapse %s' % value) + self.replaceInnerHTML('#text', 'works, collapse %s' % value) return self.render() def cancelSubmitSave(self, text_save): # we receive in utf encoding, but the methods receive utf, so we convert. - self.setHtmlAsChild('div#async', 'Async saved %s' % unicode(text_save, 'utf')) + self.replaceInnerHTML('div#async', 'Async saved %s' % unicode(text_save, 'utf')) return self.render() def removeNodeXpath(self): + # XXX the xpath selector is now moved out of the core, see suppl, product "azaxslt" sel = self.getSelector('xpath', "//P[@id='xpath']/following-sibling::*[position()=1]") - self.removeNode(sel) + self.deleteNode(sel) return self.render() def clickedButton(self, id): 'Show status of the button clicked' - self.setHtmlAsChild('#update-status', "

Button %s clicked. %s

" % (id, datetime.datetime.now())) + self.replaceInnerHTML('#update-status', "

Button %s clicked. %s

" % (id, datetime.datetime.now())) return self.render() From reebalazs at codespeak.net Mon Aug 7 19:33:44 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Mon, 7 Aug 2006 19:33:44 +0200 (CEST) Subject: [Kukit-checkins] r31138 - kukit/kukit.js/trunk/kukit Message-ID: <20060807173344.8013310072@code0.codespeak.net> Author: reebalazs Date: Mon Aug 7 19:33:41 2006 New Revision: 31138 Modified: kukit/kukit.js/trunk/kukit/dom.js kukit/kukit.js/trunk/kukit/forms.js kukit/kukit.js/trunk/kukit/kssparser.js kukit/kukit.js/trunk/kukit/kukit.js kukit/kukit.js/trunk/kukit/plugin.js kukit/kukit.js/trunk/kukit/resourcedata.js Log: IMplement passing all form variables as parms and fixing the cssQuery bug - fixing currentForm, disabling submits from result - Adding parameter producers form, currentForm and arrange marshalling in the standard transport - Add logging elapsed times for loading, parsing and binding - Fix appendHTML, fix rebinding node scopes to inserted nodes - fix cssQuery bug: cssQuery needs to be called always on global document at the moment, because of "tag1 tag2" queries. We mark bound nodes never to be rebound again. - Extending param method currentFormVar to use current node, if no parameter is given - Adding new helper to view Modified: kukit/kukit.js/trunk/kukit/dom.js ============================================================================== --- kukit/kukit.js/trunk/kukit/dom.js (original) +++ kukit/kukit.js/trunk/kukit/dom.js Mon Aug 7 19:33:41 2006 @@ -131,3 +131,16 @@ return param; }; +// really the query should start from the document root, but +// limited to in_nodes subtrees! +kukit.dom.cssQuery = function(selector, in_nodes) { + if (typeof(in_nodes) == null) { + throw 'Selection error in kukit.dom.cssQuery'; + } + // global scope, always. + return cssQuery(selector); + // This is very bad. However the binding makes sure that + // nodes once bound will never be bound again + // (also, noticed the following issue: cssQuery, when called + // on an element, does not check the element itself.) +}; Modified: kukit/kukit.js/trunk/kukit/forms.js ============================================================================== --- kukit/kukit.js/trunk/kukit/forms.js (original) +++ kukit/kukit.js/trunk/kukit/forms.js Mon Aug 7 19:33:41 2006 @@ -26,6 +26,13 @@ /* form query assembler */ +// Prefix constants for dict marshalling, +// pattern: %s(dictprefix)%(name)s%(dictseparator)s%(key)s%(dictpostfix)s +// XXX this should be settable +kukit.fo.dictprefix = '' +kukit.fo.dictseparator = '.' +kukit.fo.dictpostfix = ':record' + kukit.fo.FormQueryElem = function(name, value) { this.name = name; this.value = value; @@ -40,8 +47,18 @@ }; kukit.fo.FormQuery.prototype.appendElem = function(name, value) { - var elem = new kukit.fo.FormQueryElem(name, value); - this.l[this.l.length] = elem; + if (typeof(value) == 'object') { + // Special marshalling of dicts + for (var key in value) { + var qkey = kukit.fo.dictprefix + name + kukit.fo.dictseparator + key + kukit.fo.dictpostfix; + var elem = new kukit.fo.FormQueryElem(qkey, value[key]); + this.l.push(elem); + } + } else { + // normal strings + var elem = new kukit.fo.FormQueryElem(name, value); + this.l.push(elem); + } }; kukit.fo.FormQuery.prototype.encode = function() { @@ -61,7 +78,7 @@ return d; }; -/* Form data extraction */ +/* Form data extraction, helpers */ kukit.fo.findContainer = function(node, func) { // Starting with the given node, find the nearest containing element @@ -110,8 +127,8 @@ value = null; } } else if ((element.tagName.toLowerCase() == 'textarea') - || (element.tagName.toLowerCase() == 'input') - || (element.tagName.toLowerCase() == 'button') + || (element.tagName.toLowerCase() == 'input' && + element.type != 'submit' && element.type != 'reset') ) { value = element.value; } else { @@ -120,26 +137,6 @@ return value; }; -kukit.fo.getFormVarFromCurrentForm = function(target, name) { - // Just get one formvar, from the form that contains the target node - var form = kukit.fo.getCurrentForm(target); - if (!form) { - kukit.logWarning("No form found"); - return null; - } - return kukit.fo.getFormVar(form, name); -} - -kukit.fo.getFormVarFromNamedForm = function(formname, name) { - // Just get one formvar, from the named form - var form = document.forms[formname]; - if (!form) { - kukit.logWarning("No form found"); - return null; - } - return kukit.fo.getFormVar(form, name); -} - kukit.fo.getFormVar = function(form, name) { // Extract the value of a formvar, from a given form var value = null; @@ -157,41 +154,62 @@ return value; }; - -// XXX XXX below this it will be deprecated, I think -// since now we extract stuff into the parms first.... - -kukit.fo.extractFormQuery = function(target) { - throw 'Deprecated?'; - // Extract the query that contains the target. - var query = new kukit.fo.FormQuery(); - // We now put isKukitRequest=1; XXX now deprecated. - //query.appendElem("isKukitRequest", "1"); - if (!target) { - kukit.logWarning("No target given"); - return query; - } - var form = kukit.fo.getFormForNode(target); - if (!form) { - kukit.logWarning("No form found"); - return query; - } +kukit.fo.getAllFormVars = function(form) { + // extracts all elements of a given form + var data = {}; var elements = form.elements; - for (var y=0;y < elements.length;y++) { + for (var y=0; y 0) - kukit.logDebug('EventRule #' + this.getNr() + ' mergeid ' + this.kss_selector.mergeid + ' selected ' + nodes.length + ' nodes'); + var nodes = kukit.dom.cssQuery(this.kss_selector.css, in_nodes); + var counter = 0; for (var y=0; y < nodes.length; y++) { - ruletable.add(nodes[y], this); + var node = nodes[y]; + // XXX never rebind to any node again! + // this compensates that cssQuery is returning results out of the subtree + if (! node._kukitmark) { + ruletable.add(node, this); + counter += 1; + } + } + if (counter > 0) { + kukit.logDebug('EventRule #' + this.getNr() + ' mergeid ' + this.kss_selector.mergeid + ' selected ' + counter + ' nodes'); } - } else if (typeof(in_node) == 'undefined') { + } else if (typeof(in_nodes) == 'undefined') { // Method selector. They only need to be handled on the initial - // pageload, when the in_node parameter is ommitted. + // pageload, when the in_nodes parameter is ommitted. kukit.rd.methodTable.add(this); } }; @@ -523,6 +549,8 @@ eventrule.bind(nodeval.node); } counter += 1; + // XXX Mark the node, disabling rebinding in a second round + nodeval.node._kukitmark = true; } kukit.logDebug('Binding to ' + counter + ' nodes in grand total'); }; @@ -534,7 +562,6 @@ // However we try to set this right. // We generate an uniqueID on the node. This does not work // on MSIE but it already has an uniqueID. - // XXX Konqueror? Other browsers? var id = node.uniqueID; if (typeof(id) == 'undefined') { id = kukit.rd.uid; From reebalazs at codespeak.net Mon Aug 7 19:34:33 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Mon, 7 Aug 2006 19:34:33 +0200 (CEST) Subject: [Kukit-checkins] r31139 - kukit/azax/trunk Message-ID: <20060807173433.1C09210072@code0.codespeak.net> Author: reebalazs Date: Mon Aug 7 19:34:31 2006 New Revision: 31139 Modified: kukit/azax/trunk/azaxview.py Log: Adding new helper to azax view Modified: kukit/azax/trunk/azaxview.py ============================================================================== --- kukit/azax/trunk/azaxview.py (original) +++ kukit/azax/trunk/azaxview.py Mon Aug 7 19:34:31 2006 @@ -80,6 +80,12 @@ new_value = HtmlParser(new_value)().encode('ascii', 'xmlcharrefreplace') command = self.addCommand('replaceInnerHTML', selector) data = command.addParam('html', new_value) + + def replaceHTML(self, selector, new_value): + """ see interfaces.py """ + new_value = HtmlParser(new_value)().encode('ascii', 'xmlcharrefreplace') + command = self.addCommand('replaceHTML', selector) + data = command.addParam('html', new_value) def setAttribute(self, selector, name, value): """ see interfaces.py """ From reebalazs at codespeak.net Mon Aug 7 19:35:30 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Mon, 7 Aug 2006 19:35:30 +0200 (CEST) Subject: [Kukit-checkins] r31140 - in kukit/azaxdemo/trunk: . browser Message-ID: <20060807173530.99E6B10072@code0.codespeak.net> Author: reebalazs Date: Mon Aug 7 19:35:27 2006 New Revision: 31140 Added: kukit/azaxdemo/trunk/browser/form_submit.kss kukit/azaxdemo/trunk/browser/form_submit.pt kukit/azaxdemo/trunk/browser/two_select_revisited.kss kukit/azaxdemo/trunk/browser/two_select_revisited.pt Modified: kukit/azaxdemo/trunk/azaxview.py kukit/azaxdemo/trunk/browser/azax_demo_index.pt kukit/azaxdemo/trunk/browser/azax_two_select.pt kukit/azaxdemo/trunk/configure.zcml Log: Adding two new demos: two select revisited, and form submit Modified: kukit/azaxdemo/trunk/azaxview.py ============================================================================== --- kukit/azaxdemo/trunk/azaxview.py (original) +++ kukit/azaxdemo/trunk/azaxview.py Mon Aug 7 19:35:27 2006 @@ -61,8 +61,16 @@ mapping['']=[] mapping['animals']=['dog', 'cat', 'cow'] mapping['machines']=['computer', 'car', 'airplane'] - result = ['' % item for item in mapping[value]] - self.replaceInnerHTML('select#second', ' '.join(result)) + # XXX Note that originally we just used replaceInnerHTML to just put + # the options inside the select, however this is principally broken + # on IE due to an IE bug. Microsoft has confirmed the bug but is not + # giving information on whether it has or it will ever be fixed. + # For further info, see http://support.microsoft.com/default.aspx?scid=kb;en-us;276228 + # The current solution, replace the outer node, works solidly. + result = ['') + self.replaceHTML('select#second', ' '.join(result)) return self.render() def getAutoupdateMarkup(self): @@ -117,3 +125,29 @@ 'Show status of the button clicked' self.replaceInnerHTML('#update-status', "

Button %s clicked. %s

" % (id, datetime.datetime.now())) return self.render() + + def updateSlaveSelector(self, masterid, value): + """ returns select content """ + mapping = {} + mapping['']=[] + mapping['animals']=['dog', 'cat', 'cow'] + mapping['machines']=['computer', 'car', 'airplane'] + # calculate the slave id + master, _dummy = masterid.split('-') + slaveid = '%s-slave' % master + # make the payload + result = ['') + # XXX See above remark why we need to replace the outer select. + self.replaceHTML('select#%s' % slaveid, ' '.join(result)) + return self.render() + + def formSubmitSave(self, data): + result = ['

Async saved:

'] + for key, value in data.items(): + result.append('' % (key, value)) + result.append('
Name:Value:
%s%s
') + # we build command with unicode, but the methods receive utf, so we convert. + self.replaceInnerHTML('div#async', unicode(''.join(result), 'utf')) + return self.render() Modified: kukit/azaxdemo/trunk/browser/azax_demo_index.pt ============================================================================== --- kukit/azaxdemo/trunk/browser/azax_demo_index.pt (original) +++ kukit/azaxdemo/trunk/browser/azax_demo_index.pt Mon Aug 7 19:35:27 2006 @@ -11,6 +11,8 @@
  • Cancel Submit Click
  • Tree
  • More complex selectors
  • +
  • Master-slave selects revisited
  • +
  • Form submit
  • Modified: kukit/azaxdemo/trunk/browser/azax_two_select.pt ============================================================================== --- kukit/azaxdemo/trunk/browser/azax_two_select.pt (original) +++ kukit/azaxdemo/trunk/browser/azax_two_select.pt Mon Aug 7 19:35:27 2006 @@ -7,9 +7,6 @@

    All demos

    Two Selects

    -

    Note this demo does not work on IE; there is a bug that disables - the injection of OPTION tags under a SELECT. (To make it work, the entire - select should be replaced, for example.)

    + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + + +
    +
    +
    + + Added: kukit/azaxdemo/trunk/browser/two_select_revisited.kss ============================================================================== --- (empty file) +++ kukit/azaxdemo/trunk/browser/two_select_revisited.kss Mon Aug 7 19:35:27 2006 @@ -0,0 +1,5 @@ +select.master-select:change { + action-server: updateSlaveSelector; + updateSlaveSelector-masterid: nodeAttr("id"); + updateSlaveSelector-value: currentFormVar(); +} Added: kukit/azaxdemo/trunk/browser/two_select_revisited.pt ============================================================================== --- (empty file) +++ kukit/azaxdemo/trunk/browser/two_select_revisited.pt Mon Aug 7 19:35:27 2006 @@ -0,0 +1,37 @@ + + + + + + +

    All demos

    +

    Master-slave selects revisited

    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + Modified: kukit/azaxdemo/trunk/configure.zcml ============================================================================== --- kukit/azaxdemo/trunk/configure.zcml (original) +++ kukit/azaxdemo/trunk/configure.zcml Mon Aug 7 19:35:27 2006 @@ -276,4 +276,49 @@ jsfile="browser/more_selectors.js" /> + + + + + + + + + + + + + + + + + From reebalazs at codespeak.net Tue Aug 8 10:11:45 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Tue, 8 Aug 2006 10:11:45 +0200 (CEST) Subject: [Kukit-checkins] r31152 - kukit/kukit.js/trunk/kukit Message-ID: <20060808081145.74A5D10053@code0.codespeak.net> Author: reebalazs Date: Tue Aug 8 10:11:42 2006 New Revision: 31152 Modified: kukit/kukit.js/trunk/kukit/dom.js kukit/kukit.js/trunk/kukit/resourcedata.js Log: Adding nodeContent parameter producer function Modified: kukit/kukit.js/trunk/kukit/dom.js ============================================================================== --- kukit/kukit.js/trunk/kukit/dom.js (original) +++ kukit/kukit.js/trunk/kukit/dom.js Tue Aug 8 10:11:42 2006 @@ -144,3 +144,32 @@ // (also, noticed the following issue: cssQuery, when called // on an element, does not check the element itself.) }; + +// Gets the textual content of the node +// if recursive=false (default), does not descend into sub nodes +kukit.dom.textContent = function(node, recursive) { + var value = kukit.dom._textContent(node, recursive); + // replace newline with spaces + value = value.replace(/\r\n/g, ' '); + value = value.replace(/[\r\n]/g, ' '); + return value; +}; + +kukit.dom._textContent = function(node, recursive) { + if (typeof(recursive) == 'undefined') { + recursive = false; + } + var value = ''; + var childnodes = node.childNodes; + for (var i=0; i Author: reebalazs Date: Thu Aug 10 08:22:54 2006 New Revision: 31215 Modified: kukit/kukit.js/trunk/kukit/kukit.js kukit/kukit.js/trunk/kukit/plugin.js Log: Fix triggerEvent to be a global command, remove the dummy selector hack as it's not needed any more. Make command parser non-permissive for errors. Modified: kukit/kukit.js/trunk/kukit/kukit.js ============================================================================== --- kukit/kukit.js/trunk/kukit/kukit.js (original) +++ kukit/kukit.js/trunk/kukit/kukit.js Thu Aug 10 08:22:54 2006 @@ -253,52 +253,47 @@ var name = ""; selector = command.getAttribute("selector"); - if (selector == null) - selector = ""; - if (selector) { - name = command.getAttribute("name"); - type = command.getAttribute("selectorType"); - if (name == null) - name = ""; - //kukit.log('Command name: '+ name); - var childNodes = command.childNodes; - for (var n=0;n < childNodes.length;n++) { - var childNode = childNodes[n]; - //kukit.logDebug('NodeType: '+ childNode.nodeType); - if (childNode.nodeType != 1) - continue; - //kukit.logDebug('NodeName: '+ childNode.nodeName); - if (childNode.localName) { - if (childNode.localName.toLowerCase() != "param") - continue; - } else { - //IE does not know DOM2 - if (childNode.nodeName.toLowerCase() != "kukit:param") - continue; - } - data = childNode.getAttribute('name'); - if (data != null) { - // Decide if we have a string or a dom parameter - var childCount = childNode.childNodes.length; - var result; - if (childCount == 0) { - // We take this a string (although this could be dom) - result = ''; - } else if (childCount == 1 && childNode.firstChild.nodeType == 3) { - // we have a single text node - result = childNode.firstChild.nodeValue; - } else { - // dom - result = childNode; - } - params[data] = result; - } - } - if (selector != "" && name != "") { - var command = new kukit.cr.makeCommand(selector, name, type, params, transport); - this.addCommand(command); + name = command.getAttribute("name"); + type = command.getAttribute("selectorType"); + if (name == null) + name = ""; + var childNodes = command.childNodes; + for (var n=0;n < childNodes.length;n++) { + var childNode = childNodes[n]; + if (childNode.nodeType != 1) + continue; + if (childNode.localName) { + if (childNode.localName.toLowerCase() != "param") { + throw 'Bad payload, expected param'; + } + } else { + //IE does not know DOM2 + if (childNode.nodeName.toLowerCase() != "kukit:param") { + throw 'Bad payload, expected kukit:param'; + } + } + data = childNode.getAttribute('name'); + if (data != null) { + // Decide if we have a string or a dom parameter + var childCount = childNode.childNodes.length; + var result; + if (childCount == 0) { + // We take this a string (although this could be dom) + result = ''; + } else if (childCount == 1 && childNode.firstChild.nodeType == 3) { + // we have a single text node + result = childNode.firstChild.nodeValue; + } else { + // dom + result = childNode; + } + params[data] = result; + } else { + throw 'Bad payload, expected attribute "name"'; } } + var command = new kukit.cr.makeCommand(selector, name, type, params, transport); + this.addCommand(command); }; kukit.CommandProcessor.prototype.addCommand = function(command) { Modified: kukit/kukit.js/trunk/kukit/plugin.js ============================================================================== --- kukit/kukit.js/trunk/kukit/plugin.js (original) +++ kukit/kukit.js/trunk/kukit/plugin.js Thu Aug 10 08:22:54 2006 @@ -466,7 +466,7 @@ } eventinstance.__trigger_event__(parms.name, actionparms, node); }); -kukit.cr.commandRegistry.registerFromAction('triggerEvent', kukit.cr.makeSelectorCommand); +kukit.cr.commandRegistry.registerFromAction('triggerEvent', kukit.cr.makeGlobalCommand); // XXX Compatibility settings for old command names. // These will be removed as soon as all current use cases are changed. From reebalazs at codespeak.net Thu Aug 10 08:23:20 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Thu, 10 Aug 2006 08:23:20 +0200 (CEST) Subject: [Kukit-checkins] r31216 - kukit/azax/trunk Message-ID: <20060810062320.842291005A@code0.codespeak.net> Author: reebalazs Date: Thu Aug 10 08:23:18 2006 New Revision: 31216 Modified: kukit/azax/trunk/commands.py Log: Remove the dummy selector hack as it's not needed any more. Modified: kukit/azax/trunk/commands.py ============================================================================== --- kukit/azax/trunk/commands.py (original) +++ kukit/azax/trunk/commands.py Thu Aug 10 08:23:18 2006 @@ -91,18 +91,18 @@ % (name, )), DeprecationWarning, 2) else: plugins.Action.checkRegisteredCommand(name) - if isinstance(selector, basestring): - self.selector = selector - self.selectorType = '' - else: - if selector is not None: + if selector is not None: + if isinstance(selector, basestring): + # the default selector - given just as a string + self.selector = selector + self.selectorType = '' + else: plugins.SelectorType.checkRegistered(selector.type) self.selector = selector.value self.selectorType = selector.type - else: - # XXX TODO get rid of these... - self.selector = 'dummy' #None - self.selectorType = 'dummy' #None + else: + self.selector = None + self.selectorType = None self.name = name self.params = [] From reebalazs at codespeak.net Thu Aug 10 08:45:19 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Thu, 10 Aug 2006 08:45:19 +0200 (CEST) Subject: [Kukit-checkins] r31218 - kukit/azax/trunk Message-ID: <20060810064519.29FDE1005A@code0.codespeak.net> Author: reebalazs Date: Thu Aug 10 08:45:17 2006 New Revision: 31218 Modified: kukit/azax/trunk/commands.py Log: Enable adding command parameters from addCommand Modified: kukit/azax/trunk/commands.py ============================================================================== --- kukit/azax/trunk/commands.py (original) +++ kukit/azax/trunk/commands.py Thu Aug 10 08:45:17 2006 @@ -37,8 +37,8 @@ class AzaxCommands(list): implements(IAzaxCommands) - def addCommand(self, name, selector=None): - command = AzaxCommand(name, selector=selector) + def addCommand(self, command_name, selector=None, **kw): + command = AzaxCommand(command_name, selector=selector, **kw) self.append(command) return command @@ -81,16 +81,16 @@ class AzaxCommand: implements(IAzaxCommand) - def __init__(self, name, selector=None): - if plugins.Command.isRegistered(name): + def __init__(self, command_name, selector=None, **kw): + if plugins.Command.isRegistered(command_name): # ok. XXX this will be deprecated # All registerCommand commands are obsolete, by default import warnings, textwrap warnings.warn(textwrap.dedent('''\ The usage of the kss command "%s" is deprecated''' - % (name, )), DeprecationWarning, 2) + % (command_name, )), DeprecationWarning, 2) else: - plugins.Action.checkRegisteredCommand(name) + plugins.Action.checkRegisteredCommand(command_name) if selector is not None: if isinstance(selector, basestring): # the default selector - given just as a string @@ -103,8 +103,11 @@ else: self.selector = None self.selectorType = None - self.name = name + self.name = command_name self.params = [] + # Add parameters passed in **kw + for key, value in kw.iteritems(): + self.addParam(key, value) # -- # Different parameter conversions From reebalazs at codespeak.net Thu Aug 10 09:43:38 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Thu, 10 Aug 2006 09:43:38 +0200 (CEST) Subject: [Kukit-checkins] r31219 - kukit/kukit.js/trunk/kukit Message-ID: <20060810074338.6B23510050@code0.codespeak.net> Author: reebalazs Date: Thu Aug 10 09:43:36 2006 New Revision: 31219 Modified: kukit/kukit.js/trunk/kukit/plugin.js kukit/kukit.js/trunk/kukit/resourcedata.js Log: Adding setStyle action, and restricting setAttribute to reject 'style'. Modified: kukit/kukit.js/trunk/kukit/plugin.js ============================================================================== --- kukit/kukit.js/trunk/kukit/plugin.js (original) +++ kukit/kukit.js/trunk/kukit/plugin.js Thu Aug 10 09:43:36 2006 @@ -351,10 +351,19 @@ kukit.ar.actionRegistry.register('setAttribute', function(node, parms, eventrule) { parms = kukit.pl.completeParms(parms, ['name', 'value'], {}, 'setAttribute action'); + if (parms.name.toLowerCase() == 'style') { + throw 'Style attribute is not allowed with setAttribute'; + } node.setAttribute(parms.name, parms.value); }); kukit.cr.commandRegistry.registerFromAction('setAttribute', kukit.cr.makeSelectorCommand); +kukit.ar.actionRegistry.register('setStyle', function(node, parms, eventrule) { + parms = kukit.pl.completeParms(parms, ['name', 'value'], {}, 'setStyle action'); + node.style[parms.name] = parms.value; +}); +kukit.cr.commandRegistry.registerFromAction('setStyle', kukit.cr.makeSelectorCommand); + kukit.ar.actionRegistry.register('insertHTMLAfter', function(node, parms, eventrule) { parms = kukit.pl.completeParms(parms, ['html'], {}, 'insertHTMLAfter action'); parms.html = kukit.dom.forceToDom(parms.html); Modified: kukit/kukit.js/trunk/kukit/resourcedata.js ============================================================================== --- kukit/kukit.js/trunk/kukit/resourcedata.js (original) +++ kukit/kukit.js/trunk/kukit/resourcedata.js Thu Aug 10 09:43:36 2006 @@ -130,6 +130,9 @@ if (this.args.length != 1) { throw 'nodeAttr method needs 1 argument (attrname)'; } + if (this.args[0].toLowerCase() == 'style') { + throw 'nodeAttr method does not accept "style" as attrname'; + } } break; case 'nodeContent': { if (this.args.length != 0 && this.args.length != 1) { From reebalazs at codespeak.net Thu Aug 10 09:44:12 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Thu, 10 Aug 2006 09:44:12 +0200 (CEST) Subject: [Kukit-checkins] r31220 - kukit/azax/trunk/plugins Message-ID: <20060810074412.1A6A410050@code0.codespeak.net> Author: reebalazs Date: Thu Aug 10 09:44:10 2006 New Revision: 31220 Modified: kukit/azax/trunk/plugins/configure.zcml Log: Adding setStyle action, and restricting setAttribute to reject 'style'. Modified: kukit/azax/trunk/plugins/configure.zcml ============================================================================== --- kukit/azax/trunk/plugins/configure.zcml (original) +++ kukit/azax/trunk/plugins/configure.zcml Thu Aug 10 09:44:10 2006 @@ -197,6 +197,12 @@ /> + + Author: reebalazs Date: Thu Aug 10 17:22:46 2006 New Revision: 31233 Modified: kukit/kukit.js/trunk/kukit/plugin.js Log: Add allowbubbling option to events Modified: kukit/kukit.js/trunk/kukit/plugin.js ============================================================================== --- kukit/kukit.js/trunk/kukit/plugin.js (original) +++ kukit/kukit.js/trunk/kukit/plugin.js Thu Aug 10 17:22:46 2006 @@ -135,14 +135,15 @@ }; kukit.pl.NativeEventBinder.prototype.__bind__ = function(name, parms, func_to_bind, node) { - parms = kukit.pl.completeParms(parms, [], {'preventdefault': ''}, 'native event binding'); + parms = kukit.pl.completeParms(parms, [], {'preventdefault': '', 'allowbubbling': ''}, 'native event binding'); kukit.pl.evalBool(parms, 'preventdefault', 'native event binding'); + kukit.pl.evalBool(parms, 'allowbubbling', 'native event binding'); if (parms.preventdefault && name != 'click') { throw 'In native events only the click event can have preventdefault.'; } var func = function(e) { target = kukit.pl.getTargetForBrowserEvent(e); - if (target == node) { + if (parms.allowbubbling || target == node) { // Execute the action func_to_bind(); } else { From reebalazs at codespeak.net Thu Aug 10 17:49:33 2006 From: reebalazs at codespeak.net (reebalazs at codespeak.net) Date: Thu, 10 Aug 2006 17:49:33 +0200 (CEST) Subject: [Kukit-checkins] r31235 - kukit/azax/trunk Message-ID: <20060810154933.BBA8B10063@code0.codespeak.net> Author: reebalazs Date: Thu Aug 10 17:49:29 2006 New Revision: 31235 Added: kukit/azax/trunk/LICENSE Modified: kukit/azax/trunk/BeautifulSoup.py kukit/azax/trunk/DEPENDENCIES.txt Log: Upgrade BeautifulSoup to version 3.0.3 Modified: kukit/azax/trunk/BeautifulSoup.py ============================================================================== --- kukit/azax/trunk/BeautifulSoup.py (original) +++ kukit/azax/trunk/BeautifulSoup.py Thu Aug 10 17:49:29 2006 @@ -1,206 +1,304 @@ """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: +Beautiful Soup parses a (possibly invalid) XML or HTML document into a +tree representation. It provides methods and Pythonic idioms that make +it easy to navigate, search, and modify the tree. + +A well-structured XML/HTML document yields a well-behaved data +structure. An ill-structured XML/HTML document yields a +correspondingly ill-behaved data structure. If your document is only +locally well-structured, you can use this library to find and process +the well-structured part of it. + +Beautiful Soup works with Python 2.2 and up. It has no external +dependencies, but you'll have more success at converting data to UTF-8 +if you also install these three packages: + +* chardet, for auto-detecting character encodings + http://chardet.feedparser.org/ +* cjkcodecs and iconv_codec, which add more encodings to the ones supported + by stock Python. + http://cjkpython.i18n.org/ +Beautiful Soup defines classes for two main 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. + or invalid. This class has web browser-like heuristics for + obtaining a sensible parse tree in the face of common HTML errors. - * 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. +Beautiful Soup also defines a class (UnicodeDammit) for autodetecting +the encoding of an HTML or XML document, and converting it to +Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed +Parser. + +For more than you ever wanted to know about Beautiful Soup, see the +documentation: +http://www.crummy.com/software/BeautifulSoup/documentation.html """ 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" +__author__ = "Leonard Richardson (crummy.com)" +__contributors__ = ["Sam Ruby (intertwingly.net)", + "the unwitting Mark Pilgrim (diveintomark.org)", + "http://www.crummy.com/software/BeautifulSoup/AUTHORS.html"] +__version__ = "3.0.3" +__copyright__ = "Copyright (c) 2004-2006 Leonard Richardson" __license__ = "PSF" from sgmllib import SGMLParser, SGMLParseError +import codecs import types import re import sgmllib +from htmlentitydefs import name2codepoint -#This code makes Beautiful Soup able to parse XML with namespaces +# This RE makes Beautiful Soup able to parse XML with namespaces. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*') -class NullType(object): +# This RE makes Beautiful Soup capable of recognizing numeric character +# references that use hexadecimal. +sgmllib.charref = re.compile('&#(\d+|x[0-9a-fA-F]+);') - """Similar to NoneType with a corresponding singleton instance - 'Null' that, unlike None, accepts any message and returns itself. +DEFAULT_OUTPUT_ENCODING = "utf-8" - 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) +# First, the classes that represent markup elements. 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): + def setup(self, parent=None, previous=None): """Sets up the initial relations between this element and - other elements.""" + other elements.""" self.parent = parent self.previous = previous - self.next = Null - self.previousSibling = Null - self.nextSibling = Null + self.next = None + self.previousSibling = None + self.nextSibling = None 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): + def replaceWith(self, replaceWith): + oldParent = self.parent + myIndex = self.parent.contents.index(self) + if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent: + # We're replacing this element with one of its siblings. + index = self.parent.contents.index(replaceWith) + if index and index < myIndex: + # Furthermore, it comes before this element. That + # means that when we extract it, the index of this + # element will change. + myIndex = myIndex - 1 + self.extract() + oldParent.insert(myIndex, replaceWith) + + def extract(self): + """Destructively rips this element out of the tree.""" + if self.parent: + try: + self.parent.contents.remove(self) + except ValueError: + pass + + #Find the two elements that would be next to each other if + #this element (and any children) hadn't been parsed. Connect + #the two. + lastChild = self._lastRecursiveChild() + nextElement = lastChild.next + + if self.previous: + self.previous.next = nextElement + if nextElement: + nextElement.previous = self.previous + self.previous = None + lastChild.next = None + + self.parent = None + if self.previousSibling: + self.previousSibling.nextSibling = self.nextSibling + if self.nextSibling: + self.nextSibling.previousSibling = self.previousSibling + self.previousSibling = self.nextSibling = None + + def _lastRecursiveChild(self): + "Finds the last element beneath this object to be parsed." + lastChild = self + while hasattr(lastChild, 'contents') and lastChild.contents: + lastChild = lastChild.contents[-1] + return lastChild + + def insert(self, position, newChild): + if (isinstance(newChild, basestring) + or isinstance(newChild, unicode)) \ + and not isinstance(newChild, NavigableString): + newChild = NavigableString(newChild) + + position = min(position, len(self.contents)) + if hasattr(newChild, 'parent') and newChild.parent != None: + # We're 'inserting' an element that's already one + # of this object's children. + if newChild.parent == self: + index = self.find(newChild) + if index and index < position: + # Furthermore we're moving it further down the + # list of this object's children. That means that + # when we extract this element, our target index + # will jump down one. + position = position - 1 + newChild.extract() + + newChild.parent = self + previousChild = None + if position == 0: + newChild.previousSibling = None + newChild.previous = self + else: + previousChild = self.contents[position-1] + newChild.previousSibling = previousChild + newChild.previousSibling.nextSibling = newChild + newChild.previous = previousChild._lastRecursiveChild() + if newChild.previous: + newChild.previous.next = newChild + + newChildsLastElement = newChild._lastRecursiveChild() + + if position >= len(self.contents): + newChild.nextSibling = None + + parent = self + parentsNextSibling = None + while not parentsNextSibling: + parentsNextSibling = parent.nextSibling + parent = parent.parent + if not parent: # This is the last element in the document. + break + if parentsNextSibling: + newChildsLastElement.next = parentsNextSibling + else: + newChildsLastElement.next = None + else: + nextChild = self.contents[position] + newChild.nextSibling = nextChild + if newChild.nextSibling: + newChild.nextSibling.previousSibling = newChild + newChildsLastElement.next = nextChild + + if newChildsLastElement.next: + newChildsLastElement.next.previous = newChildsLastElement + self.contents.insert(position, newChild) + + def findNext(self, name=None, attrs={}, text=None, **kwargs): """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 + return self._findOne(self.findAllNext, name, attrs, text, **kwargs) - def fetchNext(self, name=None, attrs={}, text=None, limit=None): + def findAllNext(self, name=None, attrs={}, text=None, limit=None, + **kwargs): """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) + return self._findAll(name, attrs, text, limit, self.nextGenerator) - def findNextSibling(self, name=None, attrs={}, text=None): + def findNextSibling(self, name=None, attrs={}, text=None, **kwargs): """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 + return self._findOne(self.findNextSiblings, name, attrs, text, + **kwargs) - def fetchNextSiblings(self, name=None, attrs={}, text=None, limit=None): + def findNextSiblings(self, name=None, attrs={}, text=None, limit=None, + **kwargs): """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) + return self._findAll(name, attrs, text, limit, + self.nextSiblingGenerator, **kwargs) + fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x - def findPrevious(self, name=None, attrs={}, text=None): + def findPrevious(self, name=None, attrs={}, text=None, **kwargs): """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) + return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs) - def fetchPrevious(self, name=None, attrs={}, text=None, limit=None): + def findAllPrevious(self, name=None, attrs={}, text=None, limit=None, + **kwargs): """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 + return self._findAll(name, attrs, text, limit, self.previousGenerator, + **kwargs) + fetchPrevious = findAllPrevious # Compatibility with pre-3.x - def findPreviousSibling(self, name=None, attrs={}, text=None): + def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs): """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 + return self._findOne(self.findPreviousSiblings, name, attrs, text, + **kwargs) - def fetchPreviousSiblings(self, name=None, attrs={}, text=None, - limit=None): + def findPreviousSiblings(self, name=None, attrs={}, text=None, + limit=None, **kwargs): """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) + return self._findAll(name, attrs, text, limit, + self.previousSiblingGenerator, **kwargs) + fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x - def findParent(self, name=None, attrs={}): + def findParent(self, name=None, attrs={}, **kwargs): """Returns the closest parent of this Tag that matches the given criteria.""" - r = Null - l = self.fetchParents(name, attrs, 1) + # NOTE: We can't use _findOne because findParents takes a different + # set of arguments. + r = None + l = self.findParents(name, attrs, 1) if l: r = l[0] return r - firstParent = findParent - def fetchParents(self, name=None, attrs={}, limit=None): + def findParents(self, name=None, attrs={}, limit=None, **kwargs): """Returns the parents of this Tag that match the given criteria.""" - return self._fetch(name, attrs, None, limit, self.parentGenerator) + + return self._findAll(name, attrs, None, limit, self.parentGenerator, + **kwargs) + fetchParents = findParents # Compatibility with pre-3.x #These methods do the real heavy lifting. - def _first(self, method, name, attrs, text): - r = Null - l = method(name, attrs, text, 1) + def _findOne(self, method, name, attrs, text, **kwargs): + r = None + l = method(name, attrs, text, 1, **kwargs) if l: r = l[0] return r - def _fetch(self, name, attrs, text, limit, generator): + def _findAll(self, name, attrs, text, limit, generator, **kwargs): "Iterates over a generator looking for things that match." - if not hasattr(attrs, 'items'): - attrs = {'class' : attrs} - results = [] + if isinstance(name, SoupStrainer): + strainer = name + else: + # Build a SoupStrainer + strainer = SoupStrainer(name, attrs, text, **kwargs) + results = ResultSet(strainer) 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 + if i: + found = strainer.search(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. + #These Generators can be used to navigate starting from both + #NavigableStrings and Tags. def nextGenerator(self): i = self while i: @@ -231,56 +329,91 @@ 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 + # Utility methods + def substituteEncoding(self, str, encoding=None): + encoding = encoding or "utf-8" + return str.replace("%SOUP-ENCODING%", encoding) + + def toEncoding(self, s, encoding=None): + """Encodes an object to a string in some encoding, or to Unicode. + .""" + if isinstance(s, unicode): + if encoding: + s = s.encode(encoding) + elif isinstance(s, str): + if encoding: + s = s.encode(encoding) + else: + s = unicode(s) + else: + if encoding: + s = self.toEncoding(str(s), encoding) + else: + s = unicode(s) + return s -class NavigableText(PageElement): +class NavigableString(unicode, PageElement): def __getattr__(self, attr): - "For backwards compatibility, text.string gives you text" + """text.string gives you text. This is for backwards + compatibility for Navigable*String, but for CData* it lets you + get the string without the CData wrapper.""" if attr == 'string': return self else: raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr) + + def __unicode__(self): + return __str__(self, None) + + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + if encoding: + return self.encode(encoding) + else: + return self -class NavigableString(str, NavigableText): - pass +class CData(NavigableString): -class NavigableUnicodeString(unicode, NavigableText): - pass + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) -class Tag(PageElement): +class ProcessingInstruction(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + output = self + if "%SOUP-ENCODING%" in output: + output = self.substituteEncoding(output, encoding) + return "" % self.toEncoding(output, encoding) + +class Comment(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) + +class Declaration(NavigableString): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING): + return "" % NavigableString.__str__(self, encoding) +class Tag(PageElement): """Represents a found HTML tag with its attributes and contents.""" - def __init__(self, name, attrs=None, parent=Null, previous=Null): + XML_ENTITIES_TO_CHARS = { 'apos' : "'", + "quot" : '"', + "amp" : "&", + "lt" : "<", + "gt" : ">" + } + # An RE for finding ampersands that aren't the start of of a + # numeric entity. + BARE_AMPERSAND = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)") + + def __init__(self, parser, name, attrs=None, parent=None, + previous=None): "Basic constructor." + + # We don't actually store the parser object: that lets extracted + # chunks be garbage-collected + self.parserClass = parser.__class__ + self.isSelfClosing = parser.isSelfClosingTag(name) + self.convertHTMLEntities = parser.convertHTMLEntities self.name = name if attrs == None: attrs = [] @@ -288,6 +421,7 @@ self.contents = [] self.setup(parent, previous) self.hidden = False + self.containsSubstitutions = False def get(self, key, default=None): """Returns the value of the 'key' attribute for the tag, or @@ -295,6 +429,9 @@ attribute.""" return self._getAttrMap().get(key, default) + def has_key(self, key): + return self._getAttrMap().has_key(key) + 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.""" @@ -342,15 +479,16 @@ 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 + findAll() method. Eg. tag('a') returns a list of all the A tags found within this tag.""" - return apply(self.fetch, args, kwargs) + return apply(self.findAll, args, kwargs) def __getattr__(self, tag): + #print "Getattr %s.%s" % (self.__class__, tag) if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3: - return self.first(tag[:-3]) + return self.find(tag[:-3]) elif tag.find('__') != 0: - return self.first(tag) + return self.find(tag) def __eq__(self, other): """Returns true iff this tag has the same name, the same attributes, @@ -370,39 +508,91 @@ as defined in __eq__.""" return not self == other - def __repr__(self): + def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING): """Renders this tag as a string.""" - return str(self) + return self.__str__(encoding) def __unicode__(self): - return self.__str__(1) + return self.__str__(None) + + def _convertEntities(self, match): + x = match.group(1) + if x in name2codepoint: + return unichr(name2codepoint[x]) + elif "&" + x + ";" in self.XML_ENTITIES_TO_CHARS: + return '&%s;' % x + else: + return '&%s;' % x - def __str__(self, needUnicode=None, showStructureIndent=None): + def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): """Returns a string or Unicode representation of this tag and - its contents. + its contents. To get Unicode, pass None for encoding. NOTE: since Python's HTML parser consumes whitespace, this method is not certain to reproduce the whitespace present in the original string.""" - + + encodedName = self.toEncoding(self.name, encoding) + attrs = [] if self.attrs: for key, val in self.attrs: - attrs.append('%s="%s"' % (key, val)) + fmt = '%s="%s"' + if isString(val): + if self.containsSubstitutions and '%SOUP-ENCODING%' in val: + val = self.substituteEncoding(val, encoding) + + # The attribute value either: + # + # * Contains no embedded double quotes or single quotes. + # No problem: we enclose it in double quotes. + # * Contains embedded single quotes. No problem: + # double quotes work here too. + # * Contains embedded double quotes. No problem: + # we enclose it in single quotes. + # * Embeds both single _and_ double quotes. This + # can't happen naturally, but it can happen if + # you modify an attribute value after parsing + # the document. Now we have a bit of a + # problem. We solve it by enclosing the + # attribute in single quotes, and escaping any + # embedded single quotes to XML entities. + if '"' in val: + # This can't happen naturally, but it can happen + # if you modify an attribute value after parsing. + if "'" in val: + val = val.replace('"', """) + else: + fmt = "%s='%s'" + + # Optionally convert any HTML entities + if self.convertHTMLEntities: + val = re.sub("&(\w+);", self._convertEntities, val) + + # Now we're okay w/r/t quotes. But the attribute + # value might also contain angle brackets, or + # ampersands that aren't part of entities. We need + # to escape those to XML entities too. + val = val.replace("<", "<").replace(">", ">") + val = self.BARE_AMPERSAND.sub("&", val) + + + attrs.append(fmt % (self.toEncoding(key, encoding), + self.toEncoding(val, encoding))) close = '' closeTag = '' - if self.isSelfClosing(): + 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) + closeTag = '' % encodedName + + indentTag, indentContents = 0, 0 + if prettyPrint: + indentTag = indentLevel + space = (' ' * (indentTag-1)) + indentContents = indentTag + 1 + contents = self.renderContents(encoding, prettyPrint, indentContents) if self.hidden: s = contents else: @@ -410,73 +600,61 @@ attributeString = '' if attrs: attributeString = ' ' + ' '.join(attrs) - if showStructureIndent: + if prettyPrint: s.append(space) - s.append('<%s%s%s>' % (self.name, attributeString, close)) + s.append('<%s%s%s>' % (encodedName, attributeString, close)) + if prettyPrint: + s.append("\n") s.append(contents) - if closeTag and showStructureIndent != None: + if prettyPrint and contents and contents[-1] != "\n": + s.append("\n") + if prettyPrint and closeTag: s.append(space) s.append(closeTag) + if prettyPrint and closeTag and self.nextSibling: + s.append("\n") 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 prettify(self, encoding=DEFAULT_OUTPUT_ENCODING): + return self.__str__(encoding, True) - def renderContents(self, showStructureIndent=None, needUnicode=None): - """Renders the contents of this tag as a (possibly Unicode) - string.""" + def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING, + prettyPrint=False, indentLevel=0): + """Renders the contents of this tag as a string in the given + encoding. If encoding is None, returns a Unicode string..""" s=[] for c in self: text = None - if isinstance(c, NavigableUnicodeString) or type(c) == types.UnicodeType: - text = unicode(c) + if isinstance(c, NavigableString): + text = c.__str__(encoding) elif isinstance(c, Tag): - s.append(c.__str__(needUnicode, showStructureIndent)) - elif needUnicode: - text = unicode(c) - else: - text = str(c) + s.append(c.__str__(encoding, prettyPrint, indentLevel)) + if text and prettyPrint: + text = text.strip() if text: - if showStructureIndent != None: - if text[-1] == '\n': - text = text[:-1] + if prettyPrint: + s.append(" " * (indentLevel-1)) s.append(text) + if prettyPrint: + s.append("\n") 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) + def find(self, name=None, attrs={}, recursive=True, text=None, + **kwargs): + """Return only the first child of this Tag matching the given + criteria.""" + r = None + l = self.findAll(name, attrs, recursive, text, 1, **kwargs) if l: r = l[0] return r - findChild = first + findChild = find - def fetch(self, name=None, attrs={}, recursive=True, text=None, - limit=None): + def findAll(self, name=None, attrs={}, recursive=True, text=None, + limit=None, **kwargs): """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. @@ -489,18 +667,20 @@ generator = self.recursiveChildGenerator if not recursive: generator = self.childGenerator - return self._fetch(name, attrs, text, limit, generator) - fetchChildren = fetch + return self._findAll(name, attrs, text, limit, generator, **kwargs) + findChildren = findAll + + # Pre-3.x compatibility methods + first = find + fetch = findAll - #Utility methods + def fetchText(self, text=None, recursive=True, limit=None): + return self.findAll(text=text, recursive=recursive, limit=limit) - 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 firstText(self, text=None, recursive=True): + return self.find(text=text, recursive=recursive) + + #Utility methods def append(self, tag): """Appends the given tag to the contents of this tag.""" @@ -538,6 +718,134 @@ break raise StopIteration +# Next, a couple classes to represent queries and their results. +class SoupStrainer: + """Encapsulates a number of ways of matching a markup element (tag or + text).""" + + def __init__(self, name=None, attrs={}, text=None, **kwargs): + self.name = name + if isString(attrs): + kwargs['class'] = attrs + attrs = None + if kwargs: + if attrs: + attrs = attrs.copy() + attrs.update(kwargs) + else: + attrs = kwargs + self.attrs = attrs + self.text = text + + def __str__(self): + if self.text: + return self.text + else: + return "%s|%s" % (self.name, self.attrs) + + def searchTag(self, markupName=None, markupAttrs={}): + found = None + markup = None + if isinstance(markupName, Tag): + markup = markupName + markupAttrs = markup + callFunctionWithTagData = callable(self.name) \ + and not isinstance(markupName, Tag) + + if (not self.name) \ + or callFunctionWithTagData \ + or (markup and self._matches(markup, self.name)) \ + or (not markup and self._matches(markupName, self.name)): + if callFunctionWithTagData: + match = self.name(markupName, markupAttrs) + else: + match = True + markupAttrMap = None + for attr, matchAgainst in self.attrs.items(): + if not markupAttrMap: + if hasattr(markupAttrs, 'get'): + markupAttrMap = markupAttrs + else: + markupAttrMap = {} + for k,v in markupAttrs: + markupAttrMap[k] = v + attrValue = markupAttrMap.get(attr) + if not self._matches(attrValue, matchAgainst): + match = False + break + if match: + if markup: + found = markup + else: + found = markupName + return found + + def search(self, markup): + #print 'looking for %s in %s' % (self, markup) + found = None + # If given a list of items, scan it for a text element that + # matches. + if isList(markup) and not isinstance(markup, Tag): + for element in markup: + if isinstance(element, NavigableString) \ + and self.search(element): + found = element + break + # If it's a Tag, make sure its name or attributes match. + # Don't bother with Tags if we're searching for text. + elif isinstance(markup, Tag): + if not self.text: + found = self.searchTag(markup) + # If it's text, make sure the text matches. + elif isinstance(markup, NavigableString) or \ + isString(markup): + if self._matches(markup, self.text): + found = markup + else: + raise Exception, "I don't know how to match against a %s" \ + % markup.__class__ + return found + + def _matches(self, markup, matchAgainst): + #print "Matching %s against %s" % (markup, matchAgainst) + result = False + if matchAgainst == True and type(matchAgainst) == types.BooleanType: + result = markup != None + elif callable(matchAgainst): + result = matchAgainst(markup) + else: + #Custom match methods take the tag as an argument, but all + #other ways of matching match the tag name as a string. + if isinstance(markup, Tag): + markup = markup.name + if markup and not isString(markup): + markup = unicode(markup) + #Now we know that chunk is either a string, or None. + if hasattr(matchAgainst, 'match'): + # It's a regexp object. + result = markup and matchAgainst.search(markup) + elif isList(matchAgainst): + result = markup in matchAgainst + elif hasattr(matchAgainst, 'items'): + result = markup.has_key(matchAgainst) + elif matchAgainst and isString(markup): + if isinstance(markup, unicode): + matchAgainst = unicode(matchAgainst) + else: + matchAgainst = str(matchAgainst) + + if not result: + result = matchAgainst == markup + return result + +class ResultSet(list): + """A ResultSet is just a list that keeps track of the SoupStrainer + that created it.""" + def __init__(self, source): + list.__init__([]) + self.source = source + +# Now, some helper functions. def isList(l): """Convenience method that works with all 2.x versions of Python @@ -545,10 +853,18 @@ return hasattr(l, '__iter__') \ or (type(l) in (types.ListType, types.TupleType)) +def isString(s): + """Convenience method that works with all 2.x versions of Python + to determine whether or not something is stringlike.""" + try: + return isinstance(s, unicode) or isintance(s, basestring) + except NameError: + return isinstance(s, str) + 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.""" + Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and + NESTING_RESET_TAGS maps out of lists and partial maps.""" built = {} for portion in args: if hasattr(portion, 'items'): @@ -564,9 +880,11 @@ built[portion] = default return built +# Now, the parser classes. + class BeautifulStoneSoup(Tag, SGMLParser): - """This class contains the basic parser and fetch code. It defines + """This class contains the basic parser and search code. It defines a parser that knows nothing about tag behavior except for the following: @@ -587,70 +905,38 @@ 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) + ' />'), + MARKUP_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))) + lambda x: '') ] - ROOT_TAG_NAME = '[document]' + ROOT_TAG_NAME = u'[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. + HTML_ENTITIES = "html" + XML_ENTITIES = "xml" + ALL_ENTITIES = [HTML_ENTITIES, XML_ENTITIES] + + def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None, + markupMassage=True, smartQuotesTo=XML_ENTITIES, + convertEntities=None, selfClosingTags=None): + """The Soup object is initialized as the 'root tag', and the + provided markup (which can be a string or a file-like object) + is fed into the underlying parser. + + sgmllib will process most bad HTML, and the BeautifulSoup + class has some 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 uses regexes to sanitize input, + avoiding the vast majority of these problems. If the problems + don't apply to you, pass in False for markupMassage, and + you'll get better performance. - The two most common instances of invalid HTML that will choke - sgmllib are fixed by the default parser massage techniques: + The default parser massage techniques fix the two most common + instances of invalid HTML that choke sgmllib:
    (No space between name of closing tag and tag close) (Extraneous whitespace in declaration) @@ -658,26 +944,69 @@ 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 + + self.parseOnlyThese = parseOnlyThese + self.fromEncoding = fromEncoding + self.smartQuotesTo = smartQuotesTo + + if convertEntities: + # It doesn't make sense to convert encoded characters to + # entities even while you're converting entities to Unicode. + # Just convert it all to Unicode. + self.smartQuotesTo = None + + if isList(convertEntities): + self.convertHTMLEntities = self.HTML_ENTITIES in convertEntities + self.convertXMLEntities = self.XML_ENTITIES in convertEntities + else: + self.convertHTMLEntities = self.HTML_ENTITIES == convertEntities + self.convertXMLEntities = self.XML_ENTITIES == convertEntities + + self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags) SGMLParser.__init__(self) - self.quoteStack = [] - self.hidden = 1 + + if hasattr(markup, 'read'): # It's a file-type object. + markup = markup.read() + self.markup = markup + self.markupMassage = markupMassage + try: + self._feed() + except StopParsing: + pass + self.markup = None # The markup can now be GCed + + def _feed(self, inDocumentEncoding=None): + # Convert the document to Unicode. + markup = self.markup + if isinstance(markup, unicode): + if not hasattr(self, 'originalEncoding'): + self.originalEncoding = None + else: + dammit = UnicodeDammit\ + (markup, [self.fromEncoding, inDocumentEncoding], + smartQuotesTo=self.smartQuotesTo) + markup = dammit.unicode + self.originalEncoding = dammit.originalEncoding + if markup: + if self.markupMassage: + if not isList(self.markupMassage): + self.markupMassage = self.MARKUP_MASSAGE + for fix, m in self.markupMassage: + markup = fix.sub(m, markup) self.reset() - if hasattr(text, 'read'): - #It's a file-type object. - text = text.read() - if text: - self.feed(text) - if initialTextIsEverything: - self.done() + + SGMLParser.feed(self, markup or "") + SGMLParser.close(self) + # Close out any unfinished strings and close all the open tags. + self.endData() + while self.currentTag.name != self.ROOT_TAG_NAME: + self.popTag() def __getattr__(self, methodName): """This method routes method call requests to either the SGMLParser superclass or the Tag superclass, depending on the method name.""" + #print "__getattr__ called on %s.%s" % (self.__class__, methodName) + if methodName.find('start_') == 0 or methodName.find('end_') == 0 \ or methodName.find('do_') == 0: return SGMLParser.__getattr__(self, methodName) @@ -686,25 +1015,21 @@ 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 isSelfClosingTag(self, name): + """Returns true iff the given string is the name of a + self-closing tag according to this parser.""" + return self.SELF_CLOSING_TAGS.has_key(name) \ + or self.instanceSelfClosingTags.has_key(name) def reset(self): + Tag.__init__(self, self, self.ROOT_TAG_NAME) + self.hidden = 1 SGMLParser.reset(self) self.currentData = [] self.currentTag = None self.tagStack = [] - self.pushTag(self) + self.quoteStack = [] + self.pushTag(self) def popTag(self): tag = self.tagStack.pop() @@ -712,7 +1037,7 @@ # '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): + isinstance(self.currentTag.contents[0], NavigableString): self.currentTag.string = self.currentTag.contents[0] #print "Pop", tag.name @@ -727,30 +1052,35 @@ self.tagStack.append(tag) self.currentTag = self.tagStack[-1] - def endData(self): - currentData = ''.join(self.currentData) - if currentData: + def endData(self, containerClass=NavigableString): + if self.currentData: + currentData = ''.join(self.currentData) + if currentData.endswith('<') and self.convertHTMLEntities: + currentData = currentData[:-1] + '<' if not currentData.strip(): if '\n' in currentData: currentData = '\n' else: currentData = ' ' - c = NavigableString - if type(currentData) == types.UnicodeType: - c = NavigableUnicodeString - o = c(currentData) + self.currentData = [] + if self.parseOnlyThese and len(self.tagStack) <= 1 and \ + (not self.parseOnlyThese.text or \ + not self.parseOnlyThese.search(currentData)): + return + o = containerClass(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.""" + #print "Popping to %s" % name if name == self.ROOT_TAG_NAME: return @@ -807,7 +1137,6 @@ #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 @@ -816,33 +1145,41 @@ self._popToTag(popTo, inclusive) def unknown_starttag(self, name, attrs, selfClosing=0): - #print "Start tag %s" % name + #print "Start tag %s: %s" % (name, attrs) 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.currentData.append('<%s%s>' % (name, attrs)) + return self.endData() - if not name in self.SELF_CLOSING_TAGS and not selfClosing: + + if not self.isSelfClosingTag(name) and not selfClosing: self._smartPop(name) - tag = Tag(name, attrs, self.currentTag, self.previous) + + if self.parseOnlyThese and len(self.tagStack) <= 1 \ + and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)): + return + + tag = Tag(self, 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: + if selfClosing or self.isSelfClosingTag(name): self.popTag() if name in self.QUOTE_TAGS: #print "Beginning quote (%s)" % name self.quoteStack.append(name) self.literal = 1 + return tag def unknown_endtag(self, name): + #print "End tag %s" % 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) + self.currentData.append('' % name) return self.endData() self._popToTag(name) @@ -851,38 +1188,81 @@ self.literal = (len(self.quoteStack) > 0) def handle_data(self, data): + if self.convertHTMLEntities: + if data[0] == '&': + data = self.BARE_AMPERSAND.sub("&",data) + else: + data = data.replace('&','&') \ + .replace('<','<') \ + .replace('>','>') self.currentData.append(data) + def _toStringSubclass(self, text, subclass): + """Adds a certain piece of text to the tree as a NavigableString + subclass.""" + self.endData() + self.handle_data(text) + self.endData(subclass) + def handle_pi(self, text): - "Propagate processing instructions right through." - self.handle_data("" % text) + """Handle a processing instruction as a ProcessingInstruction + object, possibly one with a %SOUP-ENCODING% slot into which an + encoding will be plugged later.""" + if text[:3] == "xml": + text = "xml version='1.0' encoding='%SOUP-ENCODING%'" + self._toStringSubclass(text, ProcessingInstruction) def handle_comment(self, text): - "Propagate comments right through." - self.handle_data("" % text) + "Handle comments as Comment objects." + self._toStringSubclass(text, Comment) def handle_charref(self, ref): - "Propagate char refs right through." - self.handle_data('&#%s;' % ref) + "Handle character references as data." + if ref[0] == 'x': + data = unichr(int(ref[1:],16)) + else: + data = unichr(int(ref)) + + if u'\x80' <= data <= u'\x9F': + data = UnicodeDammit.subMSChar(chr(ord(data)), self.smartQuotesTo) + elif not self.convertHTMLEntities and not self.convertXMLEntities: + data = '&#%s;' % ref + + self.handle_data(data) def handle_entityref(self, ref): - "Propagate entity refs right through." - self.handle_data('&%s;' % ref) + """Handle entity references as data, possibly converting known + HTML entity references to the corresponding Unicode + characters.""" + replaceWithXMLEntity = self.convertXMLEntities and \ + self.XML_ENTITIES_TO_CHARS.has_key(ref) + if self.convertHTMLEntities or replaceWithXMLEntity: + try: + data = unichr(name2codepoint[ref]) + except KeyError: + if replaceWithXMLEntity: + data = self.XML_ENTITIES_TO_CHARS.get(ref) + else: + data="&%s" % ref + else: + data = '&%s;' % ref + self.handle_data(data) def handle_decl(self, data): - "Propagate DOCTYPEs and the like right through." - self.handle_data('' % data) + "Handle DOCTYPEs and the like as Declaration objects." + self._toStringSubclass(data, Declaration) def parse_declaration(self, i): """Treat a bogus SGML declaration as raw data. Treat a CDATA - declaration as regular data.""" + declaration as a CData object.""" j = None if self.rawdata[i:i+9] == '', i) if k == -1: k = len(self.rawdata) - self.handle_data(self.rawdata[i+9:k]) + data = self.rawdata[i+9:k] j = k+3 + self._toStringSubclass(data, CData) else: try: j = SGMLParser.parse_declaration(self, i) @@ -937,11 +1317,17 @@ 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.""" + try ICantBelieveItsBeautifulSoup, MinimalSoup, or + BeautifulStoneSoup before writing your own subclass.""" - SELF_CLOSING_TAGS = buildTagMap(None, ['br' , 'hr', 'input', 'img', 'meta', - 'spacer', 'link', 'frame', 'base']) + def __init__(self, *args, **kwargs): + if not kwargs.has_key('smartQuotesTo'): + kwargs['smartQuotesTo'] = self.HTML_ENTITIES + BeautifulStoneSoup.__init__(self, *args, **kwargs) + + SELF_CLOSING_TAGS = buildTagMap(None, + ['br' , 'hr', 'input', 'img', 'meta', + 'spacer', 'link', 'frame', 'base']) QUOTE_TAGS = {'script': None} @@ -969,6 +1355,9 @@ 'tr' : ['table', 'tbody', 'tfoot', 'thead'], 'td' : ['tr'], 'th' : ['tr'], + 'thead' : ['table'], + 'tbody' : ['table'], + 'tfoot' : ['table'], } NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre'] @@ -982,7 +1371,57 @@ NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS) - + + # Used to detect the charset in a META tag; see start_meta + CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)") + + def start_meta(self, attrs): + """Beautiful Soup can detect a charset included in a META tag, + try to convert the document to that charset, and re-parse the + document from the beginning.""" + httpEquiv = None + contentType = None + contentTypeIndex = None + tagNeedsEncodingSubstitution = False + + for i in range(0, len(attrs)): + key, value = attrs[i] + key = key.lower() + if key == 'http-equiv': + httpEquiv = value + elif key == 'content': + contentType = value + contentTypeIndex = i + + if httpEquiv and contentType: # It's an interesting meta tag. + match = self.CHARSET_RE.search(contentType) + if match: + if getattr(self, 'declaredHTMLEncoding') or \ + (self.originalEncoding == self.fromEncoding): + # This is our second pass through the document, or + # else an encoding was specified explicitly and it + # worked. Rewrite the meta tag. + newAttr = self.CHARSET_RE.sub\ + (lambda(match):match.group(1) + + "%SOUP-ENCODING%", value) + attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], + newAttr) + tagNeedsEncodingSubstitution = True + else: + # This is our first pass through the document. + # Go through it again with the new information. + newCharset = match.group(3) + if newCharset and newCharset != self.originalEncoding: + self.declaredHTMLEncoding = newCharset + self._feed(self.declaredHTMLEncoding) + raise StopParsing + tag = self.unknown_starttag("meta", attrs) + if tag and tagNeedsEncodingSubstitution: + tag.containsSubstitutions = True + +class StopParsing(Exception): + pass + class ICantBelieveItsBeautifulSoup(BeautifulSoup): """The BeautifulSoup class is oriented towards skipping over @@ -1001,15 +1440,12 @@ 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.""" + It's much more common for someone to forget to close 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.""" I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \ ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong', @@ -1022,6 +1458,19 @@ I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS) +class MinimalSoup(BeautifulSoup): + """The MinimalSoup class is for parsing HTML that contains + pathologically bad markup. It makes no assumptions about tag + nesting, but it does know which tags are self-closing, that +
    @@ -24,22 +24,22 @@ Modified: kukit/azaxdemo/trunk/browser/more_selectors.pt ============================================================================== --- kukit/azaxdemo/trunk/browser/more_selectors.pt (original) +++ kukit/azaxdemo/trunk/browser/more_selectors.pt Sun Aug 13 15:35:20 2006 @@ -1,8 +1,8 @@ - + tal:attributes="href string:${context/@@absolute_url}/++resource++more_selectors.kss"/> + Modified: kukit/azaxdemo/branch/zope2.8/browser/header_macros.pt ============================================================================== --- kukit/azaxdemo/branch/zope2.8/browser/header_macros.pt (original) +++ kukit/azaxdemo/branch/zope2.8/browser/header_macros.pt Thu Aug 17 11:23:58 2006 @@ -2,19 +2,19 @@ @@ -24,22 +24,22 @@ Modified: kukit/azaxdemo/branch/zope2.8/browser/more_selectors.pt ============================================================================== --- kukit/azaxdemo/branch/zope2.8/browser/more_selectors.pt (original) +++ kukit/azaxdemo/branch/zope2.8/browser/more_selectors.pt Thu Aug 17 11:23:58 2006 @@ -1,8 +1,8 @@ - + tal:attributes="href string:${context/absolute_url}/++resource++more_selectors.kss"/> +