From f2d11021a3f8b79f5234bf1918dd3fecb58382f1 Mon Sep 17 00:00:00 2001 From: Andrea Cecchi Date: Tue, 4 Feb 2014 11:35:20 +0100 Subject: [PATCH] rebased branch from master --- CHANGES.rst | 29 +- buildout.cfg | 7 + plone/app/discussion/bbb/__init__.py | 0 plone/app/discussion/bbb/discussiontool.py | 0 plone/app/discussion/bbb/migration.py | 0 plone/app/discussion/bbb/talkback.py | 0 plone/app/discussion/browser/comments.py | 3 + plone/app/discussion/interfaces.py | 40 +- .../da/LC_MESSAGES/plone.app.discussion.po | 28 +- .../eu/LC_MESSAGES/plone.app.discussion.po | 115 +- .../uk/LC_MESSAGES/plone.app.discussion.po | 98 +- plone/app/discussion/patches.py | 9 +- .../profiles/default/componentregistry.xml | 9 - .../discussion/profiles/default/toolset.xml | 5 - plone/app/discussion/subscribers.py | 15 + plone/app/discussion/subscribers.zcml | 4 +- .../discussion/tests/javascripts/README.txt | 41 - .../discussion/tests/javascripts/jquery.js | 154 --- .../tests/javascripts/test_comments.html | 42 - .../tests/javascripts/test_comments.js | 146 --- .../javascripts/test_moderation.html.txt | 40 - .../tests/javascripts/test_moderation.js.txt | 72 -- plone/app/discussion/tests/jsTestDriver.conf | 18 - plone/app/discussion/tests/jsTestDriver.txt | 5 - .../discussion/tests/qunit/QUnitAdapter.js | 85 -- plone/app/discussion/tests/qunit/equiv.js | 185 ---- plone/app/discussion/tests/qunit/qunit.css | 17 - plone/app/discussion/tests/qunit/qunit.js | 997 ------------------ plone/app/discussion/tests/test_catalog.py | 35 +- plone/app/discussion/tests/test_comment.py | 3 - .../discussion/tests/test_comments_viewlet.py | 63 +- .../app/discussion/tests/test_controlpanel.py | 3 - .../app/discussion/tests/test_conversation.py | 19 +- plone/app/discussion/tests/test_indexers.py | 3 - plone/app/discussion/tests/test_migration.py | 328 ------ .../discussion/tests/test_moderation_view.py | 28 - .../discussion/tests/test_notifications.py | 5 - plone/app/discussion/tests/test_tool.py | 56 - plone/app/discussion/tests/test_workflow.py | 5 - 39 files changed, 235 insertions(+), 2477 deletions(-) delete mode 100644 plone/app/discussion/bbb/__init__.py delete mode 100644 plone/app/discussion/bbb/discussiontool.py delete mode 100644 plone/app/discussion/bbb/migration.py delete mode 100644 plone/app/discussion/bbb/talkback.py delete mode 100644 plone/app/discussion/profiles/default/componentregistry.xml delete mode 100644 plone/app/discussion/profiles/default/toolset.xml create mode 100644 plone/app/discussion/subscribers.py delete mode 100644 plone/app/discussion/tests/javascripts/README.txt delete mode 100644 plone/app/discussion/tests/javascripts/jquery.js delete mode 100644 plone/app/discussion/tests/javascripts/test_comments.html delete mode 100644 plone/app/discussion/tests/javascripts/test_comments.js delete mode 100644 plone/app/discussion/tests/javascripts/test_moderation.html.txt delete mode 100644 plone/app/discussion/tests/javascripts/test_moderation.js.txt delete mode 100644 plone/app/discussion/tests/jsTestDriver.conf delete mode 100644 plone/app/discussion/tests/jsTestDriver.txt delete mode 100644 plone/app/discussion/tests/qunit/QUnitAdapter.js delete mode 100644 plone/app/discussion/tests/qunit/equiv.js delete mode 100644 plone/app/discussion/tests/qunit/qunit.css delete mode 100644 plone/app/discussion/tests/qunit/qunit.js delete mode 100644 plone/app/discussion/tests/test_migration.py delete mode 100644 plone/app/discussion/tests/test_tool.py diff --git a/CHANGES.rst b/CHANGES.rst index 17efe2d..9cdfec9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,20 +4,39 @@ Changelog 2.3.0 (unreleased) ------------------ +- Corrections and additions to the Danish translation + [aputtu] + +- Put defaultUser.png instead of old defaultUser.gif + [bsuttor] + +- Remove bbb directory. bbb was never really implemented. + [timo] + +- Replace deprecated test assert statements. + [timo] + +- Remove portal_discussion tool. + [timo] + +- Refactor tests to use the PLONE_APP_CONTENTTYPES_FIXTURE instead of + PLONE_FIXTURE. + [timo] + +- Fix ownership of comments. [toutpt] + - Provide 'delete own comments' as a configurable option [gyst] - Make comments editable. [pjstevns, gyst] -- Refactor tests to use the PLONE_APP_CONTENTTYPES_FIXTURE instead of the PLONE_FIXTURE. - [timo] - - 2.2.10 (2013-09-24) ------------------- -- Revert "Refactor tests to use the PLONE_APP_CONTENTTYPES_FIXTURE instead of the PLONE_FIXTURE." that has been accidentially introduced into the 2.2.9 release. +- Revert "Refactor tests to use the PLONE_APP_CONTENTTYPES_FIXTURE instead of + the PLONE_FIXTURE." that has been accidentially introduced into the 2.2.9 + release. [timo] diff --git a/buildout.cfg b/buildout.cfg index 0b66ca6..d8d5f93 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -5,6 +5,7 @@ package-extras = [test] parts += mkrelease pocompile + code-analysis [mkrelease] recipe = zc.recipe.egg @@ -14,7 +15,13 @@ eggs = jarn.mkrelease recipe = zc.recipe.egg eggs = zest.pocompile +[code-analysis] +recipe = plone.recipe.codeanalysis +directory = ${buildout:directory}/plone/app/discussion +flake8-max-complexity = 50 + [versions] plone.app.discussion = zope.interface = 4.0.5 +plone.app.portlets = 2.5a1 diff --git a/plone/app/discussion/bbb/__init__.py b/plone/app/discussion/bbb/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plone/app/discussion/bbb/discussiontool.py b/plone/app/discussion/bbb/discussiontool.py deleted file mode 100644 index e69de29..0000000 diff --git a/plone/app/discussion/bbb/migration.py b/plone/app/discussion/bbb/migration.py deleted file mode 100644 index e69de29..0000000 diff --git a/plone/app/discussion/bbb/talkback.py b/plone/app/discussion/bbb/talkback.py deleted file mode 100644 index e69de29..0000000 diff --git a/plone/app/discussion/browser/comments.py b/plone/app/discussion/browser/comments.py index 7f9b59d..d58dfb7 100644 --- a/plone/app/discussion/browser/comments.py +++ b/plone/app/discussion/browser/comments.py @@ -196,6 +196,7 @@ class CommentForm(extensible.ExtensibleForm, form.Form): # Member member = portal_membership.getAuthenticatedMember() username = member.getUserName() + user = member.getUser() email = member.getProperty('email') fullname = member.getProperty('fullname') if not fullname or fullname == '': @@ -205,6 +206,8 @@ class CommentForm(extensible.ExtensibleForm, form.Form): fullname = unicode(fullname, 'utf-8') if email and isinstance(email, str): email = unicode(email, 'utf-8') + comment.changeOwnership(user, recursive=False) + comment.manage_setLocalRoles(username, ["Owner"]) comment.creator = username comment.author_username = username comment.author_name = fullname diff --git a/plone/app/discussion/interfaces.py b/plone/app/discussion/interfaces.py index 8bf188a..70b9f4b 100644 --- a/plone/app/discussion/interfaces.py +++ b/plone/app/discussion/interfaces.py @@ -184,38 +184,6 @@ class ICaptcha(Interface): required=False) -class ICommentingTool(Interface): - """A tool that indexes all comments for usage by the management interface. - - This means the management interface can still work even though we don't - index the comments in portal_catalog. - - The default implementation of this interface simply defers to - portal_catalog, but a custom version of the tool can be used to provide - an alternate indexing mechanism. - """ - - def indexObject(comment): - """Indexes a comment - """ - - def reindexObject(comment): - """Reindex a comment - """ - - def unindexObject(comment): - """Removes a comment from the indexes - """ - - def uniqueValuesFor(name): - """Get unique values for FieldIndex name - """ - - def searchResults(REQUEST=None, **kw): - """Perform a search over all indexed comments. - """ - - class IDiscussionSettings(Interface): """Global discussion settings. This describes records stored in the configuration registry and obtainable via plone.registry. @@ -392,3 +360,11 @@ class IDiscussionSettings(Interface): class IDiscussionLayer(Interface): """Request marker installed via browserlayer.xml. """ + + +class ICommentingTool(Interface): + """For backwards-compatibility. + + This can be removed once we no longer support upgrading from versions + of Plone that had a portal_discussion tool. + """ diff --git a/plone/app/discussion/locales/da/LC_MESSAGES/plone.app.discussion.po b/plone/app/discussion/locales/da/LC_MESSAGES/plone.app.discussion.po index ec13feb..1fba6a5 100644 --- a/plone/app/discussion/locales/da/LC_MESSAGES/plone.app.discussion.po +++ b/plone/app/discussion/locales/da/LC_MESSAGES/plone.app.discussion.po @@ -16,11 +16,11 @@ msgstr "" #: ../comment.py:311 msgid "A comment has been posted." -msgstr "En kommentar er gem." +msgstr "Der er oprettet en kommentar." #: ../interfaces.py:141 msgid "A comment id unique to this conversation" -msgstr "En kommentar-id som er unik for denne dialog" +msgstr "Et kommentar-id som er unikt for denne dialog" #: ../browser/comments.py:72 msgid "Add a comment" @@ -64,7 +64,7 @@ msgstr "Oprettelses-dato" #: ../interfaces.py:40 msgid "Date of the most recent public comment" -msgstr "" +msgstr "Dato for den seneste, offentlige kommentar" #: ../vocabularies.py:44 msgid "Disabled" @@ -80,7 +80,7 @@ msgstr "Redigering blev afbrudt" #: ../interfaces.py:153 msgid "Email" -msgstr "Email" +msgstr "E-mail" #: ../browser/controlpanel.py:63 msgid "Enable Comments" @@ -88,7 +88,7 @@ msgstr "Slå kommentarer til" #: ../interfaces.py:144 msgid "Id of comment this comment is in reply to" -msgstr "Id'en på den kommentar, denne kommentar er et svar til" +msgstr "Id'et på den kommentar, denne kommentar er et svar til" #: ../interfaces.py:158 msgid "MIME type" @@ -108,7 +108,7 @@ msgstr "Navn" #: ../interfaces.py:162 msgid "Notify me of new comments via email." -msgstr "Send besked om nye kommentarer pr. email." +msgstr "Send besked om nye kommentarer per e-mail." #: ./plone.app.discussion/plone/app/discussion/configure.zcml msgid "Plone Discussions" @@ -116,7 +116,7 @@ msgstr "Plone diskussioner" #: ../interfaces.py:131 msgid "Portal type" -msgstr "Portal type" +msgstr "Portaltype" #: ../browser/controlpanel.py:71 msgid "Save" @@ -128,27 +128,27 @@ msgstr "Liste over kommentatorer (brugernavne)" #: ../interfaces.py:50 msgid "The set of unique commentators (usernames) of published_comments" -msgstr "" +msgstr "Gruppen af unikke kommentatorer (brugernavne) fra published_comments" #: ../interfaces.py:34 msgid "Total number of public comments on this item" -msgstr "" +msgstr "Det samlede antal offentlige kommentarer til dette element" #: ../comment.py:158 msgid "Transform '%s' => '%s' not available. Failed to transform comment '%s'." -msgstr "" +msgstr "Transformation '%s' => '%s' er ikke muligt. Mislykkedes med at transformere kommentaren '%s'." #: ../browser/controlpanel.py:69 msgid "User Email Notification" -msgstr "Email-notificering af brugere" +msgstr "E-mail-notificering af brugere" #: ../interfaces.py:166 msgid "Username of the commenter" -msgstr "" +msgstr "Kommentatorens brugernavn" #: ../browser/comments.py:244 msgid "Your comment awaits moderator approval." -msgstr "Din kommentar venter på godkendelse." +msgstr "Din kommentar venter på godkendelse." #. Default: "Comment" #: ../browser/comments.py:131 @@ -168,7 +168,7 @@ msgstr "Godkend" #. Default: "You can add a comment by filling out the form below. Plain text formatting. Web and email addresses are transformed into clickable links." #: ../browser/comments.py:57 msgid "comment_description_intelligent_text" -msgstr "Du kan tilføje en kommentar ved at udfylde formularen nedenfor. Ren tekst-formattering. Web- og emailadresser bliver automatisk lavet om til klikbare links." +msgstr "Du kan tilføje en kommentar ved at udfylde formularen nedenfor. Ren tekst-formattering. Web- og e-mailadresser bliver automatisk lavet om til klikbare links." #. Default: "You can add a comment by filling out the form below. Plain text formatting. You can use the Markdown syntax for links and images." #: ../browser/comments.py:51 diff --git a/plone/app/discussion/locales/eu/LC_MESSAGES/plone.app.discussion.po b/plone/app/discussion/locales/eu/LC_MESSAGES/plone.app.discussion.po index df9ac4a..a699fbc 100644 --- a/plone/app/discussion/locales/eu/LC_MESSAGES/plone.app.discussion.po +++ b/plone/app/discussion/locales/eu/LC_MESSAGES/plone.app.discussion.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: plone.app.discussion\n" "POT-Creation-Date: YEAR-MO-DA HO:MI +ZONE\n" -"PO-Revision-Date: 2012-02-08 09:14+0100\n" +"PO-Revision-Date: 2013-11-14 09:09+0100\n" "Last-Translator: Mikel Larreategi \n" "Language-Team: eu \n" "MIME-Version: 1.0\n" @@ -15,11 +15,11 @@ msgstr "" "Domain: DOMAIN\n" "X-Poedit-Language: Basque\n" -#: ../comment.py:311 +#: ../comment.py:326 msgid "A comment has been posted." msgstr "Erantzun bat argitaratu da." -#: ../interfaces.py:141 +#: ../interfaces.py:143 msgid "A comment id unique to this conversation" msgstr "Eztabaida honetarako bakarra den erantzunaren id-a." @@ -27,16 +27,16 @@ msgstr "Eztabaida honetarako bakarra den erantzunaren id-a." msgid "Add a comment" msgstr "Erantzuna gehitu" -#: ../browser/controlpanel.py:64 +#: ../browser/controlpanel.py:66 msgid "Anonymous Comments" msgstr "Erantzun anonimoak" -#: ../browser/comments.py:251 -#: ../browser/controlpanel.py:82 +#: ../browser/comments.py:258 +#: ../browser/controlpanel.py:84 msgid "Cancel" msgstr "Utzi" -#: ../browser/controlpanel.py:78 +#: ../browser/controlpanel.py:80 msgid "Changes saved" msgstr "Aldaketak gordeta" @@ -48,24 +48,24 @@ msgstr "Erantzuna onartuta." msgid "Comment deleted." msgstr "Erantzuna ezabatuta." -#: ../browser/controlpanel.py:65 +#: ../browser/controlpanel.py:67 msgid "Commenter Image" msgstr "Erantzuna eman duenaren irudia." msgid "Commenting infrastructure for Plone" msgstr "Ploneren Erantzunen Azpiegitura" -#: ../interfaces.py:136 +#: ../interfaces.py:138 msgid "Conversation" msgstr "Eztabaida" -#: ../interfaces.py:167 +#: ../interfaces.py:176 msgid "Creation date" msgstr "Sorrera data" #: ../interfaces.py:40 msgid "Date of the most recent public comment" -msgstr "" +msgstr "Azken erantzunaren data" #: ../vocabularies.py:44 msgid "Disabled" @@ -75,39 +75,39 @@ msgstr "Desaktibatuta" msgid "Discussion settings" msgstr "Eztabaidaren ezarpenak" -#: ../browser/controlpanel.py:84 +#: ../browser/controlpanel.py:86 msgid "Edit cancelled" msgstr "Edizioa utzita" -#: ../interfaces.py:153 +#: ../interfaces.py:155 msgid "Email" msgstr "E-posta" -#: ../browser/controlpanel.py:63 +#: ../browser/controlpanel.py:65 msgid "Enable Comments" msgstr "Erantzunak aktibatu" -#: ../interfaces.py:144 +#: ../interfaces.py:146 msgid "Id of comment this comment is in reply to" msgstr "Erantzun honek erreferentzia egiten dion erantzunaren id-a" -#: ../interfaces.py:158 +#: ../interfaces.py:160 msgid "MIME type" msgstr "MIME mota" -#: ../browser/controlpanel.py:67 +#: ../browser/controlpanel.py:69 msgid "Moderator Email Notification" msgstr "Moderatzailea e-postaz abisatu" -#: ../interfaces.py:168 +#: ../interfaces.py:177 msgid "Modification date" msgstr "Aldaketa data" -#: ../interfaces.py:138 +#: ../interfaces.py:140 msgid "Name" msgstr "Izena" -#: ../interfaces.py:162 +#: ../interfaces.py:169 msgid "Notify me of new comments via email." msgstr "Erantzun berriak e-postaz bidali" @@ -115,11 +115,11 @@ msgstr "Erantzun berriak e-postaz bidali" msgid "Plone Discussions" msgstr "Plone Eztabaidak" -#: ../interfaces.py:131 +#: ../interfaces.py:133 msgid "Portal type" msgstr "Elementu mota" -#: ../browser/controlpanel.py:71 +#: ../browser/controlpanel.py:73 msgid "Save" msgstr "Gorde" @@ -129,30 +129,30 @@ msgstr "Erantzunak eman dituzten erabiltzaileak (erabiltzaile-izenak)" #: ../interfaces.py:50 msgid "The set of unique commentators (usernames) of published_comments" -msgstr "" +msgstr "Erantzun-emaleen erabiltzaile izenak" #: ../interfaces.py:34 msgid "Total number of public comments on this item" -msgstr "" +msgstr "Elementu honen erantzun publiko kopurua" #: ../comment.py:158 -msgid "Transform '%s' => '%s' not available. Failed to transform comment '%s'." -msgstr "" +msgid "Transform '%s' => '%s' not available." +msgstr "'%s' => '%s' eraldaketa ez dago erabilgarri." -#: ../browser/controlpanel.py:69 +#: ../browser/controlpanel.py:71 msgid "User Email Notification" msgstr "E-posta abisuak" -#: ../interfaces.py:166 +#: ../interfaces.py:175 msgid "Username of the commenter" -msgstr "" +msgstr "Erantzuna utzi duenaren erabiltzaile-izena" -#: ../browser/comments.py:244 +#: ../browser/comments.py:251 msgid "Your comment awaits moderator approval." msgstr "Zure erantzuna moderazio kolan dago." #. Default: "Comment" -#: ../browser/comments.py:131 +#: ../browser/comments.py:132 msgid "add_comment_button" msgstr "Eman erantzuna" @@ -188,9 +188,8 @@ msgstr "Erantzuna formulario hau betez utzi dezakezu. Formatua testu arruntarena #. Default: "${author_name} on ${content}" #: ../comment.py:48 -#, fuzzy msgid "comment_title" -msgstr "${creator} ${content} buruz" +msgstr "${content} - ${creator}" #. Default: "Action" #: ../browser/moderation.pt:85 @@ -223,17 +222,17 @@ msgid "heading_moderate_comments" msgstr "Erantzunak moderatu" #. Default: "If selected, anonymous users are able to post comments without loggin in. It is highly recommended to use a captcha solution to prevent spam if this setting is enabled." -#: ../interfaces.py:236 +#: ../interfaces.py:215 msgid "help_anonymous_comments" msgstr "Aukeratuta badago, erabiltzaile anonimoek erantzunak gehitu ditzakete login egin gabe. Berariaz gomendatzen dizugu Captcha kontrolen bat aktibatzea anonimoen erantzunak baimentzen badituzu." #. Default: "If selected, anonymous user will have to give their email." -#: ../interfaces.py:334 +#: ../interfaces.py:330 msgid "help_anonymous_email_enabled" -msgstr "" +msgstr "Aukeratuta badago, erabiltzaile anonimoek eposta helbidea idatzi beharko dute" #. Default: "Use this setting to enable or disable Captcha validation for comments. Install plone.formwidget.captcha, plone.formwidget.recaptcha, collective.akismet, or collective.z3cform.norobots if there are no options available." -#: ../interfaces.py:279 +#: ../interfaces.py:265 msgid "help_captcha" msgstr "Erabili aukera hau Captcha aktibatu edo desaktibatzeko. Instalatu plone.formwidget.captcha, plone.formwidget.recaptcha, collective.akismet edo collective.z3cform.norobots aukerarik ez baldin badago." @@ -246,55 +245,55 @@ msgstr "" "Erantzunen Moderazio Workflowa aktibatzeko, joan elementu-moten kontrol panelera, aukeratu 'Erantzuna' eta ezarri 'Erantzunen Moderazio Workflowa'." #. Default: "If selected, users are able to post comments on the site. Though, you have to enable comments for specific content types, folders or content objects before users will be able to post comments." -#: ../interfaces.py:224 +#: ../interfaces.py:201 msgid "help_globally_enabled" msgstr "Aukeratuta badago, atarian erantzunak gehitu daitezke. Edonola ere, elementu-mota bakoitzarentzat erantzunak baimendu beharko dituzu erabiltzaileak erantzunak ematen hasi aurretik." #. Default: "If selected, comments will enter a 'Pending' state in which they are invisible to the public. A user with the 'Review comments' permission ('Reviewer' or 'Manager') can approve comments to make them visible to the public. If you want to enable a custom comment workflow, you have to go to the types control panel." -#: ../interfaces.py:248 +#: ../interfaces.py:231 msgid "help_moderation_enabled" msgstr "Aukeratuta badago, erantzuna 'Zain' izeneko egoeran geldituko da eta ez da argitaratuko. 'Erantzunak errebisatu' baimena duten erabiltzaileek ('Zuzentzailea' edo 'Kudeatzailea') argitaratu ditzakete albisteak. Erantzunen worfklowa pertsonalizatu nahi baduzu, elementu-moten kontrol panelera joan zaitez." #. Default: "Address to which moderator notifications will be sent." -#: ../interfaces.py:316 +#: ../interfaces.py:307 msgid "help_moderator_email" msgstr "Moderatzailearen notifikazioak bidali behar diren helbidea." #. Default: "If selected, the moderator is notified if a comment needs attention. The moderator email address can be found in the 'Mail settings' control panel (Site 'From' address)" -#: ../interfaces.py:304 +#: ../interfaces.py:292 msgid "help_moderator_notification_enabled" msgstr "Aukeratuta badago, moderatzaileari e-posta abisua helduko zaio erantzun bat gehitzean. Moderatzailearen e-posta atariaren E-postaren konfigurazioan dago (Atariaren 'Nork' helbidea)" #. Default: "If selected, an image of the user is shown next to the comment." -#: ../interfaces.py:294 +#: ../interfaces.py:281 msgid "help_show_commenter_image" msgstr "Aukeratuta badago, erantzuna eman duenaren irudi bat agertuko da testuaren ondoan." #. Default: "Use this setting to choose if the comment text should be transformed in any way. You can choose between 'Plain text' and 'Intelligent text'. 'Intelligent text' converts plain text into HTML where line breaks and indentation is preserved, and web and email addresses are made into clickable links." -#: ../interfaces.py:263 +#: ../interfaces.py:248 msgid "help_text_transform" msgstr "Erabili aukera hau erantzunaren testua nolabait eraldatu behar bada. 'Testu arrunta' edo 'Testu argia'ren artean aukeratu dezakezu. 'Testu argia'-k testu arrunta HTML bihurtzen du lerro saltoak eta espazioak mantenduz, eta web helbideak eta e-postak klikagarri eginez." #. Default: "If selected, users can choose to be notified of new comments by email." -#: ../interfaces.py:325 +#: ../interfaces.py:319 msgid "help_user_notification_enabled" msgstr "Aukeratuta badago, erabiltzaileek euren erantzunen erantzunak e-postaz jasotzea aktibatu dezakete." #. Default: "Anonymous" #: ../browser/comments.pt:71 -#: ../comment.py:173 +#: ../comment.py:176 msgid "label_anonymous" msgstr "Anonimoak" #. Default: "Enable anonymous comments" -#: ../interfaces.py:234 +#: ../interfaces.py:213 msgid "label_anonymous_comments" msgstr "Aktibatu erabiltzaile anonimoen erantzunak" #. Default: "Enable anonymous email field" -#: ../interfaces.py:332 +#: ../interfaces.py:328 msgid "label_anonymous_email_enabled" -msgstr "" +msgstr "Aktibatu anonimoentzat eposta eremua" #. Default: "Apply" #: ../browser/moderation.pt:71 @@ -302,12 +301,12 @@ msgid "label_apply" msgstr "Aplikatu" #. Default: "Captcha" -#: ../interfaces.py:277 +#: ../interfaces.py:263 msgid "label_captcha" msgstr "Captcha" #. Default: "Comment" -#: ../interfaces.py:159 +#: ../interfaces.py:162 msgid "label_comment" msgstr "Erantzuna" @@ -322,22 +321,22 @@ msgid "label_delete" msgstr "Ezabatu" #. Default: "Globally enable comments" -#: ../interfaces.py:222 +#: ../interfaces.py:199 msgid "label_globally_enabled" msgstr "Erantzunak globalki aktibatu" #. Default: "Enable comment moderation" -#: ../interfaces.py:246 +#: ../interfaces.py:227 msgid "label_moderation_enabled" msgstr "Erantzunen moderazioa aktibatu." #. Default: "Moderator Email Address" -#: ../interfaces.py:314 +#: ../interfaces.py:303 msgid "label_moderator_email" msgstr "Moderatzailearen e-posta helbidea" #. Default: "Enable moderator email notification" -#: ../interfaces.py:302 +#: ../interfaces.py:290 msgid "label_moderator_notification_enabled" msgstr "Aktibatu moderatzaileari e-postaz abisatzea" @@ -352,7 +351,7 @@ msgid "label_says" msgstr "dio:" #. Default: "Show commenter image" -#: ../interfaces.py:292 +#: ../interfaces.py:279 msgid "label_show_commenter_image" msgstr "Erantzuna eman duenaren irudia erakutsi" @@ -362,17 +361,17 @@ msgid "label_show_full_comment_text" msgstr "Erakutsi testu osoa" #. Default: "Subject" -#: ../interfaces.py:155 +#: ../interfaces.py:157 msgid "label_subject" msgstr "Gaia" #. Default: "Comment text transform" -#: ../interfaces.py:261 +#: ../interfaces.py:246 msgid "label_text_transform" msgstr "Erantzunari aplikatu beharreko transformazioa" #. Default: "Enable user email notification" -#: ../interfaces.py:323 +#: ../interfaces.py:315 msgid "label_user_notification_enabled" msgstr "Aktibatu erabiltzaileek e-postaz jakinaraztea" diff --git a/plone/app/discussion/locales/uk/LC_MESSAGES/plone.app.discussion.po b/plone/app/discussion/locales/uk/LC_MESSAGES/plone.app.discussion.po index 615f639..c9a1694 100644 --- a/plone/app/discussion/locales/uk/LC_MESSAGES/plone.app.discussion.po +++ b/plone/app/discussion/locales/uk/LC_MESSAGES/plone.app.discussion.po @@ -14,11 +14,11 @@ msgstr "" "Preferred-Encodings: utf-8 latin1\n" "Domain: DOMAIN\n" -#: ../comment.py:311 +#: ../comment.py:326 msgid "A comment has been posted." msgstr "Коментар додано." -#: ../interfaces.py:141 +#: ../interfaces.py:143 msgid "A comment id unique to this conversation" msgstr "Унікальний ідентифікатор коментаря для цієї розмови" @@ -26,16 +26,16 @@ msgstr "Унікальний ідентифікатор коментаря дл msgid "Add a comment" msgstr "Додати коментар" -#: ../browser/controlpanel.py:64 +#: ../browser/controlpanel.py:66 msgid "Anonymous Comments" msgstr "Анонімне коментування" -#: ../browser/comments.py:251 -#: ../browser/controlpanel.py:82 +#: ../browser/comments.py:258 +#: ../browser/controlpanel.py:84 msgid "Cancel" msgstr "Скасувати" -#: ../browser/controlpanel.py:78 +#: ../browser/controlpanel.py:80 msgid "Changes saved" msgstr "Зміни збережено" @@ -47,18 +47,18 @@ msgstr "Коментар опубліковано." msgid "Comment deleted." msgstr "Коментар знищено." -#: ../browser/controlpanel.py:65 +#: ../browser/controlpanel.py:67 msgid "Commenter Image" msgstr "Зображення коментатора" msgid "Commenting infrastructure for Plone" msgstr "Інфраструктура коментування в Plone" -#: ../interfaces.py:136 +#: ../interfaces.py:138 msgid "Conversation" msgstr "Розмова" -#: ../interfaces.py:167 +#: ../interfaces.py:176 msgid "Creation date" msgstr "Дата створення" @@ -74,39 +74,39 @@ msgstr "Вимкнено" msgid "Discussion settings" msgstr "Налаштуванняя коментування" -#: ../browser/controlpanel.py:84 +#: ../browser/controlpanel.py:86 msgid "Edit cancelled" msgstr "Редагування скасовано" -#: ../interfaces.py:153 +#: ../interfaces.py:155 msgid "Email" msgstr "Електронна адреса" -#: ../browser/controlpanel.py:63 +#: ../browser/controlpanel.py:65 msgid "Enable Comments" msgstr "Увімкнути можливість додавати коментарі" -#: ../interfaces.py:144 +#: ../interfaces.py:146 msgid "Id of comment this comment is in reply to" msgstr "Id коментаря, відповіддю на який - є цей коментар" -#: ../interfaces.py:158 +#: ../interfaces.py:160 msgid "MIME type" msgstr "MIME-тип" -#: ../browser/controlpanel.py:67 +#: ../browser/controlpanel.py:69 msgid "Moderator Email Notification" msgstr "Сповіщення модератора електронною поштою" -#: ../interfaces.py:168 +#: ../interfaces.py:177 msgid "Modification date" msgstr "Дата зміни" -#: ../interfaces.py:138 +#: ../interfaces.py:140 msgid "Name" msgstr "Ім'я" -#: ../interfaces.py:162 +#: ../interfaces.py:169 msgid "Notify me of new comments via email." msgstr "Повідомляти про нові коментарі поштою." @@ -114,11 +114,11 @@ msgstr "Повідомляти про нові коментарі поштою." msgid "Plone Discussions" msgstr "Коментування в Plone" -#: ../interfaces.py:131 +#: ../interfaces.py:133 msgid "Portal type" msgstr "Портал тип" -#: ../browser/controlpanel.py:71 +#: ../browser/controlpanel.py:73 msgid "Save" msgstr "Зберегти" @@ -135,23 +135,23 @@ msgid "Total number of public comments on this item" msgstr "Загальна кількість публічних коментарів для даного елемента" #: ../comment.py:158 -msgid "Transform '%s' => '%s' not available. Failed to transform comment '%s'." -msgstr "Не доступне перетворення '%s' => '%s'. Не вдалося перетворити коментар '%s'." +msgid "Transform '%s' => '%s' not available." +msgstr "Не доступне перетворення '%s' => '%s'." -#: ../browser/controlpanel.py:69 +#: ../browser/controlpanel.py:71 msgid "User Email Notification" msgstr "Сповіщення користувача електронною поштою" -#: ../interfaces.py:166 +#: ../interfaces.py:175 msgid "Username of the commenter" msgstr "Ім'я автора коментаря" -#: ../browser/comments.py:244 +#: ../browser/comments.py:251 msgid "Your comment awaits moderator approval." msgstr "Ваш коментар очікує затвердження модератором." #. Default: "Comment" -#: ../browser/comments.py:131 +#: ../browser/comments.py:132 msgid "add_comment_button" msgstr "Коментар" @@ -221,17 +221,17 @@ msgid "heading_moderate_comments" msgstr "Модерування коментарів" #. Default: "If selected, anonymous users are able to post comments without loggin in. It is highly recommended to use a captcha solution to prevent spam if this setting is enabled." -#: ../interfaces.py:236 +#: ../interfaces.py:215 msgid "help_anonymous_comments" msgstr "Якщо вибрано - то анонімні користувачі зможуть додавати коментарі без входу в систему. Для таких випадків рекомендуєтсья використовувати капчу, щоб запобігти надсиланню спаму." #. Default: "If selected, anonymous user will have to give their email." -#: ../interfaces.py:334 +#: ../interfaces.py:330 msgid "help_anonymous_email_enabled" msgstr "Якщо вибрано, анонімний користувач повинен буде вказати свою електронну пошту." #. Default: "Use this setting to enable or disable Captcha validation for comments. Install plone.formwidget.captcha, plone.formwidget.recaptcha, collective.akismet, or collective.z3cform.norobots if there are no options available." -#: ../interfaces.py:279 +#: ../interfaces.py:265 msgid "help_captcha" msgstr "Використовуйте цей параметр, щоб увімкнути або вимкнути капчу для коментарів. Для цього спершу встановіть plone.formwidget.captcha, plone.formwidget.recaptcha, collective.akismet або collective.z3cform.norobots." @@ -244,53 +244,53 @@ msgstr "" "To enable the moderation workflow for comments, go to the Types Control Panel, choose \"Comment\" and set workflow to \"Comment Review Workflow\".\"" #. Default: "If selected, users are able to post comments on the site. Though, you have to enable comments for specific content types, folders or content objects before users will be able to post comments." -#: ../interfaces.py:224 +#: ../interfaces.py:201 msgid "help_globally_enabled" msgstr "Якщо вибрано, користувачі зможуть додавати коментарі на сайт. Але спочатку необхідно увімкнути можливість коментування для певних типів вмісту, тек, об'єктів." #. Default: "If selected, comments will enter a 'Pending' state in which they are invisible to the public. A user with the 'Review comments' permission ('Reviewer' or 'Manager') can approve comments to make them visible to the public. If you want to enable a custom comment workflow, you have to go to the types control panel." -#: ../interfaces.py:248 +#: ../interfaces.py:231 msgid "help_moderation_enabled" msgstr "Якщо вибрано, коментарі увійде в стан 'В очікуванні', у якому вони невидимі для громадськості. Користувач з правом 'Огляд коментарів' ('Рецензент' або 'Менеджер') може схвалити коментар, щоб зробити їх видимими для громадськості. Якщо ви хочете налаштувати робочий процес коментарів, ви повинні піти в панель керування типів." #. Default: "Address to which moderator notifications will be sent." -#: ../interfaces.py:316 +#: ../interfaces.py:307 msgid "help_moderator_email" msgstr "Адреса, за якою модератору будуть надсилатися повідомлення." #. Default: "If selected, the moderator is notified if a comment needs attention. The moderator email address can be found in the 'Mail settings' control panel (Site 'From' address)" -#: ../interfaces.py:304 +#: ../interfaces.py:292 msgid "help_moderator_notification_enabled" msgstr "Якщо вибрано, модератор отримує повідомлення, якщо коментар вимагає уваги. Адресу електронної пошти модератора можна знайти в 'Пошта' панелі керування (Адреса 'Від')" #. Default: "If selected, an image of the user is shown next to the comment." -#: ../interfaces.py:294 +#: ../interfaces.py:281 msgid "help_show_commenter_image" msgstr "Якщо вибрано, зображення коментатора буде відображатись поруч з коментарем." #. Default: "Use this setting to choose if the comment text should be transformed in any way. You can choose between 'Plain text' and 'Intelligent text'. 'Intelligent text' converts plain text into HTML where line breaks and indentation is preserved, and web and email addresses are made into clickable links." -#: ../interfaces.py:263 +#: ../interfaces.py:248 msgid "help_text_transform" msgstr "Виберіть як повинен бути перетворений текст коментаря. Ви можете вибрати між 'Звичайний текст' і 'Інтелектуальні тексту'. 'Інтелектуальний текст' перетворює текст в HTML, де рядки і відступи зберігаються, інтернет адреси та адреси електронної пошти перетворяться в активні посилання." #. Default: "If selected, users can choose to be notified of new comments by email." -#: ../interfaces.py:325 +#: ../interfaces.py:319 msgid "help_user_notification_enabled" msgstr "Якщо вибрано, користувачі зможуть обрати можливість отримувати нотифікації про нові коментарі поштою." #. Default: "Anonymous" #: ../browser/comments.pt:71 -#: ../comment.py:173 +#: ../comment.py:176 msgid "label_anonymous" msgstr "Анонім" #. Default: "Enable anonymous comments" -#: ../interfaces.py:234 +#: ../interfaces.py:213 msgid "label_anonymous_comments" msgstr "Увімкнути можливість анонімного коментування" #. Default: "Enable anonymous email field" -#: ../interfaces.py:332 +#: ../interfaces.py:328 msgid "label_anonymous_email_enabled" msgstr "Увімкнути поле електронної адреси для аноніма" @@ -300,12 +300,12 @@ msgid "label_apply" msgstr "Застосувати" #. Default: "Captcha" -#: ../interfaces.py:277 +#: ../interfaces.py:263 msgid "label_captcha" msgstr "Капча" #. Default: "Comment" -#: ../interfaces.py:159 +#: ../interfaces.py:162 msgid "label_comment" msgstr "Коментар" @@ -320,22 +320,22 @@ msgid "label_delete" msgstr "Знищити" #. Default: "Globally enable comments" -#: ../interfaces.py:222 +#: ../interfaces.py:199 msgid "label_globally_enabled" msgstr "Увімкнути коментування для цілого сайту" #. Default: "Enable comment moderation" -#: ../interfaces.py:246 +#: ../interfaces.py:227 msgid "label_moderation_enabled" msgstr "Увімкнути модерування коментарів" #. Default: "Moderator Email Address" -#: ../interfaces.py:314 +#: ../interfaces.py:303 msgid "label_moderator_email" msgstr "Електронна адреса модератора" #. Default: "Enable moderator email notification" -#: ../interfaces.py:302 +#: ../interfaces.py:290 msgid "label_moderator_notification_enabled" msgstr "Увімкнути сповіщення модератора" @@ -350,7 +350,7 @@ msgid "label_says" msgstr "каже:" #. Default: "Show commenter image" -#: ../interfaces.py:292 +#: ../interfaces.py:279 msgid "label_show_commenter_image" msgstr "Показати зображення коментатора" @@ -360,17 +360,17 @@ msgid "label_show_full_comment_text" msgstr "показати повний текст коментаря" #. Default: "Subject" -#: ../interfaces.py:155 +#: ../interfaces.py:157 msgid "label_subject" msgstr "Тема" #. Default: "Comment text transform" -#: ../interfaces.py:261 +#: ../interfaces.py:246 msgid "label_text_transform" msgstr "Перетворення тексту коментаря" #. Default: "Enable user email notification" -#: ../interfaces.py:323 +#: ../interfaces.py:315 msgid "label_user_notification_enabled" msgstr "Увімкнути надcилання нотифікації користувачу через електронну адресу" diff --git a/plone/app/discussion/patches.py b/plone/app/discussion/patches.py index 47ec27b..9491702 100644 --- a/plone/app/discussion/patches.py +++ b/plone/app/discussion/patches.py @@ -1,4 +1,4 @@ -from zope.component import queryUtility +from Products.CMFCore.utils import getToolByName from Acquisition import aq_inner, aq_parent @@ -8,7 +8,6 @@ from Products.CMFPlone.utils import base_hasattr from Products.CMFPlone.utils import safe_callable from plone.app.discussion.conversation import ANNOTATION_KEY -from plone.app.discussion.interfaces import ICommentingTool def patchedClearFindAndRebuild(self): @@ -26,14 +25,14 @@ def patchedClearFindAndRebuild(self): obj.indexObject() annotions = IAnnotations(obj) - ctool = queryUtility(ICommentingTool) + catalog = getToolByName(obj, "portal_catalog") if ANNOTATION_KEY in annotions: conversation = annotions[ANNOTATION_KEY] conversation = conversation.__of__(obj) for comment in conversation.getComments(): try: - if ctool: - ctool.indexObject(comment) + if catalog: + catalog.indexObject(comment) except StopIteration: # pragma: no cover pass diff --git a/plone/app/discussion/profiles/default/componentregistry.xml b/plone/app/discussion/profiles/default/componentregistry.xml deleted file mode 100644 index 635cae9..0000000 --- a/plone/app/discussion/profiles/default/componentregistry.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/plone/app/discussion/profiles/default/toolset.xml b/plone/app/discussion/profiles/default/toolset.xml deleted file mode 100644 index 0823a74..0000000 --- a/plone/app/discussion/profiles/default/toolset.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/plone/app/discussion/subscribers.py b/plone/app/discussion/subscribers.py new file mode 100644 index 0000000..07e0bb5 --- /dev/null +++ b/plone/app/discussion/subscribers.py @@ -0,0 +1,15 @@ +from Products.CMFCore.utils import getToolByName + + +def index_object(obj, event): + """Index the object when it is added to the conversation. + """ + catalog = getToolByName(obj, 'portal_catalog') + return catalog.reindexObject(obj) + + +def unindex_object(obj, event): + """Unindex the object when it is removed from the conversation. + """ + catalog = getToolByName(obj, 'portal_catalog') + return catalog.unindexObject(obj) diff --git a/plone/app/discussion/subscribers.zcml b/plone/app/discussion/subscribers.zcml index d0d848d..e563f80 100644 --- a/plone/app/discussion/subscribers.zcml +++ b/plone/app/discussion/subscribers.zcml @@ -24,13 +24,13 @@ `__, and -the test suite will run instrumented in an iframe. Select the Summary tab to see -the results. - -The command-line options ensure that only our tests and the modules being -tested are instrumented for coverage, not the testing framework nor jQuery. - -Note that JSCoverage adds instrumentation statements to the code, so don't try -to debug your tests when running via the jscoverage server. - -.. _QUnit: http://docs.jquery.com/QUnit -.. _JSCoverage: http://siliconforks.com/jscoverage/ diff --git a/plone/app/discussion/tests/javascripts/jquery.js b/plone/app/discussion/tests/javascripts/jquery.js deleted file mode 100644 index 7c24308..0000000 --- a/plone/app/discussion/tests/javascripts/jquery.js +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/plone/app/discussion/tests/javascripts/test_comments.html b/plone/app/discussion/tests/javascripts/test_comments.html deleted file mode 100644 index be846e0..0000000 --- a/plone/app/discussion/tests/javascripts/test_comments.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - plone.app.discussion comments Test Suite - - - - - - - - - - - - - - - - - -

plone.app.discussion comments Test - Suite

- -

- -
-
- -

- -
    -
- - - - diff --git a/plone/app/discussion/tests/javascripts/test_comments.js b/plone/app/discussion/tests/javascripts/test_comments.js deleted file mode 100644 index 8bca11f..0000000 --- a/plone/app/discussion/tests/javascripts/test_comments.js +++ /dev/null @@ -1,146 +0,0 @@ - -$(document).ready(function () { - - /* TEST SETUP */ - - module("comments", { - setup: function () { - - // Create a comments section with one comment inside - // - //
- //
- //
- //

Lorem ipsum.

- //
- //
- // - //
- //
- //
- - var comments = $(document.createElement("div")) - .addClass("discussion") - .append($(document.createElement("div")) - .addClass("comment") - .attr("id", "1282720906349675") - .append($(document.createElement("div")) - .addClass("commentBody") - .append($(document.createElement("p")) - .text("Lorem ipsum.") - ) - ) - .append($(document.createElement("div")) - .addClass("commentActions") - .append($(document.createElement("button")) - .addClass("reply-to-comment-button") - )) - ); - $(document.body).append(comments); - - // Create a basic commenting form - // - //
- //
- // - //
- //
- // - //
- //
- // - //
- //
- // - // - //
- //
- - var commentform = $(document.createElement("div")) - .append($(document.createElement("form")) - .addClass("form") - .append($(document.createElement("div")) - .addClass("formfield-form-widgets-in_reply_to") - .append($(document.createElement("input")) - .attr("name", "form.widgets.in_reply_to") - .val("") - ) - ) - .append($(document.createElement("div")) - .addClass("formfield-form-widgets-author_name") - .append($(document.createElement("input")) - .attr("name", "form.widgets.author") - .attr("type", "text") - ) - ) - .append($(document.createElement("div")) - .addClass("formfield-form-widgets-text") - .append($(document.createElement("textarea")) - .attr("name", "form.widgets.text") - ) - ) - .append($(document.createElement("div")) - .addClass("formControls") - .append($(document.createElement("input")) - .attr("name", "form.buttons.comment")) - .append($(document.createElement("input")) - .attr("name", "form.buttons.cancel")) - ) - ) - .addClass("reply") - .attr("id", "commenting"); - $(document.body).append(commentform); - }, - teardown: function () { - $("#commenting").remove(); - $(".discussion").remove(); - } - }); - - - /* TESTS */ - - test("Hide the reply and the cancel button for the comment form", function(){ - expect(1); - $(".reply").find("input[name='form.buttons.cancel']").css("display", "none"); - equals($("input[name='form.buttons.cancel']").css("display"), "none", "The cancel button should be hidden"); - }); - - test("Show the reply button only when Javascript is enabled", function(){ - expect(1); - $(".reply-to-comment-button").css("display", "inline"); - equals($("button[class='reply-to-comment-button']").attr("style"), "display: inline;", "The reply button should show up when Javascript is enabled"); - }); - - test("Create a comment reply form.", function() { - expect(2); - var comment_div = $("#1282720906349675"); - var reply_button = comment_div.children(".reply-to-comment-button"); - $.createReplyForm(comment_div); - var reply_form = comment_div.children(".reply"); - ok(reply_form.find("input[name='form.widgets.in_reply_to']"), "Reply form has been copied"); - same(reply_form.find("input[name='form.widgets.in_reply_to']").val(), "1282720906349675", "The reply for should have the id of the comment in the in_reply_to field"); - }); - - test("Clear all form values from a form.", function() { - // Create a reply form with some values - var comment_div = $("#1282720906349675"); - $.createReplyForm(comment_div); - var reply_form = comment_div.find(".reply"); - var author = reply_form.find("input[name='form.widgets.author']"); - var text = comment_div.find("input[name='form.widgets.text']"); - author.val("my author"); - text.val("my text"); - // Call the clearForm function to clear the form - $.clearForm(comment_div); - // Check if all form fields have been cleared - var author = comment_div.find("input[name='form.widgets.author']"); - var text = comment_div.find("input[name='form.widgets.text']"); - equals(author.val(), "", "The author form value should be empty"); - equals(text.text(), "", "The text form value should be empty"); - - }); - -}); - - diff --git a/plone/app/discussion/tests/javascripts/test_moderation.html.txt b/plone/app/discussion/tests/javascripts/test_moderation.html.txt deleted file mode 100644 index 36afc81..0000000 --- a/plone/app/discussion/tests/javascripts/test_moderation.html.txt +++ /dev/null @@ -1,40 +0,0 @@ - - - - - plone.app.discussion moderation Test Suite - - - - - - - - - - - - - - - - - - -

plone.app.discussion moderation Test Suite

- -

- -
- -

- -
    - - - - diff --git a/plone/app/discussion/tests/javascripts/test_moderation.js.txt b/plone/app/discussion/tests/javascripts/test_moderation.js.txt deleted file mode 100644 index 49755d0..0000000 --- a/plone/app/discussion/tests/javascripts/test_moderation.js.txt +++ /dev/null @@ -1,72 +0,0 @@ -/* TEST SETUP */ - -module("comments", { - setup: function () { - //
    - // - // - // - // - // - // - // - //
    - // My comment - // - // - // - //
    - //
    - var review_table = $(document.createElement("form")) - .append($(document.createElement("table")) - .attr("id", "review-comments") - .append($(document.createElement("tbody")) - .append($(document.createElement("tr")) - .append($(document.createElement("td")) - .append($(document.createElement("a")) - .text("My comment.") - .attr("href", "http://localhost:8080/Plone/front-page/++conversation++default/1285339036601284") - ) - ) - .append($(document.createElement("td")) - .append($(document.createElement("input")) - .attr("id", "1285339036601284") - .attr("value", "Publish") - .attr("name", "form.button.Publish") - ) - .append($(document.createElement("input")) - .attr("id", "1285339036601284") - .attr("value", "Delete") - .attr("name", "form.button.Delete") - ) - .addClass("actions") - ) - ) - ) - ); - $(document.body).append(review_table); - }, - teardown: function () { - $("form").remove(); - } -}); - - -/* TESTS */ - -test("Delete a single comment", function(){ - expect(1); - stop(); - var delete_button = $(".actions").children("input[name='form.button.Delete']"); - delete_button.trigger('click'); - start(); - equals($("#1285339036601284").attr("name", "form.button.Delete").length, 0, "The comment row should have been deleted."); -}); - - -test("Publish a single comment", function(){ - expect(1); - var publish_button = $(".actions").children("input[name='form.button.Publish']"); - publish_button.trigger('click'); - equals($("#1285339036601284").attr("name", "form.button.Publish").length, 0, "The comment row should have been removed since the comment has been published."); -}); diff --git a/plone/app/discussion/tests/jsTestDriver.conf b/plone/app/discussion/tests/jsTestDriver.conf deleted file mode 100644 index c5335fb..0000000 --- a/plone/app/discussion/tests/jsTestDriver.conf +++ /dev/null @@ -1,18 +0,0 @@ -server: http://localhost:9876 - -load: - # Add these lines to load the equiv function and adapter in order, before the - # tests (assuming they are saved to tests/qunit/) - - qunit/equiv.js - - qunit/QUnitAdapter.js - - # This is where we load the qunit tests - - javascripts/*.js - - # And this loads the source files we are testing - - ../browser/javascripts/*.js - -plugin: - - name: "coverage" - jar: "../../../../parts/jstestdriver/coverage.jar" - module: "com.google.jstestdriver.coverage.CoverageModule" diff --git a/plone/app/discussion/tests/jsTestDriver.txt b/plone/app/discussion/tests/jsTestDriver.txt deleted file mode 100644 index 7751bb8..0000000 --- a/plone/app/discussion/tests/jsTestDriver.txt +++ /dev/null @@ -1,5 +0,0 @@ -============== -JS TEST DRIVER -============== - -java -jar JsTestDriver.jar --port 9876 --config jsTestDriver.conf --browser /usr/bin/firefox --tests all \ No newline at end of file diff --git a/plone/app/discussion/tests/qunit/QUnitAdapter.js b/plone/app/discussion/tests/qunit/QUnitAdapter.js deleted file mode 100644 index a3fc18c..0000000 --- a/plone/app/discussion/tests/qunit/QUnitAdapter.js +++ /dev/null @@ -1,85 +0,0 @@ -/* -QUnitAdapter -Version: 1.1.0 - -Run qunit tests using JS Test Driver - -This provides almost the same api as qunit. - -Tests must run sychronously, which means no use of stop and start methods. -You can use jsUnit Clock object to deal with timeouts and intervals: -http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html - -The qunit #main DOM element is not included. If you need to do any DOM manipulation -you need to set it up and tear it down in each test. - -*/ -(function() { - - if(!(window.equiv)) { - throw new Error("QUnitAdapter.js - Unable to find equiv function. Ensure you have added equiv.js to the load section of your jsTestDriver.conf"); - } - - var QUnitTestCase; - - window.module = function(name, lifecycle) { - QUnitTestCase = TestCase(name); - QUnitTestCase.prototype.lifecycle = lifecycle || {}; - }; - - window.test = function(name, expected, test) { - QUnitTestCase.prototype['test ' + name] = function() { - if(this.lifecycle.setup) { - this.lifecycle.setup(); - } - if(expected.constructor === Number) { - expectAsserts(expected); - } else { - test = expected; - } - test.call(this.lifecycle); - - if(this.lifecycle.teardown) { - this.lifecycle.teardown(); - } - }; - }; - - window.expect = function(count) { - expectAsserts(count); - }; - - window.ok = function(actual, msg) { - assertTrue(msg ? msg : '', !!actual); - }; - - window.equals = function(a, b, msg) { - assertEqual(msg ? msg : '', b, a); - }; - - window.start = window.stop = function() { - fail('start and stop methods are not available when using JS Test Driver.\n' + - 'Use jsUnit Clock object to deal with timeouts and intervals:\n' + - 'http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html.'); - }; - - window.same = function(a, b, msg) { - assertTrue(msg ? msg : '', window.equiv(b, a)); - }; - - window.reset = function() { - fail('reset method is not available when using JS Test Driver'); - }; - - window.isLocal = function() { - return false; - }; - - window.QUnit = { - equiv: window.equiv, - ok: window.ok - }; - - module('Default Module'); - -})(); diff --git a/plone/app/discussion/tests/qunit/equiv.js b/plone/app/discussion/tests/qunit/equiv.js deleted file mode 100644 index 2f5b9f3..0000000 --- a/plone/app/discussion/tests/qunit/equiv.js +++ /dev/null @@ -1,185 +0,0 @@ - -// Tests for equality any JavaScript type and structure without unexpected results. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv -// Author: Philippe Rath -window.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - - // Determine what is o. - function hoozit(o) { - if (typeof o === "string") { - return "string"; - - } else if (typeof o === "boolean") { - return "boolean"; - - } else if (typeof o === "number") { - - if (isNaN(o)) { - return "nan"; - } else { - return "number"; - } - - } else if (typeof o === "undefined") { - return "undefined"; - - // consider: typeof null === object - } else if (o === null) { - return "null"; - - // consider: typeof [] === object - } else if (o instanceof Array) { - return "array"; - - // consider: typeof new Date() === object - } else if (o instanceof Date) { - return "date"; - - // consider: /./ instanceof Object; - // /./ instanceof RegExp; - // typeof /./ === "function"; // => false in IE and Opera, - // true in FF and Safari - } else if (o instanceof RegExp) { - return "regexp"; - - } else if (typeof o === "object") { - return "object"; - - } else if (o instanceof Function) { - return "function"; - } - } - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = hoozit(o); - if (prop) { - if (hoozit(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - return a === b; - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function (b) { - return isNaN(b); - }, - - "date": function (b, a) { - return hoozit(b) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function (b, a) { - return hoozit(b) === "regexp" && - a.source === b.source && // the regex itself - a.global === b.global && // and its modifers (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function () { - var caller = callers[callers.length - 1]; - return caller !== Object && - typeof caller !== "undefined"; - }, - - "array": function (b, a) { - var i; - var len; - - // b could be an object literal here - if ( ! (hoozit(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - for (i = 0; i < len; i++) { - if( ! innerEquiv(a[i], b[i])) { - return false; - } - } - return true; - }, - - "object": function (b, a) { - var i; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of strings - - // comparing constructors is more strict than using instanceof - if ( a.constructor !== b.constructor) { - return false; - } - - // stack constructor before traversing properties - callers.push(a.constructor); - - for (i in a) { // be strict: don't ensures hasOwnProperty and go deep - - aProperties.push(i); // collect a's properties - - if ( ! innerEquiv(a[i], b[i])) { - eq = false; - } - } - - callers.pop(); // unstack, we are done - - for (i in b) { - bProperties.push(i); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv(aProperties.sort(), bProperties.sort()); - } - }; - }(); - - innerEquiv = function () { // can take multiple arguments - var args = Array.prototype.slice.apply(arguments); - if (args.length < 2) { - return true; // end transition - } - - return (function (a, b) { - if (a === b) { - return true; // catch the most you can - - } else if (typeof a !== typeof b || a === null || b === null || typeof a === "undefined" || typeof b === "undefined") { - return false; // don't lose time with error prone cases - - } else { - return bindCallbacks(a, callbacks, [b, a]); - } - - // apply transition with (1..n) arguments - })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); - }; - - return innerEquiv; -}(); // equiv diff --git a/plone/app/discussion/tests/qunit/qunit.css b/plone/app/discussion/tests/qunit/qunit.css deleted file mode 100644 index 4542933..0000000 --- a/plone/app/discussion/tests/qunit/qunit.css +++ /dev/null @@ -1,17 +0,0 @@ -h1#qunit-header { padding: 15px; font-size: large; background-color: #06b; color: white; font-family: 'trebuchet ms', verdana, arial; margin: 0; } -h1#qunit-header a { color: white; } - -h2#qunit-banner { height: 2em; border-bottom: 1px solid white; background-color: #eee; margin: 0; font-family: 'trebuchet ms', verdana, arial; } -h2#qunit-banner.pass { background-color: green; } -h2#qunit-banner.fail { background-color: red; } - -h2#qunit-userAgent { padding: 10px; background-color: #eee; color: black; margin: 0; font-size: small; font-weight: normal; font-family: 'trebuchet ms', verdana, arial; font-size: 10pt; } - -div#qunit-testrunner-toolbar { background: #eee; border-top: 1px solid black; padding: 10px; font-family: 'trebuchet ms', verdana, arial; margin: 0; font-size: 10pt; } - -ol#qunit-tests { font-family: 'trebuchet ms', verdana, arial; font-size: 10pt; } -ol#qunit-tests li strong { cursor:pointer; } -ol#qunit-tests .pass { color: green; } -ol#qunit-tests .fail { color: red; } - -p#qunit-testresult { margin-left: 1em; font-size: 10pt; font-family: 'trebuchet ms', verdana, arial; } diff --git a/plone/app/discussion/tests/qunit/qunit.js b/plone/app/discussion/tests/qunit/qunit.js deleted file mode 100644 index 51ec655..0000000 --- a/plone/app/discussion/tests/qunit/qunit.js +++ /dev/null @@ -1,997 +0,0 @@ -/* - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2009 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - */ - -(function(window) { - -var QUnit = { - - // Initialize the configuration options - init: function init() { - config = { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date, - blocking: false, - autorun: false, - assertions: [], - filters: [], - queue: [] - }; - - var tests = id("qunit-tests"), - banner = id("qunit-banner"), - result = id("qunit-testresult"); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - }, - - // call on start of module test to prepend name to all tests - module: function module(name, testEnvironment) { - - synchronize(function() { - if ( config.currentModule ) { - QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); - } - - config.currentModule = name; - config.moduleTestEnvironment = testEnvironment; - config.moduleStats = { all: 0, bad: 0 }; - - QUnit.moduleStart( name, testEnvironment ); - }); - }, - - asyncTest: function asyncTest(testName, expected, callback) { - if ( arguments.length === 2 ) { - callback = expected; - expected = 0; - } - - QUnit.test(testName, expected, callback, true); - }, - - test: function test(testName, expected, callback, async) { - var name = testName, testEnvironment = {}; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - - if ( config.currentModule ) { - name = config.currentModule + " module: " + name; - } - - if ( !validTest(name) ) { - return; - } - - synchronize(function() { - QUnit.testStart( testName ); - - testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, config.moduleTestEnvironment); - - config.assertions = []; - config.expected = null; - - if ( arguments.length >= 3 ) { - config.expected = callback; - callback = arguments[2]; - } - - try { - if ( !config.pollution ) { - saveGlobal(); - } - - testEnvironment.setup.call(testEnvironment); - } catch(e) { - QUnit.ok( false, "Setup failed on " + name + ": " + e.message ); - } - - if ( async ) { - QUnit.stop(); - } - - try { - callback.call(testEnvironment); - } catch(e) { - fail("Test " + name + " died, exception and test follows", e, callback); - QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - start(); - } - } - }); - - synchronize(function() { - try { - checkPollution(); - testEnvironment.teardown.call(testEnvironment); - } catch(e) { - QUnit.ok( false, "Teardown failed on " + name + ": " + e.message ); - } - - try { - QUnit.reset(); - } catch(e) { - fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset); - } - - if ( config.expected && config.expected != config.assertions.length ) { - QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" ); - } - - var good = 0, bad = 0, - tests = id("qunit-tests"); - - config.stats.all += config.assertions.length; - config.moduleStats.all += config.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); - ol.style.display = "none"; - - for ( var i = 0; i < config.assertions.length; i++ ) { - var assertion = config.assertions[i]; - - var li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || "(no message)"; - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - var b = document.createElement("strong"); - b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")"; - - addEvent(b, "click", function() { - var next = b.nextSibling, display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function(e) { - var target = (e || window.event).target; - if ( target.nodeName.toLowerCase() === "strong" ) { - var text = "", node = target.firstChild; - - while ( node.nodeType === 3 ) { - text += node.nodeValue; - node = node.nextSibling; - } - - text = text.replace(/(^\s*|\s*$)/g, ""); - - if ( window.location ) { - window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text); - } - } - }); - - var li = document.createElement("li"); - li.className = bad ? "fail" : "pass"; - li.appendChild( b ); - li.appendChild( ol ); - tests.appendChild( li ); - - if ( bad ) { - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - toolbar.style.display = "block"; - id("qunit-filter-pass").disabled = null; - id("qunit-filter-missing").disabled = null; - } - } - - } else { - for ( var i = 0; i < config.assertions.length; i++ ) { - if ( !config.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - QUnit.testDone( testName, bad, config.assertions.length ); - - if ( !window.setTimeout && !config.queue.length ) { - done(); - } - }); - - if ( window.setTimeout && !config.doneTimer ) { - config.doneTimer = window.setTimeout(function(){ - if ( !config.queue.length ) { - done(); - } else { - synchronize( done ); - } - }, 13); - } - }, - - /** - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - */ - expect: function expect(asserts) { - config.expected = asserts; - }, - - /** - * Asserts true. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function ok(a, msg) { - QUnit.log(a, msg); - - config.assertions.push({ - result: !!a, - message: msg - }); - }, - - /** - * Checks that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * - * Prefered to ok( actual == expected, message ) - * - * @example equals( format("Received {0} bytes.", 2), "Received 2 bytes." ); - * - * @param Object actual - * @param Object expected - * @param String message (optional) - */ - equals: function equals(actual, expected, message) { - push(expected == actual, actual, expected, message); - }, - - same: function(a, b, message) { - push(QUnit.equiv(a, b), a, b, message); - }, - - start: function start() { - // A slight delay, to avoid any current callbacks - if ( window.setTimeout ) { - window.setTimeout(function() { - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(); - }, 13); - } else { - config.blocking = false; - process(); - } - }, - - stop: function stop(timeout) { - config.blocking = true; - - if ( timeout && window.setTimeout ) { - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - QUnit.start(); - }, timeout); - } - }, - - /** - * Resets the test setup. Useful for tests that modify the DOM. - */ - reset: function reset() { - if ( window.jQuery ) { - jQuery("#main").html( config.fixture ); - jQuery.event.global = {}; - jQuery.ajaxSettings = extend({}, config.ajaxSettings); - } - }, - - /** - * Trigger an event on an element. - * - * @example triggerEvent( document.body, "click" ); - * - * @param DOMElement elem - * @param String type - */ - triggerEvent: function triggerEvent( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, - - // Logging callbacks - done: function done(failures, total) {}, - log: function log(result, message) {}, - testStart: function testStart(name) {}, - testDone: function testDone(name, failures, total) {}, - moduleStart: function moduleStart(name, testEnvironment) {}, - moduleDone: function moduleDone(name, failures, total) {} -}; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - GETParams = location.search.slice(1).split('&'); - - for ( var i = 0; i < GETParams.length; i++ ) { - GETParams[i] = decodeURIComponent( GETParams[i] ); - if ( GETParams[i] === "noglobals" ) { - GETParams.splice( i, 1 ); - i--; - config.noglobals = true; - } - } - - // restrict modules/tests by get parameters - config.filters = GETParams; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = !!(location.protocol === 'file:'); -})(); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} else { - extend(exports, QUnit); - exports.QUnit = QUnit; -} - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -addEvent(window, "load", function() { - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - toolbar.style.display = "none"; - - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - filter.disabled = true; - addEvent( filter, "click", function() { - var li = document.getElementsByTagName("li"); - for ( var i = 0; i < li.length; i++ ) { - if ( li[i].className.indexOf("pass") > -1 ) { - li[i].style.display = filter.checked ? "none" : "block"; - } - } - }); - toolbar.appendChild( filter ); - - var label = document.createElement("label"); - label.setAttribute("for", "filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - - var missing = document.createElement("input"); - missing.type = "checkbox"; - missing.id = "qunit-filter-missing"; - missing.disabled = true; - addEvent( missing, "click", function() { - var li = document.getElementsByTagName("li"); - for ( var i = 0; i < li.length; i++ ) { - if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) { - li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block"; - } - } - }); - toolbar.appendChild( missing ); - - label = document.createElement("label"); - label.setAttribute("for", "filter-missing"); - label.innerHTML = "Hide missing tests (untested code is broken code)"; - toolbar.appendChild( label ); - } - - var main = id('main'); - if ( main ) { - config.fixture = main.innerHTML; - } - - if ( window.jQuery ) { - config.ajaxSettings = window.jQuery.ajaxSettings; - } - - QUnit.start(); -}); - -function done() { - if ( config.doneTimer && window.clearTimeout ) { - window.clearTimeout( config.doneTimer ); - config.doneTimer = null; - } - - if ( config.queue.length ) { - config.doneTimer = window.setTimeout(function(){ - if ( !config.queue.length ) { - done(); - } else { - synchronize( done ); - } - }, 13); - - return; - } - - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - html = ['Tests completed in ', - +new Date - config.started, ' milliseconds.
    ', - '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); - - if ( banner ) { - banner.className += " " + (config.stats.bad ? "fail" : "pass"); - } - - if ( tests ) { - var result = id("qunit-testresult"); - - if ( !result ) { - result = document.createElement("p"); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests.nextSibling ); - } - - result.innerHTML = html; - } - - QUnit.done( config.stats.bad, config.stats.all ); -} - -function validTest( name ) { - var i = config.filters.length, - run = false; - - if ( !i ) { - return true; - } - - while ( i-- ) { - var filter = config.filters[i], - not = filter.charAt(0) == '!'; - - if ( not ) { - filter = filter.slice(1); - } - - if ( name.indexOf(filter) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - } - - return run; -} - -function push(result, actual, expected, message) { - message = message || (result ? "okay" : "failed"); - QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) ); -} - -function synchronize( callback ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process(); - } -} - -function process() { - while ( config.queue.length && !config.blocking ) { - config.queue.shift()(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var old = config.pollution; - saveGlobal(); - - var newGlobals = diff( old, config.pollution ); - if ( newGlobals.length > 0 ) { - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - config.expected++; - } - - var deletedGlobals = diff( config.pollution, old ); - if ( deletedGlobals.length > 0 ) { - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - config.expected++; - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; -} - -function fail(message, exception, callback) { - if ( typeof console !== "undefined" && console.error && console.warn ) { - console.error(message); - console.error(exception); - console.warn(callback.toString()); - - } else if ( window.opera && opera.postError ) { - opera.postError(message, exception, callback.toString); - } -} - -function extend(a, b) { - for ( var prop in b ) { - a[prop] = b[prop]; - } - - return a; -} - -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); -} - -// Test for equality any JavaScript type. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv -// Author: Philippe Rathé -QUnit.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - - - // Determine what is o. - function hoozit(o) { - if (o.constructor === String) { - return "string"; - - } else if (o.constructor === Boolean) { - return "boolean"; - - } else if (o.constructor === Number) { - - if (isNaN(o)) { - return "nan"; - } else { - return "number"; - } - - } else if (typeof o === "undefined") { - return "undefined"; - - // consider: typeof null === object - } else if (o === null) { - return "null"; - - // consider: typeof [] === object - } else if (o instanceof Array) { - return "array"; - - // consider: typeof new Date() === object - } else if (o instanceof Date) { - return "date"; - - // consider: /./ instanceof Object; - // /./ instanceof RegExp; - // typeof /./ === "function"; // => false in IE and Opera, - // true in FF and Safari - } else if (o instanceof RegExp) { - return "regexp"; - - } else if (typeof o === "object") { - return "object"; - - } else if (o instanceof Function) { - return "function"; - } else { - return undefined; - } - } - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = hoozit(o); - if (prop) { - if (hoozit(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function (b) { - return isNaN(b); - }, - - "date": function (b, a) { - return hoozit(b) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function (b, a) { - return hoozit(b) === "regexp" && - a.source === b.source && // the regex itself - a.global === b.global && // and its modifers (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function () { - var caller = callers[callers.length - 1]; - return caller !== Object && - typeof caller !== "undefined"; - }, - - "array": function (b, a) { - var i; - var len; - - // b could be an object literal here - if ( ! (hoozit(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - for (i = 0; i < len; i++) { - if ( ! innerEquiv(a[i], b[i])) { - return false; - } - } - return true; - }, - - "object": function (b, a) { - var i; - var eq = true; // unless we can proove it - var aProperties = [], bProperties = []; // collection of strings - - // comparing constructors is more strict than using instanceof - if ( a.constructor !== b.constructor) { - return false; - } - - // stack constructor before traversing properties - callers.push(a.constructor); - - for (i in a) { // be strict: don't ensures hasOwnProperty and go deep - - aProperties.push(i); // collect a's properties - - if ( ! innerEquiv(a[i], b[i])) { - eq = false; - } - } - - callers.pop(); // unstack, we are done - - for (i in b) { - bProperties.push(i); // collect b's properties - } - - // Ensures identical properties name - return eq && innerEquiv(aProperties.sort(), bProperties.sort()); - } - }; - }(); - - innerEquiv = function () { // can take multiple arguments - var args = Array.prototype.slice.apply(arguments); - if (args.length < 2) { - return true; // end transition - } - - return (function (a, b) { - if (a === b) { - return true; // catch the most you can - } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [b, a]); - } - - // apply transition with (1..n) arguments - })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); - }; - - return innerEquiv; - -}(); - -/** - * jsDump - * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) - * Date: 5/15/2008 - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace(/"/g, '\\"') + '"'; - }; - function literal( o ) { - return o + ''; - }; - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) - arr = arr.join( ',' + s + inner ); - if ( !arr ) - return pre + post; - return [ pre, inner + arr, base + post ].join(s); - }; - function array( arr ) { - var i = arr.length, ret = Array(i); - this.up(); - while ( i-- ) - ret[i] = this.parse( arr[i] ); - this.down(); - return join( '[', ret, ']' ); - }; - - var reName = /^function (\w+)/; - - var jsDump = { - parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance - var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - - return type == 'function' ? parser.call( this, obj ) : - type == 'string' ? parser : - this.parsers.error; - }, - typeOf:function( obj ) { - var type = typeof obj, - f = 'function';//we'll use it 3 times, save it - return type != 'object' && type != f ? type : - !obj ? 'null' : - obj.exec ? 'regexp' :// some browsers (FF) consider regexps functions - obj.getHours ? 'date' : - obj.scrollBy ? 'window' : - obj.nodeName == '#document' ? 'document' : - obj.nodeName ? 'node' : - obj.item ? 'nodelist' : // Safari reports nodelists as functions - obj.callee ? 'arguments' : - obj.call || obj.constructor != Array && //an array would also fall on this hack - (obj+'').indexOf(f) != -1 ? f : //IE reports functions like alert, as objects - 'length' in obj ? 'array' : - type; - }, - separator:function() { - return this.multiline ? this.HTML ? '
    ' : '\n' : this.HTML ? ' ' : ' '; - }, - indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) - return ''; - var chr = this.indentChar; - if ( this.HTML ) - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - return Array( this._depth_ + (extra||0) ).join(chr); - }, - up:function( a ) { - this._depth_ += a || 1; - }, - down:function( a ) { - this._depth_ -= a || 1; - }, - setParser:function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote:quote, - literal:literal, - join:join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers:{ - window: '[Window]', - document: '[Document]', - error:'[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null':'null', - undefined:'undefined', - 'function':function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) - ret += ' ' + name; - ret += '('; - - ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, this.parse(fn,'functionCode'), '}' ); - }, - array: array, - nodelist: array, - arguments: array, - object:function( map ) { - var ret = [ ]; - this.up(); - for ( var key in map ) - ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); - this.down(); - return join( '{', ret, '}' ); - }, - node:function( node ) { - var open = this.HTML ? '<' : '<', - close = this.HTML ? '>' : '>'; - - var tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( var a in this.DOMAttrs ) { - var val = node[this.DOMAttrs[a]]; - if ( val ) - ret += ' ' + a + '=' + this.parse( val, 'attribute' ); - } - return ret + close + open + '/' + tag + close; - }, - functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) return ''; - - var args = Array(l); - while ( l-- ) - args[l] = String.fromCharCode(97+l);//97 is 'a' - return ' ' + args.join(', ') + ' '; - }, - key:quote, //object calls it internally, the key part of an item in a map - functionCode:'[code]', //function calls it internally, it's the content of the function - attribute:quote, //node calls it internally, it's an html attribute value - string:quote, - date:quote, - regexp:literal, //regex - number:literal, - 'boolean':literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:true,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -})(); - -})(this); diff --git a/plone/app/discussion/tests/test_catalog.py b/plone/app/discussion/tests/test_catalog.py index 5cb26a7..cbff0cd 100644 --- a/plone/app/discussion/tests/test_catalog.py +++ b/plone/app/discussion/tests/test_catalog.py @@ -19,8 +19,6 @@ from plone.app.discussion.testing import ( from plone.app.discussion.interfaces import IConversation -from plone.app.discussion.testing import COLLECTION_TYPE - class CatalogSetupTest(unittest.TestCase): @@ -48,6 +46,8 @@ class CatalogSetupTest(unittest.TestCase): ) def test_collection_criteria_installed(self): + if 'portal_atct' not in self.portal: + return try: self.portal.portal_atct.getIndex('commentators') self.portal.portal_atct.getIndex('total_comments') @@ -459,6 +459,9 @@ class CommentCatalogTest(unittest.TestCase): ) def test_clear_and_rebuild_catalog(self): + brains = self.catalog.searchResults({'portal_type': 'Discussion Item'}) + self.assertTrue(brains) + # Clear and rebuild catalog self.catalog.clearFindAndRebuild() @@ -529,31 +532,6 @@ class CommentCatalogTest(unittest.TestCase): self.assertTrue(brains) self.assertEqual(len(brains), 6) - def test_collection(self): - if COLLECTION_TYPE == "Topic": - self.portal.invokeFactory('Topic', id='topic') - topic = self.portal.topic - crit = topic.addCriterion('Type', 'ATSimpleStringCriterion') - crit.setValue('Comment') - query = topic.buildQuery() - - self.assertEqual(len(query), 1) - self.assertEqual(query['Type'], 'Comment') - self.assertEqual(len(topic.queryCatalog()), 1) - else: - self.portal.invokeFactory('Collection', id='collection') - collection = self.portal.collection - collection.query = [{ - 'i': 'Type', - 'o': 'plone.app.querystring.operation.string.is', - 'v': 'Comment', - }] - - self.assertEqual(collection.results().length, 1) - self.assertEqual(collection.results()[0].text, 'Comment text') - self.assertEqual(collection.results()[0].creator, 'jim') - self.assertEqual(collection.results()[0].author_name, 'Jim') - class NoConversationCatalogTest(unittest.TestCase): @@ -591,6 +569,3 @@ class NoConversationCatalogTest(unittest.TestCase): IAnnotations(self.portal.doc1) ) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_comment.py b/plone/app/discussion/tests/test_comment.py index 09bdea2..88abff3 100644 --- a/plone/app/discussion/tests/test_comment.py +++ b/plone/app/discussion/tests/test_comment.py @@ -509,6 +509,3 @@ class RepliesTest(unittest.TestCase): re_re_re_comment.absolute_url() ) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_comments_viewlet.py b/plone/app/discussion/tests/test_comments_viewlet.py index 7ed9da4..a12d909 100644 --- a/plone/app/discussion/tests/test_comments_viewlet.py +++ b/plone/app/discussion/tests/test_comments_viewlet.py @@ -63,12 +63,7 @@ class TestCommentForm(unittest.TestCase): typetool.constructContent('Document', self.portal, 'doc1') wftool = getToolByName(self.portal, "portal_workflow") wftool.doActionFor(self.portal.doc1, action='publish') - self.discussionTool = getToolByName( - self.portal, - 'portal_discussion', - None - ) - self.discussionTool.overrideDiscussionFor(self.portal.doc1, False) + self.portal.doc1.allow_discussion = True self.membershipTool = getToolByName(self.folder, 'portal_membership') self.memberdata = self.portal.portal_memberdata self.context = getattr(self.portal, 'doc1') @@ -83,7 +78,7 @@ class TestCommentForm(unittest.TestCase): """ # Allow discussion - self.discussionTool.overrideDiscussionFor(self.portal.doc1, True) + self.portal.doc1.allow_discussion = True self.viewlet = CommentsViewlet(self.context, self.request, None, None) def make_request(form={}): @@ -186,8 +181,23 @@ class TestCommentForm(unittest.TestCase): comment = [x for x in conversation.getComments()][-1] self.assertEquals(comment.text, u"foobar") + comments = IConversation(commentForm.context).getComments() + comments = [comment for comment in comments] # consume itertor + self.assertEqual(len(comments), 1) + + for comment in comments: + self.assertEqual(comment.text, u"bar") + self.assertEqual(comment.creator, "test-user") + self.assertEqual(comment.getOwner().getUserName(), "test-user") + local_roles = comment.get_local_roles() + self.assertEqual(len(local_roles), 1) + userid, roles = local_roles[0] + self.assertEqual(userid, 'test-user') + self.assertEqual(len(roles), 1) + self.assertEqual(roles[0], 'Owner') + def test_add_anonymous_comment(self): - self.discussionTool.overrideDiscussionFor(self.portal.doc1, True) + self.portal.doc1.allow_discussion = True self.viewlet = CommentsViewlet(self.context, self.request, None, None) @@ -226,11 +236,24 @@ class TestCommentForm(unittest.TestCase): self.assertEqual(len(errors), 0) self.assertFalse(commentForm.handleComment(commentForm, "action")) + comments = IConversation(commentForm.context).getComments() + comments = [comment for comment in comments] # consume itertor + self.assertEqual(len(comments), 1) + + for comment in IConversation(commentForm.context).getComments(): + self.assertEqual(comment.text, u"bar") + self.assertIsNone(comment.creator) + roles = comment.get_local_roles() + self.assertEqual(len(roles), 0) + def test_can_not_add_comments_if_discussion_is_not_allowed(self): """Make sure that comments can't be posted if discussion is disabled. """ - # Discussion is disabled by default + # Disable discussion + registry = queryUtility(IRegistry) + settings = registry.forInterface(IDiscussionSettings) + settings.globally_enabled = False def make_request(form={}): request = TestRequest() @@ -256,6 +279,7 @@ class TestCommentForm(unittest.TestCase): # No form errors, but raise unauthorized because discussion is not # allowed self.assertEqual(len(errors), 0) + self.assertRaises(Unauthorized, commentForm.handleComment, commentForm, @@ -318,11 +342,6 @@ class TestCommentsViewlet(unittest.TestCase): typetool = self.portal.portal_types typetool.constructContent('Document', self.portal, 'doc1') - self.portal_discussion = getToolByName( - self.portal, - 'portal_discussion', - None - ) self.membershipTool = getToolByName(self.folder, 'portal_membership') self.memberdata = self.portal.portal_memberdata context = getattr(self.portal, 'doc1') @@ -372,8 +391,7 @@ class TestCommentsViewlet(unittest.TestCase): # By default, discussion is disabled self.assertFalse(self.viewlet.is_discussion_allowed()) # Enable discussion - portal_discussion = getToolByName(self.portal, 'portal_discussion') - portal_discussion.overrideDiscussionFor(self.portal.doc1, True) + self.portal.doc1.allow_discussion = True # Test if discussion has been enabled self.assertTrue(self.viewlet.is_discussion_allowed()) @@ -520,11 +538,11 @@ class TestCommentsViewlet(unittest.TestCase): ) def test_get_commenter_portrait_is_none(self): - self.assertTrue( - self.viewlet.get_commenter_portrait() in ( - 'defaultUser.png', - 'defaultUser.gif', - ) + + self.assertEqual( + self.viewlet.get_commenter_portrait(), + 'defaultUser.png' + ) def test_get_commenter_portrait_without_userimage(self): @@ -604,6 +622,3 @@ class TestCommentsViewlet(unittest.TestCase): self.assertTrue( localized_time in ['Feb 01, 2009 11:32 PM', '2009-02-01 23:32']) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_controlpanel.py b/plone/app/discussion/tests/test_controlpanel.py index ca56c0b..1fe8115 100644 --- a/plone/app/discussion/tests/test_controlpanel.py +++ b/plone/app/discussion/tests/test_controlpanel.py @@ -230,6 +230,3 @@ class ConfigurationChangedSubscriberTest(unittest.TestCase): # setting itself remains unchanged. self.settings.moderation_enabled = True - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_conversation.py b/plone/app/discussion/tests/test_conversation.py index 59bca9d..29976e8 100644 --- a/plone/app/discussion/tests/test_conversation.py +++ b/plone/app/discussion/tests/test_conversation.py @@ -91,10 +91,10 @@ class ConversationTest(unittest.TestCase): comment.author_username = "nobody" conversation.addComment(comment) comment.manage_permission("View", roles=tuple()) - self.assertEquals(0, conversation.total_comments) - self.assertEquals(None, conversation.last_comment_date) - self.assertEquals(["nobody"], list(conversation.commentators)) - self.assertEquals([], list(conversation.public_commentators)) + self.assertEqual(0, conversation.total_comments) + self.assertEqual(None, conversation.last_comment_date) + self.assertEqual(["nobody"], list(conversation.commentators)) + self.assertEqual([], list(conversation.public_commentators)) def test_delete_comment(self): # Create a conversation. In this case we doesn't assign it to an @@ -308,12 +308,12 @@ class ConversationTest(unittest.TestCase): # Create a folder self.typetool.constructContent('Folder', self.portal, 'f1') - f1 = self.portal.f1 + # Usually we don't create a conversation on a folder conversation = self.portal.f1.restrictedTraverse('@@conversation_view') # Allow discussion for the folder - self.portal_discussion.overrideDiscussionFor(f1, True) + self.portal.f1.allow_discussion = True # Allow discussion on Folder content type portal_types = getToolByName(self.portal, 'portal_types') @@ -334,12 +334,12 @@ class ConversationTest(unittest.TestCase): self.assertEqual(conversation.enabled(), False) # Allow discussion on content object - self.portal_discussion.overrideDiscussionFor(self.portal.doc1, True) + self.portal.doc1.allow_discussion = True # Check if discussion is now allowed on the content object self.assertEqual(conversation.enabled(), True) - self.portal_discussion.overrideDiscussionFor(self.portal.doc1, False) + self.portal.doc1.allow_discussion = False self.assertEqual(conversation.enabled(), False) def test_dict_operations(self): @@ -874,6 +874,3 @@ class RepliesTest(unittest.TestCase): self.assertEqual(len(replies_to_comment1_1), 1) self.assertEqual(len(replies_to_comment2), 1) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_indexers.py b/plone/app/discussion/tests/test_indexers.py index 0701e27..2d4e6da 100644 --- a/plone/app/discussion/tests/test_indexers.py +++ b/plone/app/discussion/tests/test_indexers.py @@ -204,6 +204,3 @@ class CommentIndexersTest(unittest.TestCase): # object the comment was added to self.assertEqual(catalog.in_response_to(self.comment)(), 'Document 1') - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_migration.py b/plone/app/discussion/tests/test_migration.py deleted file mode 100644 index eec1215..0000000 --- a/plone/app/discussion/tests/test_migration.py +++ /dev/null @@ -1,328 +0,0 @@ -from datetime import datetime -from DateTime import DateTime - -import unittest2 as unittest - -from zope.annotation.interfaces import IAnnotations - -from Products.CMFCore.utils import getToolByName - -from plone.app.testing import TEST_USER_ID, setRoles - -from plone.app.discussion.testing import ( - PLONE_APP_DISCUSSION_INTEGRATION_TESTING -) - -from plone.app.discussion.browser.migration import View - -from plone.app.discussion.interfaces import IConversation, IComment - - -class MigrationTest(unittest.TestCase): - - layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING - - def _publish(self, reply): - # publish the reply - status = self.portal.portal_workflow.getStatusOf( - 'comment_review_workflow', reply - ).copy() - status['review_state'] = 'published' - self.portal.portal_workflow.setStatusOf( - 'comment_review_workflow', - reply, - status, - ) - - def setUp(self): - self.portal = self.layer['portal'] - self.request = self.layer['request'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) - - self.portal.invokeFactory( - id='doc', - title='Document 1', - type_name='Document' - ) - # Create a document - self.discussion = getToolByName(self.portal, 'portal_discussion', None) - self.discussion.overrideDiscussionFor(self.portal.doc, 1) - # Publish it - self.workflowTool = getToolByName(self.portal, 'portal_workflow') - self.workflowTool.setDefaultChain('simple_publication_workflow') - self.workflowTool.doActionFor(self.portal.doc, 'publish') - - self.request.set("test", True) - self.view = View(self.portal, self.request) - self.workflowTool.setChainForPortalTypes( - ('Discussion Item',), - 'comment_review_workflow' - ) - - # Create a user Jimmy Jones so comments creator migration can work? - acl_users = getToolByName(self.portal, 'acl_users') - acl_users.userFolderAddUser('Jim', 'secret', ['Member'], []) - mt = getToolByName(self.portal, 'portal_membership') - member = mt.getMemberById('Jim') - member.fullname = 'Jimmy Jones' - - self.doc = self.portal.doc - - def test_migrate_comment(self): - - # Create a comment - talkback = self.discussion.getDiscussionFor(self.doc) - self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') - reply = talkback.getReplies()[0] - reply.setReplyTo(self.doc) - reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') - reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') - - self._publish(reply) - self.assertEqual(reply.Title(), 'My Title') - self.assertEqual(reply.EditableBody(), 'My Text') - self.assertTrue('Jim' in reply.listCreators()) - self.assertEqual(talkback.replyCount(self.doc), 1) - self.assertEqual(reply.inReplyTo(), self.doc) - - # Call migration script - self.view() - - # Make sure a conversation has been created - self.assertTrue( - 'plone.app.discussion:conversation' in IAnnotations(self.doc) - ) - conversation = IConversation(self.doc) - - # Check migration - self.assertEqual(conversation.total_comments, 1) - self.assertTrue(conversation.getComments().next()) - comment1 = conversation.values()[0] - self.assertTrue(IComment.providedBy(comment1)) - self.assertEqual(comment1.Title(), 'My Title') - self.assertEqual(comment1.text, '

    My Text

    \n') - self.assertEqual(comment1.mime_type, 'text/html') - self.assertEqual(comment1.Creator(), 'Jim') - self.assertEqual( - comment1.creation_date, - datetime(2003, 3, 11, 9, 28, 6) - ) - self.assertEqual( - comment1.modification_date, - datetime(2009, 7, 12, 19, 38, 7) - ) - self.assertEqual([ - {'comment': comment1, 'depth': 0, 'id': long(comment1.id)} - ], list(conversation.getThreads())) - self.assertFalse(self.doc.talkback) - - def test_migrate_comment_with_creator(self): - # Create a comment - talkback = self.discussion.getDiscussionFor(self.doc) - self.doc.talkback.createReply('My Title', 'My Text', Creator='Jim') - reply = talkback.getReplies()[0] - reply.setReplyTo(self.doc) - reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6, 'GMT') - reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7, 'GMT') - reply.author_username = 'Jim' - reply.email = 'jimmy@jones.xyz' - - self._publish(reply) - self.assertEqual(reply.Title(), 'My Title') - self.assertEqual(reply.EditableBody(), 'My Text') - self.assertTrue('Jim' in reply.listCreators()) - self.assertEqual(talkback.replyCount(self.doc), 1) - self.assertEqual(reply.inReplyTo(), self.doc) - self.assertEqual(reply.author_username, 'Jim') - self.assertEqual(reply.email, 'jimmy@jones.xyz') - - # Call migration script - self.view() - - # Make sure a conversation has been created - self.assertTrue( - 'plone.app.discussion:conversation' in IAnnotations(self.doc) - ) - conversation = IConversation(self.doc) - - # Check migration - self.assertEqual(conversation.total_comments, 1) - self.assertTrue(conversation.getComments().next()) - comment1 = conversation.values()[0] - self.assertTrue(IComment.providedBy(comment1)) - self.assertEqual(comment1.Title(), 'My Title') - self.assertEqual(comment1.text, '

    My Text

    \n') - self.assertEqual(comment1.mime_type, 'text/html') - self.assertEqual(comment1.Creator(), 'Jim') - self.assertEqual( - comment1.creation_date, - datetime(2003, 3, 11, 9, 28, 6) - ) - self.assertEqual( - comment1.modification_date, - datetime(2009, 7, 12, 19, 38, 7) - ) - self.assertEqual([ - {'comment': comment1, 'depth': 0, 'id': long(comment1.id)} - ], list(conversation.getThreads())) - self.assertFalse(self.doc.talkback) - - # Though this should be Jimmy, but looks like getProperty won't pick - # up 'author_username' (reply.author_username is not None), so it's - # propagating Creator()..? - self.assertEqual(comment1.author_username, 'Jim') - - self.assertEqual(comment1.author_name, 'Jimmy Jones') - self.assertEqual(comment1.author_email, 'jimmy@jones.xyz') - - def test_migrate_nested_comments(self): - # Create some nested comments and migrate them - # - # self.doc - # +- First comment - # +- Re: First comment - # + Re: Re: First comment - # + Re: Re: Re: First comment - # +- Re: First comment (2) - # +- Re: First comment (3) - # +- Re: First comment (4) - # +- Second comment - - talkback = self.discussion.getDiscussionFor(self.doc) - - # First comment - talkback.createReply(title='First comment', - text='This is my first comment.') - comment1 = talkback.getReplies()[0] - self._publish(comment1) - - talkback_comment1 = self.discussion.getDiscussionFor(comment1) - - # Re: First comment - talkback_comment1.createReply(title='Re: First comment', - text='This is my first reply.') - comment1_1 = talkback_comment1.getReplies()[0] - self._publish(comment1_1) - - talkback_comment1_1 = self.discussion.getDiscussionFor(comment1_1) - - self.assertEqual(len(talkback.getReplies()), 1) - self.assertEqual(len(talkback_comment1.getReplies()), 1) - self.assertEqual(len(talkback_comment1_1.getReplies()), 0) - - #Re: Re: First comment - talkback_comment1_1.createReply(title='Re: Re: First comment', - text='This is my first re-reply.') - comment1_1_1 = talkback_comment1_1.getReplies()[0] - self._publish(comment1_1_1) - - talkback_comment1_1_1 = self.discussion.getDiscussionFor(comment1_1_1) - - # Re: Re: Re: First comment - talkback_comment1_1_1.createReply(title='Re: Re: Re: First comment', - text='This is my first re-re-reply.') - self._publish(talkback_comment1_1_1.getReplies()[0]) - - # Re: First comment (2) - talkback_comment1.createReply(title='Re: First comment (2)', - text='This is my first reply (2).') - self._publish(talkback_comment1.getReplies()[1]) - - # Re: First comment (3) - talkback_comment1.createReply(title='Re: First comment (3)', - text='This is my first reply (3).') - self._publish(talkback_comment1.getReplies()[2]) - - # Re: First comment (4) - talkback_comment1.createReply(title='Re: First comment (4)', - text='This is my first reply (4).') - self._publish(talkback_comment1.getReplies()[3]) - - # Second comment - talkback.createReply(title='Second comment', - text='This is my second comment.') - self._publish(talkback.getReplies()[1]) - - # Call migration script - self.view() - - # Check migration - conversation = IConversation(self.doc) - self.assertEqual(conversation.total_comments, 8) - - comment1 = conversation.values()[0] - comment1_1 = conversation.values()[1] - comment1_1_1 = conversation.values()[2] - comment1_1_1_1 = conversation.values()[3] - comment1_2 = conversation.values()[4] - comment1_3 = conversation.values()[5] - comment1_4 = conversation.values()[6] - comment2 = conversation.values()[7] - - self.assertEqual([ - {'comment': comment1, 'depth': 0, 'id': long(comment1.id)}, - {'comment': comment1_1, 'depth': 1, 'id': long(comment1_1.id)}, - {'comment': comment1_1_1, 'depth': 2, 'id': long(comment1_1_1.id)}, - {'comment': comment1_1_1_1, 'depth': 3, - 'id': long(comment1_1_1_1.id)}, - {'comment': comment1_2, 'depth': 1, 'id': long(comment1_2.id)}, - {'comment': comment1_3, 'depth': 1, 'id': long(comment1_3.id)}, - {'comment': comment1_4, 'depth': 1, 'id': long(comment1_4.id)}, - {'comment': comment2, 'depth': 0, 'id': long(comment2.id)}, - ], list(conversation.getThreads())) - - talkback = self.discussion.getDiscussionFor(self.doc) - self.assertEqual(len(talkback.getReplies()), 0) - - def test_migrate_nested_comments_with_filter(self): - # Create some nested comments and migrate them. - # But use a filter that filters the top-level comment. - # All the comments should be removed, but not migrated. - # - # self.doc - # +- First comment - # +- Re: First comment - - talkback = self.discussion.getDiscussionFor(self.doc) - - # First comment - talkback.createReply(title='First comment', - text='This is my first comment.') - comment1 = talkback.getReplies()[0] - talkback_comment1 = self.discussion.getDiscussionFor(comment1) - - # Re: First comment - talkback_comment1.createReply(title='Re: First comment', - text='This is my first reply.') - comment1_1 = talkback_comment1.getReplies()[0] - talkback_comment1_1 = self.discussion.getDiscussionFor(comment1_1) - - self.assertEqual(len(talkback.getReplies()), 1) - self.assertEqual(len(talkback_comment1.getReplies()), 1) - self.assertEqual(len(talkback_comment1_1.getReplies()), 0) - - def deny_comments(reply): - return False - - # Call migration script - self.view(filter_callback=deny_comments) - - # Check migration - conversation = IConversation(self.doc) - self.assertEqual(conversation.total_comments, 0) - talkback = self.discussion.getDiscussionFor(self.doc) - self.assertEqual(len(talkback.getReplies()), 0) - - def test_migrate_no_comment(self): - - # Call migration script - self.view() - - # Make sure no conversation has been created - self.assertTrue( - 'plone.app.discussion:conversation' not in IAnnotations(self.doc) - ) - - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_moderation_view.py b/plone/app/discussion/tests/test_moderation_view.py index ac0a7bd..1c9cf18 100644 --- a/plone/app/discussion/tests/test_moderation_view.py +++ b/plone/app/discussion/tests/test_moderation_view.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- import unittest -from DateTime import DateTime - from zope.component import createObject from Products.CMFCore.utils import getToolByName @@ -58,29 +56,6 @@ class ModerationViewTest(unittest.TestCase): ('comment_review_workflow,')) self.assertEqual(self.view.moderation_enabled(), True) - def test_old_comments_not_shown_in_moderation_view(self): - # Create old comment - discussion = getToolByName(self.portal, 'portal_discussion', None) - discussion.overrideDiscussionFor(self.portal.doc1, 1) - talkback = discussion.getDiscussionFor(self.portal.doc1) - self.portal.doc1.talkback.createReply('My Title', - 'My Text', - Creator='Jim') - reply = talkback.getReplies()[0] - reply.setReplyTo(self.portal.doc1) - reply.creation_date = DateTime(2003, 3, 11, 9, 28, 6) - reply.modification_date = DateTime(2009, 7, 12, 19, 38, 7) - self.assertEqual(reply.Title(), 'My Title') - self.assertEqual(reply.EditableBody(), 'My Text') - self.assertTrue('Jim' in reply.listCreators()) - self.assertEqual(talkback.replyCount(self.portal.doc1), 1) - self.assertEqual(reply.inReplyTo(), self.portal.doc1) - - view = self.view() - - self.assertTrue('No comments to moderate' in view) - self.assertEqual(len(self.view.comments), 0) - class ModerationBulkActionsViewTest(unittest.TestCase): @@ -189,6 +164,3 @@ class ModerationBulkActionsViewTest(unittest.TestCase): self.assertTrue(comment) self.assertEqual(comment, self.comment2) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_notifications.py b/plone/app/discussion/tests/test_notifications.py index 1c397c1..548681c 100644 --- a/plone/app/discussion/tests/test_notifications.py +++ b/plone/app/discussion/tests/test_notifications.py @@ -42,7 +42,6 @@ class TestUserNotificationUnit(unittest.TestCase): '.user_notification_enabled'] = True # Create test content self.portal.invokeFactory('Document', 'doc1') - self.portal_discussion = self.portal.portal_discussion # Archetypes content types store data as utf-8 encoded strings # The missing u in front of a string is therefor not missing self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"? @@ -189,7 +188,6 @@ class TestModeratorNotificationUnit(unittest.TestCase): ] = True # Create test content self.portal.invokeFactory('Document', 'doc1') - self.portal_discussion = self.portal.portal_discussion # Archetypes content types store data as utf-8 encoded strings # The missing u in front of a string is therefor not missing self.portal.doc1.title = 'Kölle Alaaf' # What is "Fasching"? @@ -278,6 +276,3 @@ class TestModeratorNotificationUnit(unittest.TestCase): self.assertEqual(len(self.mailhost.messages), 0) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_tool.py b/plone/app/discussion/tests/test_tool.py deleted file mode 100644 index 1386cff..0000000 --- a/plone/app/discussion/tests/test_tool.py +++ /dev/null @@ -1,56 +0,0 @@ -import unittest2 as unittest - -from zope.component import queryUtility, createObject - -from plone.app.testing import TEST_USER_ID, setRoles - -from plone.app.discussion.testing import \ - PLONE_APP_DISCUSSION_INTEGRATION_TESTING - -from plone.app.discussion.interfaces import ICommentingTool, IConversation - - -class ToolTest(unittest.TestCase): - - layer = PLONE_APP_DISCUSSION_INTEGRATION_TESTING - - def setUp(self): - self.portal = self.layer['portal'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) - self.portal.invokeFactory(id='doc1', - title='Document 1', - type_name='Document') - - def test_tool_indexing(self): - # Create a conversation. In this case we doesn't assign it to an - # object, as we just want to check the Conversation object API. - conversation = IConversation(self.portal.doc1) - - # Add a comment. - comment = createObject('plone.Comment') - comment.creator = 'jim' - comment.author_name = "Jim" - comment.text = 'Comment text' - - conversation.addComment(comment) - - # Check that the comment got indexed in the tool: - tool = queryUtility(ICommentingTool) - comment = list(tool.searchResults()) - self.assertTrue( - len(comment) == 1, - "There is only one comment, but we got" - " %s results in the search" % len(comment) - ) - self.assertEqual(comment[0].Title, 'Jim on Document 1') - - def test_unindexing(self): - pass - - def test_search(self): - # search returns only comments - pass - - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/plone/app/discussion/tests/test_workflow.py b/plone/app/discussion/tests/test_workflow.py index 8ea9e30..65361aa 100644 --- a/plone/app/discussion/tests/test_workflow.py +++ b/plone/app/discussion/tests/test_workflow.py @@ -32,7 +32,6 @@ class WorkflowSetupTest(unittest.TestCase): self.portal.invokeFactory('Folder', 'test-folder') self.folder = self.portal['test-folder'] self.portal.portal_types['Document'].allow_discussion = True - self.portal_discussion = self.portal.portal_discussion self.folder.invokeFactory('Document', 'doc1') self.doc = self.folder.doc1 @@ -190,7 +189,6 @@ class CommentReviewWorkflowTest(unittest.TestCase): # Create a Document self.portal.invokeFactory('Document', 'doc1') - self.portal_discussion = self.portal.portal_discussion # Create a conversation for this Document conversation = IConversation(self.portal.doc1) @@ -277,6 +275,3 @@ class CommentReviewWorkflowTest(unittest.TestCase): ) ) - -def test_suite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__)