[Jifty-commit] r3744 - in jifty/branches/js-refactor: . debian doc/talks examples/Example-Todo examples/Yada examples/Yada/etc examples/Yada/inc/Module examples/Yada/inc/Module/Install examples/Yada/lib examples/Yada/lib/Yada examples/Yada/lib/Yada/Action examples/Yada/lib/Yada/Model examples/Yada/lib/Yada/View examples/Yada/share/web/static/js examples/Yada/share/web/static/js/Asynapse examples/Yada/t inc/Module lib lib/Jifty lib/Jifty/Action lib/Jifty/Action/Record lib/Jifty/Event lib/Jifty/Manual lib/Jifty/Mason lib/Jifty/Model lib/Jifty/Module lib/Jifty/Plugin lib/Jifty/Plugin/ActorMetadata lib/Jifty/Plugin/ActorMetadata/Mixin lib/Jifty/Plugin/ActorMetadata/Mixin/Model lib/Jifty/Plugin/Authentication lib/Jifty/Plugin/Authentication/Facebook lib/Jifty/Plugin/Authentication/Facebook/Action lib/Jifty/Plugin/Authentication/Facebook/Mixin lib/Jifty/Plugin/Authentication/Facebook/Mixin/Model lib/Jifty/Plugin/Authentication/Password lib/Jifty/Plugin/Authentication/Password/Mixin/Model lib/Jifty/Plugin/Chart lib/Jifty/Plugin/Chart/Renderer lib/Jifty/Plugin/CompressedCSSandJS lib/Jifty/Plugin/Debug lib/Jifty/Plugin/ErrorTemplates lib/Jifty/Plugin/Feedback lib/Jifty/Plugin/GoogleMap lib/Jifty/Plugin/OpenID lib/Jifty/Plugin/OpenID/Action lib/Jifty/Plugin/OpenID/Mixin/Model lib/Jifty/Plugin/REST lib/Jifty/Plugin/SinglePage lib/Jifty/Plugin/SiteNews lib/Jifty/Plugin/SiteNews/Mixin/Model lib/Jifty/Plugin/SiteNews/View lib/Jifty/Plugin/SkeletonApp lib/Jifty/Plugin/TabView lib/Jifty/Plugin/UUID lib/Jifty/Plugin/User/Mixin/Model lib/Jifty/Plugin/Userpic lib/Jifty/Request lib/Jifty/Script lib/Jifty/Subs lib/Jifty/Test/WWW lib/Jifty/Upgrade lib/Jifty/View lib/Jifty/View/Declare lib/Jifty/View/Mason lib/Jifty/View/Static lib/Jifty/Web lib/Jifty/Web/Form lib/Jifty/Web/Form/Field plugins/CodePress plugins/CodePress/lib/Jifty/Plugin plugins/CodePress/share/web/static/js share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin share/plugins/Jifty/Plugin/GoogleMap share/plugins/Jifty/Plugin/GoogleMap/web share/plugins/Jifty/Plugin/GoogleMap/web/static share/plugins/Jifty/Plugin/GoogleMap/web/static/css share/plugins/Jifty/Plugin/GoogleMap/web/static/js share/web/static/js share/web/static/js/yui share/web/templates/__jifty/webservices t t/Mapper/lib/Mapper/Action t/Mapper/share/web/templates t/Mapper/t t/TestApp-Plugin-AppPluginHasModels t/TestApp-Plugin-AppPluginHasModels/bin t/TestApp-Plugin-AppPluginHasModels/doc t/TestApp-Plugin-AppPluginHasModels/etc t/TestApp-Plugin-AppPluginHasModels/lib t/TestApp-Plugin-AppPluginHasModels/lib/TestApp t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Action t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Model t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/Model t/TestApp-Plugin-AppPluginHasModels/log t/TestApp-Plugin-AppPluginHasModels/share t/TestApp-Plugin-AppPluginHasModels/share/po t/TestApp-Plugin-AppPluginHasModels/share/web t/TestApp-Plugin-AppPluginHasModels/share/web/static t/TestApp-Plugin-AppPluginHasModels/share/web/templates t/TestApp-Plugin-AppPluginHasModels/t t/TestApp-Plugin-AppPluginHasModels/var t/TestApp-Plugin-AppPluginHasModels/var/mason/cache t/TestApp-Plugin-AppPluginHasModels/var/mason/obj t/TestApp-Plugin-Chart t/TestApp-Plugin-Chart/bin t/TestApp-Plugin-Chart/doc t/TestApp-Plugin-Chart/etc t/TestApp-Plugin-Chart/lib t/TestApp-Plugin-Chart/lib/TestApp t/TestApp-Plugin-Chart/lib/TestApp/Plugin t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Action t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Model t/TestApp-Plugin-Chart/log t/TestApp-Plugin-Chart/share t/TestApp-Plugin-Chart/share/po t/TestApp-Plugin-Chart/share/web t/TestApp-Plugin-Chart/share/web/static t/TestApp-Plugin-Chart/share/web/templates t/TestApp-Plugin-Chart/t t/TestApp-Plugin-Chart/var t/TestApp-Plugin-Chart/var/mason t/TestApp-Plugin-Chart/var/mason/cache t/TestApp-Plugin-Chart/var/mason/obj t/TestApp/lib/TestApp t/TestApp/lib/TestApp/Model t/TestApp/lib/TestApp/View t/TestApp/share/web/templates/dispatch t/TestApp/t t/clientside

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Tue Jul 31 17:24:00 EDT 2007


Author: trs
Date: Tue Jul 31 17:23:52 2007
New Revision: 3744

Added:
   jifty/branches/js-refactor/examples/Yada/
      - copied from r3400, /jifty/branches/js-refactor/examples/Example-Todo/
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Can.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Fetch.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Makefile.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Win32.pm
   jifty/branches/js-refactor/examples/Yada/lib/Yada/
   jifty/branches/js-refactor/examples/Yada/lib/Yada.pm
   jifty/branches/js-refactor/examples/Yada/lib/Yada/Action/
   jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/
   jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/Todo.pm
   jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/User.pm
   jifty/branches/js-refactor/examples/Yada/lib/Yada/View/
   jifty/branches/js-refactor/examples/Yada/lib/Yada/View.pm
   jifty/branches/js-refactor/examples/Yada/lib/Yada/View/Todo.pm
   jifty/branches/js-refactor/examples/Yada/share/web/static/js/
   jifty/branches/js-refactor/examples/Yada/share/web/static/js/Asynapse/
   jifty/branches/js-refactor/examples/Yada/share/web/static/js/Asynapse/REST.js
   jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/
   jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/Mixin/
   jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/
   jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LinkFacebookUser.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LoginFacebookUser.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Mixin/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Mixin/Model/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Mixin/Model/User.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer/Chart.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Web.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Debug/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Debug.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Debug/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap/
   jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap/Widget.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage/
   jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/UUID/
   jifty/branches/js-refactor/lib/Jifty/Plugin/UUID.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/UUID/Widget.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/
   jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Widget.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/Compile.pm
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/css/
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/css/google_map.css
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/js/
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/js/google_map.js
   jifty/branches/js-refactor/share/web/static/js/template_declare.js
   jifty/branches/js-refactor/share/web/static/js/yui/oom_select.patch
   jifty/branches/js-refactor/t/13-sessions.t
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/Makefile.PL
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/bin/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/bin/jifty   (contents, props changed)
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/doc/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/etc/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/etc/config.yml
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Action/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Model/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin.pm
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/Model/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/Model/Color.pm
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/log/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/share/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/share/po/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/share/web/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/share/web/static/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/share/web/templates/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/t/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/t/plugin-model.t
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/var/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/var/mason/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/var/mason/cache/
   jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/var/mason/obj/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/Makefile.PL
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/bin/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/bin/jifty   (contents, props changed)
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/doc/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/etc/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/etc/config.yml
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Action/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/Model/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/log/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/share/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/share/po/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/share/web/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/share/web/static/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/share/web/templates/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/t/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/t/chart.t
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/var/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/var/mason/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/var/mason/cache/
   jifty/branches/js-refactor/t/TestApp-Plugin-Chart/var/mason/obj/
   jifty/branches/js-refactor/t/TestApp/share/web/templates/dispatch/protocol
   jifty/branches/js-refactor/t/TestApp/t/02-dispatch-http.t
   jifty/branches/js-refactor/t/TestApp/t/02-dispatch-https.t
   jifty/branches/js-refactor/t/clientside/
   jifty/branches/js-refactor/t/clientside/td.t
Removed:
   jifty/branches/js-refactor/doc/talks/
   jifty/branches/js-refactor/examples/Example-Todo/
Modified:
   jifty/branches/js-refactor/   (props changed)
   jifty/branches/js-refactor/AUTHORS
   jifty/branches/js-refactor/META.yml
   jifty/branches/js-refactor/Makefile.PL
   jifty/branches/js-refactor/debian/control
   jifty/branches/js-refactor/examples/Yada/META.yml
   jifty/branches/js-refactor/examples/Yada/Makefile.PL
   jifty/branches/js-refactor/examples/Yada/etc/config.yml
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Base.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Metadata.pm
   jifty/branches/js-refactor/examples/Yada/inc/Module/Install/WriteAll.pm
   jifty/branches/js-refactor/examples/Yada/t/00-model-Todo.t
   jifty/branches/js-refactor/inc/Module/Install.pm
   jifty/branches/js-refactor/lib/Jifty.pm
   jifty/branches/js-refactor/lib/Jifty/API.pm
   jifty/branches/js-refactor/lib/Jifty/Action.pm
   jifty/branches/js-refactor/lib/Jifty/Action/Record.pm
   jifty/branches/js-refactor/lib/Jifty/Action/Record/Search.pm
   jifty/branches/js-refactor/lib/Jifty/ClassLoader.pm
   jifty/branches/js-refactor/lib/Jifty/Config.pm
   jifty/branches/js-refactor/lib/Jifty/Continuation.pm
   jifty/branches/js-refactor/lib/Jifty/CurrentUser.pm
   jifty/branches/js-refactor/lib/Jifty/DateTime.pm
   jifty/branches/js-refactor/lib/Jifty/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Event.pm
   jifty/branches/js-refactor/lib/Jifty/Event/Model.pm
   jifty/branches/js-refactor/lib/Jifty/Manual/Continuations.pod
   jifty/branches/js-refactor/lib/Jifty/Manual/Cookbook.pod
   jifty/branches/js-refactor/lib/Jifty/Manual/Glossary.pod
   jifty/branches/js-refactor/lib/Jifty/Mason/Halo.pm
   jifty/branches/js-refactor/lib/Jifty/Model/SessionCollection.pm
   jifty/branches/js-refactor/lib/Jifty/Module/Pluggable.pm
   jifty/branches/js-refactor/lib/Jifty/Notification.pm
   jifty/branches/js-refactor/lib/Jifty/Object.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/ErrorTemplates/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/REST/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Dispatcher.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/View/News.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/SkeletonApp/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/TabView/View.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/User.pm
   jifty/branches/js-refactor/lib/Jifty/Plugin/User/Mixin/Model/User.pm
   jifty/branches/js-refactor/lib/Jifty/Record.pm
   jifty/branches/js-refactor/lib/Jifty/Request.pm
   jifty/branches/js-refactor/lib/Jifty/Request/Mapper.pm
   jifty/branches/js-refactor/lib/Jifty/RightsFrom.pm
   jifty/branches/js-refactor/lib/Jifty/Schema.pm
   jifty/branches/js-refactor/lib/Jifty/Script.pm
   jifty/branches/js-refactor/lib/Jifty/Script/Deps.pm
   jifty/branches/js-refactor/lib/Jifty/Script/Schema.pm
   jifty/branches/js-refactor/lib/Jifty/Server.pm
   jifty/branches/js-refactor/lib/Jifty/Subs.pm
   jifty/branches/js-refactor/lib/Jifty/Subs/Render.pm
   jifty/branches/js-refactor/lib/Jifty/Test.pm
   jifty/branches/js-refactor/lib/Jifty/Test/WWW/Mechanize.pm
   jifty/branches/js-refactor/lib/Jifty/Upgrade.pm
   jifty/branches/js-refactor/lib/Jifty/Upgrade/Internal.pm
   jifty/branches/js-refactor/lib/Jifty/Util.pm
   jifty/branches/js-refactor/lib/Jifty/View.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/BaseClass.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/CRUD.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/CoreTemplates.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/Handler.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/Helpers.pm
   jifty/branches/js-refactor/lib/Jifty/View/Declare/Page.pm
   jifty/branches/js-refactor/lib/Jifty/View/Mason/Handler.pm
   jifty/branches/js-refactor/lib/Jifty/View/Static/Handler.pm
   jifty/branches/js-refactor/lib/Jifty/Web.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Clickable.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Element.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Button.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Checkbox.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Combobox.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Date.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Hidden.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/InlineButton.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Password.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Radio.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/ResetButton.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Select.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Text.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Textarea.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Unrendered.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Upload.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Form/Link.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Menu.pm
   jifty/branches/js-refactor/lib/Jifty/Web/PageRegion.pm
   jifty/branches/js-refactor/lib/Jifty/Web/Session.pm
   jifty/branches/js-refactor/plugins/CodePress/Makefile.PL
   jifty/branches/js-refactor/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm
   jifty/branches/js-refactor/plugins/CodePress/share/web/static/js/codepress.js
   jifty/branches/js-refactor/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html
   jifty/branches/js-refactor/share/web/static/js/jifty.js
   jifty/branches/js-refactor/share/web/static/js/key_bindings.js
   jifty/branches/js-refactor/share/web/static/js/yui/calendar.js
   jifty/branches/js-refactor/share/web/templates/__jifty/webservices/xml
   jifty/branches/js-refactor/t/Mapper/lib/Mapper/Action/CrossBridge.pm
   jifty/branches/js-refactor/t/Mapper/share/web/templates/index.html
   jifty/branches/js-refactor/t/Mapper/t/02-api.t
   jifty/branches/js-refactor/t/TestApp/lib/TestApp/Dispatcher.pm
   jifty/branches/js-refactor/t/TestApp/lib/TestApp/Model/User.pm
   jifty/branches/js-refactor/t/TestApp/lib/TestApp/View.pm
   jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/base.pm
   jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/instance.pm
   jifty/branches/js-refactor/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t
   jifty/branches/js-refactor/t/TestApp/t/05-editactions-Record.t
   jifty/branches/js-refactor/t/TestApp/t/08-notifications.t
   jifty/branches/js-refactor/t/TestApp/t/15-template-subclass.t
   jifty/branches/js-refactor/t/TestApp/t/config-Record

Log:
 r21838 at zot (orig r3385):  yves | 2007-06-07 09:27:32 -0400
 * debian deps
 
 r21842 at zot (orig r3389):  sartak | 2007-06-07 18:07:12 -0400
  r20399 at caladan:  sartak | 2007-06-07 16:23:12 -0400
  Make sure there's always a stash during testing since we don't always go through the web
 
 r21844 at zot (orig r3391):  sartak | 2007-06-07 18:14:44 -0400
  r20405 at caladan:  sartak | 2007-06-07 18:07:59 -0400
  Pushed the wrong change, oops :)
 
 r21869 at zot (orig r3405):  clkao | 2007-06-08 16:46:17 -0400
 Merge from fragcont branch to include minimum support of continuation in
 webservices requests to make SinglePage work.
 
  r18076 at ubuntu (orig r3386):  clkao | 2007-06-07 15:59:34 -0400
  branch for supporting fragment calls with continuation.
  r18077 at ubuntu (orig r3387):  clkao | 2007-06-07 16:05:17 -0400
  first cut of continuation serialization & calling with webservices.
  r18078 at ubuntu (orig r3388):  clkao | 2007-06-07 17:35:03 -0400
  Have call continuation work on webservices, but not return properly yet.
  r18080 at ubuntu (orig r3390):  clkao | 2007-06-07 18:11:09 -0400
  keeping __page's state won't get us anywhere...
  r18093 at ubuntu (orig r3392):  clkao | 2007-06-07 19:19:35 -0400
  ditto for other implicit __page fragment to ignore region state.
  r18094 at ubuntu (orig r3393):  clkao | 2007-06-07 19:20:11 -0400
  fallback value being array makes the crud action on server side sad.
  r18110 at ubuntu (orig r3397):  clkao | 2007-06-08 02:43:23 -0400
  Support redirect to external url in webservices.
  r18111 at ubuntu (orig r3398):  clkao | 2007-06-08 03:01:27 -0400
  Prevent infinite loop when unable to render mason_internal_error.
  
  r18112 at ubuntu (orig r3399):  clkao | 2007-06-08 03:15:31 -0400
  Handle J:C internal redirect, which is used by error raised by dispatcher.
  r18118 at ubuntu (orig r3401):  clkao | 2007-06-08 13:00:58 -0400
  * If we are doing external redirect, supress fragment replacement.
  * fix refresh_self in submit so on failed action response we get
    __page refreshed that is like page reload.
  
  r18119 at ubuntu (orig r3402):  clkao | 2007-06-08 13:13:54 -0400
  On internal redirect, reset nav and page_nav.
  r18120 at ubuntu (orig r3403):  clkao | 2007-06-08 15:42:44 -0400
  There's no point removing url in clickable and put in _orig_url anymore,
  which also happens to break redirect($clickable).
  
  r18125 at ubuntu (orig r3404):  clkao | 2007-06-08 16:42:32 -0400
  misc cleanups.
 
 r21871 at zot (orig r3407):  jesse | 2007-06-08 17:03:11 -0400
  r58173 at pinglin:  jesse | 2007-06-08 16:58:00 -0400
  * Back out a Croak back to a die. (We're using it for exception handling, not to-user reporting)
 
 r21872 at zot (orig r3408):  jesse | 2007-06-08 17:03:17 -0400
  r58174 at pinglin:  jesse | 2007-06-08 17:01:58 -0400
   * Refactor handle_request to extract out methods
   * Only warn about denied actions on validate if they're also actions we want to run
  
 
 r21873 at zot (orig r3409):  clkao | 2007-06-08 17:15:38 -0400
 More comment about the JAFF handling special case from update().
 
 r21874 at zot (orig r3410):  clkao | 2007-06-08 17:21:23 -0400
 misc webservices_redirect cleanup.
 
 r21875 at zot (orig r3411):  sartak | 2007-06-08 17:47:39 -0400
  r20470 at caladan:  sartak | 2007-06-08 17:46:44 -0400
  Add a little pod coverage
 
 r21876 at zot (orig r3412):  jesse | 2007-06-08 17:52:00 -0400
  r58183 at pinglin:  jesse | 2007-06-08 17:51:41 -0400
  * We'd gotten the $actions logic  slightly wrong during refactoring. The new code lets you submit everything/nothing/something, rather than just something or nothing ;)
 
 r21877 at zot (orig r3413):  jesse | 2007-06-08 19:16:51 -0400
  r58188 at pinglin:  jesse | 2007-06-08 19:16:30 -0400
  * switch url from an attribute to a child node for ajax redirects. (The old behaviour didn't work in safari)
 
 r21878 at zot (orig r3414):  clkao | 2007-06-08 22:32:57 -0400
 only do spa magic if onclick hook is hash - so onclick='someJavascript();' would work
 r21907 at zot (orig r3415):  clkao | 2007-06-09 10:44:09 -0400
 Allow spa to be disabled by page class.
 r21908 at zot (orig r3416):  clkao | 2007-06-09 10:51:59 -0400
 Fix onclick initialization in spa.
 r21909 at zot (orig r3417):  jesse | 2007-06-09 17:23:55 -0400
  r58190 at pinglin:  jesse | 2007-06-09 16:08:50 -0400
  * The cookbook was wrong about the auth recipe
 
 r21910 at zot (orig r3418):  jesse | 2007-06-09 17:24:05 -0400
  r58191 at pinglin:  jesse | 2007-06-09 16:08:59 -0400
  * Quiet a dispatcher warning
 
 r21911 at zot (orig r3419):  jesse | 2007-06-09 17:24:11 -0400
  r58192 at pinglin:  jesse | 2007-06-09 17:23:24 -0400
  meta.yml updates
 
 r21912 at zot (orig r3420):  jesse | 2007-06-09 17:24:22 -0400
  r58193 at pinglin:  jesse | 2007-06-09 17:23:33 -0400
   * Don't trip over classes we can't require
 
 r21913 at zot (orig r3421):  dpavlin | 2007-06-09 19:06:29 -0400
 bump version to 0.02, remove requirement to modify submit form button (and
 in process break next_page after it, because it turns normal form submit
 into Ajax form submit which doesn't handle redirects very well -- because it
 usually redirects to page which isn't valid XML)
 r21914 at zot (orig r3422):  dpavlin | 2007-06-09 19:08:58 -0400
 include verison string in module code
 r21915 at zot (orig r3423):  bartb | 2007-06-09 23:16:04 -0400
 typo fix
 r21916 at zot (orig r3424):  clkao | 2007-06-10 02:04:16 -0400
 For post fragment update script evaluation, use YUI OnAvailable
 rather than hardcoded setTimeout.
 
 r21917 at zot (orig r3425):  clkao | 2007-06-10 02:05:48 -0400
 when rendering the keybindings div, reset Jifty.KeyBindings.
 Ideally we might want to keep the state with the div so it resets
 itself when replaced.
 
 r22019 at zot (orig r3426):  clkao | 2007-06-10 18:47:53 -0400
 Make multiple use base lines into one for readability.
 
 r22032 at zot (orig r3427):  clkao | 2007-06-10 19:15:50 -0400
 Move the javascript concatenating logic from Jifty::Web to the plugin.
 
 r22033 at zot (orig r3428):  clkao | 2007-06-10 19:40:25 -0400
 CompressedCSSandJS: support optional javascript minifier.
 
 r22037 at zot (orig r3432):  clkao | 2007-06-10 20:49:10 -0400
 minor cleanups for ccjs.
 r22038 at zot (orig r3433):  sky | 2007-06-10 21:24:04 -0400
 remove trailing whitespace
 r22039 at zot (orig r3434):  clkao | 2007-06-10 21:29:18 -0400
 Unbreak nojs form submitting when SPA is enabled.
 r22040 at zot (orig r3435):  sky | 2007-06-10 21:33:24 -0400
 trailing whitespace....
 r22041 at zot (orig r3436):  clkao | 2007-06-10 21:46:34 -0400
 add IPC::Run3 to dependency.
 r22042 at zot (orig r3437):  sky | 2007-06-11 00:12:24 -0400
 work around annoying Module::Pluggable bug
 r22043 at zot (orig r3438):  jesse | 2007-06-11 02:37:40 -0400
  r58206 at pinglin:  jesse | 2007-06-11 02:29:31 -0400
  * Got the tabview plugin working with non-CRUD code. 
  * got the tabview plugin preloading the first tab, even if it's marked as a region rather than a static tab
 
 r22044 at zot (orig r3439):  jesse | 2007-06-11 02:37:50 -0400
  r58207 at pinglin:  jesse | 2007-06-11 02:31:00 -0400
  * Refactor the CRUD view to make it easier to customize the output
 
 r22045 at zot (orig r3440):  clkao | 2007-06-11 03:32:36 -0400
 Fix javascript emission when CompressedCSSandJS plugin is not loaded noticed
 by dpavlin++.
 
 r22046 at zot (orig r3441):  clkao | 2007-06-11 05:05:43 -0400
 Example::Todo is too hard to type.  Yada!
 
  r18180 at ubuntu:  clkao | 2007-06-11 04:42:19 -0400
  yada!
  r18181 at ubuntu:  clkao | 2007-06-11 04:44:38 -0400
  yada!
  r18182 at ubuntu:  clkao | 2007-06-11 04:46:17 -0400
  yada!
  r18183 at ubuntu:  clkao | 2007-06-11 05:03:12 -0400
  yada!
 
 r22047 at zot (orig r3442):  clkao | 2007-06-11 05:07:42 -0400
 Plugin view class should respect app page.
 r22048 at zot (orig r3443):  clkao | 2007-06-11 05:36:08 -0400
 Helper method for mounting crud view.
 r22065 at zot (orig r3448):  clkao | 2007-06-12 08:28:28 -0400
 Do not call update() when ctrl-clicked
 r22072 at zot (orig r3453):  clkao | 2007-06-12 11:28:57 -0400
 Always give the dispatcher unescaped path like fastcgi does,
 from standalone, webservice, and region entrance.
 
 r22074 at zot (orig r3455):  clkao | 2007-06-12 12:34:29 -0400
 Fix the template subclassing tests.
 
 r22076 at zot (orig r3456):  clkao | 2007-06-12 15:14:08 -0400
 YourApp::View->use_mason_wrapper now lets your write page {} in TD but uses
 your mason wrapper.
 
 r22084 at zot (orig r3461):  clkao | 2007-06-12 15:54:44 -0400
 Make use_mason_wrapper actually work.
 
 r22086 at zot (orig r3462):  trs | 2007-06-12 16:23:20 -0400
 
 r22087 at zot (orig r3463):  trs | 2007-06-12 16:23:37 -0400
  r22078 at zot:  tom | 2007-06-12 15:30:55 -0400
  Extraneous warning
 
 r22088 at zot (orig r3464):  trs | 2007-06-12 16:23:47 -0400
  r22079 at zot:  tom | 2007-06-12 16:22:25 -0400
  Be more in line with original code and apply Behaviour immediately, not in the callback
 
 r22107 at zot (orig r3466):  jesse | 2007-06-12 18:05:18 -0400
 
 r22108 at zot (orig r3467):  jesse | 2007-06-12 18:05:48 -0400
 
 r22109 at zot (orig r3468):  jesse | 2007-06-12 18:06:30 -0400
  r58252 at pinglin:  jesse | 2007-06-12 17:54:09 -0400
  * try not to interfere when a user clicks on a link with a modifier key pressed
 
 r22110 at zot (orig r3469):  clkao | 2007-06-12 18:56:44 -0400
 Fix jifty->web->out from without td used with mason wrapper.
 r22111 at zot (orig r3470):  jesse | 2007-06-12 19:15:10 -0400
  r58268 at pinglin:  jesse | 2007-06-12 19:15:01 -0400
   * _redirect expects a local path, not a fully formed URL. (This only actually broke internal redirects)
 
 r22112 at zot (orig r3471):  clkao | 2007-06-12 20:11:27 -0400
 New Template::Declare page syntax for additional code block run
 before wrapper, and have the returned value passed to wrapper:
 
 template foo => 
   page { title => 'this is title', other => 'foo' }
   content {
     h1 { 'blah }
   };
 
 r22143 at zot (orig r3474):  jesse | 2007-06-14 02:15:34 -0400
  r58297 at pinglin:  jesse | 2007-06-14 02:14:20 -0400
  * Jifty->web->form->next_page got dropped when you submit only a specific action with Jifty->web->form->submit. This commit autoadds next_page if you submit only a subset of actions.
 
 r22150 at zot (orig r3481):  sterling | 2007-06-14 10:46:41 -0400
 
 r22151 at zot (orig r3482):  sterling | 2007-06-14 10:49:08 -0400
  r7618 at dynpc145:  andrew | 2007-06-14 09:42:56 -0500
  Cleaning up and improving some of the view documentation.
 
 r22152 at zot (orig r3483):  yves | 2007-06-14 11:16:25 -0400
 * debian dependancies
 * T::D 0.21 needed to pass tests
 
 r22193 at zot (orig r3492):  trs | 2007-06-14 17:42:02 -0400
 
 r22194 at zot (orig r3493):  trs | 2007-06-14 17:42:15 -0400
  r22078 at zot:  tom | 2007-06-12 15:30:55 -0400
  Extraneous warning
 
 r22195 at zot (orig r3494):  trs | 2007-06-14 17:42:18 -0400
  r22079 at zot:  tom | 2007-06-12 16:22:25 -0400
  Be more in line with original code and apply Behaviour immediately, not in the callback
 
 r22196 at zot (orig r3495):  trs | 2007-06-14 17:42:31 -0400
  r22184 at zot:  tom | 2007-06-14 17:41:23 -0400
  Also export display_columns so it's not required to override it
 
 r22217 at zot (orig r3496):  jesse | 2007-06-14 20:18:13 -0400
  r58374 at pinglin:  jesse | 2007-06-14 20:17:33 -0400
   * Full doc for the existing CRUD templates
 
 r22222 at zot (orig r3501):  jesse | 2007-06-15 00:17:09 -0400
  r58389 at pinglin:  jesse | 2007-06-15 00:16:54 -0400
  * CRUD plugin cleanup and refactoring
 
 r24653 at zot (orig r3507):  jesse | 2007-06-15 12:07:40 -0400
 * Extracting talks from the jifty dist
 r24686 at zot (orig r3514):  trs | 2007-06-17 16:53:50 -0400
  r24679 at zot:  tom | 2007-06-16 15:20:42 -0400
  HTTPS and HTTP adjectives for dispatcher rules
 
 r24687 at zot (orig r3515):  trs | 2007-06-17 16:53:55 -0400
  r24680 at zot:  tom | 2007-06-16 16:41:16 -0400
  Allow scheme to be specified for Jifty->web->url.  This functionality was taken out during the move from "heuristics" to URI.pm, but for a non-apparent reason.
 
 r24688 at zot (orig r3516):  trs | 2007-06-17 16:54:01 -0400
  r24683 at zot:  tom | 2007-06-17 16:49:49 -0400
  Now with more POD!  (and passing POD tests)
 
 r24689 at zot (orig r3517):  trs | 2007-06-17 16:54:09 -0400
  r24684 at zot:  tom | 2007-06-17 16:53:22 -0400
  Fix typos
 
 r24699 at zot (orig r3527):  bartb | 2007-06-18 05:25:24 -0400
 typo fix
 r24700 at zot (orig r3528):  audreyt | 2007-06-18 11:03:37 -0400
 * Web::Form::Link - When escape_label was set, the tooltip property was
   (very sillily!) first escaped, then discarded away, displaying the
   unescaped text instead.  It now escapes properly.
 * Also make tooltips with value '0' display properly.
 r24705 at zot (orig r3529):  trs | 2007-06-18 16:40:38 -0400
  r24704 at zot:  tom | 2007-06-18 16:40:03 -0400
  Allow app changeable cookie names
 
 r24764 at zot (orig r3532):  clkao | 2007-06-19 18:56:51 -0400
 debug plugin for logging dispatched rules and current user.
 r24765 at zot (orig r3533):  clkao | 2007-06-19 19:05:52 -0400
 info rather than debug.
 r24767 at zot (orig r3534):  trs | 2007-06-19 22:57:00 -0400
  r24761 at zot:  tom | 2007-06-19 22:56:13 -0400
  * Use Jifty::Util->share_root for finding plugin share roots so that if Jifty isn't installed it still DTRT
  * Only calculate static roots once and report on plugins adding roots (like the mason handler does)
 
 r24789 at zot (orig r3542):  trs | 2007-06-21 00:06:09 -0400
  r24784 at zot:  tom | 2007-06-21 00:03:42 -0400
  Convert to using the form of declaring titles that works when using the mason wrapper with TD
 
 r24795 at zot (orig r3543):  trs | 2007-06-21 02:47:05 -0400
  r24794 at zot:  tom | 2007-06-21 02:46:02 -0400
  Restore and update the OOM patch to the YUI calendar
 
 r24818 at zot (orig r3544):  ruz | 2007-06-21 11:03:23 -0400
 * protect ourself from circular references between User and CurrentUser
 
 we do it in user_object, so solution is generic should work in any case, but
 only if people use the method to set user_object and user model uses _current_user
 key to store reference to the CurrentUser
 
 r24819 at zot (orig r3545):  gugod | 2007-06-22 01:23:43 -0400
 Add a "next" parameter so one can specify the next page to go after
 openid login.
 
 r24820 at zot (orig r3546):  ruz | 2007-06-22 09:33:34 -0400
 * add {_resurect_current_user} field into user module
 r24821 at zot (orig r3547):  bartb | 2007-06-22 18:19:29 -0400
 added see also section to Jifty::Upgrade
 r24822 at zot (orig r3548):  bartb | 2007-06-23 18:26:00 -0400
 typo
 r24823 at zot (orig r3549):  alexmv | 2007-06-24 17:19:47 -0400
  r19898 at zoq-fot-pik:  chmrr | 2007-06-24 17:17:51 -0400
   * Mapper edge case failure (when no 'name' was given)
 
 r24824 at zot (orig r3550):  audreyt | 2007-06-25 13:48:22 -0400
 * No matter what _resurect_current_user is intended for, it ought
   to be spelled _resurrect_current_user instead. :-)
 r24844 at zot (orig r3570):  sterling | 2007-06-26 12:38:44 -0400
  r7829 at riddle:  andrew | 2007-06-26 11:37:40 -0500
  Added a many-to-many relationship recipe to the cookbook.
 
 r24845 at zot (orig r3571):  alexmv | 2007-06-26 16:11:04 -0400
  r19955 at zoq-fot-pik:  chmrr | 2007-06-26 16:10:33 -0400
   * Recent YAML::Syck's get confused by trying to be too smart with
     $test_config, which is a filehandle which *also* stringifies to the
     path.  Force stringification to get consistent (working) results.
 
 r24920 at zot (orig r3580):  sterling | 2007-06-27 21:43:41 -0400
  r7874 at dynpc145:  andrew | 2007-06-27 20:42:34 -0500
  Fleshing out and cleaning up the documentation for Jifty::Plugin::User.
 
 r24921 at zot (orig r3581):  sterling | 2007-06-27 21:43:53 -0400
  r7875 at dynpc145:  andrew | 2007-06-27 20:43:00 -0500
  Fleshing out and cleaning up the documentation for Jifty::Plugin::Authentication::Password.
 
 r24924 at zot (orig r3584):  nelhage | 2007-06-29 19:25:18 -0400
 word-wrapping some POD
 r24925 at zot (orig r3585):  alexmv | 2007-06-29 19:48:14 -0400
  r19971 at zoq-fot-pik:  chmrr | 2007-06-29 19:46:25 -0400
   * Move pulling action defaults from action in request into the
     action's new, instead of a "private" argument.
   * Remove an extra place where we default the moniker to
     _generate_moniker
   * A bunch of POD (re)wrapping.
 
 r24926 at zot (orig r3586):  alexmv | 2007-06-29 19:56:06 -0400
  r19973 at zoq-fot-pik:  chmrr | 2007-06-29 19:54:26 -0400
   * The true return value of the module needs to not be part of the POD
   * Fix a POD nitpick
 
 r24950 at zot (orig r3588):  sterling | 2007-07-01 20:25:25 -0400
  r7901 at dynpc145:  andrew | 2007-07-01 19:21:40 -0500
  Altered the ClassLoader to create Collections for models deeper under the model namespace,like "App::Model::Foo::Bar". These is consistent with the handling for actions and other components already in place.
 
 r24951 at zot (orig r3589):  sterling | 2007-07-01 20:25:30 -0400
  r7902 at dynpc145:  andrew | 2007-07-01 19:23:07 -0500
  Fixed the formatting of the comment blocks in the POD.
 
 r24952 at zot (orig r3590):  sterling | 2007-07-01 20:25:37 -0400
  r7903 at dynpc145:  andrew | 2007-07-01 19:24:12 -0500
  Added an additional see also to Jifty::Manual::AccessControl to the User and Authentication::Password plugins.
 
 r24953 at zot (orig r3591):  sterling | 2007-07-01 20:35:12 -0400
  r7908 at dynpc145:  andrew | 2007-07-01 19:34:08 -0500
  Adding documentation to Jifty::Plugin::Debug to fix POD coverage failures.
 
 r24954 at zot (orig r3592):  sterling | 2007-07-01 20:35:18 -0400
  r7909 at dynpc145:  andrew | 2007-07-01 19:34:48 -0500
  Changing the Jifty console requirement back to default => 0.
 
 r24979 at zot (orig r3603):  trs | 2007-07-03 03:22:37 -0400
  r24976 at zot:  tom | 2007-07-03 02:49:39 -0400
  Basic Facebook auth plugin
 
 r24980 at zot (orig r3604):  trs | 2007-07-03 03:22:43 -0400
  r24977 at zot:  tom | 2007-07-03 03:04:34 -0400
  More pod, forceable login
 
 r24981 at zot (orig r3605):  trs | 2007-07-03 03:22:46 -0400
  r24978 at zot:  tom | 2007-07-03 03:22:01 -0400
  Provide a way to link existing users with a Facebook account
 
 r25010 at zot (orig r3610):  dpavlin | 2007-07-04 17:23:45 -0400
 check if current_user->can('user_object') when tring to figure out timezone
 r25011 at zot (orig r3611):  sterling | 2007-07-06 12:36:35 -0400
  r7940 at dynpc145:  andrew | 2007-07-06 11:34:54 -0500
  Added NAME sections and short descriptions for the CPAN module summary.
 
 r25038 at zot (orig r3612):  sterling | 2007-07-06 17:57:05 -0400
  r7942 at dynpc145:  andrew | 2007-07-06 16:56:27 -0500
  Added a recipe for using "result_of" to fetch information about actions for use in appends.
 
 r25041 at zot (orig r3613):  trs | 2007-07-06 19:05:11 -0400
  r25037 at zot:  tom | 2007-07-06 19:04:08 -0400
  - Fix our test mech to not choke on non-default cookie names
  - Clean up page headers
  - Remove unecessary var
 
 r25053 at zot (orig r3614):  sterling | 2007-07-07 15:29:31 -0400
  r7946 at dynpc145:  andrew | 2007-07-07 14:12:17 -0500
  Added support for application-specific plugins (i.e., App::Plugin::XXX) and plugins named using the fully-qualified Jifty::Plugin::XXX name.
 
 r25058 at zot (orig r3619):  jesse | 2007-07-09 05:02:03 -0400
  r60264 at 102:  jesse | 2007-07-08 21:52:09 -0700
   * Template::Declare behaviour change for "show 'foo'" to now show foo at the current template depth.
 
 r25059 at zot (orig r3620):  jesse | 2007-07-09 05:02:16 -0400
  r60267 at 102:  jesse | 2007-07-08 22:03:24 -0700
  * Relative to absolute pathing fix
 
 r25060 at zot (orig r3621):  jesse | 2007-07-09 05:02:29 -0400
  r60271 at 102:  jesse | 2007-07-08 22:23:13 -0700
  * minor refactoring
 
 r25071 at zot (orig r3632):  clkao | 2007-07-09 11:16:35 -0400
 Fix typo.
 r25075 at zot (orig r3634):  clkao | 2007-07-09 18:15:55 -0400
 support external javascripts.
 r25078 at zot (orig r3637):  clkao | 2007-07-09 19:37:52 -0400
 First cut of GoogleMap plugin.  You can now declare a column
 as geolocation, and you'll be able to display and edit it
 with a google map widget.
 
 r25079 at zot (orig r3638):  clkao | 2007-07-09 19:51:51 -0400
 Oops, debug info not killed before last commit.
 r25080 at zot (orig r3639):  clkao | 2007-07-09 19:54:55 -0400
 Fix typo.
 r25086 at zot (orig r3642):  clkao | 2007-07-09 23:46:50 -0400
 address search.  only works for accurate result for now.
 r25087 at zot (orig r3643):  clkao | 2007-07-10 00:15:50 -0400
 Minor fine tune.
 r25089 at zot (orig r3645):  clkao | 2007-07-10 08:59:25 -0400
 move most js from googlemap::widget into google_map.js
 r25091 at zot (orig r3646):  clkao | 2007-07-10 09:17:04 -0400
 Support readonly for googlemap widget.
 r25093 at zot (orig r3647):  clkao | 2007-07-10 10:26:55 -0400
 Address search!
 r25096 at zot (orig r3649):  clkao | 2007-07-10 17:26:09 -0400
 in REST record_to_data, skip container columns.
 r25119 at zot (orig r3650):  clkao | 2007-07-10 17:44:12 -0400
 type can be empty for container columns.
 r25120 at zot (orig r3651):  clkao | 2007-07-10 17:44:51 -0400
 Minor cleanup.
 
 r25121 at zot (orig r3652):  clkao | 2007-07-10 18:09:06 -0400
 Revert the logic change.  We still want to include container fields in list.
 r25123 at zot (orig r3653):  trs | 2007-07-10 21:34:21 -0400
 
 r25124 at zot (orig r3654):  trs | 2007-07-10 21:34:34 -0400
  r25118 at zot:  tom | 2007-07-10 21:33:18 -0400
  - Plugin CSS and JS (which should be put in share/plugin/Jifty/Plugin/Foo/web/static/{css,js}) is now compressed by CCJS
  - CCJS can compress more than just main.css by calling Jifty->web->add_css("file.css")
 
 r25126 at zot (orig r3655):  jesse | 2007-07-10 21:41:40 -0400
  r60363 at pinglin:  jesse | 2007-07-10 18:40:21 -0700
   * Doc updates
 
 r25128 at zot (orig r3656):  trs | 2007-07-10 22:03:26 -0400
  r25125 at zot:  tom | 2007-07-10 22:02:35 -0400
  Move GoogleMap plugin CSS and JS to proper spot now
 
 r25131 at zot (orig r3657):  trs | 2007-07-10 22:04:51 -0400
  r25130 at zot:  tom | 2007-07-10 22:04:40 -0400
  Supposed to be deleted
 
 r25160 at zot (orig r3658):  clkao | 2007-07-10 23:27:51 -0400
 Asset model mixin for adding created_by for default permission.
 
 r25161 at zot (orig r3659):  clkao | 2007-07-11 00:18:52 -0400
 googlemap widget  alignment fix.
 r25162 at zot (orig r3660):  clkao | 2007-07-11 10:07:46 -0400
 add_external_javascript for googlemap
 r25163 at zot (orig r3661):  clkao | 2007-07-11 10:12:06 -0400
 add_external_javascript takes array
 r25164 at zot (orig r3662):  clkao | 2007-07-11 10:30:45 -0400
 tabview plugin: allow render_tab in top level without current_region.
 r25165 at zot (orig r3663):  clkao | 2007-07-11 12:51:45 -0400
 * Make geocoder search usable even not in edit mode.
 * Add Cancel button in edit mode.
 
 r25166 at zot (orig r3664):  clkao | 2007-07-11 13:26:18 -0400
 Don't bother showing edit link if current user can't
 r25167 at zot (orig r3665):  jesse | 2007-07-11 14:02:56 -0400
  r60378 at mobile131:  jesse | 2007-07-10 21:59:41 -0700
  rename checkpoint
 
 r25168 at zot (orig r3666):  jesse | 2007-07-11 14:03:08 -0400
  r60379 at mobile131:  jesse | 2007-07-10 22:00:20 -0700
   Asset renamed to ActorMetadata
  
 
 r25169 at zot (orig r3667):  jesse | 2007-07-11 14:03:16 -0400
  r60382 at mobile131:  jesse | 2007-07-10 23:07:23 -0700
  * Incredibly naive "image column" userpic plugin. 
      - should mark the column as not to be preloaded in regular operation
      - should not hardocde mimetype
      - should have a pretty ajax picker
      - should limit the image's size
  
 
 r25170 at zot (orig r3668):  clkao | 2007-07-11 15:57:22 -0400
 i18n for feedback plugin.
 r25171 at zot (orig r3669):  clkao | 2007-07-11 16:07:13 -0400
 Double precision
 r25177 at zot (orig r3673):  trs | 2007-07-11 20:04:13 -0400
  r25176 at zot:  tom | 2007-07-11 20:03:57 -0400
  - Minor refactoring for easy wording change
  - Provide the D in CRUD
 
 r25179 at zot (orig r3674):  clkao | 2007-07-11 20:58:19 -0400
 Fix IE issues with search div.
 r25184 at zot (orig r3678):  trs | 2007-07-12 00:47:47 -0400
  r25178 at zot:  tom | 2007-07-12 00:45:24 -0400
  Add some classes
 
 r25186 at zot (orig r3679):  trs | 2007-07-12 01:01:48 -0400
  r25185 at zot:  tom | 2007-07-12 00:50:05 -0400
  Extract out per_page
 
 r25188 at zot (orig r3681):  clkao | 2007-07-12 16:50:34 -0400
 * Make enter key submit in the address search popup.
 * Fix the close link in ambigious address popup.
 
 r25191 at zot (orig r3683):  ternus | 2007-07-13 01:12:32 -0400
 
 HTML branch merge
 
 r25194 at zot (orig r3685):  trs | 2007-07-13 03:49:08 -0400
 merge down again
 r25195 at zot (orig r3686):  falcone | 2007-07-13 11:40:56 -0400
 
 r25196 at zot (orig r3687):  falcone | 2007-07-13 11:42:09 -0400
  r22635 at ketch:  falcone | 2007-07-13 11:34:09 -0400
  * add missing Test::Log4perl dep and update the Jifty::DBI requirement
 
 r25198 at zot (orig r3688):  bartb | 2007-07-13 16:45:39 -0400
 typo fixes
 r25199 at zot (orig r3689):  jesse | 2007-07-14 00:02:15 -0400
  r60637 at pinglin:  jesse | 2007-07-13 22:06:16 -0400
  * Updated tabview plugin to work when it's not already inside a region
  
 
 r25200 at zot (orig r3690):  jesse | 2007-07-14 00:02:23 -0400
  r60638 at pinglin:  jesse | 2007-07-13 22:07:04 -0400
  * Fixed CRUD view to no longer require you to specify a base_path for the plugin's view. It was redundant and we could intuit it.
 
 r25208 at zot (orig r3695):  trs | 2007-07-16 19:06:58 -0400
  r25207 at zot:  tom | 2007-07-16 19:06:19 -0400
  Need parens
 
 r25294 at zot (orig r3696):  sterling | 2007-07-16 22:28:58 -0400
  r8092 at dynpc145:  andrew | 2007-07-16 21:28:04 -0500
  Plugins may now have regular models. These are deployed after the schema is setup after adding the plugin to installation. Plugins can bootstrap and upgrade their schema with their own version numbers just like applications (though with slightly different details).
 
 r25295 at zot (orig r3697):  sterling | 2007-07-16 23:21:16 -0400
  r8098 at dynpc145:  andrew | 2007-07-16 22:20:31 -0500
  Cleaned up the class loader a bit to make all the auto-generated methods use the same name, remove an unnecessary elsif, and add a few more comments.
 
 r25547 at zot (orig r3699):  trs | 2007-07-18 20:39:45 -0400
  r25546 at zot:  tom | 2007-07-18 20:38:59 -0400
  Don't mess with the HTML by default.  This should likely become configurable in the future.
 
 r25575 at zot (orig r3700):  sterling | 2007-07-19 09:36:47 -0400
  r8113 at dynpc145:  andrew | 2007-07-18 21:01:53 -0500
  Fixing API qualification to make it possible to access actions associated with plugins via ->new_class.
 
 r25583 at zot (orig r3702):  trs | 2007-07-19 15:23:09 -0400
  r25574 at zot:  tom | 2007-07-19 15:22:29 -0400
  Make sure we get UTF-8
 
 r25586 at zot (orig r3703):  trs | 2007-07-19 16:10:55 -0400
  r25585 at zot:  tom | 2007-07-19 16:10:15 -0400
  Now make sure it's actually UTF-8 
 
 r25609 at zot (orig r3705):  clkao | 2007-07-20 10:00:23 -0400
 Push milestone 1 of trimclient to trunk.
 
 r25621 at zot (orig r3706):  jesse | 2007-07-22 16:21:40 -0400
 
 r25622 at zot (orig r3707):  jesse | 2007-07-22 16:21:58 -0400
  r61112 at 106:  jesse | 2007-07-22 10:20:34 -0700
  * The warnings come from the server code after the fork. Test::Log4Perl isn't going to catch them.
 
 r25625 at zot (orig r3708):  jesse | 2007-07-22 16:22:02 -0400
  r61115 at 106:  jesse | 2007-07-22 12:48:51 -0700
  * added some pod to help make pod tests pass
 
 r25639 at zot (orig r3715):  clkao | 2007-07-24 06:09:47 -0400
 Fix a fragment update regression caused by the trimclient merge.
 
 r25702 at zot (orig r3723):  sterling | 2007-07-27 15:53:01 -0400
 
 r25703 at zot (orig r3724):  sterling | 2007-07-27 15:54:54 -0400
  r8183 at riddle:  andrew | 2007-07-27 12:49:07 -0700
  Adding a plugin for rendering charts of data.
 
 r25704 at zot (orig r3725):  sterling | 2007-07-27 15:56:25 -0400
  r8184 at riddle:  andrew | 2007-07-27 12:49:42 -0700
  Removing a directory that should not have committed previously.
 
 r25705 at zot (orig r3726):  sterling | 2007-07-27 15:58:53 -0400
 
 r25708 at zot (orig r3727):  sterling | 2007-07-27 16:12:43 -0400
  r8192 at riddle:  andrew | 2007-07-27 13:11:49 -0700
  Reverting mistaken revision r3723
 
 r25710 at zot (orig r3728):  sterling | 2007-07-27 16:17:32 -0400
  r8194 at riddle:  andrew | 2007-07-27 13:16:59 -0700
  Reverting mistaken revision r3723, take 2
 
 r25714 at zot (orig r3730):  jesse | 2007-07-28 20:31:47 -0400
  r64900 at pinglin:  jesse | 2007-07-28 18:18:31 -0500
  * Moniker bulletproofing. Suggested by Mikko Lapasti
 
 r25716 at zot (orig r3731):  sterling | 2007-07-29 16:15:59 -0400
  r8199 at dynpc145:  andrew | 2007-07-29 15:13:38 -0500
  Added documentation to the experimental Chart plugin.
 
 r25718 at zot (orig r3732):  sterling | 2007-07-29 16:16:07 -0400
  r8200 at dynpc145:  andrew | 2007-07-29 15:15:21 -0500
  Added the Chart::Base recommendation for the Chart plugin.
 
 r25722 at zot (orig r3734):  jesse | 2007-07-29 17:53:45 -0400
  r64915 at pinglin:  jesse | 2007-07-29 17:53:35 -0400
  * Resolve import conflicts now that T::D and J::V::D::Helpers have a thingy with the same name
 
 r25724 at zot (orig r3735):  sterling | 2007-07-29 22:16:24 -0400
  r8203 at dynpc145:  andrew | 2007-07-29 21:13:01 -0500
  Adding a test suite for Jifty::Plugin::Chart, but it is having weird troubles loading Chart::* because that seems to disconnect the server output or something.
 
 r25726 at zot (orig r3736):  sterling | 2007-07-29 22:16:30 -0400
  r8204 at dynpc145:  andrew | 2007-07-29 21:13:35 -0500
  Adding Image::Info dependency used during testing of Jifty::Plugin::Chart
 
 r25728 at zot (orig r3737):  sterling | 2007-07-29 22:16:37 -0400
  r8205 at dynpc145:  andrew | 2007-07-29 21:15:43 -0500
  Regarding Jifty::Plugin::Chart: Added better comments. Fixed some error handling. Switched to using scalar_png(). Switched to using ->require rather than an eval to load Chart classes. Eliminated the need for IO::String. Moved some processing out of View and into Dispatcher.
 
 r25730 at zot (orig r3738):  sartak | 2007-07-30 16:57:21 -0400
  r29652 at caladan:  sartak | 2007-07-30 16:56:47 -0400
  Add a load_by_kv to Jifty::Web::Session
 
 r25732 at zot (orig r3739):  sartak | 2007-07-30 16:58:18 -0400
  r29654 at caladan:  sartak | 2007-07-30 16:58:03 -0400
  add myself to AUTHORS :)
 
 r25737 at zot (orig r3742):  jesse | 2007-07-30 20:38:33 -0400
  r64932 at pinglin:  jesse | 2007-07-30 20:37:16 -0400
  * First cut of a UUID column plugin, with a basic test in the user model
 


Modified: jifty/branches/js-refactor/AUTHORS
==============================================================================
--- jifty/branches/js-refactor/AUTHORS	(original)
+++ jifty/branches/js-refactor/AUTHORS	Tue Jul 31 17:23:52 2007
@@ -30,3 +30,4 @@
 Alexander Klink <alech at cpan.org>
 Andreas Koenig <andreas.koenig.7os6VVqR at franz.ak.mind.de>
 sunnavy <sunnavy at gmail.com>
+Shawn M Moore <sartak at gmail.com>

Modified: jifty/branches/js-refactor/META.yml
==============================================================================
--- jifty/branches/js-refactor/META.yml	(original)
+++ jifty/branches/js-refactor/META.yml	Tue Jul 31 17:23:52 2007
@@ -2,7 +2,7 @@
 build_requires: 
   ExtUtils::MakeMaker: 6.11
 distribution_type: module
-generated_by: Module::Install version 0.650
+generated_by: Module::Install version 0.670
 license: Perl
 meta-spec: 
   url: http://module-build.sourceforge.net/META-spec-v1.3.html
@@ -39,6 +39,7 @@
   Test::HTTP::Server::Simple: 0.02
   Test::MockModule: 0.05
   Test::MockObject: 1.07
+  WWW::Facebook::API: 0.3.6
 requires: 
   App::CLI: 0.03
   CGI: 3.19
@@ -49,6 +50,7 @@
   Class::Accessor: 0
   Class::Container: 0
   Class::Data::Inheritable: 0
+  Class::Trigger: 0
   Compress::Zlib: 0
   Crypt::CBC: 0
   Crypt::Rijndael: 0
@@ -61,7 +63,9 @@
   Email::LocalDelivery: 0
   Email::MIME: 0
   Email::MIME::ContentType: 0
+  Email::MIME::CreateHTML: 0
   Email::MIME::Creator: 0
+  Email::MIME::Modifier: 0
   Email::Send: 1.99_01
   Email::Simple: 0
   Email::Simple::Creator: 0
@@ -80,27 +84,31 @@
   Hash::Merge: 0
   Hook::LexWrap: 0
   IPC::PubSub: 0.23
+  IPC::Run3: 0
   JSON::Syck: 0.15
-  Jifty::DBI: 0.40
+  Jifty::DBI: 0.42
   LWP::UserAgent: 0
   Locale::Maketext::Extract: 0.20
   Locale::Maketext::Lexicon: 0.60
   Log::Log4perl: 1.04
   MIME::Types: 0
   Module::CoreList: 0
-  Module::Pluggable: 3.1
+  Module::Pluggable: 3.5
   Module::Pluggable::Object: 0
   Module::Refresh: 0
   Module::ScanDeps: 0
   Object::Declare: 0.13
+  PadWalker: 0
   Params::Validate: 0
   Pod::Simple: 0
   SQL::ReservedWords: 0
   Scalar::Defer: 0.10
   Shell::Command: 0
   String::Koremutake: 0
-  Template::Declare: 0.07
+  Template::Declare: 0.21
   Test::Base: 0
+  Test::Log4perl: 0
+  Test::LongString: 0
   Test::More: 0.62
   Test::Pod::Coverage: 0
   Test::WWW::Mechanize: 1.04

Modified: jifty/branches/js-refactor/Makefile.PL
==============================================================================
--- jifty/branches/js-refactor/Makefile.PL	(original)
+++ jifty/branches/js-refactor/Makefile.PL	Tue Jul 31 17:23:52 2007
@@ -25,6 +25,8 @@
 requires('Email::MIME');
 requires('Email::MIME::Creator');
 requires('Email::MIME::ContentType');
+requires('Email::MIME::CreateHTML');
+requires('Email::MIME::Modifier');
 requires('Email::Send' => '1.99_01'); # Email::Send::Jifty::Test
 requires('Email::Simple');
 requires('Email::Simple::Creator');
@@ -43,7 +45,8 @@
 requires('Hash::Merge');
 requires('Hook::LexWrap');
 requires('IPC::PubSub' => '0.23' );
-requires('Jifty::DBI' => '0.40' );            # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
+requires('IPC::Run3');
+requires('Jifty::DBI' => '0.42' );            # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
 requires('Locale::Maketext::Extract' => '0.20');
 requires('Locale::Maketext::Lexicon' => '0.60');
 requires('Log::Log4perl' => '1.04');
@@ -55,14 +58,16 @@
 requires('Module::Refresh');
 requires('Module::ScanDeps');
 requires('Object::Declare' => '0.13');
+requires('PadWalker');
 requires('Params::Validate');
 requires('Scalar::Defer' => '0.10');
 requires('Shell::Command');
 requires('String::Koremutake');
 requires('SQL::ReservedWords');
-requires('Template::Declare' => '0.07');                # Template::Declare::Tags
+requires('Template::Declare' => '0.21');                # Template::Declare::Tags
 requires('Test::Base');
 requires('Test::LongString');
+requires('Test::Log4perl');
 requires('Test::More' => 0.62 ),
 requires('Test::Pod::Coverage'),
 requires('Test::WWW::Mechanize' => 1.04 ),
@@ -133,9 +138,18 @@
         recommends('Cache::FileCache'),
         recommends('LWPx::ParanoidAgent'),
     ],
+    'Facebook Login Plugin' => [
+        -default => 0,
+        recommends('WWW::Facebook::API' => '0.3.6'),
+    ],
     'Jifty console' => [
-        -default => 1,
-        recommends('Devel::EvalContext')
+        -default => 0,
+        recommends('Devel::EvalContext'),
+    ],
+    'Chart Plugin' => [
+        -default => 0,
+        recommends('Chart::Base'),
+        recommends('Image::Info'), # for testing
     ],
 );
 

Modified: jifty/branches/js-refactor/debian/control
==============================================================================
--- jifty/branches/js-refactor/debian/control	(original)
+++ jifty/branches/js-refactor/debian/control	Tue Jul 31 17:23:52 2007
@@ -53,6 +53,7 @@
  perl-modules, libcgi-cookie-splitter-perl, libcgi-simple-perl,
  libcrypt-cbc-perl, libcrypt-rijndael-perl,
  libcompress-zlib-perl, libcss-squish-perl (>> 0.05), 
+ libclass-trigger-perl, libdevel-evalcontext-perl,
  libdbd-sqlite3-perl, libdata-page-perl, libossp-uuid-perl,
  libdatetime-perl, libdatetime-format-builder-perl, 
  libdate-manip-perl, libemail-folder-perl,
@@ -65,14 +66,14 @@
  libhtml-lint-perl, libhtml-mason-perl (>> 1.31), 
  libwww-perl, libhttp-server-simple-perl (>> 0.26), 
  libhttp-server-simple-recorder-perl, libhash-merge-perl, libhook-lexwrap-perl,
- libipc-pubsub-perl (>> 0.23), libjifty-dbi-perl (>> 0.40),
+ libipc-pubsub-perl (>> 0.23), libjifty-dbi-perl (>> 0.40), libipc-run3-perl,
  liblocale-maketext-lexicon-perl, liblocale-maketext-simple-perl, liblog-log4perl-perl,
- libmime-types-perl, libmodule-pluggable-perl (>> 3.1),
+ libmime-types-perl, libmodule-pluggable-perl (>> 3.5),
  libmodule-corelist-perl, libmodule-refresh-perl,
  libmodule-scandeps-perl, libobject-declare-perl (>> 0.22),
  libparams-validate-perl, libscalar-defer-perl (>> 0.10),
  libstring-koremutake-perl, libsql-reservedwords-perl,
- libtemplate-declare-perl (>> 0.07), 
+ libtemplate-declare-perl (>> 0.21), 
  libtest-base-perl, libuniversal-require-perl, liburi-perl,
  libxml-writer-perl (>> 0.601), libxml-simple-perl,
  libxml-xpath-perl, libversion-perl, libyaml-syck-perl (>> 0.72), 

Modified: jifty/branches/js-refactor/examples/Yada/META.yml
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/META.yml	(original)
+++ jifty/branches/js-refactor/examples/Yada/META.yml	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,15 @@
+--- 
+distribution_type: module
+generated_by: Module::Install version 0.67
+license: unknown
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Yada
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Jifty: 0.70117
+version: 0.01

Modified: jifty/branches/js-refactor/examples/Yada/Makefile.PL
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/Makefile.PL	(original)
+++ jifty/branches/js-refactor/examples/Yada/Makefile.PL	Tue Jul 31 17:23:52 2007
@@ -1,6 +1,6 @@
 use inc::Module::Install;
 
-name        'Example::Todo';
+name        'Yada';
 version     '0.01';
 requires    'Jifty' => '0.70117';
 

Modified: jifty/branches/js-refactor/examples/Yada/etc/config.yml
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/etc/config.yml	(original)
+++ jifty/branches/js-refactor/examples/Yada/etc/config.yml	Tue Jul 31 17:23:52 2007
@@ -2,21 +2,22 @@
 application:
   OpenIDSecret: sekrit13
 framework: 
+  ConfigFileVersion: 2
   AdminMode: 1
   SkipAccessControl: 1
-  ApplicationClass: Example::Todo
-  ApplicationName: Example-Todo
+  ApplicationClass: Yada
+  ApplicationName: Yada
   ApplicationUUID: 80DF397A-D999-11DB-B80A-9318EBB6763A
   Database: 
     CheckSchema: 1
-    Database: example_todo
+    Database: yada
     Driver: SQLite
     Host: localhost
     Password: ''
     RecordBaseClass: Jifty::DBI::Record::Cachable
     User: ''
     Version: 0.0.1
-  DevelMode: 0
+  DevelMode: 1
   L10N: 
     PoDir: share/po
   LogLevel: INFO
@@ -34,11 +35,13 @@
     - User: {}
     - Authentication::Password: {}
     - OpenID: {}
+    - SinglePage: {}
 
   PubSub: 
     Backend: Memcached
     Enable: ~
-  TemplateClass: Example::Todo::View
+  TemplateClass: Yada::View
+  ClientTemplate: 1
   Web: 
     BaseURL: http://localhost
     DataDir: var/mason

Modified: jifty/branches/js-refactor/examples/Yada/inc/Module/Install.pm
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/inc/Module/Install.pm	(original)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install.pm	Tue Jul 31 17:23:52 2007
@@ -28,7 +28,7 @@
     # This is not enforced yet, but will be some time in the next few
     # releases once we can make sure it won't clash with custom
     # Module::Install extensions.
-    $VERSION = '0.64';
+    $VERSION = '0.67';
 }
 
 # Whether or not inc::Module::Install is actually loaded, the
@@ -86,7 +86,7 @@
             # delegate back to parent dirs
             goto &$code unless $cwd eq $pwd;
         }
-        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+        $$sym =~ /([^:]+)$/ or Carp::confess "Cannot autoload $who - $sym";
         unshift @_, ($self, $1);
         goto &{$self->can('call')} unless uc($1) eq $1;
     };

Modified: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Base.pm
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/inc/Module/Install/Base.pm	(original)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Base.pm	Tue Jul 31 17:23:52 2007
@@ -1,7 +1,7 @@
 #line 1
 package Module::Install::Base;
 
-$VERSION = '0.64';
+$VERSION = '0.67';
 
 # Suspend handler for "redefined" warnings
 BEGIN {

Added: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Can.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Can.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+	my ($self, $mod, $ver) = @_;
+	$mod =~ s{::|\\}{/}g;
+	$mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+	my $pkg = $mod;
+	$pkg =~ s{/}{::}g;
+	$pkg =~ s{\.pm$}{}i;
+
+	local $@;
+	eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+	my ($self, $cmd) = @_;
+
+	my $_cmd = $cmd;
+	return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+	for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+		my $abs = File::Spec->catfile($dir, $_[1]);
+		return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+	}
+
+	return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+	my $self   = shift;
+	my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+	# $Config{cc} may contain args; try to find out the program part
+	while (@chunks) {
+		return $self->can_run("@chunks") || (pop(@chunks), next);
+	}
+
+	return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+	require ExtUtils::MM_Cygwin;
+	require ExtUtils::MM_Win32;
+	if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+		*ExtUtils::MM_Cygwin::maybe_command = sub {
+			my ($self, $file) = @_;
+			if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+				ExtUtils::MM_Win32->maybe_command($file);
+			} else {
+				ExtUtils::MM_Unix->maybe_command($file);
+			}
+		}
+	}
+}
+
+1;
+
+__END__
+
+#line 157

Added: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Fetch.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Fetch.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,93 @@
+#line 1
+package Module::Install::Fetch;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub get_file {
+    my ($self, %args) = @_;
+    my ($scheme, $host, $path, $file) = 
+        $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+
+    if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) {
+        $args{url} = $args{ftp_url}
+            or (warn("LWP support unavailable!\n"), return);
+        ($scheme, $host, $path, $file) = 
+            $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return;
+    }
+
+    $|++;
+    print "Fetching '$file' from $host... ";
+
+    unless (eval { require Socket; Socket::inet_aton($host) }) {
+        warn "'$host' resolve failed!\n";
+        return;
+    }
+
+    return unless $scheme eq 'ftp' or $scheme eq 'http';
+
+    require Cwd;
+    my $dir = Cwd::getcwd();
+    chdir $args{local_dir} or return if exists $args{local_dir};
+
+    if (eval { require LWP::Simple; 1 }) {
+        LWP::Simple::mirror($args{url}, $file);
+    }
+    elsif (eval { require Net::FTP; 1 }) { eval {
+        # use Net::FTP to get past firewall
+        my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600);
+        $ftp->login("anonymous", 'anonymous at example.com');
+        $ftp->cwd($path);
+        $ftp->binary;
+        $ftp->get($file) or (warn("$!\n"), return);
+        $ftp->quit;
+    } }
+    elsif (my $ftp = $self->can_run('ftp')) { eval {
+        # no Net::FTP, fallback to ftp.exe
+        require FileHandle;
+        my $fh = FileHandle->new;
+
+        local $SIG{CHLD} = 'IGNORE';
+        unless ($fh->open("|$ftp -n")) {
+            warn "Couldn't open ftp: $!\n";
+            chdir $dir; return;
+        }
+
+        my @dialog = split(/\n/, <<"END_FTP");
+open $host
+user anonymous anonymous\@example.com
+cd $path
+binary
+get $file $file
+quit
+END_FTP
+        foreach (@dialog) { $fh->print("$_\n") }
+        $fh->close;
+    } }
+    else {
+        warn "No working 'ftp' program available!\n";
+        chdir $dir; return;
+    }
+
+    unless (-f $file) {
+        warn "Fetching failed: $@\n";
+        chdir $dir; return;
+    }
+
+    return if exists $args{size} and -s $file != $args{size};
+    system($args{run}) if exists $args{run};
+    unlink($file) if $args{remove};
+
+    print(((!exists $args{check_for} or -e $args{check_for})
+        ? "done!" : "failed! ($!)"), "\n");
+    chdir $dir; return !$?;
+}
+
+1;

Added: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Makefile.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Makefile.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,237 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+	shift;
+
+	# Infinite loop protection
+	my @c = caller();
+	if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+		die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+	}
+
+	# In automated testing, always use defaults
+	if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+		local $ENV{PERL_MM_USE_DEFAULT} = 1;
+		goto &ExtUtils::MakeMaker::prompt;
+	} else {
+		goto &ExtUtils::MakeMaker::prompt;
+	}
+}
+
+sub makemaker_args {
+	my $self = shift;
+	my $args = ($self->{makemaker_args} ||= {});
+	%$args = ( %$args, @_ ) if @_;
+	$args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+	my $self = sShift;
+	my $name = shift;
+	my $args = $self->makemaker_args;
+	$args->{name} = defined $args->{$name}
+		? join( ' ', $args->{name}, @_ )
+		: join( ' ', @_ );
+}
+
+sub build_subdirs {
+	my $self    = shift;
+	my $subdirs = $self->makemaker_args->{DIR} ||= [];
+	for my $subdir (@_) {
+		push @$subdirs, $subdir;
+	}
+}
+
+sub clean_files {
+	my $self  = shift;
+	my $clean = $self->makemaker_args->{clean} ||= {};
+	%$clean = (
+		%$clean, 
+		FILES => join(' ', grep length, $clean->{FILES}, @_),
+	);
+}
+
+sub realclean_files {
+	my $self  = shift;
+	my $realclean = $self->makemaker_args->{realclean} ||= {};
+	%$realclean = (
+		%$realclean, 
+		FILES => join(' ', grep length, $realclean->{FILES}, @_),
+	);
+}
+
+sub libs {
+	my $self = shift;
+	my $libs = ref $_[0] ? shift : [ shift ];
+	$self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+	my $self = shift;
+	$self->makemaker_args( INC => shift );
+}
+
+my %test_dir = ();
+
+sub _wanted_t {
+	/\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1;
+}
+
+sub tests_recursive {
+	my $self = shift;
+	if ( $self->tests ) {
+		die "tests_recursive will not work if tests are already defined";
+	}
+	my $dir = shift || 't';
+	unless ( -d $dir ) {
+		die "tests_recursive dir '$dir' does not exist";
+	}
+	require File::Find;
+	%test_dir = ();
+	File::Find::find( \&_wanted_t, $dir );
+	$self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
+}
+
+sub write {
+	my $self = shift;
+	die "&Makefile->write() takes no arguments\n" if @_;
+
+	my $args = $self->makemaker_args;
+	$args->{DISTNAME} = $self->name;
+	$args->{NAME}     = $self->module_name || $self->name || $self->determine_NAME($args);
+	$args->{VERSION}  = $self->version || $self->determine_VERSION($args);
+	$args->{NAME}     =~ s/-/::/g;
+	if ( $self->tests ) {
+		$args->{test} = { TESTS => $self->tests };
+	}
+	if ($] >= 5.005) {
+		$args->{ABSTRACT} = $self->abstract;
+		$args->{AUTHOR}   = $self->author;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+		$args->{NO_META} = 1;
+	}
+	if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+		$args->{SIGN} = 1;
+	}
+	unless ( $self->is_admin ) {
+		delete $args->{SIGN};
+	}
+
+	# merge both kinds of requires into prereq_pm
+	my $prereq = ($args->{PREREQ_PM} ||= {});
+	%$prereq = ( %$prereq,
+		map { @$_ }
+		map { @$_ }
+		grep $_,
+		($self->build_requires, $self->requires)
+	);
+
+	# merge both kinds of requires into prereq_pm
+	my $subdirs = ($args->{DIR} ||= []);
+	if ($self->bundles) {
+		foreach my $bundle (@{ $self->bundles }) {
+			my ($file, $dir) = @$bundle;
+			push @$subdirs, $dir if -d $dir;
+			delete $prereq->{$file};
+		}
+	}
+
+	if ( my $perl_version = $self->perl_version ) {
+		eval "use $perl_version; 1"
+			or die "ERROR: perl: Version $] is installed, "
+			. "but we need version >= $perl_version";
+	}
+
+	$args->{INSTALLDIRS} = $self->installdirs;
+
+	my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+
+	my $user_preop = delete $args{dist}->{PREOP};
+	if (my $preop = $self->admin->preop($user_preop)) {
+		$args{dist} = $preop;
+	}
+
+	my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+	$self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+	my $self          = shift;
+	my $makefile_name = shift;
+	my $top_class     = ref($self->_top) || '';
+	my $top_version   = $self->_top->VERSION || '';
+
+	my $preamble = $self->preamble 
+		? "# Preamble by $top_class $top_version\n"
+			. $self->preamble
+		: '';
+	my $postamble = "# Postamble by $top_class $top_version\n"
+		. ($self->postamble || '');
+
+	local *MAKEFILE;
+	open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	my $makefile = do { local $/; <MAKEFILE> };
+	close MAKEFILE or die $!;
+
+	$makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+	$makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+	$makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+	$makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+	$makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+	# Module::Install will never be used to build the Core Perl
+	# Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+	# PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+	$makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+	#$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+	# Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+	$makefile =~ s/("?)-I\$\(PERL_LIB\)\1//g;
+
+	# XXX - This is currently unused; not sure if it breaks other MM-users
+	# $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+	open  MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+	print MAKEFILE  "$preamble$makefile$postamble" or die $!;
+	close MAKEFILE  or die $!;
+
+	1;
+}
+
+sub preamble {
+	my ($self, $text) = @_;
+	$self->{preamble} = $text . $self->{preamble} if defined $text;
+	$self->{preamble};
+}
+
+sub postamble {
+	my ($self, $text) = @_;
+	$self->{postamble} ||= $self->admin->postamble;
+	$self->{postamble} .= $text if defined $text;
+	$self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 363

Modified: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Metadata.pm
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/inc/Module/Install/Metadata.pm	(original)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Metadata.pm	Tue Jul 31 17:23:52 2007
@@ -6,14 +6,14 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.64';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }
 
 my @scalar_keys = qw{
     name module_name abstract author version license
-    distribution_type perl_version tests
+    distribution_type perl_version tests installdirs
 };
 
 my @tuple_keys = qw{
@@ -56,9 +56,23 @@
     };
 }
 
+# configure_requires is currently a null-op
+sub configure_requires { 1 }
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires      { shift->build_requires(@_)  }
+sub install_requires   { shift->build_requires(@_)  }
+
+# Aliases for installdirs options
+sub install_as_core    { $_[0]->installdirs('perl')   }
+sub install_as_cpan    { $_[0]->installdirs('site')   }
+sub install_as_site    { $_[0]->installdirs('site')   }
+sub install_as_vendor  { $_[0]->installdirs('vendor') }
+
 sub sign {
     my $self = shift;
-    return $self->{'values'}{'sign'} if defined wantarray and !@_;
+    return $self->{'values'}{'sign'} if defined wantarray and ! @_;
     $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
     return $self;
 }
@@ -279,9 +293,11 @@
 
     if (
         $self->_slurp($file) =~ m/
-        =head \d \s+
-        (?:licen[cs]e|licensing|copyright|legal)\b
-        (.*?)
+        (
+            =head \d \s+
+            (?:licen[cs]e|licensing|copyright|legal)\b
+            .*?
+        )
         (=head\\d.*|=cut.*|)
         \z
     /ixms
@@ -289,19 +305,24 @@
     {
         my $license_text = $1;
         my @phrases      = (
-            'under the same (?:terms|license) as perl itself' => 'perl',
-            'GNU public license'                              => 'gpl',
-            'GNU lesser public license'                       => 'gpl',
-            'BSD license'                                     => 'bsd',
-            'Artistic license'                                => 'artistic',
-            'GPL'                                             => 'gpl',
-            'LGPL'                                            => 'lgpl',
-            'BSD'                                             => 'bsd',
-            'Artistic'                                        => 'artistic',
+            'under the same (?:terms|license) as perl itself' => 'perl',        1,
+            'GNU public license'                              => 'gpl',         1,
+            'GNU lesser public license'                       => 'gpl',         1,
+            'BSD license'                                     => 'bsd',         1,
+            'Artistic license'                                => 'artistic',    1,
+            'GPL'                                             => 'gpl',         1,
+            'LGPL'                                            => 'lgpl',        1,
+            'BSD'                                             => 'bsd',         1,
+            'Artistic'                                        => 'artistic',    1,
+            'MIT'                                             => 'mit',         1,
+            'proprietary'                                     => 'proprietary', 0,
         );
-        while ( my ( $pattern, $license ) = splice( @phrases, 0, 2 ) ) {
+        while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
             $pattern =~ s{\s+}{\\s+}g;
             if ( $license_text =~ /\b$pattern\b/i ) {
+                if ( $osi and $license_text =~ /All rights reserved/i ) {
+                        warn "LEGAL WARNING: 'All rights reserved' may invalidate Open Source licenses. Consider removing it.";
+		}
                 $self->license($license);
                 return 1;
             }

Added: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Win32.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/Win32.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,65 @@
+#line 1
+package Module::Install::Win32;
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+	$VERSION = '0.67';
+	$ISCORE  = 1;
+	@ISA     = qw{Module::Install::Base};
+}
+
+# determine if the user needs nmake, and download it if needed
+sub check_nmake {
+	my $self = shift;
+	$self->load('can_run');
+	$self->load('get_file');
+	
+	require Config;
+	return unless (
+		$^O eq 'MSWin32'                     and
+		$Config::Config{make}                and
+		$Config::Config{make} =~ /^nmake\b/i and
+		! $self->can_run('nmake')
+	);
+
+	print "The required 'nmake' executable not found, fetching it...\n";
+
+	require File::Basename;
+	my $rv = $self->get_file(
+		url       => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe',
+		ftp_url   => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe',
+		local_dir => File::Basename::dirname($^X),
+		size      => 51928,
+		run       => 'Nmake15.exe /o > nul',
+		check_for => 'Nmake.exe',
+		remove    => 1,
+	);
+
+	if (!$rv) {
+        die <<'END_MESSAGE';
+
+-------------------------------------------------------------------------------
+
+Since you are using Microsoft Windows, you will need the 'nmake' utility
+before installation. It's available at:
+
+  http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe
+      or
+  ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe
+
+Please download the file manually, save it to a directory in %PATH% (e.g.
+C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to
+that directory, and run "Nmake15.exe" from there; that will create the
+'nmake.exe' file needed by this module.
+
+You may then resume the installation process described in README.
+
+-------------------------------------------------------------------------------
+END_MESSAGE
+	}
+}
+
+1;

Modified: jifty/branches/js-refactor/examples/Yada/inc/Module/Install/WriteAll.pm
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/inc/Module/Install/WriteAll.pm	(original)
+++ jifty/branches/js-refactor/examples/Yada/inc/Module/Install/WriteAll.pm	Tue Jul 31 17:23:52 2007
@@ -6,7 +6,7 @@
 
 use vars qw{$VERSION $ISCORE @ISA};
 BEGIN {
-	$VERSION = '0.64';
+	$VERSION = '0.67';
 	$ISCORE  = 1;
 	@ISA     = qw{Module::Install::Base};
 }

Added: jifty/branches/js-refactor/examples/Yada/lib/Yada.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/lib/Yada.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,6 @@
+package Yada;
+
+
+Jifty->web->add_javascript(qw( Asynapse/REST.js trimpath-template.js ) );
+
+1;

Added: jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/Todo.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/Todo.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+
+package Yada::Model::Todo;
+use Jifty::DBI::Schema;
+
+use Yada::Record schema {
+
+    column done => type is 'bool';
+    column description => type is 'text';
+
+};
+
+# Your model-specific methods go here.
+
+1;
+

Added: jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/User.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/lib/Yada/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,11 @@
+package Yada::Model::User;
+#use base 'Example::Todo::Record';
+use Jifty::DBI::Schema;
+
+use Yada::Record schema {};
+
+use Jifty::Plugin::User::Mixin::Model::User;
+use Jifty::Plugin::Authentication::Password::Mixin::Model::User;
+use Jifty::Plugin::OpenID::Mixin::Model::User;
+
+1;

Added: jifty/branches/js-refactor/examples/Yada/lib/Yada/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/lib/Yada/View.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,63 @@
+package Yada::View;
+use Jifty::View::Declare -base;
+use strict;
+
+use Jifty::View::Declare::CRUD;
+for (qw/todo/) {
+    Jifty::View::Declare::CRUD->mount_view($_);
+}
+
+template 'index.html' => page {
+    my $self = shift;
+    title { _('Yada!') };
+
+    render_region('test_region');
+
+    hyperlink(label => 'FAQ',
+	      onclick => [{region => 'test_region',
+			   replace_with => '_faq',
+			  }]);
+
+    form {
+	set(item_path => '/todo/view_brief');
+	render_region(name => 'list', path => '/todo/list');
+    }
+};
+
+template '_faq' => sub :Static {
+    hyperlink(label => 'close', onclick => [{replace_with => '/__jifty/empty'}]);
+
+    div {
+        attr { id => "faq" };
+        h2 { _('Using Yada') }
+        dl {
+            dt { 'Yada Yada Yada!'}
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+template 'signup' => page {
+    title is _('Sign up');
+    render_region(name => 'signup_widget', path => '_signup');
+};
+
+template '_signup' => sub :Action {
+    my $action = Jifty->web->new_action( class => 'Signup', moniker => 'signupnow');
+    my $next = undef;
+#    with ( call => $next ),
+    form {
+	render_param( $action => 'name' , focus => 1);
+	render_param( $action => $_ ) for ( grep {$_ ne 'name'} $action->argument_names );
+
+	form_return( label => _('Sign up'), submit => $action );
+    }
+
+};
+
+
+1;

Added: jifty/branches/js-refactor/examples/Yada/lib/Yada/View/Todo.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/lib/Yada/View/Todo.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,21 @@
+package Yada::View::Todo;
+use strict;
+use base 'Jifty::View::Declare::CRUD';
+use Jifty::View::Declare -base;
+
+template 'view_brief' => sub {
+    my $self = shift;
+    my ( $object_type, $id ) = ( $self->object_type, get('id') );
+    my $record = $self->_get_record($id);
+
+    div { {class is "description" };
+	  outs($record->description);
+	  hyperlink(label => 'details',
+		    onclick => [{region => 'test_region',
+				 replace_with => $self->fragment_for('view'),
+				 args         => { id => $id },
+				}]);
+      };
+};
+
+1;

Added: jifty/branches/js-refactor/examples/Yada/share/web/static/js/Asynapse/REST.js
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/examples/Yada/share/web/static/js/Asynapse/REST.js	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,285 @@
+if ( typeof Asynapse == 'undefined' ) {
+    Asynapse = {}
+}
+
+if ( typeof Asynapse.REST == 'undefined' ) {
+    Asynapse.REST = {}
+}
+
+Asynapse.REST.VERSION = "0.10"
+
+Asynapse.REST.Model = function(model) {
+    this._model = model
+    return this;
+}
+
+Asynapse.REST.Model.prototype = {
+    /* Corresponds Jifty's REST Pluing API */
+    show_item_field: function(column, key, field) {
+        var url = "/=/model/*/*/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+            .replace("*", field)
+
+        return this.eval_ajax_get(url);        
+    },
+    
+    show_item: function(column, key) {
+        var url = "/=/model/*/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_model_items: function(column) {
+        var url = "/=/model/*/*.js"
+            .replace("*", this._model)
+            .replace("*", column)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_model_columns: function() {
+        var url = "/=/model/*.js"
+            .replace("*", this._model)
+
+        return this.eval_ajax_get(url);
+    },
+
+    list_models: function() {
+        var url = "/=/model.js"
+
+        return this.eval_ajax_get(url);
+    },
+
+    create_item: function(item) {
+        var url ="/=/model/*.js"
+            .replace("*", this._model)
+
+        var req = new Ajax.Request(url, {
+            method: 'post',
+            asynchronous: false,
+            postBody: $H(item).toQueryString()
+        });
+        if ( req.responseIsSuccess() ) {
+            eval(req.transport.responseText);
+            return $H($_)
+        } else {
+            return null;
+        }
+    },
+    
+    replace_item: function(item) {
+        var url = "/=/action/update" + this._model + ".js"
+        new Ajax.Request(url, {
+            method: 'post',
+            contentType: 'application/x-www-form-urlencoded',
+            postBody: $H(item).toQueryString()            
+        });
+    },
+
+    delete_item: function(column, key) {
+        var url = "/=/model/*/*/*"
+            .replace("*", this._model)
+            .replace("*", column)
+            .replace("*", key)
+        
+        new Ajax.Request(url, {
+            method: 'DELETE',
+            contentType: 'application/x-www-form-urlencoded'
+        });
+        return null;
+    },
+    
+    /* Internal Helpers */
+    eval_ajax_get: function(url) {
+        eval(this.ajax_get(url));
+        return $_ ? Object.extend({},$_) : null;
+    },
+    ajax_get: function(url) {
+        var req = new Ajax.Request(url, {
+            method: 'GET',
+            asynchronous: false
+        })
+        if ( req.responseIsSuccess() ) {
+            return req.transport.responseText;
+        }
+        else {
+            return "var $_ = null";
+        }
+    }
+}
+
+Asynapse.REST.Model.ActiveRecord = function(model) {
+    Object.extend(this, new Asynapse.REST.Model(model));
+    this._attributes = {}
+    return this;
+}
+
+Asynapse.REST.Model.ActiveRecord.prototype = {
+    new: function() {
+        return this;
+    },
+    
+    find: function(param) {
+        if ( typeof param == 'number' ) {
+            return this.show_item("id", param)
+        }
+    },
+
+    find_by_id: function(id) {
+        return this.show_item("id", id)
+    },
+
+    create: function(attributes) {
+        var r = this.create_item(attributes);
+
+        if (r.success) {
+            return this.show_item("id", Number(r.content.id) )
+        }
+        return null;
+    },
+
+    delete: function(id) {
+        this.delete_item("id", id)
+        return null
+    },
+
+    update: function(id, attributes) {
+        var obj = this.find(id)
+        obj = Object.extend(obj, attributes)
+        return this.replace_item( obj )
+    },
+
+    write_attribute: function(attr, value) {
+    }
+}
+
+/* Great Aliases */
+Asynapse.Model = Asynapse.REST.Model
+Asynapse.ActiveRecord = Asynapse.REST.Model.ActiveRecord
+AsynapseRecord = Asynapse.REST.Model.ActiveRecord
+
+/**
+=head1 NAME
+
+Asynapse.REST - Asynapse REST Client
+
+=head1 VERSION
+
+This document describes Asynapse.REST version 0.10
+
+=head1 SYNOPSIS
+
+    # Define Your own AsynapseRecord Class.
+    Person = new AsynapseRecord('person')
+
+    # Use it
+    var p = Person.find(1)
+
+=head1 DESCRIPTION
+
+Asynapse.REST is the namespace for being a general REST client in
+Asynapse framework. Under this namespace, so far we arrange
+C<Asynapse.REST.Model> for Asynapse Model Classes. It means to
+provide an abstration layer for data existing at given REST server(s).
+
+With many flavours of data abstration layer in the world, we choose
+to emulate ActiveRecord as our first target, which has a plain
+simple object semantics, and very compatible to javascript.
+The implementation of it called C<AsynapseRecord>.
+
+To use it, you must first create your own record classes, like this:
+
+    Person = new AsynapseRecord('person')
+
+After this, Person becomes your Person model class, and then you
+can do:
+
+    var p = Person.find(1)
+
+To find a person by its id. Besides C<find>, C<create>, C<update>,
+and C<delete> are also implemented.
+
+Here's more detail about how to use these interfaces. They are all
+"class methods".
+
+=over
+
+=item find( id )
+
+Retrieve a record from this model with given id.
+
+=item create( attr )
+
+Create a new with attributes specified in attr hash.
+
+=item update( id, attr )
+
+Update the record with primary key id with new sets of
+attributes specified in attr hash.
+
+=item delete( id )
+
+Remove the record with given id.
+
+=back
+
+=head1 CONFIGURATION AND ENVIRONMENT
+
+AsynapseRecord requires no configuration files or environment
+variables.  However, you need a Jifty instance with REST plugin
+(which is given by default now.)
+
+Since JavaScript cannot do XSS, it assumed the your Jifty instance's
+URL resides at C</>, and entry points of REST servces starts from
+C</=/>. It should be made possible to change this assumption in the
+future to match more presets in different frameworks.
+
+=head1 BUGS AND LIMITATIONS
+
+The asynapse project is hosted at L<http://code.google.com/p/asynapse/>.
+You may contact the authors or submit issues using the web interface.
+
+=head1 AUTHOR
+
+Kang-min Liu  C<< <gugod at gugod.org> >>
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Kang-min Liu C<< <gugod at gugod.org> >>. All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
+
+
+=head1 DISCLAIMER OF WARRANTY
+
+BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
+YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR, OR CORRECTION.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
+OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+=cut
+
+*/ 
+

Modified: jifty/branches/js-refactor/examples/Yada/t/00-model-Todo.t
==============================================================================
--- /jifty/branches/js-refactor/examples/Example-Todo/t/00-model-Todo.t	(original)
+++ jifty/branches/js-refactor/examples/Yada/t/00-model-Todo.t	Tue Jul 31 17:23:52 2007
@@ -11,14 +11,14 @@
 use Jifty::Test tests => 11;
 
 # Make sure we can load the model
-use_ok('Example::Todo::Model::Todo');
+use_ok('Yada::Model::Todo');
 
 # Grab a system user
-my $system_user = Example::Todo::CurrentUser->superuser;
+my $system_user = Yada::CurrentUser->superuser;
 ok($system_user, "Found a system user");
 
 # Try testing a create
-my $o = Example::Todo::Model::Todo->new(current_user => $system_user);
+my $o = Yada::Model::Todo->new(current_user => $system_user);
 my ($id) = $o->create();
 ok($id, "Todo create returned success");
 ok($o->id, "New Todo has valid id set");
@@ -30,7 +30,7 @@
 isnt($o->id, $id, "And it is different from the previous one");
 
 # Searches in general
-my $collection =  Example::Todo::Model::TodoCollection->new(current_user => $system_user);
+my $collection =  Yada::Model::TodoCollection->new(current_user => $system_user);
 $collection->unlimit;
 is($collection->count, 2, "Finds two records");
 

Modified: jifty/branches/js-refactor/inc/Module/Install.pm
==============================================================================
--- jifty/branches/js-refactor/inc/Module/Install.pm	(original)
+++ jifty/branches/js-refactor/inc/Module/Install.pm	Tue Jul 31 17:23:52 2007
@@ -86,7 +86,7 @@
             # delegate back to parent dirs
             goto &$code unless $cwd eq $pwd;
         }
-        $$sym =~ /([^:]+)$/ or Carp::confess "Cannot autoload $who - $sym";
+        $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
         unshift @_, ($self, $1);
         goto &{$self->can('call')} unless uc($1) eq $1;
     };

Modified: jifty/branches/js-refactor/lib/Jifty.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty.pm	Tue Jul 31 17:23:52 2007
@@ -167,9 +167,19 @@
     # Set up plugins
     my @plugins;
     my @plugins_to_load = @{Jifty->config->framework('Plugins')};
+    my $app_plugin = Jifty->app_class('Plugin');
     for (my $i = 0; my $plugin = $plugins_to_load[$i]; $i++) {
-        my $class = "Jifty::Plugin::".(keys %{$plugin})[0];
-        my %options = %{ $plugin->{(keys %{$plugin})[0]} };
+        my $plugin_name = (keys %{$plugin})[0];
+        my $class;
+        if ($plugin_name =~ /^(?:Jifty::Plugin|$app_plugin)::/) {
+            # app-specific plugins use fully qualified names, Jifty plugins may
+            $class = $plugin_name; 
+        }
+        # otherwise, assume it's a short name, qualify it
+        else {
+            $class = "Jifty::Plugin::".$plugin_name;
+        }
+        my %options = %{ $plugin->{ $plugin_name } };
         Jifty::Util->require($class);
         Jifty::ClassLoader->new(base => $class)->require;
         my $plugin_obj = $class->new(%options);

Modified: jifty/branches/js-refactor/lib/Jifty/API.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/API.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/API.pm	Tue Jul 31 17:23:52 2007
@@ -85,13 +85,13 @@
     my $self   = shift;
     my $action = shift;
 
-    my $base_path = Jifty->app_class("Action");
+    my $base_path = Jifty->app_class;
 
     return $action
-        if $action =~ /^Jifty::/
-        or $action =~ /^\Q$base_path\E/;
+        if ($action =~ /^Jifty::/
+        or $action =~ /^\Q$base_path\E::/);
 
-    return $base_path . "::" . $action;
+    return $base_path . "::Action::" . $action;
 }
 
 =head2 reset

Modified: jifty/branches/js-refactor/lib/Jifty/Action.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Action.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Action.pm	Tue Jul 31 17:23:52 2007
@@ -36,7 +36,7 @@
 See also L<Jifty::Action::Record> for data-oriented actions, 
 L<Jifty::Result> for how to return values from actions.
 
-See L<Jifty::Param::Schema> for more details on the declarative 
+See L<Jifty::Param::Schema> for more details on the declarative
 syntax.
 
 See L<Jifty::Manual::Actions> for examples of using actions.
@@ -57,7 +57,8 @@
 
 B<Do not call this directly>; always go through C<< Jifty->web->new_action >>! 
 
-This method constructs a new action. Subclasses who need do custom initialization should start with:
+This method constructs a new action. Subclasses who need do custom
+initialization should start with:
 
     my $class = shift;
     my $self = $class->SUPER::new(@_)
@@ -81,29 +82,20 @@
 =item arguments
 
 A hash reference of default values for the
-L<arguments|Jifty::Manual::Glossary/argument> of the action.  Defaults to
-none.
+L<arguments|Jifty::Manual::Glossary/argument> of the action.  Defaults
+to none.
 
 =item sticky_on_failure
 
 A boolean value that determines if the form fields are
-L<sticky|Jifty::Manual::Glossary/sticky> when the action fails.  Defaults to
-true.
+L<sticky|Jifty::Manual::Glossary/sticky> when the action fails.
+Defaults to true.
 
 =item sticky_on_success
 
 A boolean value that determines if the form fields are
-L<sticky|Jifty::Manual::Glossary/sticky> when the action succeeds.  Defaults
-to false.
-
-=begin private
-
-=item request_arguments
-
-A hashref of arguments passed in as part of the
-L<Jifty::Request>. Internal use only.
-
-=end private
+L<sticky|Jifty::Manual::Glossary/sticky> when the action succeeds.
+Defaults to false.
 
 =back
 
@@ -127,13 +119,36 @@
         $self->_get_current_user();
     }
 
-    if ($args{'moniker'}) {
-        $self->moniker($args{'moniker'});
-    } else {
-        $self->moniker($self->_generate_moniker);
+
+    if ( $args{'moniker'} ) {
+        if ( $args{'moniker'} =~ /[\;]/ ) {
+            $args{'moniker'} =~ s/[\;]/_/g;
+            $self->log->warn(
+                "Moniker @{[$args{'moniker'}]} contains invalid characters. It should not contain any ';' characters. "
+                    . "It has been autocorrected, but you should correct your code"
+            );
+        }
+        if ( $args{'moniker'} =~ /^\d/ ) {
+            $args{'moniker'} = "fixup-" . $args{'moniker'};
+            $self->log->warn(
+                "Moniker @{[$args{'moniker'}]} contains invalid characters. It can not begin with a digit. "
+                    . "It has been autocorrected, but you should correct your code"
+            );
+
+        }
     }
+
+
+    $self->moniker($args{'moniker'} || $self->_generate_moniker);
     $self->order($args{'order'});
 
+    my $action_in_request = Jifty->web->request->action( $self->moniker );
+    # Fields explicitly passed to new_action take precedence over those passed
+    # from the request; we read from the request to implement "sticky fields".
+    if ( $action_in_request and $action_in_request->arguments ) {
+        $args{'request_arguments'} = $action_in_request->arguments;
+    }
+
     $self->argument_values( { %{ $args{'request_arguments' } }, %{ $args{'arguments'} } } );
 
     # Keep track of whether arguments came from the request, or were
@@ -153,11 +168,12 @@
 
 =head2 _generate_moniker 
 
-Construct a moniker for a new (or soon-to-be-constructed) action that did not have
-an explicit moniker specified.  The algorithm is simple: We snapshot the call stack,
-prefix it with the action class, and then append it with an per-request autoincrement
-counter in case the same class/stack is encountered twice, which can happen if the
-programmer placed a C<new_action> call inside a loop.
+Construct a moniker for a new (or soon-to-be-constructed) action that
+did not have an explicit moniker specified.  The algorithm is simple:
+We snapshot the call stack, prefix it with the action class, and then
+append it with an per-request autoincrement counter in case the same
+class/stack is encountered twice, which can happen if the programmer
+placed a C<new_action> call inside a loop.
 
 Monikers generated this way are guaranteed to work across requests.
 
@@ -1135,9 +1151,13 @@
 
 =head2 Canonicalization
 
-If you wish to have the data in a field normalized into a particular format (such as changing a date into YYYY-MM-DD format, adding commas to numbers, capitalizing words, or whatever you need) you can do so using a canonicalizer. 
+If you wish to have the data in a field normalized into a particular
+format (such as changing a date into YYYY-MM-DD format, adding commas
+to numbers, capitalizing words, or whatever you need) you can do so
+using a canonicalizer.
 
-This is just a method titled C<canonicalize_FIELD> where C<FIELD> is the name of the field be normalized. Here is an example:
+This is just a method titled C<canonicalize_FIELD> where C<FIELD> is
+the name of the field be normalized. Here is an example:
 
   sub canonicalize_foo {
       my ($self, $value) = @_;
@@ -1148,21 +1168,28 @@
       return $normal_form;
   }
 
-In this case, all values in the "foo" field will be changed into lower case.
+In this case, all values in the "foo" field will be changed into lower
+case.
 
-While doing this you might also want to call the L</canonicalization_note> to inform the client of the modification:
+While doing this you might also want to call the
+L</canonicalization_note> to inform the client of the modification:
 
   my $normal_form = lc($value);
   $self->canonicalization_note( 
       foo => _('Foo values are always in lowercase.'));
 
-If the "foo" field has "ajax canoncalizes" set in the action schema, then this process will be performed automatically as the form is being filled without reloading the page.
+If the "foo" field has "ajax canoncalizes" set in the action schema,
+then this process will be performed automatically as the form is being
+filled without reloading the page.
 
 =head2 Validation
 
-If a value must follow a certain format, you can provide a validation method for fields to make sure that no value enters the database until it is in a valid form.
+If a value must follow a certain format, you can provide a validation
+method for fields to make sure that no value enters the database until
+it is in a valid form.
 
-A validation method is one named C<validate_FIELD> where C<FIELD> is the name of the field being checked. Here is an example:
+A validation method is one named C<validate_FIELD> where C<FIELD> is
+the name of the field being checked. Here is an example:
 
   sub validate_foo {
       my ($self, $value) = @_;
@@ -1182,15 +1209,26 @@
       return 1;
   }
 
-Here the "foo" field should not contain uppercase letters and must not contain the characters '-', '*', '+', or '?'. You can use L</validation_error> and L</validation_warning> to return the results of your validation to the user or simply return 1 to indicate a valid value.
+Here the "foo" field should not contain uppercase letters and must not
+contain the characters '-', '*', '+', or '?'. You can use
+L</validation_error> and L</validation_warning> to return the results
+of your validation to the user or simply return 1 to indicate a valid
+value.
 
-If you just have a list of valid values, you may want to use the C<valid_values> schema parameter to perform this task instead.
+If you just have a list of valid values, you may want to use the
+C<valid_values> schema parameter to perform this task instead.
 
 =head2 Autocompletion
 
-Autocompletion provides a way of suggesting choices to the client based upon partial data entry. This doesn't necessarily force the client to use one of the choices given but gives hints in an application specific way.
-
-To create an autocompletion field, you implement a method named C<autocomplete_FIELD> where C<FIELD> is the field to autocomplete. This is generally done with fields rendered as 'Text'. Here is an example:
+Autocompletion provides a way of suggesting choices to the client
+based upon partial data entry. This doesn't necessarily force the
+client to use one of the choices given but gives hints in an
+application specific way.
+
+To create an autocompletion field, you implement a method named
+C<autocomplete_FIELD> where C<FIELD> is the field to
+autocomplete. This is generally done with fields rendered as
+'Text'. Here is an example:
 
   sub autocomplete_foo {
       my ($self, $value) = @_;
@@ -1209,16 +1247,25 @@
       return map { $_->name } @{ $foos->items_array_ref };
   }
 
-In this example, the "foo" field is autocompleted from names matched from the C<MyApp::Model::Foo> table. The match, in this case, matches any substring found in the database. I could have matched any item that starts with the string, ends with the string, matches other fields than the one returned, etc. It's up to you to decide.
-
-Note also that I have untainted the value coming in to make sure a malicious user doesn't get anyway. You should always perform a check like this when data is coming in from an outside source.
-
-If you need a more complicated solution, you can return the autocompletion values as a list of hash references containing the keys C<value> and (optionally) C<label>:
+In this example, the "foo" field is autocompleted from names matched
+from the C<MyApp::Model::Foo> table. The match, in this case, matches
+any substring found in the database. I could have matched any item
+that starts with the string, ends with the string, matches other
+fields than the one returned, etc. It's up to you to decide.
+
+Note also that I have untainted the value coming in to make sure a
+malicious user doesn't get anyway. You should always perform a check
+like this when data is coming in from an outside source.
+
+If you need a more complicated solution, you can return the
+autocompletion values as a list of hash references containing the keys
+C<value> and (optionally) C<label>:
 
   return map { { value => $_->name, label => $_->label } }
             @{ $foos->items_array_ref };
 
-In this case, the labels will be shown to the client, but the selected value would be returned to your application.
+In this case, the labels will be shown to the client, but the selected
+value would be returned to your application.
 
 =cut
 
@@ -1226,7 +1273,8 @@
 
 =head1 SEE ALSO
 
-L<Jifty>, L<Jifty::API>, L<Jifty::Action::Record>, L<Jifty::Result>, L<Jifty::Param::Schema>, L<Jifty::Manual::Actions>
+L<Jifty>, L<Jifty::API>, L<Jifty::Action::Record>, L<Jifty::Result>,
+L<Jifty::Param::Schema>, L<Jifty::Manual::Actions>
 
 =head1 LICENSE
 

Modified: jifty/branches/js-refactor/lib/Jifty/Action/Record.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Action/Record.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Action/Record.pm	Tue Jul 31 17:23:52 2007
@@ -309,7 +309,7 @@
             }
 
             # If we're hand-coding a render_as, hints or label, let's use it.
-            for (qw(render_as label hints max_length mandatory sort_order)) {
+            for (qw(render_as label hints max_length mandatory sort_order container)) {
 
                 if ( defined (my $val = $column->$_) ) {
                     $info->{$_} = $val;
@@ -352,7 +352,7 @@
 
 sub possible_fields {
     my $self = shift;
-    return map { $_->name } grep { $_->type ne "serial" } $self->record->columns;
+    return map { $_->name } grep { $_->container || $_->type ne "serial" } $self->record->columns;
 }
 
 =head2 take_action

Modified: jifty/branches/js-refactor/lib/Jifty/Action/Record/Search.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Action/Record/Search.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Action/Record/Search.pm	Tue Jul 31 17:23:52 2007
@@ -3,7 +3,7 @@
 
 =head1 NAME
 
-Jifty::Action::Record::Search
+Jifty::Action::Record::Search - Automagic search action
 
 =head1 DESCRIPTION
 

Modified: jifty/branches/js-refactor/lib/Jifty/ClassLoader.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/ClassLoader.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/ClassLoader.pm	Tue Jul 31 17:23:52 2007
@@ -120,69 +120,82 @@
     # The quick check. We only want to handle things for our app
     return undef unless $module =~ /^$base/;
 
+    # If the module is the same as the base, build the application class
     if ( $module =~ /^(?:$base)$/ ) {
         return $self->return_class( "package " . $base . ";\n");
     }
+
+    # Handle most of the standard App::Class ISA Jifty::Class
     elsif ( $module =~ /^(?:$base)::(Record|Collection|Notification|
-                                      Dispatcher|Bootstrap|Upgrade|
+                                      Dispatcher|Bootstrap|Upgrade|CurrentUser|
                                       Handle|Event|Event::Model|Action|
                                       Action::Record::\w+)$/x ) {
         return $self->return_class(
                   "package $module;\n"
                 . "use base qw/Jifty::$1/; sub _autogenerated { 1 };\n"
             );
-    } elsif ( $module =~ /^(?:$base)::View$/ ) {
+    } 
+    
+    # Autogenerate an empty View if none is defined
+    elsif ( $module =~ /^(?:$base)::View$/ ) {
         return $self->return_class(
                   "package $module;\n"
                 . "use Jifty::View::Declare -base; sub _autogenerated { 1 };\n"
             );
-    } elsif ( $module =~ /^(?:$base)::CurrentUser$/ ) {
-        return $self->return_class(
-                  "package $module;\n"
-                . "use base qw/Jifty::CurrentUser/; sub _autogenerated { 1 };\n"
-            );
-    } elsif ( $module =~ /^(?:$base)::Model::(\w+)Collection$/ ) {
+    } 
+    
+    # Autogenerate the Collection class for a Model
+    elsif ( $module =~ /^(?:$base)::Model::([^\.]+)Collection$/ ) {
         return $self->return_class(
                   "package $module;\n"
                 . "use base qw/@{[$base]}::Collection/;\n"
                 . "sub record_class { '@{[$base]}::Model::$1' }\n"
+                . "sub _autogenerated { 1 };\n"
             );
-    } elsif ( $module =~ /^(?:$base)::Event::Model::([^\.]+)$/ ) {
+    } 
+    
+    # Autogenerate the the event class for model changes
+    elsif ( $module =~ /^(?:$base)::Event::Model::([^\.]+)$/ ) {
+        
+        # Determine the model class and load it
         my $modelclass = $base . "::Model::" . $1;
         Jifty::Util->require($modelclass);
 
-        return undef unless eval { $modelclass->table };
+        # Don't generate an event unless it really is a model
+        return undef unless eval { $modelclass->isa('Jifty::Record') };
 
         return $self->return_class(
                   "package $module;\n"
                 . "use base qw/${base}::Event::Model/;\n"
                 . "sub record_class { '$modelclass' };\n"
-                . "sub autogenerated { 1 };\n"
+                . "sub _autogenerated { 1 };\n"
             );
-    } elsif ( $module =~ /^(?:$base)::Action::
+    } 
+    
+    # Autogenerate the record actions for a model
+    elsif ( $module =~ /^(?:$base)::Action::
                         (Create|Update|Delete|Search)([^\.]+)$/x ) {
-        my $modelclass = $base . "::Model::" . $2;
 
+        # Determine the model class and load it
+        my $modelclass = $base . "::Model::" . $2;
         Jifty::Util->_require( module => $modelclass, quiet => 1);
 
-        local $@;
-            eval { $modelclass->table } ;
-        if(!$@) {
+        # Don't generate the action unless it really is a model
+        return undef unless eval { $modelclass->isa('Jifty::Record') };
 
         return $self->return_class(
                   "package $module;\n"
                 . "use base qw/$base\::Action::Record::$1/;\n"
                 . "sub record_class { '$modelclass' };\n"
-                . "sub autogenerated { 1 };\n"
+                . "sub _autogenerated { 1 };\n"
             );
-        }
 
     }
 
-    # This is if, not elsif because we might have $base::Action::Deleteblah 
+    # This is if, not elsif because we might have $base::Action::Deleteblah
     # that matches that last elsif clause but loses on the eval.
     if ( $module =~ /^(?:$base)::(Action|Notification)::(.*)$/x and not grep {$_ eq $base} map {ref} Jifty->plugins ) {
-        my $type = $1; 
+        my $type = $1;
         my $item = $2;
         # If we don't have the action in our own app, let's try the plugins
         # the app has loaded.
@@ -193,7 +206,7 @@
         return $self->return_class(
                   "package $module;\n"
                 . "use base qw/$class/;\n"
-                . "sub autogenerated { 1 };\n"
+                . "sub _autogenerated { 1 };\n"
             );
 
 
@@ -234,15 +247,15 @@
 
 sub require {
     my $self = shift;
-    
+
     my $base = $self->{base};
     # if we don't even have an application class, this trick will not work
-    return unless ($base); 
+    return unless ($base);
     Jifty::Util->require($base);
     Jifty::Util->require($base."::CurrentUser");
 
     my %models;
-    
+
 
     Jifty::Module::Pluggable->import(
         # $base goes last so we pull in the view class AFTER the model classes
@@ -256,7 +269,7 @@
     for my $full ($self->models) {
         $self->_require_model_related_classes($full);
     }
-        
+
 }
 
 sub _require_model_related_classes {
@@ -275,13 +288,13 @@
 
 Jifty supports model classes that aren't files on disk but instead records
 in your database. It's a little bit mind bending, but basically, you can
-build an application entirely out of the database without ever writing a 
-line of code(*). 
+build an application entirely out of the database without ever writing a
+line of code(*).
 
-* As of early 2007, this forward looking statement is mostly a lie. But we're 
+* As of early 2007, this forward looking statement is mostly a lie. But we're
 working on it.
 
-This method finds all database-backed models and instantiates jifty classes for 
+This method finds all database-backed models and instantiates jifty classes for
 them it returns a list of classnames of the models it created.
 
 =cut
@@ -308,10 +321,10 @@
 
 sub require_views {
     my $self = shift;
-    
+
     my $base = $self->{base};
     # if we don't even have an application class, this trick will not work
-    return unless ($base); 
+    return unless ($base);
     Jifty::Util->require($base."::View");
 }
 

Modified: jifty/branches/js-refactor/lib/Jifty/Config.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Config.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Config.pm	Tue Jul 31 17:23:52 2007
@@ -372,6 +372,7 @@
             Web => {
                 DefaultStaticRoot => Jifty::Util->share_root . '/web/static',
                 DefaultTemplateRoot => Jifty::Util->share_root . '/web/templates',
+                SessionCookieName => 'JIFTY_SID_$PORT',
             },
         }
     };

Modified: jifty/branches/js-refactor/lib/Jifty/Continuation.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Continuation.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Continuation.pm	Tue Jul 31 17:23:52 2007
@@ -144,7 +144,7 @@
 =head2 call
 
 Call the continuation; this is generally done during request
-processing, after an actions have been run.
+processing, after actions have been run.
 L<Jifty::Request::Mapper>-controlled values are filled into the stored
 request based on the current request and response.  During the
 process, another continuation is created, with the filled-in results
@@ -156,6 +156,12 @@
 sub call {
     my $self = shift;
 
+    Jifty->log->debug("Redirect to @{[$self->request->path]} via continuation");
+    if (Jifty->web->request->argument('_webservice_redirect')) {
+	# for continuation - perform internal redirect under webservices.
+        Jifty->web->webservices_redirect($self->request->path);
+	return;
+    }
     # If we needed to fix up the path (it contains invalid
     # characters) then warn, because this may cause infinite
     # redirects
@@ -189,6 +195,7 @@
 
     # Redirect to right page if we're not there already
     Jifty->web->_redirect($next->request->path . "?J:RETURN=" . $next->id);
+    return 1;
 }
 
 =head2 return

Modified: jifty/branches/js-refactor/lib/Jifty/CurrentUser.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/CurrentUser.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/CurrentUser.pm	Tue Jul 31 17:23:52 2007
@@ -4,13 +4,14 @@
 package Jifty::CurrentUser;
 
 use base qw/Jifty::Object Class::Accessor::Fast/;
+use Scalar::Util qw();
 
-__PACKAGE__->mk_accessors(qw(is_superuser is_bootstrap_user user_object));
+__PACKAGE__->mk_accessors(qw(is_superuser is_bootstrap_user));
 
 
 =head1 NAME
 
-Jifty::CurrentUser
+Jifty::CurrentUser - Base class and basic implementation of current user object
 
 =head1 DESCRIPTION
 
@@ -98,6 +99,20 @@
 
 =cut
 
+sub user_object {
+    my $self = shift;
+    return $self->{'user_object'} unless @_;
+
+    $self->{'user_object'} = shift;
+    # protect ourself from circular refereces
+    if ( $self->{'user_object'}{'_current_user'} == $self ) {
+        Scalar::Util::weaken( $self->{'user_object'}{'_current_user'} )
+            unless Scalar::Util::isweak( $self->{'user_object'}{'_current_user'} );
+        $self->{'user_object'}{'_resurrect_current_user'} = 1;
+    }
+    return $self->{'user_object'};
+}
+
 =head2 id
 
 Returns C<0> if we don't have a L<user_object>.  When we I<do> have a

Modified: jifty/branches/js-refactor/lib/Jifty/DateTime.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/DateTime.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/DateTime.pm	Tue Jul 31 17:23:52 2007
@@ -60,6 +60,7 @@
 sub current_user_has_timezone {
     my $self = shift;
     $self->_get_current_user();
+    return unless $self->current_user->can('user_object');
     my $user_obj = $self->current_user->user_object or return;
     my $f = $user_obj->can('time_zone') or return;
     return $f->($user_obj);

Modified: jifty/branches/js-refactor/lib/Jifty/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -160,7 +160,7 @@
 All wildcards in the C<$match> string becomes capturing regex patterns.  You
 can also pass in an array reference of matches, or a regex pattern.
 
-The C<$match> string may be qualified with a HTTP method name, such as
+The C<$match> string may be qualified with a HTTP method name or protocol, such as
 
 =over
 
@@ -176,6 +176,10 @@
 
 =item HEAD
 
+=item HTTPS
+
+=item HTTP
+
 =back
 
 =head2 on $match => $rule
@@ -263,6 +267,8 @@
 
     GET POST PUT HEAD DELETE OPTIONS
 
+    HTTPS HTTP
+
     plugin
 
     get next_rule last_rule
@@ -300,6 +306,9 @@
 sub DELETE ($)  { _qualify method => @_ }
 sub OPTIONS ($) { _qualify method => @_ }
 
+sub HTTPS ($)   { _qualify https  => @_ }
+sub HTTP ($)    { _qualify http   => @_ }
+
 sub plugin ($) { return { plugin => @_ } }
 
 our $CURRENT_STAGE;
@@ -929,6 +938,30 @@
     lc( $ENV{REQUEST_METHOD} ) eq lc($method);
 }
 
+=head2 _match_https
+
+Returns true if the current request is under SSL.
+
+=cut
+
+sub _match_https {
+    my $self = shift;
+    $self->log->debug("Matching request against HTTPS");
+    return exists $ENV{HTTPS} ? 1 : 0;
+}
+
+=head2 _match_http
+
+Returns true if the current request is not under SSL.
+
+=cut
+
+sub _match_http {
+    my $self = shift;
+    $self->log->debug("Matching request against HTTP");
+    return exists $ENV{HTTPS} ? 0 : 1;
+}
+
 sub _match_plugin {
     my ( $self, $plugin ) = @_;
     warn "Deferred check shouldn't happen";
@@ -1149,6 +1182,7 @@
     my $self     = shift;
     my $template = shift;
     my $showed   = 0;
+#    local $@;
     eval {
         foreach my $handler ( Jifty->handler->view_handlers ) {
             if ( Jifty->handler->view($handler)->template_exists($template) ) {
@@ -1166,8 +1200,13 @@
     my $err = $@;
 
     # Handle parse errors
+    $self->log->fatal("view class error: $err") if $err;
     if ( $err and not eval { $err->isa('HTML::Mason::Exception::Abort') } ) {
-
+        if ($template eq '/__jifty/error/mason_internal_error') {
+            $self->log->debug("can't render internal_error: $err");
+            $self->_abort;
+            return;
+        }
         # Save the request away, and redirect to an error page
         Jifty->web->response->error($err);
         my $c = Jifty::Continuation->new(
@@ -1176,8 +1215,6 @@
             parent   => Jifty->web->request->continuation,
         );
 
-        warn "$err";
-
         # Redirect with a continuation
         Jifty->web->_redirect( "/__jifty/error/mason_internal_error?J:C=" . $c->id );
     } elsif ($err) {

Modified: jifty/branches/js-refactor/lib/Jifty/Event.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Event.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Event.pm	Tue Jul 31 17:23:52 2007
@@ -9,7 +9,7 @@
 
 =head1 NAME
 
-Jifty::Event
+Jifty::Event - Event objects for publish/subscribe communication
 
 =head1 DESCRIPTION
 

Modified: jifty/branches/js-refactor/lib/Jifty/Event/Model.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Event/Model.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Event/Model.pm	Tue Jul 31 17:23:52 2007
@@ -9,7 +9,7 @@
 
 =head1 NAME
 
-Jifty::Event::Model
+Jifty::Event::Model - Events representing changes to records
 
 =head1 DESCRIPTION
 

Modified: jifty/branches/js-refactor/lib/Jifty/Manual/Continuations.pod
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Manual/Continuations.pod	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Manual/Continuations.pod	Tue Jul 31 17:23:52 2007
@@ -167,7 +167,7 @@
 successful, calls the stored continuation, or, lacking one, redirects
 to C</protected>.
 
-As currently impelented, these redirect-from-dispatcher tangents works
+As currently implemented, these redirect-from-dispatcher tangents works
 exactly like rendered-as-links tangents, in that when they return,
 I<all> rules in the dispatcher are still executed from the start.
 Therefore the C<unless> guard in the C<before '/protected'> rule above

Modified: jifty/branches/js-refactor/lib/Jifty/Manual/Cookbook.pod
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Manual/Cookbook.pod	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Manual/Cookbook.pod	Tue Jul 31 17:23:52 2007
@@ -1,6 +1,6 @@
 =head1 NAME
 
-Jifty::Manual::Cookbook
+Jifty::Manual::Cookbook - Recipes for common tasks in Jifty
 
 =head1 DESCRIPTION
 
@@ -177,7 +177,7 @@
 
     before qr'^/secret' => run {
         unless(Jifty->web->current_user->id) {
-            Jifty->web->tangent('/login');
+            Jifty->web->tangent(url => '/login');
         }
     };
 
@@ -400,6 +400,62 @@
         Jifty->web->out($image);
     };
 
+=head2 Create a many-to-many relationship
+
+You need to create two one-to-many relationships with a linking table as you normally would in pure SQL. First, create your linking table by running:
+
+  bin/jifty model --name LinkTable
+
+Modify the newly created C<MyApp::Model::LinkTable> class to add new columns linking back to either side of the table:
+
+  use MyApp::Record schema {
+      column left_table =>
+          refers_to MyApp::Model::LeftTable;
+      column right_table =>
+          refers_to MyApp::Model::RightTable;
+  };
+
+Then create links to the linking table in C<MyApp::Model::LeftTable>:
+
+  use MyApp::Record schema {
+      # other columns...
+      
+      column right_things =>
+          refers_to MyApp::Model::LinkTableCollection by 'left_table';
+  };
+
+Then create links to the linking table in C<MyApp::Model::RightTable>:
+
+  use MyApp::Record schema {
+      # other columns...
+      
+      column left_things =>
+          refers_to MyApp::Model::LinkTableCollection by 'right_table';
+  };
+
+Now, add your records. To create a relationship between a row the two tables:
+
+  my $left = MyApp::Model::LeftTable->new;
+  $left->load(1);
+
+  my $right = MyApp::Model::RightTable->new;
+  $right->laod(1);
+
+  my $link = MyApp::Model::LinkTable->new;
+  $link->create(
+      left_table  => $left,
+      right_table => $right,
+  );
+
+And to get all the "right things" from the left table, you need to make the extra hop in your loop:
+
+  my $links = $left->right_things;
+  while (my $link = $links->next) {
+      my $right = $link->right_table;
+  
+      # Do stuff with $right
+  }
+
 =for comment
 Document how to do this with Mason
 
@@ -426,4 +482,81 @@
 would be really cool to have access to all data of the action in
 this method, so you are welcome to post a better solution.
 
+=head2 Append a new region based upon the result of the last action using AJAX
+
+In the Administration Interface, you can create new items. You enter the information and then the newly created item is appended to the end of the list immediately without reloading the page. You can use this recipe to do something like this, or to modify the page however you need based upon the result of any server-side action.
+
+Render your action fields as you normally would. The key to the process is in the submit button. Here's how the L<Jifty::View::Declare::CRUD> does this, as of this writing:
+
+  Jifty->web->form->submit(
+      label   => 'Create',
+      onclick => [
+          { submit       => $create },
+          { refresh_self => 1 },
+          {   element =>
+                  Jifty->web->current_region->parent->get_element(
+                  'div.list'),
+              append => $self->fragment_for('view'),
+              args   => {
+                  object_type => $object_type,
+                  id => { result_of => $create, name => 'id' },
+              },
+          },
+      ]
+  );
+
+This could is embedded in a call to C<outs()> for use with L<Template::Declare> templating, but you could just as easily wrap the line above in C<< <% %> >> for use with Mason templates. The keys is each item in the list past to C<onclick>:
+
+  { submit => $create },
+
+This tells Jifty submit the form elements related to the action referenced by C<$create> only. Any other actions in the same form will be ignored.
+
+  { refresh_self => 1 },
+
+This tells the browser to refresh the current region (which will be the one containing the current submit button), so that the form can be reused. You could also modify this behavior to delete the region, if you wrote:
+
+  { delete => Jifty->web->current_region },
+
+The most complicated part is the most important:
+
+  {   element =>
+          Jifty->web->current_region->parent->get_element(
+          'div.list'),
+      append => $self->fragment_for('view'),
+      args   => {
+          object_type => $object_type,
+          id => { result_of => $create, name => 'id' },
+      },
+  },
+
+=over
+
+=item element
+
+The C<element> parameter tells the browser where to insert the new item. By using C<< Jifty->web->current_region->parent->get_element('div.list') >>, the new code will be appended to the first C<div> tag found with a C<list> class within the parent region. This assumes that you have added such an element to the parent region. 
+
+You could look up an arbitrary region using C<< Jifty->web->get_region('fully-qualified-region-name') >> if you don't want to use the parent of the current region.
+
+=item append
+
+The C<append> argument gives the path to the URL of the item to insert. By using C<append>, you are telling Jifty to add your new code to the end of the element given in C<element>. If you want to add it to the beginning, you can use C<prepend> instead.
+
+=item args
+
+Last, but not least, you need to send arguments to the URL related to the action being performed. These can be anything you need for the your template to render the required code. In this example, two arguments are passed: C<object_type> and C<id>. In the case of C<object_type> a known value is passed. In the case of C<id>, the result of the action is passed, which is the key to the whole deal:
+
+  id => { result_of => $create, name => 'id' },
+
+This line tells Jifty that you want to set the "id" paramter sent to the URL given in C<append>, to the "id" set when C<$create> is executed. That is, after running the action, Jifty will contact the URL and effectively perform:
+
+  set id => $create->result->content('id');
+
+It's a lot more complicated than that in actuality, but Jifty takes care of all the nasty details.
+
+=back
+
+If you want to use a custom action other than the built-in create and want to pass something back other than the "id", you just need to set the result into the appropriate key on the C<content> method of the L<Jifty::Result>.
+
+For more details on how you can customize this further, see L<Jifty::Manual::PageRegions>, L<Jifty::Web::Form::Element>, L<Jifty::Result>, L<Jifty::Action>, L<Jifty::Web::PageRegion>, L<Jifty::Web>, and L<Jifty::Request::Mapper>.
+
 =cut

Modified: jifty/branches/js-refactor/lib/Jifty/Manual/Glossary.pod
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Manual/Glossary.pod	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Manual/Glossary.pod	Tue Jul 31 17:23:52 2007
@@ -102,13 +102,15 @@
 
 =item moniker
 
-Every instance of a L<Jifty::Action> has a B<moniker>.  A moniker is
-an arbitrary-length nonempty string containing no semicolons.
-Monikers serve as identifiers for actions, to associate arguments with
-actions and to access specific actions "by name".  Monikers need not
-be globally unique, but they must be unique within a single request.
+Every instance of a L<Jifty::Action> has a B<moniker>.  Monikers
+serve as identifiers for actions, to associate arguments with actions
+and to access specific actions "by name".  Monikers need not be
+globally unique, but they must be unique within a single request.
 Monikers have no semantic meaning. See L<Jifty::Action/monikers>
 
+A moniker is an arbitrary-length nonempty string containing no
+semicolons. It may not begin with a digit.
+
 =item parameter
 
 A B<parameter> is a named parameter to an L</action>.  Jifty generally renders

Modified: jifty/branches/js-refactor/lib/Jifty/Mason/Halo.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Mason/Halo.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Mason/Halo.pm	Tue Jul 31 17:23:52 2007
@@ -7,7 +7,7 @@
 
 =head1 NAME
 
-Jifty::Mason::Halo
+Jifty::Mason::Halo - Class for drawing "halos" around page components
 
 =head1 DESCRIPTION
 

Modified: jifty/branches/js-refactor/lib/Jifty/Model/SessionCollection.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Model/SessionCollection.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Model/SessionCollection.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Collection/;
 
+=head1 NAME
+
+Jifty::Model::SessionCollection - Specialized handling of the session collection
+
+=head2 METHODS
+
 =head2 record_class
 
 This deals with collections of L<Jifty::Model::Session>s.

Modified: jifty/branches/js-refactor/lib/Jifty/Module/Pluggable.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Module/Pluggable.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Module/Pluggable.pm	Tue Jul 31 17:23:52 2007
@@ -5,7 +5,7 @@
 
 =head1 NAME
 
-Jifty::Module::Pluggable
+Jifty::Module::Pluggable - Jifty-specific helper for Module::Pluggable
 
 
 =head1 DESCRIPTION
@@ -21,15 +21,15 @@
     To:       jesse at bestpractical.com
 
 On Mon, Oct 23, 2006 at 09:11:22PM -0700, Jesse Vincent said:
-does this thread make any sense to you? It looks like  
+does this thread make any sense to you? It looks like
 Module::Pluggable is interacting poorly with UNIVERSAL::require?
 
-Module::Pluggable used to to use UNIVERSAL::require but I switched 
+Module::Pluggable used to to use UNIVERSAL::require but I switched
 because I was trying to get rid of dependencies.
 
-I farmed the requiring stuff off to it's own _require method in order to 
-make it easy to subclass so that people could ovveride how the require 
-was done. 
+I farmed the requiring stuff off to it's own _require method in order to
+make it easy to subclass so that people could ovveride how the require
+was done.
 
 
 =head3  But Simon is lying...
@@ -51,17 +51,19 @@
     # Module::Pluggable::Object::_require expects a true value (the error message) on failure
     # On success, it expects you to return undef.
 
+    return if $module =~/\.#/;
+
     local $UNIVERSAL::require::ERROR;
 
     no warnings; # This is lexical and turns off exactly one warning below -- "Can't locate package in @ISA".
                  # (for some reason, "no warnings 'syntax'" does not work as advertised here.)
                  # Note that it does _not_ turn off warnings triggered in the $module itself.
     if ((not $module->require()) &&  ( $UNIVERSAL::require::ERROR !~ /^Can't locate/)) {
-        die $UNIVERSAL::require::ERROR;    
-    } 
+        die $UNIVERSAL::require::ERROR;
+    }
          # We'd prefer to use Jifty::Util->require() here, but it spews crazy warnings
 
     return $UNIVERSAL::require::ERROR;
-} 
+}
 
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Notification.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Notification.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Notification.pm	Tue Jul 31 17:23:52 2007
@@ -6,16 +6,22 @@
 use base qw/Jifty::Object Class::Accessor::Fast/;
 use Email::Send            ();
 use Email::MIME::Creator;
+use Email::MIME::CreateHTML;
+use Email::MIME::Modifier;
 
 __PACKAGE__->mk_accessors(
-    qw/body preface footer subject from _recipients _to_list to/);
+    qw/body html_body preface footer subject from _recipients _to_list to/);
+
+=head1 NAME
+
+Jifty::Notification - Send emails from Jifty
 
 =head1 USAGE
 
 It is recommended that you subclass L<Jifty::Notification> and
-override C<body>, C<subject>, C<recipients>, and C<from> for each
-message.  (You may want a base class to provide C<from>, C<preface>
-and C<footer> for example.)  This lets you keep all of your
+override C<body>, C<html-body>, C<subject>, C<recipients>, and C<from>
+for each message.  (You may want a base class to provide C<from>,
+C<preface> and C<footer> for example.)  This lets you keep all of your
 notifications in the same place.
 
 However, if you really want to make a notification type in code
@@ -74,8 +80,13 @@
 if mail was actually sent.  Note errors are not the only cause of mail
 not being sent -- for example, the recipients list could be empty.
 
-Be aware that if you haven't set C<recipients>, this will fail silently
-and return without doing anything useful.
+If you wish to send HTML mail, set C<html_body>.  If this is not set
+(for backwards compatibility) a plain-text email is sent.  If
+C<html_body> and C<body> are both set, a multipart mail is sent.  See
+L<Email::MIME::CreateHTML> for how this is done.
+
+Be aware that if you haven't set C<recipients>, this will fail
+silently and return without doing anything useful.
 
 =cut
 
@@ -86,18 +97,40 @@
         map { ( $_->can('email') ? $_->email : $_ ) } grep {$_} @recipients );
     $self->log->debug("Sending a ".ref($self)." to $to"); 
     return unless ($to);
-
+    my $message = "";
     my $appname = Jifty->config->framework('ApplicationName');
 
-    my $message = Email::MIME->create(
-        header => [
-            From    => ($self->from    || _('%1 <%2>' , $appname, Jifty->config->framework('AdminEmail'))) ,
-            To      => $to,
-            Subject => Encode::encode('MIME-Header', $self->subject || _("A notification from %1!",$appname )),
-        ],
-        attributes => { charset => 'UTF-8' },
-        parts => $self->parts
-    );
+    my %attrs = ( charset => 'UTF-8' );
+
+    if ($self->html_body) {
+      $message = Email::MIME->create_html(
+					     header => [
+							From    => ($self->from    || _('%1 <%2>' , $appname, Jifty->config->framework('AdminEmail'))) ,
+							To      => $to,
+							Subject => Encode::encode('MIME-Header', $self->subject || _("A notification from %1!",$appname )),
+						       ],
+					     attributes => \%attrs,
+                         text_body_attributes => \%attrs,
+                         body_attributes => \%attrs,
+					     text_body => Encode::encode_utf8($self->full_body),
+					     body => Encode::encode_utf8($self->full_html),
+                         embed => 0,
+                         inline_css => 0
+					    );
+        # Since the containing messsage will still be us-ascii otherwise
+        $message->charset_set( $attrs{'charset'} );
+    } else {
+            $message = Email::MIME->create(
+					     header => [
+							From    => ($self->from    || _('%1 <%2>' , $appname, Jifty->config->framework('AdminEmail'))) ,
+							To      => $to,
+							Subject => Encode::encode('MIME-Header', $self->subject || _("A notification from %1!",$appname )),
+						       ],
+					     attributes => \%attrs,
+					     
+					     parts => $self->parts
+					    );
+	  }
     $message->encoding_set('8bit')
         if (scalar $message->parts == 1);
     $self->set_headers($message);
@@ -259,6 +292,17 @@
   return join( "\n", grep { defined } $self->preface, $self->body, $self->footer );
 }
 
+=head2 full_html
+
+Same as full_body, but with HTML.
+
+=cut
+
+sub full_html {
+  my $self = shift;
+  return join( "\n", grep { defined } $self->preface, $self->html_body, $self->footer );
+}
+
 =head2 parts
 
 The parts of the message.  You want to override this if you want to

Modified: jifty/branches/js-refactor/lib/Jifty/Object.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Object.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Object.pm	Tue Jul 31 17:23:52 2007
@@ -5,7 +5,11 @@
 
 use Log::Log4perl ();
 
-=head1 Jifty::Object
+=head1 NAME
+
+Jifty::Object - Base class for most of Jifty's objects
+
+=head1 DESCRIPTION
 
 C<Jifty::Object> is the superclass of most of Jifty's objects.  It is
 used to provide convenient accessors to important global objects like
@@ -16,7 +20,7 @@
 thus, we will not define C<new> or C<_init> in C<Jifty::Object>.  We
 do assume, however, that C<$self> is a blessed hash reference.
 
-=cut
+=head1 METHODS
 
 =head2 current_user [USER]
 

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin.pm	Tue Jul 31 17:23:52 2007
@@ -183,4 +183,56 @@
     return ();
 }
 
+=head2 version
+
+Returns the database version of the plugin. Needs to be bumped any time the database schema needs to be updated. Plugins that do not directly define any models don't need to worry about this.
+
+=cut
+
+sub version {
+    return '0.0.1';
+}
+
+=head2 bootstrapper
+
+Returns the name of the class that can be used to bootstrap the database models. This normally returns the plugin's class name with C<::Bootstrap> added to the end. Plugin bootstrappers can be built in exactly the same way as application bootstraps.
+
+See L<Jifty::Bootstrap>.
+
+=cut
+
+sub bootstrapper {
+    my $self = shift;
+    my $class = ref $self;
+    return $class . '::Bootstrap';
+}
+
+=head2 upgrade_class
+
+Returns the name of the class that can be used to upgrade the database models and schema (such as adding new data, fixing default values, and renaming columns). This normally returns the plugin's class name with C<::Upgrade> added to the end. Plugin upgraders can be built in exactly the same was as application upgrade classes.
+
+See L<Jifty::Upgrade>.
+
+=cut
+
+sub upgrade_class {
+    my $self = shift;
+    my $class = ref $self;
+    return $class . '::Upgrade';
+}
+
+=head2 table_prefix
+
+Returns a prefix that will be placed in the front of all table names for plugin models. Be default, the plugin name is converted to an identifier based upon the class name.
+
+=cut
+
+sub table_prefix {
+    my $self = shift;
+    my $class = ref $self;
+    $class =~ s/\W+/_/g;
+    $class .= '_';
+    return lc $class;
+}
+
 1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,33 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::ActorMetadata;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::ActorMetadata
+
+=head1 DESCRIPTION
+ 
+This plugin adds a model mixin which adds C<created_by>, C<created_on> and C<updated_on> columns to a model class.
+
+=head1 EXAMPLE 
+
+use strict;
+ use warnings;
+ 
+ package MeetMeow::Model::Cat;
+ use Jifty::DBI::Schema;
+ 
+ use MeetMeow::Record schema {
+ 
+         ...
+ 
+ };
+ use Jifty::Plugin::ActorMetadata::Mixin::Model::ActorMetadata; # created_by, created_on, updated_on
+ 
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/ActorMetadata/Mixin/Model/ActorMetadata.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,128 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::ActorMetadata::Mixin::Model::ActorMetadata;
+use Jifty::DBI::Schema;
+use base 'Jifty::DBI::Record::Plugin';
+
+our @EXPORT = qw(current_user_can);
+
+=head1 NAME
+
+Jifty::Plugin::ActorMetadata::Mixin::Model::ActorMetadata - ActorMetadata mixin
+
+=head1 SYNOPSIS
+
+  package MyApp::Model::CoffeeShop;
+  use Jifty::DBI::Schema;
+  use MyApp::Record schema {
+      # custom column defrinitions
+  };
+
+  use Jifty::Plugin::ActorMetadata::Mixin::Model::ActorMetadata; # created_by, created_on, updated_on
+
+=head1 DESCRIPTION
+
+=head1 SCHEMA
+
+This mixin adds the following columns to the model schema:
+
+=head2 created_by
+
+=head2 created_on
+
+=head2 updated_on
+
+=cut
+
+# XXX: move this to somewhere
+my $app_user;
+BEGIN {
+
+# Do not call ->app_class within the schmea {} block.
+$app_user = Jifty->app_class('Model', 'User');
+Jifty::DBI::Schema->register_types(
+    Date =>
+        sub { type is 'date', input_filters are qw/Jifty::DBI::Filter::Date/ },
+    Time =>
+        sub { type is 'time', input_filters are qw/Jifty::DBI::Filter::Time/ },
+    DateTime => sub {
+        type is 'datetime',
+        input_filters are qw/Jifty::DBI::Filter::DateTime/ },
+    TimeStamp => sub {
+        type is 'timestamp',
+        filters are qw( Jifty::Filter::DateTime Jifty::DBI::Filter::DateTime),
+    }
+);
+}
+
+use Jifty::Record schema {
+
+column created_by =>
+  render_as 'hidden',
+  refers_to $app_user;
+
+column created_on => is TimeStamp,
+  render_as 'hidden';
+column updated_on => is TimeStamp,
+  render_as 'hidden';
+
+};
+
+=head1 METHODS
+
+# XXX: podcoverage should count parent classes.  these pods are useless
+
+=head2 register_triggers
+
+Adds the triggers to the model this mixin is added to.
+
+=cut
+
+sub register_triggers {
+    my $self = shift;
+    $self->add_trigger(name => 'before_create', callback => \&before_create);
+}
+
+=head2 before_create
+
+Sets C<created_by>, C<created_on>, C<updated_on> based on the current user and time.
+
+=cut
+
+sub before_create {
+    my $self = shift;
+    my $args = shift;
+
+    $args->{'created_by'} = $self->current_user->id;
+    $args->{'created_on'} = $args->{'updated_on'} = Jifty::DateTime->now;
+
+    return 1;
+}
+
+=head2 current_user_can
+
+Rejects creation unless there's a current_user. 
+Rejects update or deletion unless the current_user is the creator.  (Jesse says: this feels like wrong logic for this mixin)
+
+=cut
+
+# XXX: Move this to an abortable trigger
+
+sub current_user_can {
+    my $self = shift;
+    my $action = shift;
+    my %args = (@_);
+
+    if ($action eq 'create') {
+        return undef unless ($self->current_user and $self->current_user->id);
+    }
+
+    if ($action eq 'update' or $action eq 'delete') {
+        return undef unless ($self->current_user and $self->current_user->id eq $self->created_by->id);
+    }
+
+    return 1;
+}
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,112 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Authentication::Facebook;
+use base qw/Jifty::Plugin/;
+
+use WWW::Facebook::API;
+
+=head1 NAME
+
+Jifty::Plugin::Authentication::Facebook
+
+=head2 DESCRIPTION
+
+Provides standalone Facebook authentication for your Jifty application.
+It adds the columns C<facebook_name>, C<facebook_uid>, C<facebook_session>,
+and C<facebook_session_expires> to your User model.
+
+=head1 SYNOPSIS
+
+In your jifty config.yml under the C<framework> section:
+
+    Plugins:
+        - Authentication::Facebook:
+            api_key: xxx
+            secret: xxx
+
+You may set any options which the C<new> method of L<WWW::Facebook::API>
+understands.
+
+In your User model, you'll need to include the line
+
+    use Jifty::Plugin::Authentication::Facebook::Mixin::Model::User;
+
+B<after> your schema definition (which may be empty).  You may also wish
+to include
+
+    sub _brief_description { 'facebook_name' }
+
+To use the user's Facebook name as their description.
+
+See L<Jifty::Plugin::Authentication::Facebook::View> for the provided templates
+and L<Jifty::Plugin::Authentication::Facebook::Dispatcher> for the URLs handled.
+
+=cut
+
+our %CONFIG = ( );
+
+=head2 init
+
+=cut
+
+sub init {
+    my $self = shift;
+    %CONFIG  = @_;
+}
+
+=head2 api
+
+Generates a new L<WWW::Facebook::API> for the current user
+
+=cut
+
+sub api {
+    my $self = shift;
+    my $api  = WWW::Facebook::API->new( %CONFIG );
+    
+    if ( Jifty->web->current_user->id ) {
+        my $user = Jifty->web->current_user->user_object;
+        $api->session(
+            uid     => $user->facebook_uid,
+            key     => $user->facebook_session,
+            expires => $user->facebook_session_expires
+        ) if $user->facebook_uid;
+    }
+
+    return $api;
+}
+
+=head2 get_login_url
+
+Gets the login URL, preserving continuations
+
+=cut
+
+sub get_login_url {
+    my $self = shift;
+    my $next = '/facebook/callback';
+ 
+    if ( Jifty->web->request->continuation ) {
+        $next .= '?J:C=' . Jifty->web->request->continuation->id;
+    }
+    return $self->api->get_login_url( next => $next );
+}
+
+=head2 get_link_url
+
+Gets the login URL used for linking, preserving continuations
+
+=cut
+
+sub get_link_url {
+    my $self = shift;
+    my $next = '/facebook/callback_link';
+ 
+    if ( Jifty->web->request->continuation ) {
+        $next .= '?J:C=' . Jifty->web->request->continuation->id;
+    }
+    return $self->api->get_login_url( next => $next );
+}
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LinkFacebookUser.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LinkFacebookUser.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,75 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+Jifty::Plugin::Authentication::Facebook::Action::LinkFacebookUser;
+
+=cut
+
+package Jifty::Plugin::Authentication::Facebook::Action::LinkFacebookUser;
+use base qw/Jifty::Action/;
+
+=head1 ARGUMENTS
+
+=head2 auth_token
+
+=cut
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+    param auth_token =>
+        type is 'text',
+        is mandatory;
+};
+
+=head1 METHODS
+
+=head2 take_action
+
+Get the session key using the Facebook API.  Link to current user.
+
+=cut
+
+sub take_action {
+    my $self     = shift;
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+    my $api      = $plugin->api;
+
+    if ( not Jifty->web->current_user->id ) {
+        $self->result->error(_("You must be logged in to link your user to your Facebook account."));
+        return;
+    }
+
+    # Get the session
+    $api->auth->get_session( $self->argument_value('auth_token') );
+
+    my $user = Jifty->web->current_user->user_object;
+
+    my $name = $api->users->get_info(
+        uids    => $api->session_uid,
+        fields  => 'name'
+    )->[0]{'name'};
+
+    # Set data
+    $user->__set( column => 'facebook_name', value => $name );
+    $user->__set( column => 'facebook_uid',  value => $api->session_uid );
+    $user->__set( column => 'facebook_session', value => $api->session_key );
+    $user->__set( column => 'facebook_session_expires', value => $api->session_expires );
+
+    # Success!
+    $self->report_success;
+
+    return 1;
+}
+
+=head2 report_success
+
+=cut
+
+sub report_success {
+    my $self = shift;
+    $self->result->message(_("Your account has been successfully linked to your Facebook user %1!", Jifty->web->current_user->user_object->facebook_name ));
+}
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LoginFacebookUser.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Action/LoginFacebookUser.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,106 @@
+use warnings;
+use strict;
+
+=head1 NAME
+
+Jifty::Plugin::Authentication::Facebook::Action::LoginFacebookUser;
+
+=cut
+
+package Jifty::Plugin::Authentication::Facebook::Action::LoginFacebookUser;
+use base qw/Jifty::Action/;
+
+=head1 ARGUMENTS
+
+=head2 auth_token
+
+=cut
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+    param auth_token =>
+        type is 'text',
+        is mandatory;
+};
+
+=head1 METHODS
+
+=head2 take_action
+
+Get the session key using the Facebook API.  Check for existing user.
+If none, autocreate.  Login user.
+
+=cut
+
+sub take_action {
+    my $self    = shift;
+    my ($plugin)  = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+    my $api       = $plugin->api;
+
+    # Get the session
+    $api->auth->get_session( $self->argument_value('auth_token') );
+
+    # Load up the user
+    my $current_user = Jifty->app_class('CurrentUser');
+    my $user = $current_user->new( facebook_uid => $api->session_uid );
+
+    # Autocreate the user if necessary
+    if ( not $user->id ) {
+        my $action = Jifty->web->new_action(
+            class           => 'CreateUser',
+            current_user    => $current_user->superuser,
+            arguments       => {
+                facebook_uid     => $api->session_uid,
+                facebook_session => $api->session_key,
+                facebook_session_expires => $api->session_expires
+            }
+        );
+        $action->run;
+
+        if ( not $action->result->success ) {
+            # Should this be less "friendly"?
+            $self->result->error(_("Sorry, something weird happened (we couldn't create a user for you).  Try again later."));
+            return;
+        }
+
+        $user = $current_user->new( facebook_uid => $api->session_uid );
+    }
+
+    my $name = $api->users->get_info(
+        uids    => $api->session_uid,
+        fields  => 'name'
+    )->[0]{'name'};
+
+    my $u = $user->user_object;
+
+    # Always check name
+    $u->__set( column => 'facebook_name', value => $name )
+        if not defined $u->facebook_name or $u->facebook_name ne $name;
+
+    # Update, just in case
+    if ( $u->__value('facebook_session') ne $api->session_key ) {
+        $u->__set( column => 'facebook_session', value => $api->session_key );
+        $u->__set( column => 'facebook_session_expires', value => $api->session_expires );
+    }
+
+    # Login!
+    Jifty->web->current_user( $user );
+    Jifty->web->session->expires( (not $api->session_expires) ? '+1y' : undef );
+    Jifty->web->session->set_cookie;
+
+    # Success!
+    $self->report_success;
+
+    return 1;
+}
+
+=head2 report_success
+
+=cut
+
+sub report_success {
+    my $self = shift;
+    $self->result->message(_("Hi %1!", Jifty->web->current_user->user_object->facebook_name ));
+}
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,70 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Authentication::Facebook::Dispatcher;
+use Jifty::Dispatcher -base;
+
+=head1 NAME
+
+Jifty::Plugin::Authentication::Facebook::Dispatcher
+
+=head1 DESCRIPTION
+
+All the dispatcher rules jifty needs to support L<Jifty::Authentication::Facebook>
+
+=head1 RULES
+
+=head2 before '/facebook/callback'
+
+Handles the login callback.  You probably don't need to worry about this.
+
+=cut
+
+before qr'^/facebook/callback(_link)?' => run {
+    my $link    = $1 ? 1 : 0;
+    my $action  = $link ? 'LinkFacebookUser' : 'LoginFacebookUser';
+    my $moniker = $link ? 'facebooklink'     : 'facebooklogin';
+
+    Jifty->web->request->add_action(
+        moniker   => $moniker,
+        class     => $action,
+        arguments => {
+            auth_token => get('auth_token'),
+        }
+    );
+    if ( Jifty->web->request->continuation ) {
+        Jifty->web->request->continuation->call;
+    }
+    else {
+        redirect '/';
+    }
+};
+
+=head2 before '/facebook/force_login'
+
+Redirects user to the Facebook login page.  Useful if you want to skip
+prompting the user to login on your app.
+
+=cut
+
+before '/facebook/force_login' => run {
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+    Jifty->web->_redirect( $plugin->get_login_url );
+};
+
+=head2 before '/facebook/logout'
+
+Directing a user here will log him out of the app and Facebook.
+
+=cut
+
+before '/facebook/logout' => run {
+    if ( Jifty->web->current_user->id ) {
+        Jifty->web->current_user( undef );
+        my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+        $plugin->api->auth->logout;
+    };
+    redirect '/';
+};
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Mixin/Model/User.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/Mixin/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Authentication::Facebook::Mixin::Model::User;
+use Jifty::DBI::Schema;
+use base 'Jifty::DBI::Record::Plugin';
+
+use Jifty::Plugin::Authentication::Facebook::Record schema {
+
+    column facebook_name => 
+        type is 'text';
+
+    column facebook_uid => 
+        type is 'int',
+        is immutable,
+        is distinct;
+
+    column facebook_session =>
+        type is 'text';
+
+    column facebook_session_expires =>
+        type is 'int';
+
+};
+
+=head2 set_facebook_uid INT
+
+Sets the user's Facebook ID
+
+=cut
+
+sub set_facebook_uid {
+    my $self = shift;
+    my $id   = shift;
+    $self->_set( column => 'facebook_uid', value => $id );
+}
+
+1;
+

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Facebook/View.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,49 @@
+use warnings;
+use strict;
+package Jifty::Plugin::Authentication::Facebook::View;
+
+use Jifty::View::Declare -base;
+
+=head1 NAME
+
+Jifty::Plugin::Authentication::Facebook::View
+
+=head1 DESCRIPTION
+
+Provides the Facebook login fragment for L<Jifty::Plugin::Authentication::Facebook>
+
+=head2 /facebook/login
+
+This fragment shows the standard Facebook button used for web login.
+
+=cut
+
+template '/facebook/login' => sub {
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+    div {{ id is 'facebook_login' };
+        span { _("Login to Facebook now to get started!") };
+        a {{ href is $plugin->get_login_url };
+            img {{ src is 'http://static.ak.facebook.com/images/devsite/facebook_login.gif', border is '0' }};
+        };
+    };
+};
+
+=head2 /facebook/link
+
+This fragment shows the standard Facebook login button, prompting the user to
+link his account.
+
+=cut
+
+template '/facebook/link' => sub {
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Authentication::Facebook');
+    div {{ id is 'facebook_link' };
+        span { _("Login to Facebook now to link it with your current account!") };
+        a {{ href is $plugin->get_link_url };
+            img {{ src is 'http://static.ak.facebook.com/images/devsite/facebook_login.gif', border is '0' }};
+        };
+    };
+};
+
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password.pm	Tue Jul 31 17:23:52 2007
@@ -4,23 +4,17 @@
 package Jifty::Plugin::Authentication::Password;
 use base qw/Jifty::Plugin/;
 
-# Your plugin goes here.  If takes any configuration or arguments, you
-# probably want to override L<Jifty::Plugin/init>.
-
 =head1 NAME
 
-Jifty::Plugin::Authentication::Password
+Jifty::Plugin::Authentication::Password - password authentication plugin
 
 =head1 DESCRIPTION
 
-When finished, this plugin will provide password authentication for 
-your Jifty application. (It adds a "password" column to your "User" model class).
-
-Right now, it's useless and should be ignored.
+B<CAUTION:> This plugin is experimental.
 
+This may be combined with the L<Jifty::Plugin::User> and L<Jifty::Plugin::LetMe> plugins to provide user accounts and form-based password authentication to your application.
 
-=cut
-
+=head2 METHODS
 
 =head2 prereq_plugins
 
@@ -33,4 +27,15 @@
     return ('User', 'LetMe');
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Manual::AccessControl>, L<Jifty::Plugin::User>, L<Jifty::Plugin::LetMe>, L<Jifty::Plugin::Authentication::Password::Mixin::Model::User>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -4,25 +4,22 @@
 package Jifty::Plugin::Authentication::Password::Dispatcher;
 use Jifty::Dispatcher -base;
 
-# Put any plugin-specific dispatcher rules here.
-
-
 =head1 NAME
 
-Jifty::Plugin::Authentication::Password::Dispatcher
+Jifty::Plugin::Authentication::Password::Dispatcher - password plugin dispatcher
 
 =head1 DESCRIPTION
 
 All the dispatcher rules jifty needs to support L<Jifty::Authentication::Password/>
 
-=cut
-
-
 =head1 RULES
 
-
 =head2 before logout
 
+Logout and return home.
+
+See L<Jifty::Plugin::Authentication::Password::Action::Logout>.
+
 =cut
 
 before 'logout' => run {
@@ -35,6 +32,8 @@
 
 =head2 before *
 
+Setup the navigation menu for login or logout.
+
 =cut
 
 before '*' =>  run {
@@ -47,7 +46,11 @@
 
 };
 
-=head2 on qr/^(?:passwordreminder|signup)$/ 
+=head2 on qr/^(?:passwordreminder|signup|lost_password)$/ 
+
+Redirect to home if logged.
+
+Request a password reminder or signup for an account otherwise.
 
 =cut
 
@@ -58,6 +61,10 @@
 
 =head2 on login
 
+Redirect to home if logged.
+
+Show the login form otherwise.
+
 =cut
 
 before qr|^/(?:login)$| => run {
@@ -65,12 +72,22 @@
     set 'next' => Jifty->web->request->continuation || Jifty::Continuation->new( request => Jifty::Request->new( path => "/" ) );
 };
 
+=head2 before reset_lost_password
+
+Request a password reset.
+
+=cut
+
 before qr|(?:reset_lost_password)| => run {
     set 'next' => Jifty->web->request->continuation || Jifty::Continuation->new( request => Jifty::Request->new( path => "/" ) );
 };
 # Send a password reminder for a lost password
 
-=head2 on passwordreminder
+=head2 before passwordreminder
+
+Request a new password reminder to be sent by email.
+
+See L<Jifty::Plugin::Authentication::Password::Action::SendPasswordReminder>.
 
 =cut
 
@@ -79,9 +96,11 @@
 };
 
 
-=head2 on signup
+=head2 before signup
 
-# Sign up for an account
+Sign up for an account.
+
+See L<Jifty::Plugin::Authentication::Password::Action::Signup>.
 
 =cut
 
@@ -90,9 +109,11 @@
 
 };
 
-=head2 on login
+=head2 before login
+
+Login to your account.
 
-Login
+See L<Jifty::Plugin::Authentication::Password::Action::Login>.
 
 =cut
 
@@ -102,6 +123,8 @@
 
 =head2 not_logged_in_nav
 
+Adds the login and signup links to the navigation menu.
+
 =cut
 
 sub not_logged_in_nav {
@@ -119,6 +142,8 @@
 
 =head2 logged_in_nav
 
+Adds the logout link to the navigation menu.
+
 =cut
 
 sub logged_in_nav {
@@ -130,5 +155,15 @@
 
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Authentication::Password>, L<Jifty::Plugin::Authentication::Password::View>
+
+=head1 COPYRIGHT
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
 
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/Mixin/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -9,6 +9,40 @@
 
 our @EXPORT = qw(password_is hashed_password_is regenerate_auth_token has_alternative_auth);
 
+=head1 NAME
+
+Jifty::Plugin::Authentication::Password::Mixin::Model::User - password plugin user mixin model
+
+=head1 SYNOPSIS
+
+  package MyApp::Model::User;
+  use Jifty::DBI::Schema;
+  use MyApp::Record schema {
+      # custom column defrinitions
+  };
+
+  use Jifty::Plugin::User::Mixin::Model::User; # name, email, email_confirmed
+  use Jifty::Plugin::Authentication::Password::Mixin::Model::User;
+  # ^^ password, auth_token
+
+=head1 DESCRIPTION
+
+This mixin model is added to the application's account model for use with the password authentication plugin. This mixin should be used in combination with L<Jifty::Plugin::User::Mixin::Model::User>.
+
+=head1 SCHEMA
+
+This mixin adds the following columns to the model schema:
+
+=head2 auth_token
+
+This is a unique identifier used when confirming a user's email account and recovering a lost password.
+
+=head2 password
+
+This is the user's password. It will be stored in the database after being processed through L<Digest::MD5>, so the password cannot be directly recovered from the database.
+
+=cut
+
 use Jifty::Plugin::Authentication::Password::Record schema {
 
 
@@ -31,6 +65,14 @@
 
 };
 
+=head1 METHODS
+
+=head2 register_triggers
+
+Adds the triggers to the model this mixin is added to.
+
+=cut
+
 sub register_triggers {
     my $self = shift;
     $self->add_trigger(name => 'after_create', callback => \&after_create);
@@ -60,7 +102,9 @@
 =head2 hashed_password_is HASH TOKEN
 
 Check if the given I<HASH> is the result of hashing our (already
-salted and hashed) password with I<TOKEN>
+salted and hashed) password with I<TOKEN>.
+
+This can be used in cases where the pre-hashed password is sent during login as an additional security precaution (such as could be done via Javascript).
 
 =cut
 
@@ -93,6 +137,14 @@
     return 1;
 }
 
+=head2 after_create
+
+This trigger is added to the account model. It automatically sends a notification email to the user for password confirmation.
+
+See L<Jifty::Plugin::Authentication::Password::Notification::ConfirmEmail>.
+
+=cut
+
 
 sub after_create {
     my $self = shift;
@@ -143,7 +195,16 @@
     $self->__set(column => 'auth_token', value => $auth_token);
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Authentication::Password>, L<Jifty::Plugin::User::Mixin::Model>
 
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
 
 1;
 

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Authentication/Password/View.pm	Tue Jul 31 17:23:52 2007
@@ -4,30 +4,42 @@
 
 =head1 NAME
 
-Jifty::Plugin::Authentication::Password::Login::View
+Jifty::Plugin::Authentication::Password::View - views for password plugin
 
 =head1 DESCRIPTION
 
-This code is only useful on the new Jifty "Declarative tempaltes" branch. It shouldn't get in the way 
-if you're running a traditional (0.610 or before) Jifty.
+This code is only useful on the new Jifty "Declarative templates" branch. It shouldn't get in the way if you're running a traditional (0.610 or before) Jifty.
+
+=begin comment
+
+Is the above really true or need to said anymore? -- Sterling
+
+=end comment
+
+This provides the templates for the pages and forms used by the password authentication plugin.
 
 =cut
 
 package Jifty::Plugin::Authentication::Password::View;
-use HTML::Entities ();
 use Jifty::View::Declare -base;
 
 { no warnings 'redefine';
-sub page (&) {
+sub page (&;$) {
     no strict 'refs'; 
     BEGIN {Jifty::Util->require(Jifty->app_class('View'))};
-    &{Jifty->app_class('View') . "::page"}(@_);
+    Jifty->app_class('View')->can('page')->(@_);
 }
 }
 
+=head1 TEMPLATES
 
-template 'signup' => page {
-    title is _('Sign up');
+=head2 signup
+
+Displays a sign-up form.
+
+=cut
+
+template 'signup' => page { title => _('Sign up') } content {
     my ( $action, $next ) = get(qw(action next));
     Jifty->web->form->start( call => $next );
     render_param( $action => 'name' , focus => 1);
@@ -36,11 +48,26 @@
     Jifty->web->form->end();
 };
 
-template login => page {
-    { title is _('Login!') };
-    show('login_widget');
+=head2 login
+
+Displays the login form.
+
+=cut
+
+template login => page { title => _('Login!') } content {
+    show('/login_widget');
 };
 
+=head2 login_widget
+
+A handy template for embedding the login form. Just include it in your templates via:
+
+  show('/login_widget');
+
+See L<Jifty::Plugin::Authentication::Password::Action::Login>.
+
+=cut
+
 template login_widget => sub {
 
     my ( $action, $next ) = get( 'action', 'next' );
@@ -70,9 +97,16 @@
     }
 };
 
-template 'let/reset_lost_password' => page {
+=head2 let/reset_lost_password
+
+After requesting a password reset and clicking on the link sent by email, this receives that click and provides the form for resetting the password.
+
+See L<Jifty::Plugin::Authentication::Action::ResetLostPassword>.
+
+=cut
+
+template 'let/reset_lost_password' => page { title => 'Reset lost password' } content {
     my ( $next ) = get(qw(next));
-    title is 'Reset lost password' ;
     my $action = Jifty->web->new_action( class => 'ResetLostPassword' );
 
     Jifty->web->form->start( call => $next );
@@ -81,19 +115,34 @@
     Jifty->web->form->end();
 };
 
+=head2 let/confirm_email
+
+Handles the work of confirming an email address for a new account.
+
+See L<Jifty::Plugin::Authenticaiton::Password::View>.
+
+=cut
+
 template 'let/confirm_email' => sub {
     new_action( class => 'ConfirmEmail' )->run;
     redirect("/");
 };
 
-template 'lost_password' => page {
+=head2 lost_password
+
+Starts the process of sending a link to reset a lost password by email.
+
+See L<Jifty::Plugin::Authentication::Password::SendPasswordReminder>.
+
+=cut
+
+template 'lost_password' => page { title => 'Send a link to reset your password' } content {
     my ( $next ) = get(qw(next));
     my $action = Jifty->web->new_action(
         moniker => 'password_reminder',
         class   => 'SendPasswordReminder',
     );
 
-    title is _('Send a link to reset your password');
     outs( _(  "You lost your password. A link to reset it will be sent to the following email address:"));
     my $focused = 0;
     Jifty->web->form->start( call => $next );
@@ -103,9 +152,22 @@
 
 };
 
-template 'passwordreminder' => page {
+=head2 passwordreminder
+
+Starts the process of sending a link to reset a lost password by email.
+
+See L<Jifty::Plugin::Authentication::Password::SendPasswordReminder>.
+
+=begin comment
+
+What's the difference between lost_password and passwordreminder? -- Sterling
+
+=end comment
+
+=cut
+
+template 'passwordreminder' => page { title => 'Send a password reminder' } content {
     my $next = get('next');
-     title is  _('Send a password reminder');
     my $action = Jifty->web->new_action(
         moniker => 'password_reminder',
         class   => 'SendPasswordReminder',
@@ -119,8 +181,15 @@
     Jifty->web->form->end();
 };
 
-template 'resend_confirmation' => page {
-    attr { title => "Resend Confirmation Email" };
+=head2 resend_confirmation
+
+Request a new email confirmation message be sent to your email account.
+
+See L<Jifty::Plugin::Authentication::Password::Action::ResendConfirmation>.
+
+=cut
+
+template 'resend_confirmation' => page { title => 'Resend Confirmation Email' } content {
     my $resend = Jifty->web->new_action(
         class   => 'ResendConfirmation',
         moniker => 'resendconf'
@@ -148,6 +217,16 @@
     }
 };
 
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Authentication::Password>, L<Jifty::Plugin::Authentication::Password::Dispatcher>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
 
 
 1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,96 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart;
+use base qw/ Jifty::Plugin Class::Accessor::Fast /;
+
+use Jifty::Plugin::Chart::Web;
+
+=head1 NAME
+
+Jifty::Plugin::Chart - A charting API for Jifty
+
+=head1 SYNOPSIS
+
+In your F<config.yml>:
+
+  Plugins:
+    - Chart: {}
+
+In your Mason templates:
+
+  <% Jifty->web->chart(
+      type   => 'Bar',
+      width  => 400,
+      height => 300,
+      data   => [
+          [ '2004', '2005', '2006', '2007' ], # labels
+          [ 14,     15,     17,     22     ], # first data set
+          [ 22,     25,     20,     21     ], # second data set
+      ],
+  ) %>
+
+=head1 DESCRIPTION
+
+B<CAUTION:> This plugin is experimental. The API I<will> change.
+
+This plugin provides a charting API that can be used by Jifty applications to build data visualizations without regard to the underlying rendering mechanism.
+
+As of this writing, the API is a barely veiled interface over L<Chart>. However, I intend to expand the interface to apply to something like Maani's XML/SWF Charts or Imprise Javascript charts or even something like OpenLaszlo (or something Open Source and Perl if I can find or build such a thing in time).
+
+=head1 INTERFACE
+
+By adding this method to the plugin configuration for your Jifty application, you will cause L<Jifty::Web> to inherit a new method, C<chart>, which is the cornerstone of this API.
+
+This method is described in L<Jifty::Plugin::Chart::Web> and an example is shown in the L</SYNOPSIS> above.
+
+=head1 CONFIGURATION
+
+The plugin takes a single configuration option called C<renderer>. This may be set to a chart renderer class, which is just an implementation of L<Jifty::Plugin::Chart::Renderer>. The default, L<Jifty::Plugin::Chart::Renderer::Chart>, uses L<Chart> to render charts as PNG files which are then included in your pages for you.
+
+Here is an example configuration for F<config.yml>:
+
+  Plugins:
+    - Chart:
+        renderer: Jifty::Plugin::Chart::Renderer::Chart
+
+=cut
+
+__PACKAGE__->mk_accessors(qw/ renderer /);
+
+sub init {
+    my $self = shift;
+    my %args = (
+        renderer => __PACKAGE__.'::Renderer::Chart',
+        @_,
+    );
+
+    eval "use $args{renderer}";
+    warn $@ if $@;
+    $self->renderer( $args{renderer} );
+
+    push @Jifty::Web::ISA, 'Jifty::Plugin::Chart::Web';
+}
+
+sub render {
+    my $self = shift;
+    $self->renderer->render(@_);
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin>, L<Jifty::Web>, L<Jifty::Plugin::Chart::Renderer>, L<Jifty::Plugin::Chart::Renderer::Chart>, L<Jifty::Plugin::Chart::View>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp E<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and redistributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Dispatcher;
+use Jifty::Dispatcher -base;
+
+use Jifty::YAML;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Dispatcher - Dispatcher for the chart API plugin
+
+=head1 RULES
+
+=head2 chart/*
+
+Grabs the chart configuration stored in the key indicated in C<$1> and unpacks it using L<YAML>. It then passes it to the L<Jifty::Plugin::Chart::View/chart> template.
+
+=cut
+
+on 'chart/*' => run {
+    # Create a session ID to lookup the chart configuration
+    my $session_id = 'chart_' . $1;
+
+    # Unpack the data and then clear it from the session
+    my $args = Jifty::YAML::Load( Jifty->web->session->get( $session_id ) );
+    Jifty->web->session->remove( $session_id );
+
+    # No data? Act like a 404
+    last_rule unless defined $args;
+
+    # Use the "type" to determine which class to use
+    my $class = 'Chart::' . $args->{type};
+
+    # Load that class or die if it does not exist
+    $class->require;
+
+    # Remember the class name for the view
+    $args->{class} = $class;
+
+    # Send them on to chart the chart
+    set 'args' => $args;
+    show 'chart';
+};
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::View>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and redistributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,68 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer - Base class for chart rendering classes
+
+=head1 SYNOPSIS
+
+In your F<config.yml>:
+
+  Plugins:
+    - Chart:
+        renderer: MyApp::Renderer;
+
+In F<lib/MyApp/Renderer.pm>:
+
+  package MyApp::Renderer;
+  use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+  sub render {
+      my $self = shift;
+      my %args = (
+          type   => 'points',
+          width  => 400,
+          height => 300,
+          data   => [],
+          @_,
+      );
+
+      # Output your chart
+      Jifty->web->out( #{ Output your chart here... } );
+
+      # You could also return it as a string...
+      return;
+  }
+
+=head1 METHODS
+
+Your renderer implementation must subclass this package and implement the following methods:
+
+=head2 render
+
+  Jifty->web->out($renderer->render(%args));
+
+See L<Jifty::Plugin::Chart::Web> for the arguments. It must (at least) accept the arguments given to the L<Jifty::Plugin::Chart::Web/chart> method.
+
+The C<render> method may either return it's output or print it out using L<Jifty::Web::out>.
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::Web>, L<Jifty::Plugin::Chart::Renderer::Chart>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer/Chart.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Renderer/Chart.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,65 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Renderer::Chart;
+use base qw/ Jifty::Plugin::Chart::Renderer /;
+
+use Jifty::YAML;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Renderer::Chart - A chart renderer using PNG charts
+
+=head1 DESCRIPTION
+
+This is the default chart renderer used by the L<Jifty::Plugin::Chart> plugin. It works by rendering an IMG tag in the HTML output, which then points to a URL which, when dispatched, retrieves the stored configuration and renders the chart using the L<Chart> package.
+
+=head1 METHODS
+
+=head2 render
+
+Implemented the L<Jifty::Plugin::Chart::Renderer/render> method interface.
+
+=cut
+
+sub render {
+    my $self = shift;
+    my %args = @_;
+
+    # Turn any subs into values returned
+    for my $key (keys %args) {
+        $args{$key} = $args{$key}->(\%args) if ref $args{$key} eq 'CODE';
+    }
+
+    # Make sure the type is ready to be used as a class name
+    $args{type} = ucfirst lc $args{type};
+
+    # Save the data for retrieval from the session later
+    my $chart_id   = Jifty->web->serial;
+    my $session_id = 'chart_' . $chart_id;
+    Jifty->web->session->set( $session_id => Jifty::YAML::Dump(\%args) );
+
+    # Output the <img> tag and include the chart's configuration key
+    Jifty->web->out(qq{<img src="/chart/$chart_id" width="$args{width}" height="$args{height}"/>});
+
+    # Make sure we don't return anything that will get output
+    return;
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart>, L<Jifty::Plugin::Chart::Renderer>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/View.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,59 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::View;
+use Jifty::View::Declare -base;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::View - Views for the renderers built into the Chart plugin
+
+=head1 TEMPLATES
+
+=head2 chart
+
+This shows a chart using L<Chart>. It expects to find the arguments in the C<args> parameter, which is setup for it in L<Jifty::Plugin::Chart::Dispatcher>.
+
+This will output a PNG file unless there is an error building the chart.
+
+=cut
+
+template 'chart' => sub {
+    # Load the arguments
+    my $args = get 'args';
+
+
+    # Set the output type to the PNG file type
+    Jifty->handler->apache->content_type('image/png');
+
+    # Render the chart and output the PNG file generated
+    eval {
+        my $chart = $args->{class}->new( $args->{width}, $args->{height} );
+        # XXX scalar_png() is undocumented!!! Might bad to rely upon.
+        outs_raw($chart->scalar_png($args->{data}));
+    };
+
+    # Should have thrown an error if bad stuff happened, handle that
+    if ($@) {
+        Jifty->log->error("Failed to render chart: $@");
+        die $@;
+    }
+};
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart::Dispatcher>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Web.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Chart/Web.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,124 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Chart::Web;
+
+=head1 NAME
+
+Jifty::Plugin::Chart::Web - Base class to add to Jifty::Web's ISA
+
+=head1 DESCRIPTION
+
+When the L<Jifty::Plugin::Chart> is loaded, this class is added as a base class for L<Jifty::Web> to add the L</chart> method to that class.
+
+=head1 METHODS
+
+=head2 chart
+
+  Jifty->web->out(Jifty->web->chart(%args));
+
+The arguments passed in C<%args> may include:
+
+=over
+
+=item type
+
+This will be one of the following scalar values indicating the kind of chart:
+
+=over
+
+=item Points
+
+This is the default value. A scatter plot with each dataset represented using differnet dot styles.
+
+=item Lines
+
+A line plot with each dataset presented as separate line.
+
+=item Bars
+
+A bar chart with each dataset set side-by-side.
+
+=item StackedBars
+
+A bar chart with each dataset stacked on top of each other.
+
+=item Pie
+
+A pie chart with a single dataset representing the values for different pieces of the pie.
+
+=item HorizontalBars
+
+A bar chart turned sideways.
+
+=back
+
+=item width
+
+The width, in pixels, the chart should take on the page. Defaults to 400.
+
+=item height
+
+The height, in pixels, the chart should take on the page. Defaults to 300.
+
+=item data
+
+An array of arrays containing the data. The first array in the parent array is a list of labels. Each following array is the set of data points matching each label in the first array.
+
+Defaults to no data (i.e., it must be given if anything useful is to happen).
+
+=back
+
+Here's an example:
+
+  <% Jifty->web->chart(
+      type   => 'Pie',
+      width  => 400,
+      height => 300,
+      data   => sub {
+          [
+              [ 2004, 2005, 2006, 2007 ],
+              [ 26, 37, 12, 42 ]
+          ];
+      },
+  ) %>
+
+Be sure to output anything returned by the method (unless it returns undef).
+
+=cut
+
+sub chart {
+    my $self = shift;
+    my ($plugin) = Jifty->find_plugin('Jifty::Plugin::Chart');
+
+    # TODO It might be a good idea to make this config.yml-able
+    # Setup the defaults
+    my %args = (
+        type   => 'points',
+        width  => 400,
+        height => 300,
+        data   => [],
+        @_,
+    );
+
+    # Call the rendering plugin's render method
+    return $plugin->render(@_);
+}
+
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Chart>, L<Jifty::Plugin::Chart::Renderer>
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp C<< <andrew.hanenkamp at boomer.com> >>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2007 Boomer Consulting, Inc.
+
+This is free software and may be modified and distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS.pm	Tue Jul 31 17:23:52 2007
@@ -2,38 +2,181 @@
 use warnings;
 
 package Jifty::Plugin::CompressedCSSandJS;
-use base qw/Jifty::Plugin Class::Accessor/;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
 
-# Your plugin goes here.  If takes any configuration or arguments, you
-# probably want to override L<Jifty::Plugin/init>.
+use Digest::MD5 qw(md5_hex);
+use IPC::Run3 'run3';
+use IO::Handle ();
 
 =head1 NAME
 
 Jifty::Plugin::CompressedCSSandJS
 
+=head1 SYNOPSIS
+
+# In your jifty config.yml under the framework section:
+
+  Plugins:
+    - CompressedCSSandJS:
+        js: 1
+        css: 1
+        jsmin: /path/to/jsmin
+
 =head1 DESCRIPTION
 
-This plugin provides auto-compilation and on-wire compression of your application's CSS and Javascript. It is enabled by default.
+This plugin provides auto-compilation and on-wire compression of your
+application's CSS and Javascript. It is enabled by default, unless
+your C<ConfigFileVersion> is greater or equal than 2.
+
+It also supports js minifier, you will need to specify the full path.
+The jsmin can be obtained from
+L<http://www.crockford.com/javascript/jsmin.html>.
+
+Note that you will need to use C<ConfigFileVersion> 2 to be able to
+configure jsmin feature.
 
 =cut
 
-__PACKAGE__->mk_accessors(qw(css js));
+__PACKAGE__->mk_accessors(qw(css js jsmin cached_javascript cached_javascript_digest cached_javascript_time ));
+
+=head2 init
+
+Initializes the compression object. Takes a paramhash containing keys
+'css' and 'js' which can be used to disable compression on files of
+that type.
+
+=cut
 
 sub init {
     my $self = shift;
-    my %opt = @_;
-    $self->css($opt{css});
-    $self->js ($opt{js});
+    my %opt  = @_;
+    $self->css( $opt{css} );
+    $self->js( $opt{js} );
+    $self->jsmin( $opt{jsmin} );
+
+    Jifty::Web->add_trigger(
+        name      => 'include_javascript',
+        callback  => sub { $self->_include_javascript(@_) },
+        abortable => 1,
+    ) if $self->js_enabled;
 }
 
+=head2 js_enabled
+
+Returns whether JS compression is enabled (which it is by default)
+
+=cut
+
 sub js_enabled {
     my $self = shift;
     defined $self->js ? $self->js : 1;
 }
 
+=head2 css_enabled
+
+Returns whether CSS compression is enabled (which it is by default)
+
+=cut
+
 sub css_enabled {
     my $self = shift;
     defined $self->css ? $self->css : 1;
 }
 
+sub _include_javascript {
+    my $self = shift;
+
+    $self->_generate_javascript;
+    Jifty->web->out( qq[<script type="text/javascript" src="/__jifty/js/]
+            . $self->cached_javascript_digest
+            . qq[.js"></script>] );
+    return 0;
+}
+
+=head3 _generate_javascript
+
+Checks if the compressed JS is generated, and if it isn't, generates
+and caches it.
+
+=cut
+
+sub _generate_javascript {
+    my $self = shift;
+
+    if ( not defined $self->cached_javascript_digest
+        or Jifty->config->framework('DevelMode') ) {
+        Jifty->log->debug("Generating JS...");
+
+        my @roots = map { Jifty::Util->absolute_path( File::Spec->catdir( $_, 'js' ) ) }
+                        Jifty->handler->view('Jifty::View::Static::Handler')->roots;
+
+        my $js = "";
+
+        for my $file ( @{ Jifty::Web->javascript_libs } ) {
+            my $include;
+
+            for my $root (@roots) {
+                my @spec = File::Spec->splitpath( $root, 1 );
+                my $path = File::Spec->catpath( @spec[ 0, 1 ], $file );
+
+                if ( -e $path ) {
+                    $include = $path;
+                    last;
+                }
+            }
+
+            if ( defined $include ) {
+                my $fh;
+
+                if ( open $fh, '<', $include ) {
+                    $js .= "/* Including '$file' */\n\n";
+                    $js .= $_ while <$fh>;
+                    $js .= "\n/* End of '$file' */\n\n";
+                } else {
+                    $js .= "\n/* Unable to open '$file': $! */\n";
+                }
+            } else {
+                $js .= "\n/* Unable to find '$file' */\n";
+            }
+        }
+        if ($self->jsmin) {
+            eval { $self->minify_js(\$js) };
+            Jifty->log->error("Unable to run jsmin: $@") if $@;
+        }
+        $self->cached_javascript($js);
+        $self->cached_javascript_digest( md5_hex($js) );
+        $self->cached_javascript_time(time);
+    }
+}
+
+=head2 minify_js \$js
+
+Runs the given JS through jsmin
+
+=cut
+
+sub minify_js {
+    my $self = shift;
+    my $input = shift;
+    my $output;
+
+    Jifty->log->debug("Minifying JS...");
+
+    # We need to reopen stdout temporarily, because in FCGI
+    # environment, stdout is tied to FCGI::Stream, and the child
+    # of the run3 wouldn't be able to reopen STDOUT properly.
+    my $stdout = IO::Handle->new;
+    $stdout->fdopen( 1, 'w' );
+    local *STDOUT = $stdout;
+
+    my $stderr = IO::Handle->new;
+    $stderr->fdopen( 2, 'w' );
+    local *STDERR = $stderr;
+
+    local $SIG{'CHLD'} = 'DEFAULT';
+    run3 [$self->jsmin], $input, \$output, undef;
+
+    $$input = $output;
+}
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/CompressedCSSandJS/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -13,7 +13,8 @@
 which serve out compiled and compressed CSS and Javascript rules.
 
 =cut
-
+use Compress::Zlib qw();
+use HTTP::Date ();
 
 use Jifty::Dispatcher -base;
 
@@ -26,12 +27,13 @@
         Jifty->web->redirect( "/static/js/" . $arg );
     }
 
-    Jifty->web->generate_javascript;
+    my ($ccjs) = Jifty->find_plugin('Jifty::Plugin::CompressedCSSandJS')
+        or Jifty->web->redirect( "/static/js/" . $arg );
 
-    use HTTP::Date ();
+    $ccjs->_generate_javascript;
 
     if ( Jifty->handler->cgi->http('If-Modified-Since')
-        and $arg eq Jifty->web->cached_javascript_digest . '.js' )
+        and $arg eq $ccjs->cached_javascript_digest . '.js' )
     {
         Jifty->log->debug("Returning 304 for cached javascript");
         Jifty->handler->apache->header_out( Status => 304 );
@@ -45,25 +47,22 @@
     # XXX TODO: If we start caching the squished JS in a file somewhere, we
     # can have the static handler serve it, which would take care of gzipping
     # for us.
-    use Compress::Zlib qw();
-
     if ( Jifty::View::Static::Handler->client_accepts_gzipped_content ) {
         Jifty->log->debug("Sending gzipped squished JS");
         Jifty->handler->apache->header_out( "Content-Encoding" => "gzip" );
         Jifty->handler->apache->send_http_header();
         binmode STDOUT;
-        print Compress::Zlib::memGzip( Jifty->web->cached_javascript );
+        print Compress::Zlib::memGzip( $ccjs->cached_javascript );
     } else {
         Jifty->log->debug("Sending squished JS");
         Jifty->handler->apache->send_http_header();
-        print Jifty->web->cached_javascript;
+        print $ccjs->cached_javascript;
     }
     abort;
 };
 
 on '/__jifty/css/*' => run {
     my $arg = $1;
-    warn "My arg is $arg";
     if ( $arg !~ /^[0-9a-f]{32}\.css$/ ) {
 
         # This doesn't look like a real request for squished CSS,
@@ -73,8 +72,6 @@
 
     Jifty->web->generate_css;
 
-    use HTTP::Date ();
-
     if ( Jifty->handler->cgi->http('If-Modified-Since')
         and $arg eq Jifty->web->cached_css_digest . '.css' )
     {
@@ -89,8 +86,6 @@
     # XXX TODO: If we start caching the squished CSS in a file somewhere, we
     # can have the static handler serve it, which would take care of gzipping
     # for us.
-    use Compress::Zlib qw();
-
     if ( Jifty::View::Static::Handler->client_accepts_gzipped_content ) {
         Jifty->log->debug("Sending gzipped squished CSS");
         Jifty->handler->apache->header_out( "Content-Encoding" => "gzip" );
@@ -104,4 +99,5 @@
     }
     abort;
 };
+
 1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Debug.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Debug.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Debug;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::Debug - a plugin to log each incoming request
+
+=head1 DESCRIPTION
+
+Enable this plugin in your F<etc/config.yml> (requires no configuration) and the plugin add an INFO level log message on each request received. It will contain the PID of the current process, the URL requested, and the username (if any) of the person making the request.
+
+=cut
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Debug/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Debug/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,28 @@
+use warnings;
+use strict;
+
+package Jifty::Plugin::Debug::Dispatcher;
+use Jifty::Dispatcher -base;
+
+=head1 NAME
+
+Jifty::Plugin::Debug::Dispatcher - dispatcher for the debug plugin
+
+=head1 DESCRIPTION
+
+This adds a debugging rule to record debugging information about every request.
+
+=head1 RULES
+
+=head2 on qr'(.*)'
+
+Records the request. The INFO level log message recorded contains the PID of the current process, the URL requested, and the username (if any) attached to the current session.
+
+=cut
+
+on qr'(.*)' => run {
+    Jifty->log->info("[$$] $1 ".(Jifty->web->current_user->id ? Jifty->web->current_user->username : ''));
+};
+
+1;
+

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/ErrorTemplates/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/ErrorTemplates/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/ErrorTemplates/View.pm	Tue Jul 31 17:23:52 2007
@@ -55,7 +55,7 @@
 {
     no warnings qw'redefine';
 
-    sub wrapper ($) {
+    sub wrapper {
         my $code = shift;
         html {
             head {

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback.pm	Tue Jul 31 17:23:52 2007
@@ -32,6 +32,12 @@
 
 __PACKAGE__->mk_accessors(qw(from to));
 
+=head2 init
+
+Initializes the Feedback object. Takes a paramhash with keys 'from' and 'to', which are email addresses.
+
+=cut
+
 sub init {
     my $self = shift;
     my %opt = @_;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Feedback/View.pm	Tue Jul 31 17:23:52 2007
@@ -4,15 +4,23 @@
 
 use Jifty::View::Declare -base;
 
+=head1 NAME
+
+Jifty::Plugin::Feedback::View
+
+=head1 DESCRIPTION
+
+Provides the feedback regions for L<Jifty::Plugin::Feedback>
+
+=cut
 
 template 'feedback/request_feedback' => sub {
     div {
         attr { id => 'feedback_wrapper' };
 
         h3 { _('Send us feedback!') } p {
-            "Tell us what's good, what's bad, and what else you want "
-                . Jifty->config->framework('ApplicationName')
-                . " to do!";
+            _("Tell us what's good, what's bad, and what else you want %1 to do!",
+	      Jifty->config->framework('ApplicationName'));
         };
         render_region(
             'feedback',

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,76 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::GoogleMap;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
+
+
+=head1 NAME
+
+Jifty::Plugin::GoogleMap
+
+=head1 SYNOPSIS
+
+# In your jifty config.yml under the framework section:
+
+  Plugins:
+    - GoogleMap:
+        apikey: ABQIAAAA66LEkTHjdh-UhDZ_NkfdjBTb-vLQlFZmc2N8bgWI8YDPp5FEVBTjCfxPSocuJ53SPMNQDO7Sywpp_w
+
+# note that this is an api for http://localhost:8888/
+
+
+In your model class schema description, add the following:
+
+    column location => is GeoLocation;
+
+
+=head1 DESCRIPTION
+
+This plugin provides a Google-map widget for Jifty, as well as a new GeoLocation column type.
+
+
+=cut
+
+__PACKAGE__->mk_accessors(qw(apikey));
+
+=head2 init
+
+=cut
+
+sub init {
+    my $self = shift;
+    my %opt  = @_;
+    $self->apikey( $opt{apikey} );
+    Jifty->web->add_external_javascript("http://maps.google.com/maps?file=api&v=2&key=".$self->apikey);
+    Jifty->web->add_javascript(qw( google_map.js ) );
+    Jifty->web->add_css('google_map.css');
+}
+
+sub _geolocation {
+    my ($column, $from) = @_;
+    my $name = $column->name;
+    $column->virtual(1);
+    $column->container(1);
+    for (qw(x y)) {
+        Jifty::DBI::Schema::_init_column_for(
+            Jifty::DBI::Column->new({ type => 'double precision',
+                                      name => $name."_$_",
+                                      render_as => 'hidden',
+                                      writable => $column->writable,
+                                      readable => $column->readable }),
+            $from);
+    }
+    no strict 'refs';
+    *{$from.'::'.$name} = sub { return { map { my $method = "${name}_$_"; $_ => $_[0]->$method } qw(x y) } };
+    *{$from.'::'.'set_'.$name} = sub { die "not yet" };
+}
+
+use Jifty::DBI::Schema;
+Jifty::DBI::Schema->register_types(
+    GeoLocation =>
+        sub { _init_handler is \&_geolocation, render_as 'Jifty::Plugin::GoogleMap::Widget' },
+);
+
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap/Widget.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/GoogleMap/Widget.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,85 @@
+use warnings;
+use strict;
+ 
+package Jifty::Plugin::GoogleMap::Widget;
+
+use base qw/Jifty::Web::Form::Field/;
+
+=head1 NAME
+
+Jifty::Plugin::GoogleMap::Widget - google map widget for geolocation display and editing
+
+=head1 METHODS
+
+
+=cut
+
+=head2 accessors
+
+Returns this class's Clas::Accessor based accessors
+
+=cut
+
+
+sub accessors { shift->SUPER::accessors() };
+
+=head2 render_widget
+
+Renders form fields as googlemap widget.
+
+=cut
+
+# XXX: doesn't work
+#use Template::Declare;
+#use Template::Declare::Tags;
+
+sub render_widget {
+    my $self = shift;
+    my $readonly = shift;
+    my $action = $self->action;
+    $readonly = $readonly ? 1 : 0;
+
+    my ($x, $y) = map { $action->form_field($self->name . "_$_")->current_value } qw( x y );
+    my ($xid, $yid) = map { $action->form_field($self->name . "_$_")->element_id } qw( x y );
+    my $use_default = defined $x ? 0 : 1;
+    ($x, $y) = (-71.2, 42.4) if $use_default;
+    my $zoom_level = $use_default ? 1 : 13;
+    my $element_id = $self->element_id;
+    Jifty->web->out(qq{<div class="googlemap-widget-wrapper" style="width: 250px; height: 250px"><div @{[$self->_widget_class]} id="$element_id" style="width: 250px; height: 250px"></div>});
+    Jifty->web->out(qq{<div class="googlemap-search-results" id="$element_id-result"></div></div>});
+    Jifty->web->out(qq{<script type="text/javascript">
+Jifty.GMap.location_editor( \$("$element_id"), $x, $y, "$xid", "$yid", $zoom_level, $use_default, $readonly);
+</script>
+});
+
+
+    return '';
+    Template::Declare->new_buffer_frame;
+
+    div { { id is $self->element_id, style is "width: 200px; height: 200px" } };
+    outs('hi');
+    script { { type is "text/javascript" };
+      qq{if (GBrowserIsCompatible()) {
+         var map = new GMap2(document.getElementById("@{[$self->element_id]}"));
+         map.setCenter(new GLatLng(37.4419, -122.1419), 13);
+      }}
+    };
+
+warn "-----------_".Template::Declare->buffer->data;
+    Jifty->web->out(Template::Declare->buffer->data);
+    Template::Declare->end_buffer_frame;
+    return '';
+}
+
+=head2 render_value
+
+Renders value as a checkbox widget.
+
+=cut
+
+sub render_value {
+    $_[0]->render_widget('readonly');
+    return '';
+}
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID.pm	Tue Jul 31 17:23:52 2007
@@ -4,4 +4,14 @@
 package Jifty::Plugin::OpenID;
 use base qw/Jifty::Plugin/;
 
+=head1 NAME
+
+Jifty::Plugin::OpenID
+
+=head1 DESCRIPTION
+
+Provides OpenID authentication for your app
+
+=cut
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Action/CreateOpenIDUser.pm	Tue Jul 31 17:23:52 2007
@@ -10,6 +10,12 @@
 package Jifty::Plugin::OpenID::Action::CreateOpenIDUser;
 use base qw/Jifty::Action::Record/;
 
+=head2 record_class 
+
+Returns the record class for this action
+
+=cut
+
 sub record_class {
     Jifty->app_class("Model", "User")
 }

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -4,6 +4,16 @@
 package Jifty::Plugin::OpenID::Dispatcher;
 use Jifty::Dispatcher -base;
 
+=head1 NAME
+
+Jifty::Plugin::OpenID::Dispatcher
+
+=head1 DESCRIPTION
+
+Dispatcher for L<Jifty::Plugin::OpenID>.  Handles a lot of the work.
+
+=cut
+
 before qr'^/(?:openid/link)' => run {
     tangent('/openid/login') unless (Jifty->web->current_user->id)
 };

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/Mixin/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,16 @@
 use base 'Jifty::DBI::Record::Plugin';
 use URI;
 
+=head1 NAME
+
+Jifty::Plugin::OpenID::Mixin::Model::User
+
+=head1 DESCRIPTION
+
+L<Jifty::Plugin::OpenID> mixin for the User model.  Provides an 'openid' column.
+
+=cut
+
 use Jifty::Plugin::OpenID::Record schema {
 
 our @EXPORT = qw(has_alternative_auth link_to_openid);
@@ -18,14 +28,26 @@
 
 };
 
+=head2 has_alternative_auth
+
+=cut
+
 sub has_alternative_auth { 1 }
 
+=head2 register_triggers
+
+=cut
+
 sub register_triggers {
     my $self = shift;
     $self->add_trigger(name => 'validate_openid', callback => \&validate_openid, abortable => 1);
     $self->add_trigger(name => 'canonicalize_openid', callback => \&canonicalize_openid);
 }
 
+=head2 validate_openid
+
+=cut
+
 sub validate_openid {
     my $self   = shift;
     my $openid = shift;
@@ -45,6 +67,10 @@
     return 1;
 }
 
+=head2 canonicalize_openid
+
+=cut
+
 sub canonicalize_openid {
     my $self   = shift;
     my $openid = shift;
@@ -60,6 +86,12 @@
     return $uri->canonical;
 }
 
+=head2 link_to_openid
+
+Links User's account to the specified OpenID (bypassing ACLs)
+
+=cut
+
 sub link_to_openid {
     my $self   = shift;
     my $openid = shift;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/OpenID/View.pm	Tue Jul 31 17:23:52 2007
@@ -3,9 +3,19 @@
 use warnings;
 use Jifty::View::Declare -base;
 
+=head1 NAME
+
+Jifty::Plugin::OpenID::View
+
+=head1 DESCRIPTION
+
+The view class for L<Jifty::Plugin::OpenID>.  Provides login and create pages.
+
+=cut
+
 template 'openid/login' => page {
-    { title is _ "Login with your OpenID" }
-    my $action = get('action');
+    { title is _("Login with your OpenID") }
+    my ($action, $next) = get('action', 'next');
 
     div {
         unless ( Jifty->web->current_user->id ) {
@@ -19,13 +29,13 @@
                         }
                     }
                 );
-                form {
-                    render_action($action);
-                    form_submit(
-                        label  => _("Go for it!"),
-                        submit => $action
-                    );
-                }
+                Jifty->web->form->start( call => $next );
+                render_action($action);
+                form_submit(
+                    label  => _("Go for it!"),
+                    submit => $action
+                );
+                Jifty->web->form->end;
             };
         }
         else {

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/REST/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/REST/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/REST/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -155,9 +155,12 @@
     my $record = shift;
     # We could use ->as_hash but this method avoids transforming refers_to
     # columns into JDBI objects
+
+    # XXX: maybe just test ->virtual?
     my %data   = map {
                     $_ => (UNIVERSAL::isa( $record->column( $_ )->refers_to,
-                                           'Jifty::DBI::Collection' )
+                                           'Jifty::DBI::Collection' ) ||
+                           $record->column($_)->container
                              ? undef
                              : stringify( $record->_value( $_ ) ) )
                  } $record->readable_attributes;
@@ -212,6 +215,11 @@
     elsif ($accept =~ /j(?:ava)?s|ecmascript/i) {
         $apache->header_out('Content-Type' => 'application/javascript; charset=UTF-8');
         $apache->send_http_header;
+	# XXX: temporary hack to fix _() that aren't respected by json dumper
+	for (values %{$_[0]}) {
+	    $_->{label} = "$_->{label}" if exists $_->{label} && defined ref $_->{label};
+	    $_->{hints} = "$_->{hints}" if exists $_->{hints} && defined ref $_->{hints};
+	}
         print 'var $_ = ', Jifty::JSON::objToJson( @_, { singlequote => 1 } );
     }
     elsif ($accept =~ qr{^(?:application/x-)?(?:perl|pl)$}i) {
@@ -589,6 +597,7 @@
     label
     hints
     mandatory
+    ajax_validates
     length
 );
 

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage.pm	Tue Jul 31 17:23:52 2007
@@ -6,6 +6,20 @@
 
 __PACKAGE__->mk_accessors(qw(region_name));
 
+=head1 NAME
+
+Jifty::Plugin::SinglePage
+
+=head1 DESCRIPTION
+
+Makes your normal Jifty app into a single-page app through clever use of regions
+
+=head2 init
+
+Registers a before_new trigger to modify links and sets up the special region
+
+=cut
+
 sub init {
     my $self = shift;
     Jifty::Web::Form::Clickable->add_trigger( before_new => _sp_link($self));
@@ -13,34 +27,43 @@
     $self->region_name($opt{region_name} || '__page');
 }
 
+sub _push_onclick {
+    my $self = shift;
+    my $args = shift;
+    $args->{onclick} = [ $args->{onclick} ? $args->{onclick} : () ]
+        unless ref $args->{onclick} eq 'ARRAY';
+    push @{$args->{onclick}}, @_ if @_;
+}
+
 sub _sp_link {
     my $self = shift;
     return sub {
         my ( $clickable, $args ) = @_;
         my $url = $args->{'url'};
-        if ( $url && $url !~ m/^#/ ) {
-            delete $args->{'url'};
-            # XXX mind the existing onclick
-            warn 'ooops got original onclick' . Dumper( $args->{onclick} )
-                if $args->{onclick};
-            $args->{onclick} = {
+        if ( $url && $url !~ m/^#/ && $url !~ m{^https?://} ) {
+            $self->_push_onclick($args, {
                 region       => $self->region_name,
                 replace_with => $url,
-                args         => delete $args->{parameters}
-            };
+                args         => $args->{parameters}});
+        }
+        elsif (exists $args->{submit}) {
+            $self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} });
+            $args->{as_button} = 1;
+        }
+        if (my $form = delete $args->{_form}) {
+            $args->{call} = $form->call;
         }
-	elsif (exists $args->{submit}) {
-	    $args->{onclick} = { submit => delete $args->{submit} };
-	    $args->{refresh_self} = 1;
-	    $args->{as_button} = 1;
-	}
         my $onclick = $args->{onclick};
-        if ( ref($onclick) eq 'HASH' ) {
-            if ( $onclick->{region} && !ref( $onclick->{region} ) ) {
-		my $region = $self->region_name;
-                $onclick->{region}
-                    = $region . '-' . $onclick->{region}
-                    unless $onclick->{region} eq $region or $onclick->{region} =~ m/^\Q$region\E-/;
+        if ( $args->{onclick} ) {
+            $self->_push_onclick($args);    # make sure it's array
+            for my $onclick ( @{ $args->{onclick} } ) {
+                next unless UNIVERSAL::isa($onclick, 'HASH');
+                if ( $onclick->{region} && !ref( $onclick->{region} ) ) {
+                    my $region = $self->region_name;
+                    $onclick->{region} = $region . '-' . $onclick->{region}
+                        unless $onclick->{region} eq $region
+                        or $onclick->{region} =~ m/^\Q$region\E-/;
+                }
             }
         }
     }

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SinglePage/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,40 @@
+package Jifty::Plugin::SinglePage::Dispatcher;
+use strict;
+use warnings;
+use Jifty::Dispatcher -base;
+
+=head1 NAME
+
+Jifty::Plugin::SinglePage::Dispatcher
+
+=head1 DESCRIPTION
+
+Dispatcher for L<Jifty::Plugin::SinglePage>
+
+=cut
+
+before '__jifty/webservices/*' => run {
+    my (@actions) = grep { $_->class eq 'Jifty::Action::Redirect' } values %{ Jifty->web->request->{'actions'} };
+    $_->active(0) for @actions;
+
+    # XXX: shouldn't have multiple redirect
+    # Simply ignore Redirect from webservice if we are not in SPA
+    set '_webservice_redirect' => [map { $_->arguments->{url} } @actions]
+        if Jifty->find_plugin('Jifty::Plugin::SinglePage');
+
+};
+
+on qr{(__jifty/webservices/.*)} => run {
+    my $actions = get '_webservice_redirect';
+    for my $act (@$actions) {
+        if ($act =~ m{^https?://}) {
+            set '_webservice_external_redirect' => $act;
+        }
+        else {
+            Jifty->web->webservices_redirect($act);
+        }
+    }
+    show $1;
+};
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews.pm	Tue Jul 31 17:23:52 2007
@@ -3,5 +3,14 @@
 package Jifty::Plugin::SiteNews;
 use base qw'Jifty::Plugin';
 
+=head1 NAME
+
+Jifty::Plugin::SiteNews
+
+=head1 DESCRIPTION
+
+Provides a way to include site news in your Jifty app
+
+=cut
 
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -4,6 +4,15 @@
 package Jifty::Plugin::SiteNews::Dispatcher;
 use base 'Jifty::Dispatcher';
 
+=head1 NAME
+
+Jifty::Plugin::SiteNews::Dispatcher
+
+=head1 DESCRIPTION
+
+Dispatcher for L<Jifty::Plugin::SiteNews>
+
+=cut
 
 
 

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/Mixin/Model/News.pm	Tue Jul 31 17:23:52 2007
@@ -25,6 +25,11 @@
   render_as is 'Textarea';
 };
 
+=head2 create
+
+Create the News model. Takes a paramhash with keys author_id, created, title, and content.
+
+=cut
 
 sub create {
     my $self = shift;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/View/News.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/View/News.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SiteNews/View/News.pm	Tue Jul 31 17:23:52 2007
@@ -5,12 +5,25 @@
 use Jifty::View::Declare -base;
 use Jifty::View::Declare::CRUD;
 
+=head1 NAME
+
+Jifty::Plugin::SiteNews::View::News
+
+=head1 DESCRIPTION
+
+The /news pages for L<Jifty::Plugin::SiteNews>
+
+=cut
+
 import_templates Jifty::View::Declare::CRUD under '/';
 
-sub object_type { 'News' }
+=head2 object_type
 
-sub fragment_base_path {'/news'}
+News
 
+=cut
+
+sub object_type { 'News' }
 
 template search_region => sub {''};
 
@@ -19,7 +32,7 @@
     form {
             render_region(
                 name     => 'newslist',
-                path     => '/news/list');
+                path     => 'list');
     }
 
 };

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/SkeletonApp/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/SkeletonApp/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/SkeletonApp/View.pm	Tue Jul 31 17:23:52 2007
@@ -15,7 +15,9 @@
 =head1 DESCRIPTION
 
 This somewhat-finished (But not quite) template library implements
-Jifty's "pony" Application. It could certainly use some refactoring. (And some of the menu stuff should get factored out into a dispatcher or the other plugins that implement it.
+Jifty's "pony" Application. It could certainly use some
+refactoring. (And some of the menu stuff should get factored out into
+a dispatcher or the other plugins that implement it.
 
 
 =cut
@@ -40,7 +42,7 @@
         Jifty->web->navigation->render_as_menu; };
 };
 
-template '__jifty/empty' => sub {
+template '__jifty/empty' => sub :Static {
         '';
 };
 
@@ -67,7 +69,8 @@
 };
 
 private template 'keybindings' => sub {
-    div { id is "keybindings" };
+    div { id is "keybindings";
+      outs_raw('<script type="text/javascript"><!-- Jifty.KeyBindings.reset() --></script>') };
 };
 
 #template 'index.html' => page { { title is _('Welcome to your new Jifty application') } img { src is "/static/images/pony.jpg", alt is _( 'You said you wanted a pony. (Source %1)', 'http://hdl.loc.gov/loc.pnp/cph.3c13461'); }; };

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/TabView/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/TabView/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/TabView/View.pm	Tue Jul 31 17:23:52 2007
@@ -23,8 +23,9 @@
   template 'foo' => sub { ... };
   template 'bar' => sub { ... };
 
-=head1 DESCRIPTION
+=head2 render_tabs
 
+Returns some Template::Declare with tabs rendered with the yui tabs
 If a tab ends in _tab, it means it should contain a stub region to be
 replaced by the corresponding fragment onclick to that tab.
 
@@ -47,18 +48,21 @@
 			hyperlink(url => '#tab'.++$i, label => $tab,
 				  $tab =~ s/_tab$// ? 
 				  (onclick =>
-				  { region       => Jifty->web->current_region->qualified_name."-$tab-tab",
-				    replace_with => $self->fragment_for($tab),
+				  { region       => Jifty->web->current_region ? Jifty->web->current_region->qualified_name."-$tab-tab" : "$tab-tab",
+				    replace_with => $self->can('fragment_for') ? $self->fragment_for($tab) : $tab, # XXX: should have higher level function handling mount point
 				    args => { map { $_ => get($_)} @$args },
 				  }) : ()
 				 ) }
 	       }
 	   };
 	  div { {class is 'yui-content' };
+      my $default_shown;
 		for (@tabs) {
 		    div { 
 			if (s/_tab$//) {
-			    render_region(name => $_.'-tab');
+			    render_region(name => $_.'-tab', 
+                          ($default_shown++)? () : ( path => $_, args =>  { map { $_ => get($_)} @$args })
+                          )
 			}
 			else {
 			    die "$self $_" unless $self->has_template($_);

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/UUID.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/UUID.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,46 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::UUID;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
+
+=head1 NAME
+
+Jifty::Plugin::UUID
+
+=head1 SYNOPSIS
+
+In your model class schema description, add the following:
+
+    column photo => is UUID;
+
+
+=head1 DESCRIPTION
+
+This plugin provides user pictures for Jifty;
+
+
+=cut
+
+use Jifty::DBI::Schema;
+use Data::UUID;
+use Scalar::Defer;
+my $UUID_GEN = Data::UUID->new();
+
+my $UUID = lazy { $UUID_GEN->create_str() } ;
+sub _uuid {
+    my ($column, $from) = @_;
+    $column->readable(1);
+    $column->writable(1);
+    $column->default($UUID);
+    $column->type('varchar(32)');
+}
+
+Jifty::DBI::Schema->register_types(
+    UUID => sub {  _init_handler is \&_uuid,  render_as 'Jifty::Plugin::UUID::Widget'},
+);
+
+
+
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/UUID/Widget.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/UUID/Widget.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,43 @@
+use strict;
+use warnings;
+
+
+package Jifty::Plugin::UUID::Widget;
+
+use base qw/Jifty::Web::Form::Field/;
+
+=head1 NAME
+
+Jifty::Plugin::UUID::Widget - 
+
+=head1 METHODS
+
+
+=cut
+
+sub accessors { shift->SUPER::accessors() };
+
+=head2 render_widget
+
+Renders form fields as a uuid;
+
+=cut
+
+sub render_widget {
+    warn "Rendering form field";
+    my $self     = shift;
+    my $action   = $self->action;
+    my $readonly = 1;
+    
+        my $name = $self->name;
+    if ( $action->record->$name() ) {
+        Jifty->web->out(  $action->record->$name() );
+    } else { 
+        Jifty->web->out("<i>"._('No value yet')."</i>");
+
+    }
+    '';
+}
+
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/User.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/User.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/User.pm	Tue Jul 31 17:23:52 2007
@@ -4,18 +4,22 @@
 package Jifty::Plugin::User;
 use base qw/Jifty::Plugin/;
 
-# Your plugin goes here.  If takes any configuration or arguments, you
-# probably want to override L<Jifty::Plugin/init>.
-
 =head1 NAME
 
-Jifty::Plugin::User
+Jifty::Plugin::User - plugin for building user models
 
 =head1 DESCRIPTION
 
 This plugin provides a "user" mixin for your application's user model class.
 
-See L<Jifty::Plugin::User/> for more details.
+=head1 SEE ALSO
+
+L<Jifty::Manual::AccessControl>, L<Jifty::Plugin::User::Mixin::Model::User>, L<Jifty::Plugin::Authentication::Password>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
 
 =cut
 

Modified: jifty/branches/js-refactor/lib/Jifty/Plugin/User/Mixin/Model/User.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Plugin/User/Mixin/Model/User.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/User/Mixin/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -7,9 +7,9 @@
 
 =head1 NAME
 
-Jifty::Plugin::User::Mixin::Model::User
+Jifty::Plugin::User::Mixin::Model::User - user model base mixin
 
-=head1 DESCRIPTION
+=head1 SYNOPSIS
 
  package MyApp::Model::User;
  use Jifty::DBI::Schema;
@@ -18,8 +18,27 @@
  };
  
  use Jifty::Plugin::User::Mixin::Model::User; # Imports two columns: name and email
- 
 
+=head1 DESCRIPTION
+
+This mixin may be added to a model to give your user accounts a name and an email address. This module may be used as the basic building block for building account models in your application. It can be combined with mixins from an authentication plugin to create an object suitable for a given authentication mechanism.
+
+=head1 SCHEMA
+
+This mixin model adds the following columns to the model.
+
+=head2 name
+
+This is the username/nickname for the user of the account.
+
+=head2 email
+
+This is the email address of the account. It is intended as a bare minimum confirmation of identity and for communication of password resets and other account information.
+
+=head2 email_confirmed
+
+This is a flag indicating whether the user has confirmed ownership of the given email address.
+ 
 =cut
 
 use base 'Jifty::DBI::Record::Plugin';
@@ -37,9 +56,7 @@
 
 };
 
-# Your model-specific methods go here.
-
-
+=head1 METHODS
 
 =head2 set_email ADDRESS
 
@@ -92,6 +109,16 @@
     return 1;
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Plugin::Authentication::Password>, L<Jifty::Plugin::Authentication::Password::Mixin::Model::User>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
 
 1;
 

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Userpic;
+use base qw/Jifty::Plugin Class::Accessor::Fast/;
+
+
+=head1 NAME
+
+Jifty::Plugin::Userpic
+
+=head1 SYNOPSIS
+
+In your model class schema description, add the following:
+
+    column userpic => is Userpic;
+
+
+=head1 DESCRIPTION
+
+This plugin provides user pictures for Jifty;
+
+
+=cut
+
+use Jifty::DBI::Schema;
+
+sub _userpic {
+    my ($column, $from) = @_;
+    my $name = $column->name;
+    $column->type('blob');
+}
+
+use Jifty::DBI::Schema;
+
+Jifty::DBI::Schema->register_types(
+    Userpic =>
+        sub { _init_handler is \&_userpic,  render_as 'Jifty::Plugin::Userpic::Widget'},
+);
+
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,23 @@
+package Jifty::Plugin::Userpic::Dispatcher;
+
+use Jifty::Dispatcher -base;
+
+on '/=/plugin/userpic/*/#/*' => run {
+    my $class = $1;
+    my $id = $2;
+    my $field = $3;
+
+    if ($class->isa('Jifty::Record')) {
+
+        my $item = $class->new();
+        $item->load($id);
+
+        if ($item->id) {
+            set item => $item;
+            set field => $field;
+            show '/userpic/image';
+        }
+    }
+};
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/View.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,12 @@
+package Jifty::Plugin::Userpic::View;
+
+use Jifty::View::Declare -base;
+
+template 'userpic/image' => sub {
+    my ($item,$field) = get(qw(item field));
+    Jifty->handler->apache->content_type("image/jpeg");
+    outs_raw($item->$field());
+
+};
+
+1;

Added: jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Widget.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/Plugin/Userpic/Widget.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,59 @@
+use warnings;
+use strict;
+ 
+package Jifty::Plugin::Userpic::Widget;
+
+use base qw/Jifty::Web::Form::Field/;
+
+=head1 NAME
+
+Jifty::Plugin::Userpic::Widget - google map widget for geolocation display and editing
+
+=head1 METHODS
+
+
+=cut
+
+sub accessors { shift->SUPER::accessors() };
+
+=head2 render_widget
+
+Renders form fields as googlemap widget.
+
+=cut
+
+sub render_widget {
+    my $self     = shift;
+    my $readonly = shift;
+    my $action   = $self->action;
+    $readonly = $readonly ? 1 : 0;
+
+    if ( $self->current_value ) {
+        Jifty->web->out( qq{<img src="/=/plugin/userpic/}
+                . $self->action->record_class . "/"
+                . $action->record->id . '/'
+                . $self->name
+                . qq{">} );
+    }
+    unless ($readonly) {
+        my $field = qq!<input type="file" name="@{[ $self->input_name ]}" !;
+        $field .= $self->_widget_class();
+        $field .= qq!/>!;
+        Jifty->web->out($field);
+    }
+    '';
+}
+
+
+=head2 render_value
+
+Renders value as a checkbox widget.
+
+=cut
+
+sub render_value {
+    $_[0]->render_widget('readonly');
+    return '';
+}
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/Record.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Record.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Record.pm	Tue Jul 31 17:23:52 2007
@@ -152,6 +152,41 @@
     return ($id,$msg);
 }
 
+=head2 _guess_table_name
+
+Guesses a table name based on the class's last part. In addition to the work performed in L<Jifty::DBI::Record>, this method also prefixes the table name with the plugin table prefix, if the model belongs to a plugin.
+
+=cut
+
+sub _guess_table_name {
+    my $self = shift;
+    my $table = $self->SUPER::_guess_table_name;
+
+    # Add plugin table prefix if a plugin model
+    my $class = ref($self) ? ref($self) : $self;
+    my $app_plugin_root = Jifty->app_class('Plugin');
+    if ($class =~ /^(?:Jifty::Plugin::|$app_plugin_root)/) {
+
+        # Guess the plugin class name
+        my $plugin_class = $class;
+        $plugin_class =~ s/::Model::(.*)$//;
+
+        # Try to load that plugin's configuration
+        my ($plugin) = grep { ref $_ eq $plugin_class } Jifty->plugins;
+
+        # Add the prefix if found
+        if (defined $plugin) {
+            $table = $plugin->table_prefix . $table;
+        }
+
+        # Uh oh. Warn, but try to keep going.
+        else {
+            warn "Model $class looks like a plugin model, but $plugin_class could not be found.";
+        }
+    }
+
+    return $table;
+}
 
 =head2 current_user_can RIGHT [ATTRIBUTES]
 

Modified: jifty/branches/js-refactor/lib/Jifty/Request.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Request.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Request.pm	Tue Jul 31 17:23:52 2007
@@ -221,7 +221,9 @@
     my $self = shift;
     my ($cgi) = @_;
 
-    my $path = $cgi->path_info;
+    # always get the unescaped path for dispatcher, which is already
+    # the case for fastcgi, but not standalone.
+    my $path = URI::Escape::uri_unescape($cgi->path_info);
     $path =~ s/\?.*//;
     $self->path( $path );
 
@@ -511,7 +513,8 @@
     );
 
     # Set us up with the new continuation
-    Jifty->web->_redirect( Jifty->web->url(path => $path)
+    Jifty->web->_redirect( 
+                        $path
                       . ( $path =~ /\?/ ? "&" : "?" ) . "J:C="
                       . $c->id );
 }
@@ -530,8 +533,7 @@
     return if $self->is_subrequest;
     return unless $self->continuation_type and $self->continuation_type eq "call" and $self->continuation;
     $self->log->debug("Calling continuation ".$self->continuation->id);
-    $self->continuation->call;
-    return 1;
+    return $self->continuation->call;
 }
 
 =head2 return_from_continuation

Modified: jifty/branches/js-refactor/lib/Jifty/Request/Mapper.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Request/Mapper.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Request/Mapper.pm	Tue Jul 31 17:23:52 2007
@@ -87,7 +87,11 @@
             for (grep {/^(result(_of)?|argument(_to)?)$/} keys %mapping) {
                 my $action  = $mapping{$_};
                 my $moniker = ref $action ? $action->moniker : $action;
-                my $name = $mapping{name} || $key;
+                # If $key is for an argument of an action, we want to
+                # extract only the argument's name, and not just use
+                # the whole encoded J:A:F-... string.
+                my (undef, $a, undef) = Jifty::Request->parse_form_field_name($key);
+                my $name = $mapping{name} || $a || $key;
 
                 my $type = ($_ =~ /result/) ? "R" : "A";
 
@@ -120,7 +124,7 @@
 
 =item request
 
-The L<Jifty::Request> object to pull action arguments from.  Defauts
+The L<Jifty::Request> object to pull action arguments from.  Defaults
 to the current request.
 
 =item response

Modified: jifty/branches/js-refactor/lib/Jifty/RightsFrom.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/RightsFrom.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/RightsFrom.pm	Tue Jul 31 17:23:52 2007
@@ -3,7 +3,7 @@
 
 =head1 NAME
 
-Jifty::RightsFrom
+Jifty::RightsFrom - Delegate access control to a related object
 
 =head1 SYNOPSIS
 

Modified: jifty/branches/js-refactor/lib/Jifty/Schema.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Schema.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Schema.pm	Tue Jul 31 17:23:52 2007
@@ -26,7 +26,11 @@
 
 delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
 
+=head2 new
 
+Returns a new Jifty::Schema. Takes no arguments. Will automatically figure out and initialize the models defined in the app's source.
+
+=cut
 
 sub new {
     my $class = shift;
@@ -44,20 +48,27 @@
 =cut
 
 sub _init_model_list {
-
     my $self = shift;
 
+    # Plugins can have models too
+    my @plugins = map { (ref $_).'::Model' } Jifty->plugins;
+
     # This creates a sub "models" which when called, finds packages under
     # the application's ::Model, requires them, and returns a list of their
     # names.
     Jifty::Module::Pluggable->import(
         require     => 1,
         except      => qr/\.#/,
-        search_path => [ "Jifty::Model", Jifty->app_class("Model") ],
+        search_path => [ "Jifty::Model", Jifty->app_class("Model"), @plugins ],
         sub_name    => 'models',
     );
 }
 
+=head2 serialize_current_schema
+
+Returns a serialization of the models in the app
+
+=cut
 
 sub serialize_current_schema {
     my $self = shift;    
@@ -73,7 +84,11 @@
 }
 
 
+=head2 upgrade_schema
+
+Looks at the current schemas as defined by the source and the database and updates the database by adding, dropping, and renaming columns.
 
+=cut
 
 sub upgrade_schema {
     my $self = shift;
@@ -269,7 +284,11 @@
     return "(" . ( join ", ", @_ ) . ")";
 }
 
+=head2 connect_to_db_for_management
 
+Returns a database handle suitable for direct manipulation.
+
+=cut
 
 sub connect_to_db_for_management {
     my $handle = Jifty::Handle->new();

Modified: jifty/branches/js-refactor/lib/Jifty/Script.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Script.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Script.pm	Tue Jul 31 17:23:52 2007
@@ -2,6 +2,12 @@
 use App::CLI;
 use base qw/App::CLI App::CLI::Command/;
 
+=head1 NAME
+
+Jifty::Script - Base class for all bin/jifty commands
+
+=head1 METHODS
+
 =head2 prepare
 
 C<prepare> figures out which command to run. If the user wants

Modified: jifty/branches/js-refactor/lib/Jifty/Script/Deps.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Script/Deps.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Script/Deps.pm	Tue Jul 31 17:23:52 2007
@@ -12,6 +12,12 @@
 use version;
 use Jifty::Config;
 
+=head1 NAME
+
+Jifty::Script::Deps - Looks for module dependencies and attempts to install them from CPAN
+
+=head1 METHODS
+
 =head2 options
 
 Returns a hash of all the options this script takes. (See the usage message for details)

Modified: jifty/branches/js-refactor/lib/Jifty/Script/Schema.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Script/Schema.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Script/Schema.pm	Tue Jul 31 17:23:52 2007
@@ -50,6 +50,7 @@
     } elsif ( $self->{'setup_tables'} ) {
         $self->upgrade_jifty_tables();
         $self->upgrade_application_tables();
+        $self->upgrade_plugin_tables();
     } else {
         print "Done.\n";
     }
@@ -179,11 +180,23 @@
     Jifty::Model::Metadata->store( application_db_version => $appv );
     Jifty::Model::Metadata->store( jifty_db_version       => $jiftyv );
 
+    # For each plugin, update the plugin version
+    for my $plugin (Jifty->plugins) {
+        my $pluginv = version->new( $plugin->version );
+        Jifty::Model::Metadata->store( (ref $plugin).'_db_version' => $pluginv );
+    }
+
     # Load initial data
     eval {
         my $bootstrapper = Jifty->app_class("Bootstrap");
         Jifty::Util->require($bootstrapper);
         $bootstrapper->run() if $bootstrapper->can('run');
+
+        for my $plugin (Jifty->plugins) {
+            my $plugin_bootstrapper = $plugin->bootstrapper;
+            Jifty::Util->require($plugin_bootstrapper);
+            $plugin_bootstrapper->run() if $bootstrapper->can('run');
+        }
     };
     die $@ if $@;
 
@@ -216,20 +229,52 @@
     my $appv = version->new( Jifty->config->framework('Database')->{'Version'} );
     my $jiftyv = version->new( $Jifty::VERSION  );
 
+    my %pluginvs;
+    for my $plugin (Jifty->plugins) {
+        my $plugin_class = ref $plugin;
+        $pluginvs{ $plugin_class } = version->new( $plugin->version );
+    }
 
     for my $model ( @models) {
-       # TODO XXX FIXME:
-       #   This *will* try to generate SQL for abstract base classes you might
-       #   stick in $AC::Model::.
-        if ( $model->can( 'since' ) and ($model =~ /^Jifty::Model::/ ? $jiftyv : $appv) < $model->since ) {
-            $log->info( "Skipping $model, as it should already be in the database");
-            next;
+        # TODO XXX FIXME:
+        #   This *will* try to generate SQL for abstract base classes you might
+        #   stick in $AC::Model::.
+        if ($model->can('since')) {
+            my $app_class   = Jifty->app_class;
+            my $plugin_root = Jifty->app_class('Plugin');
+
+            my $installed_version = 0;
+
+            # Is it a Jifty core model?
+            if ($model =~ /^Jifty::Model::/) {
+                $installed_version = $jiftyv;
+            }
+
+            # Is it a Jifty or application plugin model?
+            elsif ($model =~ /^(?:Jifty::Plugin::|$plugin_root)/) {
+                my $plugin_class = $model;
+                $plugin_class =~ s/::Model::(.*)$//;
+
+                $installed_version = $pluginvs{ $plugin_class };
+            }
+
+            # Otherwise, an application model
+            else {
+                $installed_version = $appv;
+            }
+
+            if ($installed_version < $model->since) {
+                # XXX Is this message correct? 
+                $log->info("Skipping $model, as it should already be in the database");
+                next;
+            }
         }
+
         $log->info("Using $model, as it appears to be new.");
 
-            $self->schema->_check_reserved($model)
+        $self->schema->_check_reserved($model)
         unless ( $self->{'ignore_reserved'}
-            or !Jifty->config->framework('Database')->{'CheckSchema'} );
+                or !Jifty->config->framework('Database')->{'CheckSchema'} );
 
         if ( $self->{'print'} ) {
             print $model->printable_table_schema;
@@ -286,6 +331,29 @@
     }
 }
 
+=head2 upgrade_plugin_tables
+
+Upgrade the tables for each plugin.
+
+=cut
+
+sub upgrade_plugin_tables {
+    my $self   = shift;
+
+    for my $plugin (Jifty->plugins) {
+        my $plugin_class = ref $plugin;
+        my $dbv  = version->new( Jifty::Model::Metadata->load($plugin_class . '_version') || '0.0.1' );
+        my $appv = version->new( $plugin->version );
+
+        return unless $self->upgrade_tables( $plugin_class, $dbv, $appv, $plugin->upgrade_class );
+        if ( $self->{print} ) {
+            warn "Need to upgrade ${plugin_class}_db_version to $appv here!";
+        } else {
+            Jifty::Model::Metadata->store( $plugin_class . '_db_version' => $appv );
+        }
+    }
+}
+
 =head2 upgrade_tables BASECLASS, FROM, TO, [UPGRADECLASS]
 
 Given a C<BASECLASS> to upgrade, and two L<version> objects, C<FROM>

Modified: jifty/branches/js-refactor/lib/Jifty/Server.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Server.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Server.pm	Tue Jul 31 17:23:52 2007
@@ -3,6 +3,10 @@
 
 package Jifty::Server;
 
+=head1 NAME
+
+Jifty::Server - Stand-alone server implementation for Jifty
+
 =head1 SYNOPSIS
 
   use Jifty::Server;
@@ -17,8 +21,7 @@
 =cut
 
 
-use base qw/HTTP::Server::Simple::CGI/;
-use base qw/Jifty::Object/;
+use base qw/HTTP::Server::Simple::CGI Jifty::Object/;
 use File::Spec;
 use Log::Log4perl;
 use HTTP::Server::Simple;

Modified: jifty/branches/js-refactor/lib/Jifty/Subs.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Subs.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Subs.pm	Tue Jul 31 17:23:52 2007
@@ -8,7 +8,7 @@
 
 =head1 NAME
 
-Jifty::Subs - 
+Jifty::Subs - Configure subscriptions for the current window or session
 
 =head1 SYNOPSIS
 
@@ -27,7 +27,7 @@
 
 
 
-=cut
+=head1 METHODS
 
 =head2 add PARAMHASH
 

Modified: jifty/branches/js-refactor/lib/Jifty/Subs/Render.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Subs/Render.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Subs/Render.pm	Tue Jul 31 17:23:52 2007
@@ -4,7 +4,7 @@
 
 =head1 NAME
 
-Jifty::Subs::Render - 
+Jifty::Subs::Render - Helper for subscriptions rendering
 
 =head1 SYNOPSIS
 

Modified: jifty/branches/js-refactor/lib/Jifty/Test.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Test.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Test.pm	Tue Jul 31 17:23:52 2007
@@ -158,9 +158,9 @@
     my $class = shift;
 
     my $test_config = File::Temp->new( UNLINK => 0 );
-    Jifty::YAML::DumpFile($test_config, $class->test_config(Jifty::Config->new));
+    Jifty::YAML::DumpFile("$test_config", $class->test_config(Jifty::Config->new));
     # Invoking bin/jifty and friends will now have the test config ready.
-    $ENV{'JIFTY_TEST_CONFIG'} ||= $test_config;
+    $ENV{'JIFTY_TEST_CONFIG'} ||= "$test_config";
     $class->builder->{test_config} = $test_config;
     {
         # Cache::Memcached stores things. And doesn't let them expire
@@ -187,7 +187,7 @@
     # Mason's disk caching sometimes causes false tests
     rmtree([ File::Spec->canonpath("$root/var/mason") ], 0, 1);
 
-$class->setup_test_database;
+    $class->setup_test_database;
 
     $class->setup_mailbox;
 }

Modified: jifty/branches/js-refactor/lib/Jifty/Test/WWW/Mechanize.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Test/WWW/Mechanize.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Test/WWW/Mechanize.pm	Tue Jul 31 17:23:52 2007
@@ -436,7 +436,10 @@
 sub session {
     my $self = shift;
 
-    return undef unless $self->cookie_jar->as_string =~ /JIFTY_SID_\d+=([^;]+)/;
+    my $cookie = Jifty->config->framework('Web')->{'SessionCookieName'};
+    $cookie =~ s/\$PORT/(?:\\d+|NOPORT)/g;
+
+    return undef unless $self->cookie_jar->as_string =~ /$cookie=([^;]+)/;
 
     my $session = Jifty::Web::Session->new;
     $session->load($1);

Modified: jifty/branches/js-refactor/lib/Jifty/Upgrade.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Upgrade.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Upgrade.pm	Tue Jul 31 17:23:52 2007
@@ -3,7 +3,7 @@
 
 =head1 NAME
 
-Jifty::Upgrade
+Jifty::Upgrade - Superclass for schema/data upgrades to Jifty applicaitons
 
 =head1 DESCRIPTION
 
@@ -152,4 +152,12 @@
     $package->just_renamed($renamed);
 }
 
+
+
+=head1 SEE ALSO
+
+L<Jifty::Manual::Upgrading>
+
+=cut
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Upgrade/Internal.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Upgrade/Internal.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Upgrade/Internal.pm	Tue Jul 31 17:23:52 2007
@@ -9,6 +9,12 @@
 # XXX TODO: there's probably a cleaner way to cope than "INITIAL_VERSION", perhaps using ->VERSIONS
 our $INITIAL_VERSION;
 
+=head1 NAME
+
+Jifty::Upgrade::Internal - Upgrades to Jifty-specific schemas and data
+
+=head1 UPGRADES
+
 =head2 Version 0.60427
 
 Version metadata, previously stored in _db_version, get migrated to

Modified: jifty/branches/js-refactor/lib/Jifty/Util.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Util.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Util.pm	Tue Jul 31 17:23:52 2007
@@ -94,7 +94,8 @@
 =head2 share_root
 
 Returns the 'share' directory of the installed Jifty module.  This is
-currently only used to store the common Mason components.
+currently only used to store the common Mason components, CSS, and JS
+of Jifty and it's plugins.
 
 =cut
 

Modified: jifty/branches/js-refactor/lib/Jifty/View.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View.pm	Tue Jul 31 17:23:52 2007
@@ -2,6 +2,16 @@
 use strict;
 use warnings;
 
+=head1 NAME
+
+Jifty::View - Base class for view modules
+
+=head1 DESCRIPTION
+
+This is the base class for L<Jifty::View::Declare> and L<Jifty::View::Mason::Handler>, which are the two view plugins shipped with Jifty. Other view plugins can be built by extending this class.
+
+=head1 METHODS
+
 =head2 auto_send_headers
 
 Doesn't send headers if this is a subrequest (according to the current
@@ -42,5 +52,17 @@
     }
 }
 
+=head1 SEE ALSO
+
+L<Jifty::View::Declare>, L<Jifty::View::Declare::BaseClass>, L<Jifty::View::Mason::Handler>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
+
 
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,10 @@
 use warnings;
 use constant BaseClass => 'Jifty::View::Declare::BaseClass';
 
+=head1 NAME
+
+Jifty::View::Declare - Build views using Template::Declare
+
 =head1 SYNOPSIS
 
     package MyApp::View;
@@ -15,6 +19,19 @@
         b { "The Index" };
     };
 
+=head1 DESCRIPTION
+
+L<Template::Declare> is a templating system using a declarative syntax built on top of Perl. This provides a templating language built in a similar style to the dispatcher language in L<Jifty::Dispatcher>, the model language in L<Jifty::DBI::Schema>, and the action language in L<Jifty::Param::Schema>.
+
+To use this view system, you must declare a class named C<MyApp::View> (where I<MyApp> is the name of your Jifyt application). Use this library class to bring in all the details needed to make it work:
+
+  package MyApp::View;
+  use Jifty::View::Declare -base;
+
+  # Your code...
+
+For more details on how to write the individual templates, see L<Template::Declare> and also L<Jifty::View::Declare::Helpers> for Jifty specific details.
+
 =cut
 
 sub import {
@@ -29,4 +46,15 @@
     goto &{BaseClass()->can('import')};
 }
 
+=head1 SEE ALSO 
+
+L<Jifty::View::Declare::Helpers>, L<Template::Declare>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distrib uted under the same terms as Perl itself.
+
+=cut
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/BaseClass.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/BaseClass.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/BaseClass.pm	Tue Jul 31 17:23:52 2007
@@ -4,33 +4,103 @@
 use warnings;
 use base qw/Exporter Jifty::View::Declare::Helpers/;
 use Scalar::Defer;
-use Template::Declare::Tags;
-
+use PadWalker;
 
 use Jifty::View::Declare::Helpers;
 
 
 our @EXPORT = ( @Jifty::View::Declare::Helpers::EXPORT);
 
-
-1;
-__DATA__
-
 =head1 NAME
 
-Jifty::View::Declare::BaseClass
+Jifty::View::Declare::BaseClass - Base class for Template::Declare views
 
 =head1 DESCRIPTION
 
-This class provides a baseclass for your C<Template::Declare> derived view classes.
-
+This class provides a base class for your L<Template::Declare> derived view classes.
 
 =head1 METHODS
 
+=head2 use_mason_wrapper
+
+Call this function in your view class to use your mason wrapper for L<Template::Declare> templates.
+
+=cut
+
+sub use_mason_wrapper {
+    my $class = shift;
+    no strict 'refs';
+    no warnings 'redefine';
+    *{ $class . '::wrapper' } = sub {
+        my $code = shift;
+        my $args = shift;
+        # so in td handler, we made jifty::web->out appends to td
+        # buffer, we need it back for here before we call $code.
+        # someday we need to finish fixing the output system that is
+        # in Jifty::View.
+        my $td_out = \&Jifty::Web::out;
+        local *Jifty::Web::out = sub { shift->mason->out(@_) };
+
+        local *HTML::Mason::Request::content = sub {
+            local *Jifty::Web::out = $td_out;
+            $code->();
+            my $content = Template::Declare->buffer->data;
+            Template::Declare->buffer->clear;
+            $content;
+        };
+
+        Jifty->handler->fallback_view_handler->show('/_elements/wrapper', $args);
+    }
+}
+
+sub _actual_td_code {
+    my $class = shift;
+    my $path = shift;
+    my $code = Template::Declare->resolve_template($path) or return;
+    my $closed_over = PadWalker::closed_over($code)->{'$coderef'};
+    return $closed_over ? $$closed_over : $code;
+}
+
+use Attribute::Handlers;
+my (%Static, %Action);
+sub Static :ATTR(CODE,BEGIN) {
+    $Static{$_[2]}++;
+}
+
+sub Action :ATTR(CODE,BEGIN) {
+    $Action{$_[2]}++;
+}
+
+=head2 client_cacheable
+
+Returns the type of cacheable object for client
+
+=cut
+
+sub client_cacheable {
+    my $self = shift;
+    my $path = shift;
+    my $code = $self->_actual_td_code($path) or return;
+
+    return 'static' if $Static{$code};
+    return 'action' if $Action{$code};
+    return;
+}
+
+
 =head2 show templatename arguments
 
 Render a C<Template::Declare> template.
 
+=head1 SEE ALSO
+
+L<Template::Declare>, L<Jifty::View::Declare::Helpers>, L<Jifty::View::Declare>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
 
 =cut
 
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/CRUD.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/CRUD.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/CRUD.pm	Tue Jul 31 17:23:52 2007
@@ -3,14 +3,77 @@
 
 package Jifty::View::Declare::CRUD;
 use Jifty::View::Declare -base;
-use base 'Exporter';
-our @EXPORT = qw(object_type fragment_for get_record current_collection);
+
+# XXX: should register 'template type' handler, so the
+# client_cache_content & the TD sub here agrees with the arguments.
+use Attribute::Handlers;
+my %VIEW;
+sub CRUDView :ATTR(CODE,BEGIN) {
+    $VIEW{$_[2]}++;
+}
+
+
+=head1 NAME
+
+Jifty::View::Declare::CRUD - Provides typical CRUD views to a model
+
+=head1 DESCRIPTION
+
+This class provides a set of views that may be used by a model to
+display Create/Read/Update/Delete views using the L<Template::Declare>
+templating language.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 mount_view MODELCASS VIEWCLASS /path
+
+=cut
+
+sub mount_view {
+    my ($class, $model, $vclass, $path) = @_;
+    my $caller = caller(0);
+    $model = ucfirst($model);
+    $vclass ||= $caller.'::'.$model;
+    $path ||= '/'.lc($model);
+
+    Jifty::Util->require($vclass);
+    eval qq{package $caller;
+            alias $vclass under '$path'; 1} or die $@;
+    no strict 'refs';
+    *{$vclass."::object_type"} = sub { $model };
+}
+
+sub _dispatch_template {
+    my $class = shift;
+    my $code  = shift;
+    if ($VIEW{$code} && !UNIVERSAL::isa($_[0], 'Evil')) {
+	my ( $object_type, $id ) = ( $class->object_type, get('id') );
+	@_ = ($class, $class->_get_record($id), @_);
+    }
+    else {
+	unshift @_, $class;
+    }
+    goto $code;
+}
+
+
+=head2 object_type
+
+=cut
 
 sub object_type {
     my $self = shift;
     return $self->package_variable('object_type') || get('object_type');
 }
 
+
+=head2 fragment_for
+
+=cut
+
 sub fragment_for {
     my $self     = shift;
     my $fragment = shift;
@@ -23,12 +86,25 @@
         || $self->fragment_base_path . "/" . $fragment;
 }
 
+=head2 fragment_base_path
+
+=cut
+
 sub fragment_base_path {
     my $self = shift;
-    return $self->package_variable('base_path') || '/crud';
+    my @parts = split('/', current_template());
+    pop @parts;
+    my $path = join('/', @parts);
+    return $path;
 }
 
-sub get_record {
+=head2 _get_record $id
+
+Given an $id, returns a record object for the CRUD view's model class.
+
+=cut
+
+sub _get_record {
     my ( $self, $id ) = @_;
 
     my $record_class = Jifty->app_class( "Model", $self->object_type );
@@ -38,6 +114,51 @@
     return $record;
 }
 
+=head2 display_columns
+
+Returns a list of all the columns that this REST view should display
+
+=cut
+
+sub display_columns {
+    my $self = shift;
+    my $action = shift;
+     return   grep { !( m/_confirm/ || lc $action->arguments->{$_}{render_as} eq 'password' ) } $action->argument_names;
+}
+
+
+=head1 TEMPLATES
+
+
+=cut
+
+=head2 index.html
+
+
+=cut
+
+
+template 'index.html' => page {
+    my $self = shift;
+    title is $self->object_type;
+    form {
+            render_region(
+                name     => $self->object_type.'-list',
+                path     => $self->fragment_base_path.'/list');
+    }
+
+};
+
+ 
+
+
+
+=head2 search
+
+The search view displays a search screen connected to the search action of the module. See L<Jifty::Action::Record::Search>.
+
+=cut
+
 template 'search' => sub {
     my $self          = shift;
     my ($object_type) = ( $self->object_type );
@@ -63,35 +184,55 @@
         }
 };
 
-template 'view' => sub {
-    my $self = shift;
-    my ( $object_type, $id ) = ( $self->object_type, get('id') );
+=head2 view
+
+This template displays the data held by a single model record.
+
+=cut
+
+template 'view' => sub :CRUDView {
+    my ($self, $record) = @_;
     my $update = new_action(
-        class   => 'Update' . $object_type,
+        class   => 'Update' . $self->object_type,
         moniker => "update-" . Jifty->web->serial,
-        record  => $self->get_record($id)
+        record  => $record,
     );
 
     div {
         { class is 'crud read item inline' };
+        my @fields = $self->display_columns($update);
+        render_action( $update, \@fields, { render_mode => 'read' } );
+
+        show ('./view_item_controls', $record, $update); 
+
+        hr {};
+    };
+
+};
+
+private template view_item_controls  => sub {
+    my $self = shift;
+    my $record = shift;
+
+    if ( $record->current_user_can('update') ) {
         hyperlink(
-            label   => "Edit",
+            label   => _("Edit"),
             class   => "editlink",
             onclick => {
                 replace_with => $self->fragment_for('update'),
-                args         => { object_type => $object_type, id => $id }
+                args         => { id => $record->id }
             },
         );
+    }
+};
 
-        my @fields = grep {
-            !( m/_confirm/
-                || lc $update->arguments->{$_}{render_as} eq 'password' )
-        } $update->argument_names;
-        render_action( $update, \@fields, { render_mode => 'read' } );
-        hr {};
-    };
 
-};
+
+=head2 update
+
+The update template displays a form for editing the data held within a single model record. See L<Jifty::Action::Record::Update>.
+
+=cut
 
 template 'update' => sub {
     my $self = shift;
@@ -109,6 +250,35 @@
     div {
         { class is "crud update item inline " . $object_type }
 
+        show('./edit_item', $update);
+        show('./edit_item_controls', $record, $update);
+
+        hr {};
+        }
+};
+
+
+
+=head2 edit_item_controls $record $action
+
+The controls we should be rendering in the 'edit' region for a given fragment
+
+=cut
+
+private template edit_item_controls => sub {
+    my $self = shift;
+    my $record = shift;
+    my $update = shift;
+
+    my $object_type = $self->object_type;
+    my $id = $record->id;
+
+    my $delete = Jifty->web->form->add_action(
+        class   => 'Delete' . $object_type,
+        moniker => 'delete-' . Jifty->web->serial,
+        record  => $record
+    );
+
         div {
             { class is 'crud editlink' };
             hyperlink(
@@ -126,50 +296,78 @@
                     replace_with => $self->fragment_for('view'),
                     args         => { object_type => $object_type, id => $id }
                 },
-                as_button => 1
+                as_button => 1,
+                class => 'cancel'
             );
+            if ( $record->current_user_can('delete') ) {
+                $delete->button(
+                    label   => 'Delete',
+                    onclick => {
+                        submit => $delete,
+                        confirm => 'Really delete?',
+                        refresh => Jifty->web->current_region->parent,
+                    },
+                    class => 'delete'
+                );
+            }
         };
 
-        render_action($update);
-        hr {};
-        }
 };
 
+=head2 list
+
+The list template provides an interactive list for showing a list of records in the record collection, adding new records, deleting records, and updating records.
+
+=cut
+
+template 'list' => sub {
+    my $self = shift;
+
+    my ( $page ) = get(qw(page ));
+    my $item_path = get('item_path') || $self->fragment_for("view");
+    my $collection =  $self->_current_collection();
+
+    show('./search_region');
+    show( './paging_top',    $collection, $page );
+    show( './list_items',    $collection, $item_path );
+    show( './paging_bottom', $collection, $page );
+    show( './new_item_region');
+
+};
+
+=head2 per_page
+
+This routine returns how many items should be shown on each page of a listing.
+The default is 25.
+
+=cut
+
+sub per_page { 25 }
 
-sub current_collection {
+sub _current_collection {
     my $self =shift; 
     my ( $page, $search_collection ) = get(qw(page  search_collection));
-
     my $collection_class = Jifty->app_class( "Model", $self->object_type . "Collection" );
-    my $search = $search_collection || Jifty->web->response->result('search');
+    my $search = $search_collection || ( Jifty->web->response->result('search') ? Jifty->web->response->result('search')->content('search') : undef );
     my $collection;
-    if ( !$search ) {
+    if ( $search ) {
+        $collection = $search;
+    } else {
         $collection = $collection_class->new();
         $collection->unlimit();
-    } else {
-        $collection = $search;
     }
 
-    $collection->set_page_info( current_page => $page, per_page => 25 );
+    $collection->set_page_info( current_page => $page, per_page => $self->per_page );
 
     return $collection;    
 }
 
-template 'list' => sub {
-    my $self = shift;
 
-    my ( $page, $search_collection ) = get(qw(page  search_collection));
-    my $fragment_for_new_item = get('fragment_for_new_item') || $self->fragment_for('new_item');
-    my $item_path = get('item_path') || $self->fragment_for("view");
+=head2 search_region
 
-    my $collection =  $self->current_collection();
-    show('./search_region');
-    show( './paging_top',    $collection, $page );
-    show( './list_items',    $collection, $item_path );
-    show( './paging_bottom', $collection, $page );
-    show( './new_item_region', $fragment_for_new_item );
+This I<private> template renders a region to show an expandable region for a search widget.
 
-};
+=cut
 
 private template 'search_region' => sub {
     my $self        = shift;
@@ -193,9 +391,17 @@
 
     outs( $search_region->render );
 };
+
+=head2 new_item_region
+
+This I<private> template renders a region to show a the C<new_item> template.
+
+=cut
+
+
 private template 'new_item_region' => sub {
     my $self        = shift;
-    my $fragment_for_new_item = shift;
+    my $fragment_for_new_item = get('fragment_for_new_item') || $self->fragment_for('new_item');
     my $object_type = $self->object_type;
 
     if ($fragment_for_new_item) {
@@ -207,13 +413,24 @@
     }
 };
 
+
+=head2 list_items $collection $item_path
+
+Renders a div of class list with a region per item.
+
+
+
+=cut
+
+private template 'no_items_found' => sub { outs(_("No items found.")) };
+
 private template 'list_items' => sub {
     my $self        = shift;
     my $collection  = shift;
     my $item_path   = shift;
     my $object_type = $self->object_type;
     if ( $collection->pager->total_entries == 0 ) {
-        outs( _("No items found") );
+        show('no_items_found');
     }
 
     div {
@@ -229,6 +446,14 @@
 
 };
 
+
+=head2 paging_top $collection $page_number
+
+Paging for your list, rendered at the top of the list
+
+=cut
+
+
 private template 'paging_top' => sub {
     my $self       = shift;
     my $collection = shift;
@@ -244,6 +469,12 @@
 
 };
 
+=head2 paging_bottom $collection $page_number
+
+Paging for your list, rendered at the bottom of the list
+
+=cut
+
 private template paging_bottom => sub {
     my $self       = shift;
     my $collection = shift;
@@ -254,7 +485,7 @@
             span {
                 { class is 'prev-page' };
                 hyperlink(
-                    label   => "Previous Page",
+                    label   => _("Previous Page"),
                     onclick => {
                         args => { page => $collection->pager->previous_page }
                     }
@@ -265,7 +496,7 @@
             span {
                 { class is 'next-page' };
                 hyperlink(
-                    label   => "Next Page",
+                    label   => _("Next Page"),
                     onclick =>
                         { args => { page => $collection->pager->next_page } }
                 );
@@ -274,6 +505,28 @@
     };
 };
 
+
+
+=head2 edit_item $action
+
+Renders the action $Action, handing it the array ref returned by L</display_columns>.
+
+=cut
+
+
+
+private template 'edit_item' => sub {
+    my $self = shift;
+    my $action = shift;
+    render_action($action, [$self->display_columns($action)]);
+};
+
+=head1 new_item
+
+The new_item template provides a form for creating new model records. See L<Jifty::Action::Record::Create>.
+
+=cut
+
 template 'new_item' => sub {
     my $self = shift;
     my ( $object_type, $id ) = ( $self->object_type, get('id') );
@@ -283,7 +536,7 @@
 
     div {
         { class is 'crud create item inline' };
-        render_action($create);
+        show('./edit_item', $create);
 
         outs(
             Jifty->web->form->submit(
@@ -306,5 +559,16 @@
         }
 };
 
+=head1 SEE ALSO
+
+L<Jifty::Action::Record::Create>, L<Jifty::Action::Record::Search>, L<Jifty::Action::Record::Update>, L<Jifty::Action::Record::Delete>, L<Template::Declare>, L<Jifty::View::Declare::Helpers>, L<Jifty::View::Declare>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
 1;
 

Added: jifty/branches/js-refactor/lib/Jifty/View/Declare/Compile.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/Compile.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,210 @@
+package Jifty::View::Declare::Compile;
+use strict;
+use base 'B::Deparse';
+use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
+	 OPf_WANT OPf_WANT_VOID OPf_WANT_SCALAR OPf_WANT_LIST
+	 OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL OPf_MOD
+	 OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
+	 OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY
+	 OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER
+	 OPpSORT_REVERSE OPpSORT_INPLACE OPpSORT_DESCEND OPpITER_REVERSED
+	 SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG
+         CVf_METHOD CVf_LOCKED CVf_LVALUE CVf_ASSERTION
+	 PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE PMf_SKIPWHITE
+	 PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED);
+BEGIN {
+    die "You need a custom version of B::Deparse from http://svn.jifty.org/svn/jifty.org/B/"
+        unless B::Deparse->can('e_method')
+}
+sub is_scope { goto \&B::Deparse::is_scope }
+sub is_state { goto \&B::Deparse::is_state }
+sub null { goto \&B::Deparse::null }
+
+sub padname {
+    my $self = shift;
+    my $targ = shift;
+    return substr($self->padname_sv($targ)->PVX, 1);
+}
+
+require CGI;
+our %TAGS = (
+    map { $_ => +{} }
+        map {@{$_||[]}} @CGI::EXPORT_TAGS{qw/:html2 :html3 :html4 :netscape :form/}
+);
+
+sub deparse {
+    my $self = shift;
+    my $ret = $self->SUPER::deparse(@_);
+    return '' if $ret =~ m/use (strict|warnings)/;
+    return $ret;
+}
+
+sub loop_common {
+    my $self = shift;
+    my($op, $cx, $init) = @_;
+    my $enter = $op->first;
+    my $kid = $enter->sibling;
+    if ($enter->name eq "enteriter") { # foreach
+	my $ary = $enter->first->sibling; # first was pushmark
+	my $var = $ary->sibling;
+
+	if ($ary->name eq 'null' and $enter->private & OPpITER_REVERSED) {
+	    # "reverse" was optimised away
+	    return $self->SUPER::loop_common(@_);
+	} elsif ($enter->flags & OPf_STACKED
+	    and not null $ary->first->sibling->sibling)
+	{
+	    return $self->SUPER::loop_common(@_);
+	} else {
+	    $ary = $self->deparse($ary, 1);
+	}
+
+	if (null $var) {
+	    if ($enter->flags & OPf_SPECIAL) { # thread special var
+		$var = $self->pp_threadsv($enter, 1);
+	    } else { # regular my() variable
+		$var = $self->padname($enter->targ);
+	    }
+	} elsif ($var->name eq "rv2gv") {
+	    $var = $self->pp_rv2sv($var, 1);
+	    if ($enter->private & OPpOUR_INTRO) {
+		# our declarations don't have package names
+		$var =~ s/^(.).*::/$1/;
+		$var = "our $var";
+	    }
+	} elsif ($var->name eq "gv") {
+	    $var = $self->deparse($var, 1);
+	    $var = '$' . $var if $var eq '_';
+	}
+	else {
+	    return $self->SUPER::loop_common(@_);
+	}
+
+
+	my $body = $kid->first->first->sibling; # skip OP_AND and OP_ITER
+	# statement() foreach (@foo);
+	if (!is_state $body->first and $body->first->name ne "stub") {
+	    Carp::confess unless $var eq '$_';
+	    $body = $body->first;
+	    return "$ary.each(function (\$_) {".$self->deparse($body, 2)."} )";
+	}
+	# XXX not handling cont block here yet
+	return "$ary.each(function ($var) {".$self->deparse($body, 0)."} )";
+    }
+    return $self->SUPER::loop_common(@_);
+}
+
+sub maybe_my {
+    my $self = shift;
+    my($op, $cx, $text) = @_;
+    if ($op->private & OPpLVAL_INTRO and not $self->{'avoid_local'}{$$op}) {
+	if (B::Deparse::want_scalar($op)) {
+	    return "var $text";
+	} else {
+	    return $self->maybe_parens_func("my", $text, $cx, 16);
+	}
+    } else {
+	return $text;
+    }
+}
+
+sub maybe_parens_func {
+    my $self = shift;
+    my($func, $text, $cx, $prec) = @_;
+    return "$func($text)";
+
+}
+
+sub const {
+    my $self = shift;
+    my($sv, $cx) = @_;
+    if (class($sv) eq "NULL") {
+       return 'null';
+    }
+    return $self->SUPER::const(@_);
+}
+
+sub pp_undef { 'null' }
+sub pp_sne { shift->binop(@_, "!=", 14) }
+sub pp_grepwhile { shift->mapop(@_, "grep") }
+
+sub mapop {
+    my $self = shift;
+    my($op, $cx, $name) = @_;
+    return $self->SUPER::mapop(@_) unless $name eq 'grep';
+    my($expr, @exprs);
+    my $kid = $op->first; # this is the (map|grep)start
+    $kid = $kid->first->sibling; # skip a pushmark
+    my $code = $kid->first; # skip a null
+    if (is_scope $code) {
+	$code = "{" . $self->deparse($code, 0) . "} ";
+    } else {
+	$code = $self->deparse($code, 24) . ", ";
+    }
+    $kid = $kid->sibling;
+    for (; !null($kid); $kid = $kid->sibling) {
+	$expr = $self->deparse($kid, 6);
+	push @exprs, $expr if defined $expr;
+    }
+    return "(".join(", ", @exprs).").select(function (\$_) $code)";
+}
+
+sub e_anoncode {
+    my ($self, $info) = @_;
+    my $text = $self->deparse_sub($info->{code});
+    return "function () " . $text;
+}
+
+sub e_anonhash {
+    my ($self, $info) = @_;
+    my @exprs = @{$info->{exprs}};
+    my @pairs;
+    while (my @p = splice(@exprs, 0, 2)) {
+	push @pairs, join(': ', map { $self->deparse($_, 6) } @p);
+    }
+    return '{' . join(", ", @pairs) . '}';
+}
+
+sub pp_entersub {
+    my $self = shift;
+    my $ret = $self->SUPER::pp_entersub(@_);
+    $ret =~ s/return\s*\((.*)\)/return [$1]/ if $ret =~ m/^attr/;
+
+    return $ret;
+}
+
+sub e_method {
+    my ($self, $info) = @_;
+    my $obj = $info->{object};
+    if ($obj->name eq 'const') {
+        $obj = $self->const_sv($obj)->PV;
+    }
+    else {
+        $obj = $self->deparse($obj, 24);
+    }
+
+    my $meth = $info->{method};
+    $meth = $self->deparse($meth, 1) if $info->{variable_method};
+    my $args = join(", ", map { $self->deparse($_, 6) } @{$info->{args}} );
+    my $kid = $obj . "." . $meth;
+    return $kid . "(" . $args . ")"; # parens mandatory
+}
+
+sub walk_lineseq {
+    my ($self, $op, $kids, $callback) = @_;
+    my $xcallback = $callback;
+    if ((!$op || $op->next->name eq 'grepwhile') && $kids->[-1]->name ne 'return') {
+	$callback = sub { my ($expr, $index) = @_;
+			  $expr = "return ($expr)" if $index == $#{$kids};
+			  $xcallback->($expr, $index) };
+    }
+    $self->SUPER::walk_lineseq($op, $kids, $callback);
+}
+
+sub compile_to_js {
+    my $class = shift;
+    my $code = shift;
+    return 'function() '.$class->new->coderef2text($code);
+}
+
+1;

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/CoreTemplates.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/CoreTemplates.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/CoreTemplates.pm	Tue Jul 31 17:23:52 2007
@@ -9,7 +9,7 @@
 
 =head1 NAME 
 
-Jifty::View::Declare::CoreTemplates
+Jifty::View::Declare::CoreTemplates - Templates Jifty can't function without
 
 =head1 DESCRIPTION
 

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/Handler.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/Handler.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/Handler.pm	Tue Jul 31 17:23:52 2007
@@ -10,7 +10,7 @@
 
 =head1 NAME
 
-Jifty::View::Declare::Handler
+Jifty::View::Declare::Handler - The Jifty view handler for Template::Declare
 
 =head1 METHODS
 

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/Helpers.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/Helpers.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/Helpers.pm	Tue Jul 31 17:23:52 2007
@@ -2,15 +2,14 @@
 use strict;
 
 package Jifty::View::Declare::Helpers;
-use base qw/Template::Declare/;
-use base qw/Exporter/;
 use Template::Declare::Tags;
+use base qw/Template::Declare Exporter/;
 
-our @EXPORT = ( qw(form hyperlink tangent redirect new_action form_submit form_return  form_next_page page wrapper request get set render_param current_user render_action render_region), @Template::Declare::Tags::EXPORT);
+our @EXPORT = ( qw(hyperlink tangent redirect new_action form_submit form_return form_next_page page content wrapper request get set render_param current_user render_action render_region), @Template::Declare::Tags::EXPORT);
 
 =head1 NAME
 
-Jifty::View::Declare::Helpers
+Jifty::View::Declare::Helpers - Additional subroutines for Jifty TD templates
 
 =head1 DESCRIPTION
 
@@ -30,7 +29,7 @@
 
  {
     no warnings qw/redefine/;
-    sub form (&) {
+    sub form (&;$) {
         my $code = shift;
 
         smart_tag_wrapper {
@@ -265,27 +264,44 @@
 
  template 'foo' => page {{ title is 'Foo' } ... };
 
+  or
+
+ template 'foo' => page { title => 'Foo' } content { ... };
+
 Renders an HTML page wrapped in L</wrapper>, after calling
 "/_elements/nav" and setting a content type. Generally, you shouldn't
 be using "/_elements/nav" but a Dispatcher rule instead.
 
+If C<page/content> calling convention is used, the return value of the
+first sub will be passed into wrapper as the second argument as a
+hashref, as well as the last argument for the content sub.
+
 =cut
 
-sub page (&) {
-    my $code = shift;
+sub page (&;$) {
+    unshift @_, undef if $#_ == 0;
+    my ( $meta, $code ) = @_;
     sub {
         my $self = shift;
         Jifty->handler->apache->content_type('text/html; charset=utf-8');
-        if ( my $wrapper = Jifty->app_class('View')->can('wrapper') ) {
-            $wrapper->(sub { $code->($self)});
-        } else {
-
-        wrapper(sub { $code->($self) });
+        my $wrapper = Jifty->app_class('View')->can('wrapper') || \&wrapper;
+        my @metadata = $meta ? $meta->() : ();
+        my $metadata = $#metadata == 0 ? $metadata[0] : {@metadata};
+        local *is::title = sub { warn "Can't use 'title is' when mixing mason and TD" };
+        $wrapper->( sub { $code->( $self, $metadata ) }, $metadata );
     }
-    };
 }
 
+=head2 content
 
+Helper function for page { ... } content { ... }
+
+=cut
+
+sub content (&;$) {
+    # XXX: Check for only 1 arg
+    return $_[0];
+}
 
 =head2 wrapper $coderef
 
@@ -294,7 +310,7 @@
 
 =cut
 
-sub wrapper ($) {
+sub wrapper {
     my $app_class = get_current_attr('PageClass') || 'View::Page';
     delete $Template::Declare::Tags::ATTRIBUTES{ 'PageClass' };
 
@@ -308,7 +324,8 @@
 
     my ($spa) = Jifty->find_plugin('Jifty::Plugin::SinglePage');
 
-    if ($spa) {
+    # XXX: spa hooks should be moved to page handlers
+    if ($spa && $page->allow_single_page) {
 	# If it's a single page app, we want to either render a
 	# wrapper and then get the region or render just the content
         if ( !Jifty->web->current_region ) {

Modified: jifty/branches/js-refactor/lib/Jifty/View/Declare/Page.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Declare/Page.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Declare/Page.pm	Tue Jul 31 17:23:52 2007
@@ -1,10 +1,7 @@
 package Jifty::View::Declare::Page;
 use strict;
 use warnings;
-use base qw/Template::Declare/;
-use Template::Declare::Tags;
-
-use base 'Class::Accessor::Fast';
+use base qw/Template::Declare Class::Accessor::Fast/;
 
 =head1 NAME
 
@@ -21,6 +18,13 @@
 use Jifty::View::Declare::Helpers;
 
 __PACKAGE__->mk_accessors(qw(content_code done_header _title));
+use constant allow_single_page => 1;
+
+=head2 new
+
+Sets up a new page class
+
+=cut
 
 sub new {
     my $class = shift;
@@ -44,8 +48,8 @@
 
     Template::Declare->new_buffer_frame;
     outs_raw(
-        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
-      . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">' );
+        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n"
+      . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">' . "\n" );
 
     $self->_render_header($self->_title || Jifty->config->framework('ApplicationName'));
 
@@ -54,19 +58,31 @@
     return '';
 };
 
+=head2 render_body $body_code
+
+Renders $body_code inside a body tag
+
+=cut
+
 sub render_body {
     my ($self, $body_code) = @_;
 
     body { $body_code->() };
 }
 
+=head2 render_page
+
+Renders the skeleton of the page
+
+=cut
+
 sub render_page {
     my $self = shift;
 
     div {
         div {
-            show 'salutation';
-            show 'menu';
+            show '/salutation';
+            show '/menu';
         };
         div {
             attr { id is 'content' };
@@ -89,6 +105,12 @@
     };
 }
 
+=head2 mk_title_handler
+
+Returns a coderef that will make headers for each thing passed to it
+
+=cut
+
 sub mk_title_handler {
     my $self = shift;
     return sub {
@@ -110,20 +132,37 @@
     };
 }
 
+=head2 render_title
+
+Renders the in-page title
+
+=cut
+
 sub render_title {
     my $self = shift;
     my $oldt = get('title');
     set( title => $self->_title );
-    show 'heading_in_wrapper';
+    show '/heading_in_wrapper';
     set( title => $oldt );
 }
 
+=head2 render_footer
+
+Renders the page footer and prepends the header to the L<Template::Declare> buffer.
+
+=cut
+
 sub render_footer {
     my $self = shift;
     outs_raw('</html>');
     Template::Declare->buffer->data( $self->done_header . Template::Declare->buffer->data );
 }
 
+=head2 render_pre_content_hook
+
+Renders the AdminMode alert (if AdminMode is on)
+
+=cut
 
 sub render_pre_content_hook {
     if ( Jifty->config->framework('AdminMode') ) {
@@ -139,9 +178,15 @@
     }
 }
 
+=head2 render_jifty_page_detritus
+
+Renders the keybinding and PubSub javascript as well as the wait message
+
+=cut
+
 sub render_jifty_page_detritus {
 
-    show('keybindings');
+    show('/keybindings');
     with( id => "jifty-wait-message", style => "display: none" ),
         div { _('Loading...') };
 
@@ -158,7 +203,7 @@
     my $title = shift || '';
     $title =~ s/<.*?>//g;    # remove html
     HTML::Entities::decode_entities($title);
-    with( title => $title ), show('header');
+    with( title => $title ), show('/header');
 }
 
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/View/Mason/Handler.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Mason/Handler.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Mason/Handler.pm	Tue Jul 31 17:23:52 2007
@@ -183,13 +183,13 @@
 }
 
 sub handle_comp {
-    my ($self, $comp) = (shift, shift);
+    my ($self, $comp, $args) = @_;
 
     # Set up the global
     my $r = Jifty->handler->apache;
     $self->interp->set_global('$r', $r);
 
-    my %args = $self->request_args($r);
+    my %args = $args ? %$args : $self->request_args($r);
 
     my @result;
     if (wantarray) {

Modified: jifty/branches/js-refactor/lib/Jifty/View/Static/Handler.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/View/Static/Handler.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/View/Static/Handler.pm	Tue Jul 31 17:23:52 2007
@@ -14,7 +14,7 @@
 
 =head1 NAME
 
-Jifty::View::Static::Handler
+Jifty::View::Static::Handler - Jifty view handler for static files
 
 head1 DESCRIPTION
 
@@ -24,7 +24,7 @@
 
 * Static files are served out of a separate root
 * If static files go through apache:
-    * How do we merge together the two static roots?
+    * How do we merge together the N static roots?
 * If static files go through Jifty::Handler
     * We need a flag to allow them to go through the dispatcher, too
     * return "True" (304) for if-modified-since
@@ -61,12 +61,19 @@
     }
     push @roots, (Jifty->config->framework('Web')->{DefaultStaticRoot});
 
-    my $self = {
-        roots => \@roots
-    };
-    bless $self, $class;
+    return bless { roots => \@roots }, $class;
 }
 
+=head2 roots
+
+Returns all the static roots the handler will search
+
+=cut
+
+sub roots {
+    my $self = shift;
+    return wantarray ? @{$self->{roots}} : $self->{roots};
+}
 
 =head2 show $path
 
@@ -155,7 +162,7 @@
     # Chomp a leading "/static" - should this be configurable?
     $file =~ s/^\/*?static//; 
 
-    foreach my $path ( @{$self->{'roots'}} ) {
+    foreach my $path ( $self->roots ) {
         my $abspath = Jifty::Util->absolute_path( File::Spec->catdir($path,$file ));
         # If the user is trying to request something outside our static root, 
         # decline the request

Modified: jifty/branches/js-refactor/lib/Jifty/Web.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web.pm	Tue Jul 31 17:23:52 2007
@@ -26,8 +26,12 @@
 );
 
 __PACKAGE__->mk_classdata($_)
-    for qw(cached_css        cached_css_digest        cached_css_time
-           cached_javascript cached_javascript_digest cached_javascript_time javascript_libs);
+    for qw(cached_css cached_css_digest cached_css_time
+           css_files  javascript_libs   external_javascript_libs);
+
+__PACKAGE__->css_files([qw( main.css )]);
+
+__PACKAGE__->external_javascript_libs([]);
 
 __PACKAGE__->javascript_libs([qw(
     yui/yahoo.js
@@ -44,6 +48,7 @@
     scriptaculous/builder.js
     scriptaculous/effects.js
     scriptaculous/controls.js
+    template_declare.js
     jifty.js
     jifty_utils.js
     jifty_subs.js
@@ -59,6 +64,8 @@
     app_behaviour.js
 )]);
 
+use Jifty::DBI::Class::Trigger;
+
 =head1 METHODS
 
 =head3 new
@@ -113,10 +120,6 @@
                 path => undef,
                 @_);
 
-    if ($args{'scheme'}) {
-        $self->log->error("Jifty->web->url no longer accepts a 'scheme' argument");
-    }
-
     my $uri;
 
     # Try to get a host out of the environment, useful in remote testing.
@@ -153,6 +156,10 @@
       $uri->port($port);
     }
 
+    if ( defined $args{'scheme'} ) {
+        $uri->scheme( $args{'scheme'} );
+    }
+
     if (defined $args{path}) {
       my $path = $args{path};
       # strip off leading '/' because ->canonical provides one
@@ -287,20 +294,65 @@
 
 sub handle_request {
     my $self = shift;
-    die _( "No request to handle" ) unless Jifty->web->request;
+    die _("No request to handle") unless Jifty->web->request;
+
+    my ( $valid_actions, $denied_actions )
+        = $self->_validate_request_actions();
+
+    # In the case where we have a continuation and want to redirect
+    if ( $self->request->continuation_path && Jifty->web->request->argument('_webservice_redirect') ) {
+        # for continuation - perform internal redirect under webservices
+	$self->webservices_redirect($self->request->continuation_path);
+        return;
+    }
+
+    $self->request->save_continuation;
+
+    unless ( $self->request->just_validating ) {
+        $self->_process_valid_actions($valid_actions);
+        $self->_process_denied_actions($denied_actions);
+    }
 
+    # If there's a continuation call, don't do the rest of this
+    return if $self->response->success and $self->request->call_continuation;
+
+    $self->redirect if $self->redirect_required;
+    $self->request->do_mapping;
+}
+
+sub _process_denied_actions {
+    my $self           = shift;
+    my $denied_actions = shift;
+
+    for my $request_action (@$denied_actions) {
+        my $action = $self->new_action_from_request($request_action);
+        $action->deny( "Access Denied for " . ref($action) );
+        $self->response->result( $action->moniker => $action->result );
+    }
+}
+
+sub _validate_request_actions {
+        my $self = shift;
+        
     my @valid_actions;
     my @denied_actions;
+
+
     for my $request_action ( $self->request->actions ) {
-        $self->log->debug("Found action ".$request_action->class . " " . $request_action->moniker);
+        $self->log->debug( "Found action "
+                . $request_action->class . " "
+                . $request_action->moniker );
         next unless $request_action->active;
         next if $request_action->has_run;
-        unless ( Jifty->api->is_allowed( $request_action->class ) ) {
-            $self->log->warn( "Attempt to call denied action '"
-                    . $request_action->class
-                    . "'" );
-            push @denied_actions, $request_action;
-            next;
+        unless ( $self->request->just_validating ) {
+            unless ( Jifty->api->is_allowed( $request_action->class ) ) {
+                $self->log->warn( "Attempt to call denied action '"
+                        . $request_action->class
+                        . "'" );
+                Carp::cluck;
+                push @denied_actions, $request_action;
+                next;
+            }
         }
 
         # Make sure we can instantiate the action
@@ -311,16 +363,21 @@
         # Try validating -- note that this is just the first pass; as
         # actions are run, they may fill in values which alter
         # validation of later actions
-        $self->log->debug("Validating action ".ref($action). " ".$action->moniker);
+        $self->log->debug( "Validating action " . ref($action) . " " . $action->moniker );
         $self->response->result( $action->moniker => $action->result );
         $action->validate;
 
         push @valid_actions, $request_action;
     }
-    $self->request->save_continuation;
+    
+    return (\@valid_actions, \@denied_actions);
 
-    unless ( $self->request->just_validating ) {
-        for my $request_action (@valid_actions) {
+}
+
+sub _process_valid_actions {
+    my  $self = shift;
+    my $valid_actions = shift;
+        for my $request_action (@$valid_actions) {
 
             # Pull the action out of the request (again, since
             # mappings may have affected parameters).  This
@@ -329,20 +386,26 @@
             # actions (Jifty::Request::Mapper)
             my $action = $self->new_action_from_request($request_action);
             next unless $action;
-            if ($request_action->modified) {
+            if ( $request_action->modified ) {
+
                 # If the request's action was changed, re-validate
-                $action->result(Jifty::Result->new);
-                $action->result->action_class(ref $action);
-                $self->response->result( $action->moniker => $action->result );
-                $self->log->debug("Re-validating action ".ref($action). " ".$action->moniker);
+                $action->result( Jifty::Result->new );
+                $action->result->action_class( ref $action );
+                $self->response->result(
+                    $action->moniker => $action->result );
+                $self->log->debug( "Re-validating action "
+                        . ref($action) . " "
+                        . $action->moniker );
                 next unless $action->validate;
             }
 
-            $self->log->debug("Running action ".ref($action). " ".$action->moniker);
+            $self->log->debug(
+                "Running action " . ref($action) . " " . $action->moniker );
             eval { $action->run; };
             $request_action->has_run(1);
 
             if ( my $err = $@ ) {
+
                 # Poor man's exception propagation; we need to get
                 # "LAST RULE" and "ABORT" exceptions back up to the
                 # dispatcher.  This is specifically for redirects from
@@ -352,7 +415,8 @@
                 $action->result->error(
                     Jifty->config->framework("DevelMode")
                     ? $err
-                    : _("There was an error completing the request.  Please try again later.")
+                    : _("There was an error completing the request.  Please try again later."
+                    )
                 );
             }
 
@@ -360,20 +424,8 @@
             # may have yielded.
             $self->request->do_mapping;
         }
-        for my $request_action (@denied_actions) {
-            my $action = $self->new_action_from_request($request_action);
-            $action->deny("Access Denied for ".ref($action));
-            $self->response->result( $action->moniker => $action->result );
-        }
-    }
-
-    # If there's a continuation call, don't do the rest of this
-    return if $self->response->success and $self->request->call_continuation;
-
-    $self->redirect if $self->redirect_required;
-    $self->request->do_mapping;
-}
 
+    }
 =head3 request [VALUE]
 
 Gets or sets the current L<Jifty::Request> object.
@@ -398,7 +450,9 @@
 
 =head3 new_action class => CLASS, moniker => MONIKER, order => ORDER, arguments => PARAMHASH
 
-Creates a new action (an instance of a subclass of L<Jifty::Action>). The named arguments passed to this method are passed on to the C<new> method of the action named in C<CLASS>.
+Creates a new action (an instance of a subclass of
+L<Jifty::Action>). The named arguments passed to this method are
+passed on to the C<new> method of the action named in C<CLASS>.
 
 =head3 Arguments
 
@@ -463,17 +517,6 @@
     $class = Jifty->api->qualify($class);
 
     my $loaded = Jifty::Util->require( $class );
-    $args{moniker} ||= ($loaded ? $class : 'Jifty::Action')->_generate_moniker;
-
-    my $action_in_request = $self->request->action( $args{moniker} );
-
-    # Fields explicitly passed to new_action take precedence over those passed
-    # from the request; we read from the request to implement "sticky fields".
-    #
-    if ( $action_in_request and $action_in_request->arguments ) {
-        $args{'request_arguments'} = $action_in_request->arguments;
-    }
-
     # The implementation class is provided by the client, so this
     # isn't a "shouldn't happen"
     return unless $loaded;
@@ -588,13 +631,34 @@
     }
 }
 
+=head3 webservices_redirect [TO]
+
+Handle redirection inside webservices call.  This is meant to be
+hooked by plugin that knows what to do with it.
+
+=cut
+
+sub webservices_redirect {
+    my ( $self, $to ) = @_;
+    # XXX: move to singlepage plugin
+    my ($spa) = Jifty->find_plugin('Jifty::Plugin::SinglePage') or return;
+
+    Jifty->web->request->remove_state_variable( 'region-'.$spa->region_name );
+    Jifty->web->request->add_fragment(
+        name      => $spa->region_name,
+        path      => $to,
+        arguments => {},
+        wrapper   => 0
+    );
+}
+
 =head3 redirect [TO]
 
 Redirect to the next page. If you pass this method a parameter, it
 redirects to that URL rather than B<next_page>.
 
 It creates a continuation of where you want to be, and then calls it.
-If you want to redirect to a parge with parameters, pass in a
+If you want to redirect to a page with parameters, pass in a
 L<Jifty::Web::Form::Clickable> object.
 
 =cut
@@ -624,8 +688,7 @@
 
     # To submit a Jifty::Action::Redirect, we don't need to serialize a continuation,
     # unlike any other kind of actions.
-    
-    
+
     my $redirect_to_url = '' ;
 
     if (  (grep { not $_->action_class->isa('Jifty::Action::Redirect') }
@@ -675,21 +738,23 @@
     my $self = shift;
     my ($page) = @_;
 
-
-
     # It's an experimental feature to support redirect within a
-    # region.  It's currently enabled only for SPA.  We should make
-    # sure we understand what existing code is call this kind of replace.
-    my ($spa) = Jifty->find_plugin('Jifty::Plugin::SinglePage');
-
-    if ($spa && $self->current_region) { 
+    # region.
+    if ($self->current_region) { 
         # If we're within a region stack, we don't really want to
-        # redirect. We want to redispatch.
+        # redirect. We want to redispatch.  Also reset the things
+        # applied on beofre.
+        local $self->{navigation} = undef;
+        local $self->{page_navigation} = undef;
         $self->replace_current_region($page);
         Jifty::Dispatcher::_abort;
         return;
     }
 
+    if (my $redir = Jifty->web->request->argument('_webservice_redirect')) {
+	push @$redir, $page;
+	return;
+    }
     # $page can't lead with // or it assumes it's a URI scheme.
     $page =~ s{^/+}{/};
 
@@ -745,6 +810,10 @@
 sub tangent {
     my $self = shift;
 
+    if (@_ == 1  ) {
+        Jifty->log->error("Jifty::Web->tangent takes a paramhash. Perhaps you passed '".$_[0]."' , rather than 'url => ".$_[0]."'");
+        die; 
+    }
     my $clickable = Jifty::Web::Form::Clickable->new(
         returns        => { },
         preserve_state => 1,
@@ -1011,6 +1080,20 @@
     return '';
 }
 
+=head3 add_css FILE1, FILE2, ...
+
+Pushes files onto C<Jifty->web->css_files>
+
+=cut
+
+sub add_css {
+    my $self = shift;
+    Jifty->web->css_files([
+        @{ Jifty->web->css_files },
+        @_
+    ]);
+}
+
 =head3 generate_css
 
 Checks if the compressed CSS is generated, and if it isn't, generates
@@ -1026,29 +1109,15 @@
     {
         Jifty->log->debug("Generating CSS...");
         
-        my $app   = File::Spec->catdir(
-                        Jifty->config->framework('Web')->{'StaticRoot'},
-                        'css'
-                    );
-
-        my $jifty = File::Spec->catdir(
-                        Jifty->config->framework('Web')->{'DefaultStaticRoot'},
-                        'css'
-                    );
-
-        my $file = Jifty::Util->absolute_path(
-                        File::Spec->catpath( '', $app, 'main.css' )
-                   );
-
-        if ( not -e $file ) {
-            $file = Jifty::Util->absolute_path(
-                         File::Spec->catpath( '', $jifty, 'main.css' )
-                    );
-        }
+        my @roots = map { Jifty::Util->absolute_path( File::Spec->catdir( $_, 'css' ) ) }
+                        Jifty->handler->view('Jifty::View::Static::Handler')->roots;
 
-        CSS::Squish->roots( Jifty::Util->absolute_path( $app ), $jifty );
+        CSS::Squish->roots( @roots );
         
-        my $css = CSS::Squish->concatenate( $file );
+        my $css = CSS::Squish->concatenate(
+            map { CSS::Squish->_resolve_file( $_, @roots ) }
+                @{ $self->css_files }
+        );
 
         __PACKAGE__->cached_css( $css );
         __PACKAGE__->cached_css_digest( md5_hex( $css ) );
@@ -1085,98 +1154,30 @@
 directly.
 
 Jifty will look for javascript libraries under share/web/static/js/ by
-default.
+default as well as any plugin static roots.
 
 =cut
 
 sub include_javascript {
     my $self  = shift;
-    my ($ccjs) = Jifty->find_plugin('Jifty::Plugin::CompressedCSSandJS');
-    if ( $ccjs && $ccjs->js_enabled ) {
-        $self->generate_javascript;
+
+    for my $url ( @{ __PACKAGE__->external_javascript_libs } ) {
         $self->out(
-            qq[<script type="text/javascript" src="/__jifty/js/]
-            . __PACKAGE__->cached_javascript_digest . qq[.js"></script>]
+            qq[<script type="text/javascript" src="$url"></script>\n]
         );
     }
-    else {
-        for my $file ( @{ __PACKAGE__->javascript_libs } ) {
-            $self->out(
-                qq[<script type="text/javascript" src="/static/js/$file"></script>\n]
-            );
-        }
-    }
-    
-    return '';
-}
 
-=head3 generate_javascript
+    # if there's no trigger, 0 is returned.  if aborted/handled, undef
+    # is returned.
+    defined $self->call_trigger('include_javascript', @_) or return '';
 
-Checks if the compressed JS is generated, and if it isn't, generates
-and caches it.
-
-=cut
-
-sub generate_javascript {
-    my $self = shift;
-    
-    if (not defined __PACKAGE__->cached_javascript_digest
-            or Jifty->config->framework('DevelMode'))
-    {
-        Jifty->log->debug("Generating JS...");
-        
-        my @roots = (
-            Jifty::Util->absolute_path(
-                File::Spec->catdir(
-                    Jifty->config->framework('Web')->{'StaticRoot'},
-                    'js'
-                )
-            ),
-
-            Jifty::Util->absolute_path(
-                File::Spec->catdir(
-                    Jifty->config->framework('Web')->{'DefaultStaticRoot'},
-                    'js'
-                )
-            ),
+    for my $file ( @{ __PACKAGE__->javascript_libs } ) {
+        $self->out(
+            qq[<script type="text/javascript" src="/static/js/$file"></script>\n]
         );
-        
-        my $js = "";
-
-        for my $file ( @{ __PACKAGE__->javascript_libs } ) {
-            my $include;
-        
-            for my $root (@roots) {
-                my @spec = File::Spec->splitpath( $root, 1 );
-                my $path = File::Spec->catpath( @spec[0,1], $file );
-                
-                if ( -e $path ) {
-                    $include = $path;
-                    last;
-                }
-            }
-
-            if ( defined $include ) {
-                my $fh;
-
-                if ( open $fh, '<', $include ) {
-                    $js .= "/* Including '$file' */\n\n";
-                    $js .= $_ while <$fh>;
-                    $js .= "\n/* End of '$file' */\n\n";
-                }
-                else {
-                    $js .= "\n/* Unable to open '$file': $! */\n";
-                }
-            }
-            else {
-                $js .= "\n/* Unable to find '$file' */\n";
-            }
-        }
-
-        __PACKAGE__->cached_javascript( $js );
-        __PACKAGE__->cached_javascript_digest( md5_hex( $js ) );
-        __PACKAGE__->cached_javascript_time( time );
     }
+
+    return '';
 }
 
 =head3 add_javascript FILE1, FILE2, ...
@@ -1193,6 +1194,20 @@
     ]);
 }
 
+=head3 add_external_javascript URL1, URL2, ...
+
+Pushes urls onto C<Jifty->web->external_javascript_libs>
+
+=cut
+
+sub add_external_javascript {
+    my $self = shift;
+    Jifty->web->external_javascript_libs([
+        @{ Jifty->web->external_javascript_libs },
+        @_
+    ]);
+}
+
 =head2 STATE VARIABLES
 
 =head3 get_variable NAME

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form.pm	Tue Jul 31 17:23:52 2007
@@ -7,6 +7,12 @@
 
 __PACKAGE__->mk_accessors(qw(actions printed_actions name call is_open disable_autocomplete target submit_to));
 
+=head1 NAME
+
+Jifty::Web::Form - Tools for rendering and dealing with HTML forms
+
+=head1 METHODS
+
 =head2 new ARGS
 
 Creates a new L<Jifty::Web::Form>.  Arguments:
@@ -217,8 +223,18 @@
 
 sub submit {
     my $self = shift;
+    my %args = (submit => undef,
+                _form => $self,
+                @_);
+
+    my @submit = ref($args{'submit'}) eq 'ARRAY' ? @{$args{'submit'}} : $args{'submit'};
+    if ($self->actions->{'next_page'} && $submit[0] && ! grep {$_->moniker eq 'next_page' } @submit)  {
+        push @submit, $self->actions->{'next_page'};
+        $args{'submit'} = \@submit;
+    }
+
 
-    my $button = Jifty::Web::Form::Clickable->new(submit => undef, @_)->generate;
+    my $button = Jifty::Web::Form::Clickable->new(%args)->generate;
     Jifty->web->out(qq{<div class="submit_button">});
     $button->render_widget;
     Jifty->web->out(qq{</div>});

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Clickable.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Clickable.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Clickable.pm	Tue Jul 31 17:23:52 2007
@@ -455,11 +455,25 @@
           escape_label => $self->escape_label,
           url          => $self->complete_url,
           target       => $self->target,
+          continuation => $self->_continuation,
           @_ }
     );
     return $link;
 }
 
+sub _continuation {
+    # continuation info used by the update() call on client side
+    my $self = shift;
+    if ($self->call) {
+	return { 'type' => 'call', id => $self->call };
+    }
+    if ($self->returns) {
+	return { 'create' => $self->url };
+    }
+
+    return {};
+}
+
 =head2 as_button
 
 Returns the clickable as a L<Jifty::Web::Form::Field::InlineButton>,
@@ -476,6 +490,7 @@
     my $field = Jifty::Web::Form::Field->new(
         { %$args,
           type => 'InlineButton',
+          continuation => $self->_continuation,
           @_ }
     );
     my %parameters = $self->post_parameters;

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Element.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Element.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Element.pm	Tue Jul 31 17:23:52 2007
@@ -149,8 +149,8 @@
 
 =cut
 
-sub accessors { shift->handlers, qw(class key_binding key_binding_label id label tooltip) }
-__PACKAGE__->mk_accessors(qw(_onclick class key_binding key_binding_label id label tooltip));
+sub accessors { shift->handlers, qw(class key_binding key_binding_label id label tooltip continuation) }
+__PACKAGE__->mk_accessors(qw(_onclick class key_binding key_binding_label id label tooltip continuation));
 
 =head2 new PARAMHASH OVERRIDE
 
@@ -236,7 +236,10 @@
         next unless $value;
 
         my @fragments;
-        my $actions;    # Maps actions => disable?
+            # if $actions is undef, that means we're submitting _all_ actions in the clickable
+            # if $actions is defined but empty, that means we're submitting no actions
+            # if $actions is not empty, we're submitting those actions
+        my $actions = {};    # Maps actions => disable?
         my $confirm;
         my $beforeclick;
 
@@ -245,12 +248,14 @@
             my %args;
 
             # Submit action
-            if ( $hook->{submit} ) {
-                %$actions = ();
-                my $disable = exists $hook->{disable} ? $hook->{disable} : 1;
+          
+            
+            if ( exists $hook->{submit} ) {
+                $actions = undef;
+                my $disable_form_on_click = exists $hook->{disable} ? $hook->{disable} : 1;
                 # Normalize to 1/0 to pass to JS
-                $disable = $disable ? 1 : 0;
-                $actions->{$_} = $disable for (@{ $hook->{submit} }); 
+                $disable_form_on_click = $disable_form_on_click ? 1 : 0;
+                $actions->{$_} = $disable_form_on_click for (@{ $hook->{submit} || [] }); 
             }
 
             $hook->{region} ||= Jifty->web->qualified_region;
@@ -320,7 +325,8 @@
         my $string = join ";", (grep {not ref $_} (ref $value eq "ARRAY" ? @{$value} : ($value)));
         if (@fragments or (!$actions || %$actions)) {
 
-            my $update = Jifty->web->escape("update( ". Jifty::JSON::objToJson( {actions => $actions, fragments => \@fragments }, {singlequote => 1}) .", this );");
+            my $update = Jifty->web->escape("update( ". Jifty::JSON::objToJson( {actions => $actions, fragments => \@fragments, continuation => $self->continuation }, {singlequote => 1}) .", this );");
+            $string .= 'if(event.ctrlKey||event.metaKey||event.altKey||event.shiftKey) return true; ';
             $string .= $self->javascript_preempt ? "return $update" : "$update; return true;";
         }
         if ($confirm) {

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field.pm	Tue Jul 31 17:23:52 2007
@@ -79,8 +79,9 @@
 
     # If they key and/or value imply that this argument is going to be
     # a mapped argument, then do the mapping and mark the field as hidden.
+    # but ignore that if the field is a container in the model
     my ($key, $value) = Jifty::Request::Mapper->query_parameters($self->input_name, $self->current_value);
-    if ($key ne $self->input_name) {
+    if ($key ne $self->input_name && !$self->action->arguments->{$self->name}{container}) {
         Jifty::Util->require('Jifty::Web::Form::Field::Hidden');
         bless $self, "Jifty::Web::Form::Field::Hidden";
         $self->input_name($key);

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Button.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Button.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Button.pm	Tue Jul 31 17:23:52 2007
@@ -9,7 +9,7 @@
 
 =head1 NAME
 
-Jifty::Web::Form::Field::Button
+Jifty::Web::Form::Field::Button - Add buttons to your forms
 
 =cut
 

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Checkbox.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Checkbox.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Checkbox.pm	Tue Jul 31 17:23:52 2007
@@ -6,6 +6,12 @@
 use base qw/Jifty::Web::Form::Field/;
 __PACKAGE__->mk_accessors(qw/checked value/);
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Checkbox - Add checkboxes to your forms
+
+=head1 METHODS
+
 =head2 accessors
 
 Provide C<checked> and C<value> accessors, in addition to

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Combobox.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Combobox.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Combobox.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Combobox - Add comboboxes to your forms
+
+=head1 METHODS
+
 =head2 render_widget
 
 Renders the select widget.

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Date.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Date.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Date.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Date - Add date pickers to your forms
+
+=head1 METHODS
+
 =head2 classes
 
 Output date fields with the class 'date'

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Hidden.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Hidden.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Hidden.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Hidden - Add hidden fields to your forms
+
+=head1 METHODS
+
 =head2 type
 
 The HTML input type is C<hidden>.

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/InlineButton.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/InlineButton.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/InlineButton.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,11 @@
 
 use base qw/Jifty::Web::Form::Field::Button/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::InlineButton - Add buttons without a form
+
+=head1 METHODS
 
 =head2 render
 

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Password.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Password.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Password.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Password - Add a password field to your forms
+
+=head1 METHODS
+
 =head2 type
 
 The HTML input type is C<password>.

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Radio.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Radio.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Radio.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Radio - Add a radio button to your forms
+
+=head1 METHODS
+
 =head2 render_widget
 
 Renders the whole radio button collection.

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/ResetButton.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/ResetButton.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/ResetButton.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,11 @@
 
 use base qw/Jifty::Web::Form::Field::InlineButton/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::ResetButton - Add a reset button to your forms
+
+=head1 METHODS
 
 =head2 render_widget
 

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Select.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Select.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Select.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Select - Add a list/popup menu control to your forms
+
+=head1 METHODS
+
 =head2 render_widget
 
 Renders the select widget.

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Text.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Text.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Text.pm	Tue Jul 31 17:23:52 2007
@@ -8,6 +8,8 @@
 
 Jifty::Web::Form::Field::Text - Renders as a small text field
 
+=head1 METHODS
+
 =cut
 
 our $VERSION = 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Textarea.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Textarea.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Textarea.pm	Tue Jul 31 17:23:52 2007
@@ -7,6 +7,12 @@
 
 __PACKAGE__->mk_accessors(qw(rows cols));
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Textarea - Add a multiline text field to your forms
+
+=head1 METHODS
+
 =head2 accessors
 
 Provide C<rows> and C<cols> accessors, in addition to

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Unrendered.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Unrendered.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Unrendered.pm	Tue Jul 31 17:23:52 2007
@@ -5,6 +5,12 @@
 
 use base qw/Jifty::Web::Form::Field/;
 
+=head1 NAME
+
+Jifty::Web::Form::Field::Unrendered - Handle unrendered fields
+
+=head1 METHODS
+
 =head2 render
 
 Don't render anything. For one reason or another, this form field should never, ever be rendered, perhaps

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Upload.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Upload.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Field/Upload.pm	Tue Jul 31 17:23:52 2007
@@ -44,4 +44,15 @@
     '';
 }
 
+=head2 classes
+
+Add 'upload' to the rest of the classes
+
+=cut
+
+sub classes {
+    my $self = shift;
+    return join(' ', 'upload', ($self->SUPER::classes));
+}
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Form/Link.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Form/Link.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Form/Link.pm	Tue Jul 31 17:23:52 2007
@@ -101,12 +101,12 @@
 
     my $tooltip = $self->tooltip;
     $tooltip = Jifty->web->escape( $tooltip )
-        if ( $tooltip and $self->escape_label );
+        if ( defined $tooltip and $self->escape_label );
 
     Jifty->web->out(qq(<a));
     Jifty->web->out(qq( id="@{[$self->id]}"))         if $self->id;
     Jifty->web->out(qq( class="@{[$self->class]}"))   if $self->class;
-    Jifty->web->out(qq( title="@{[$self->tooltip]}")) if $tooltip;
+    Jifty->web->out(qq( title="@{[$tooltip]}"))       if defined $tooltip;
     Jifty->web->out(qq( target="@{[$self->target]}")) if $self->target;
     Jifty->web->out(qq( accesskey="@{[$self->key_binding]}")) if $self->key_binding;
     Jifty->web->out(qq( href="@{[Jifty->web->escape($self->url)]}"));

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Menu.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Menu.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Menu.pm	Tue Jul 31 17:23:52 2007
@@ -6,6 +6,12 @@
 
 __PACKAGE__->mk_accessors(qw(label parent sort_order link target escape_label class));
 
+=head1 NAME
+
+Jifty::Web::Menu - Handle the API for menu navigation
+
+=head1 METHODS
+
 =head2 new PARAMHASH
 
 Creates a new L<Jifty::Web::Menu> object.  Possible keys in the

Modified: jifty/branches/js-refactor/lib/Jifty/Web/PageRegion.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/PageRegion.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/PageRegion.pm	Tue Jul 31 17:23:52 2007
@@ -215,7 +215,7 @@
             $self->argument($2 => $value);
         }
         if ($key =~ /^region-(.*)$/ and $1 eq $self->qualified_name and $value ne $self->default_path) {
-            $self->path($value);
+            $self->path(URI::Escape::uri_unescape($value));
         }
 
         # We should always inherit the state variables from the uplevel request.
@@ -316,6 +316,10 @@
 	# clone.
 	my ($path, $arg) = split(/\?/, $self->path, 2);
 	$subrequest->path( $path );
+	my %args = (map { split /=/, $_ } split /&/, $arg);
+	if ($args{'J:C'}) {
+	    $subrequest->continuation($args{'J:C'});
+	}
     }
     # Remove all of the actions
     unless ($enable_actions) {
@@ -380,4 +384,30 @@
     return "#region-" . $self->qualified_name . ' ' . join(' ', @_);
 }
 
+my $can_compile = eval 'use Jifty::View::Declare::Compile; 1' ? 1 : 0;
+
+=head2 client_cacheable
+
+=cut
+
+sub client_cacheable {
+    my $self = shift;
+    return unless $can_compile;
+
+    return Jifty::View::Declare::BaseClass->client_cacheable($self->path);
+}
+
+=head2 client_cacheable
+
+=cut
+
+sub client_cache_content {
+    my $self = shift;
+    return unless $can_compile;
+
+    return Jifty::View::Declare::Compile->compile_to_js(
+        Jifty::View::Declare::BaseClass->_actual_td_code($self->path)
+    );
+}
+
 1;

Modified: jifty/branches/js-refactor/lib/Jifty/Web/Session.pm
==============================================================================
--- jifty/branches/js-refactor/lib/Jifty/Web/Session.pm	(original)
+++ jifty/branches/js-refactor/lib/Jifty/Web/Session.pm	Tue Jul 31 17:23:52 2007
@@ -10,7 +10,14 @@
 
 Jifty::Web::Session - A Jifty session handler
 
-=cut
+=head1 SYNOPSIS
+
+In your F<etc/config.yml> (optional):
+
+  framework:
+    Web:
+      # The default ($PORT is replaced by the port the app is running on)
+      SessionCookieName: JIFTY_SID_$PORT
 
 =head2 new
 
@@ -22,12 +29,13 @@
     my $class = shift;
 
     my $session_class = Jifty->config->framework('Web')->{'SessionClass'};
+    my $cookie_name   = Jifty->config->framework('Web')->{'SessionCookieName'};
     if ($session_class and $class ne $session_class) {
         Jifty::Util->require( $session_class );
         return $session_class->new(@_);
     }
     else {
-        return bless {}, $class;
+        return bless { _cookie_name => $cookie_name }, $class;
     }
 }
 
@@ -68,6 +76,44 @@
     $self->{cache} = undef;
 }
 
+=head2 load_by_kv key => value OR key, default, callback
+
+Load up the current session from the given (key, value) pair. If no matching
+session could be found, it will create a new session with the key, value set.
+Be sure that what you're loading by is unique. If you're loading a session
+based on, say, a timestamp, then you're asking for trouble.
+
+The second form is used when you have a complex value. Each value for the
+given key is passed to the callback. The callback should return true if
+there's a match. The default value is used to create a new entry when no
+value was chosen.
+
+=cut
+
+sub load_by_kv {
+    my $self = shift;
+    my $k = shift;
+    my $v = shift;
+    my $callback = shift || sub { $_[0] eq $v };
+    my $session_id;
+
+    # tried doing this with load_by_cols but it never returned any rows
+    my $sessions = Jifty::Model::SessionCollection->new;
+    $sessions->limit(column => 'key_type', value => 'key');
+    $sessions->limit(column => 'data_key', value => $k );
+
+    while (my $row = $sessions->next) {
+        my $match = $callback->($row->value);
+        if ($match) {
+            $session_id = $row->session_id;
+            last;
+        }
+    }
+
+    $self->load($session_id);
+    $self->set($k => $v) if !$session_id;
+}
+
 sub _get_session_id_from_client {
         my $self = shift;
         my %cookies    = CGI::Cookie->fetch();
@@ -276,8 +322,6 @@
 
     my $cookie_name = $self->cookie_name;
     my %cookies     = CGI::Cookie->fetch();
-    my $session_id
-        = $cookies{$cookie_name} ? $cookies{$cookie_name}->value() : undef;
     my $cookie = new CGI::Cookie(
         -name    => $cookie_name,
         -value   => $self->id,
@@ -302,7 +346,9 @@
 
 sub cookie_name {
     my $self = shift;
-    my $cookie_name = "JIFTY_SID_" . ( $ENV{'SERVER_PORT'} || 'NOPORT' );
+    my $cookie_name = $self->{'_cookie_name'};
+    my $port = ( $ENV{'SERVER_PORT'} || 'NOPORT' );
+    $cookie_name =~ s/\$PORT/$port/g;
     return ($cookie_name);
 }
 

Modified: jifty/branches/js-refactor/plugins/CodePress/Makefile.PL
==============================================================================
--- jifty/branches/js-refactor/plugins/CodePress/Makefile.PL	(original)
+++ jifty/branches/js-refactor/plugins/CodePress/Makefile.PL	Tue Jul 31 17:23:52 2007
@@ -1,6 +1,6 @@
 use inc::Module::Install;
 name('Jifty-Plugin-CodePress');
-version('0.01');
+version('0.02');
 requires('Jifty' => '0.70422');
 
 install_share;

Modified: jifty/branches/js-refactor/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm
==============================================================================
--- jifty/branches/js-refactor/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm	(original)
+++ jifty/branches/js-refactor/plugins/CodePress/lib/Jifty/Plugin/CodePress.pm	Tue Jul 31 17:23:52 2007
@@ -4,6 +4,8 @@
 package Jifty::Plugin::CodePress;
 use base qw/Jifty::Plugin/;
 
+our $VERSION = '0.02';
+
 =head1 NAME
 
 Jifty::Plugin::CodePress
@@ -27,14 +29,6 @@
 	render_as => 'Jifty::Plugin::CodePress::Textarea',
   );
 
-  Jifty->web->submit( 
-	label   => _("Save"), 
-    onclick => [
-      { beforeclick => "CodePress.beforeSubmit();" },
-      { args => .... }
-    ]
-  );
-
 or if you are using L<Template::Declare>
 
   render_param(
@@ -44,14 +38,6 @@
 	render_as => 'Jifty::Plugin::CodePress::Textarea',
   );
 
-  form_submit(
-	label => _("Save"),
-	onclick => [
-		{ beforeclick => "CodePress.beforeSubmit();" },
-		{ args => .... },
-	],
-  );
-
 =head1 VERSION
 
 Created from L<https://codepress.svn.sourceforge.net/svnroot/codepress/trunk/stable>
@@ -60,7 +46,10 @@
 This involved some hard-coding of paths (because automatic path detection
 from CodePress doesn't work well with Jifty's expectation of JavaScript code
 in C</js/>), addition of C<CodePress.instances> object to track all
-instances and CodePress.beforeSubmit which prepare form for submission.
+instances and additional JavaScript event handling using C<DOM.Events>
+to remove requirement to call C<CodePress.beforeSubmit> from form submit
+(If you want you can still call it, and it will turn all CodePress editors
+back to textarea).
 
 This also side-stepped problem with original calling schematic which created
 functions with names from element ids. This was problematic with Jifty

Modified: jifty/branches/js-refactor/plugins/CodePress/share/web/static/js/codepress.js
==============================================================================
--- jifty/branches/js-refactor/plugins/CodePress/share/web/static/js/codepress.js	(original)
+++ jifty/branches/js-refactor/plugins/CodePress/share/web/static/js/codepress.js	Tue Jul 31 17:23:52 2007
@@ -35,6 +35,28 @@
 		self.style.position = 'static';
 		self.style.visibility = 'visible';
 		self.style.display = 'inline';
+
+		// where blur event is delivered?
+		var iframe = self;
+		if (self.contentDocument) {          // For NS6
+			iframe = self.contentDocument;
+		} else if (self.contentWindow) {     // For IE5.5 and IE6
+			//iframe = self.contentWindow.document;
+		} else if (self.document) {          // For IE5
+			iframe = self.document;
+		} else {
+			alert("can't find frame");
+		}
+
+		DOM.Events.addListener( iframe, 'blur', function () {
+			self.textarea.value = self.getCode();
+			self.textarea.disabled = false;
+			return self;
+		});
+		DOM.Events.addListener( iframe, 'focus', function () {
+			self.textarea.disabled = true;
+			return self;
+		});
 	}
 	
 	// obj can by a textarea id or a string (code)
@@ -43,8 +65,9 @@
 		if(!self.textarea.disabled) return;
 		self.language = language ? language : self.getLanguage();
 		self.src = '/static/codepress/codepress.html?language='+self.language+'&ts='+(new Date).getTime();
-		if(self.attachEvent) self.attachEvent('onload',self.initialize);
-		else self.addEventListener('load',self.initialize,false);
+		//if(self.attachEvent) self.attachEvent('onload',self.initialize);
+		//else self.addEventListener('load',self.initialize,false);
+		DOM.Events.addListener(self, 'load', self.initialize);
 	}
 
 	self.getLanguage = function() {
@@ -134,15 +157,15 @@
 
 CodePress.beforeSubmit = function() {
 	for (instance in CodePress.instances)  {
-		//CodePress.instances[ instance ].toggleEditor();
-		var i = CodePress.instances[ instance ];
-		i.textarea.value = i.getCode();
-		i.textarea.disabled = false;
-		i.style.display = 'none';
-		i.textarea.style.display = 'inline';
+		// consider just Jifty fields
+		if ( instance.substr(0,5) == 'J:A:F' ) {
+			CodePress.instances[ instance ].toggleEditor();
+		}
 	}
 }
 
 //if(window.attachEvent) window.attachEvent('onload',CodePress.run);
 //else window.addEventListener('DOMContentLoaded',CodePress.run,false);
-DOM.Events.addListener(window, "load", CodePress.run);
+//DOM.Events.addListener(window, "load", CodePress.run);
+// Jifty-specific onLoad hook
+onLoadHook( 'CodePress.run();' );

Modified: jifty/branches/js-refactor/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html
==============================================================================
--- jifty/branches/js-refactor/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html	(original)
+++ jifty/branches/js-refactor/share/plugins/Jifty/Plugin/AdminUI/web/templates/__jifty/admin/index.html	Tue Jul 31 17:23:52 2007
@@ -19,7 +19,7 @@
 <h2><% _('Actions') %></h2>
 <ul>
 % foreach my $action (Jifty->api->actions) {
-% Jifty::Util->require($action);
+% next unless (Jifty::Util->try_to_require($action));
 % next if ( $action->can('autogenerated') and $action->autogenerated);
 <li><% Jifty->web->link( url => '/__jifty/admin/action/'.$action, label => $action) %></li>
 % }

Added: jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/css/google_map.css
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/css/google_map.css	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,15 @@
+/* XXX: move to Plugin::GoogleMap */
+
+div .googlemap-search-results {
+    background-color:white;
+    border-width: 2px;
+    border-color: black;
+    border-style:dashed;
+    display:none;
+    height:450px;
+    left:50px;
+    position:relative;
+    top:-300px;
+    width:250px;
+    z-index:10;
+}

Added: jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/js/google_map.js
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/share/plugins/Jifty/Plugin/GoogleMap/web/static/js/google_map.js	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,196 @@
+// XXX: move me to Plugin/GoogleMap's share when that works
+
+if (GMap2) {
+    //document.body.onunload = "GUnload()";
+
+if ( typeof Jifty == 'undefined' ) {
+    Jifty = {}
+}
+
+Jifty.GMap = function() {};
+Jifty.GMap.location_editor = function(element, x, y, xid, yid, zoom_level, no_marker, readonly) {
+    if (!GBrowserIsCompatible())
+	return;
+
+    var map = new GMap2(element);
+    map.enableScrollWheelZoom();
+    map._jifty_search_result = element.nextSibling;
+    map.addControl(new GSmallZoomControl());
+    if(!readonly)
+	map.addControl(new EditLocationControl());
+    map.setCenter(new GLatLng(y, x), zoom_level);
+    map._jifty_form_x = xid;
+    map._jifty_form_y = yid;
+    if (!no_marker) {
+	map._jifty_location = new GMarker(new GLatLng(y, x));
+	map.addOverlay(map._jifty_location);
+    }
+    GEvent.addListener(map, "click", function(marker, point) {
+	if (!marker && map._jifty_edit_control.editing) {
+	    map.removeOverlay(map._jifty_location);
+	    map._jifty_location = new GMarker(point)
+	    map.addOverlay(map._jifty_location);
+	}});
+}
+
+// TODO: separate edit location control and location search control
+
+function EditLocationControl() {}
+EditLocationControl.prototype = new GControl();
+
+EditLocationControl.prototype.initialize = function(map) {
+  var container = document.createElement("div");
+
+  var EditDiv = document.createElement("div");
+  this.setButtonStyle_(EditDiv);
+  EditDiv.appendChild(document.createTextNode("Edit"));
+
+  var CancelDiv = document.createElement("div");
+  this.setButtonStyle_(CancelDiv);
+  CancelDiv.appendChild(document.createTextNode("Cancel"));
+
+  var SearchDiv = document.createElement("div");
+  this.setButtonStyle_(SearchDiv);
+  SearchDiv.appendChild(document.createTextNode("Go to..."));
+
+  if(map._search_only) {
+    container.appendChild(SearchDiv);
+      map._search_result_callback = function(map, placemark) {
+	  var point = placemark.Point.coordinates;
+	  map.setCenter(new GLatLng(point[1], point[0]), 8+placemark.AddressDetails.Accuracy);
+      }
+  }
+  else {
+    container.appendChild(EditDiv);
+    map._search_result_callback = _mark_new_location;
+  }
+  var editctl = this;
+  GEvent.addDomListener(EditDiv, "click", function() {
+    if (editctl.editing) {
+        var point = map._jifty_location.getPoint();
+	$(map._jifty_form_x).value = point.lng()
+	$(map._jifty_form_y).value = point.lat()
+	EditDiv.innerHTML = "Edit";
+	container.removeChild(container.lastChild);
+	container.removeChild(container.lastChild);
+	editctl.editing = false;
+    }
+    else {
+	map._jifty_location_orig = map._jifty_location;
+        container.appendChild(CancelDiv);
+        container.appendChild(SearchDiv);
+	EditDiv.innerHTML = "Done";
+	editctl.editing = true;
+    }
+  });
+
+  GEvent.addDomListener(CancelDiv, "click", function() {
+      map.removeOverlay(map._jifty_location);
+      map._jifty_location = map._jifty_location_orig;
+      map.addOverlay(map._jifty_location);
+
+      container.removeChild(container.lastChild);
+      container.removeChild(container.lastChild);
+      EditDiv.innerHTML = "Edit";
+      editctl.editing = false;
+  });
+
+  GEvent.addDomListener(SearchDiv, "click", function() {
+      var element = document.createElement('div');
+      var field= document.createElement('input');
+      field.setAttribute('type', 'text');
+      field.style.width = '150px';
+      // port to yui event
+      field.onkeypress = function(e) {
+	  var event = e || window.event;
+	  if ((event.charCode || event.keyCode) == 13) {
+	      this.nextSibling.onclick();
+	      event.returnValue = false;
+	      return false;
+	  }
+      };
+      element.appendChild(field);
+      var submit= document.createElement('input');
+      submit.id = 'blahblah';
+      submit.value = 'Query';
+      submit.setAttribute('type', 'button');
+      submit.onclick = function() { _handle_search(map, field.value) };
+      element.appendChild(submit);
+      map.openInfoWindow(map.getCenter(), element, { maxWidth: 100 } );
+  });
+
+  map.getContainer().appendChild(container);
+  map._jifty_edit_control = this;
+  this.editing = false;
+  return container;
+}
+
+function _mark_new_location(map, placemark) {
+    var point = placemark.Point.coordinates;
+    if (map._jifty_location)
+	map.removeOverlay(map._jifty_location);
+    map._jifty_location = new GMarker(new GLatLng(point[1], point[0]));
+    map.addOverlay(map._jifty_location);
+    map.closeInfoWindow();
+    map.setCenter(map._jifty_location.getPoint(), 8+placemark.AddressDetails.Accuracy);
+}
+
+function _handle_search(map, address) {
+    var geocoder = new GClientGeocoder();
+    geocoder.getLocations
+      (address,
+       function (result) {
+	   if(result.Placemark) {
+	       if (result.Placemark.length == 1)
+		   map._search_result_callback(map, result.Placemark[0]);
+	       else
+		   _handle_multiple_results(map, result);
+	   }
+	   else {
+	       // TODO: show error in warning box in infowindow rather than alert
+	       alert('address not found');
+	   }
+       });
+}
+
+function _handle_multiple_results(map, result) {
+    var buf = '<a href="#" onclick="_handle_result_click(this.parentNode, null); return false;">Close</a><ul>';
+    for (var i = 0; i < result.Placemark.length; ++i) {
+	var data = result.Placemark[i];
+	buf += '<li><a href="#" onclick='+"'"+
+            '_handle_result_click(this.parentNode.parentNode.parentNode, '+JSON.stringify(data)+'); return false;' +
+          "'>"+data.address+'</a></li>';
+    }
+    buf += '</ul>';
+    map._jifty_search_result.innerHTML = buf;
+    map._jifty_search_result.style.display = "block";
+    map._jifty_search_result._map = map;
+}
+
+function _handle_result_click(e, data) {
+    e.style.display = 'none';
+    var map = e._map; e._map = null; /* circular reference? */
+    if (data)
+	map._search_result_callback(map, data);
+}
+
+EditLocationControl.prototype.getDefaultPosition = function() {
+  return new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(7, 7));
+}
+
+EditLocationControl.prototype.setButtonStyle_ = function(button) {
+  button.style.textDecoration = "underline";
+  button.style.color = "#0000cc";
+  button.style.backgroundColor = "white";
+  button.style.font = "small Arial";
+  button.style.fontSize = "0.8em";
+  button.style.border = "1px solid black";
+  button.style.padding = "2px";
+  button.style.marginBottom = "3px";
+  button.style.textAlign = "center";
+  button.style.width = "4em";
+  button.style.cursor = "pointer";
+}
+
+
+}

Modified: jifty/branches/js-refactor/share/web/static/js/jifty.js
==============================================================================
--- jifty/branches/js-refactor/share/web/static/js/jifty.js	(original)
+++ jifty/branches/js-refactor/share/web/static/js/jifty.js	Tue Jul 31 17:23:52 2007
@@ -1,6 +1,97 @@
 /* An empty class so we can create things inside it */
 var Jifty = Class.create();
 
+Jifty.Web = Class.create();
+Jifty.Web.current_actions = new Array;
+Jifty.Web.new_action = function() {
+    var args = _get_named_args(arguments);
+    var a;
+    Jifty.Web.current_actions.each(function(x) { if (x.moniker == args.moniker) a = x });
+    if (!a) throw "hate";
+    
+    return a;
+};
+
+Jifty.web = function() { return Jifty.Web };
+
+function _get_named_args(args) {
+    var result = {};
+    for (var i = 0; i < args.length; i+=2) {
+	result[args[i]] = args[i+1];
+    }
+    return result;
+
+}
+
+function _get_onclick(action_hash, name, args, path) {
+    var onclick = 'if(event.ctrlKey||event.metaKey||event.altKey||event.shiftKey) return true; return update('
+    + JSON.stringify({'continuation': {},
+		      'actions': action_hash,
+		      'fragments': [{'mode': 'Replace', 'args': args, 'region': name, 'path': path}]})
+    +', this)';
+    onclick = onclick.replace(/"/g, "'"); //"' )# grr emacs!
+	return onclick;
+}
+// XXX
+var hyperlink  = function() {
+    var args = _get_named_args(arguments);
+    var current_region = Jifty.Web.current_region;
+    var onclick = _get_onclick({}, current_region.name, current_region.args, args.onclick[0].replace_with);
+    outs( a(function() { attr(function()
+			      {return ['onclick', onclick, 'href', '#']});
+	    return args.label
+		}));
+}
+
+var render_param = function(a, field) { outs(a.render_param(field)) };
+var form_return  = function() {
+    var args = _get_named_args(arguments);
+    var action_hash = {};
+    action_hash[args.submit.moniker] = 1;
+    // XXX: fix the fabricated refresh-self
+    // XXX: implicit onclick only for now
+
+    // $self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} });
+    // @args{qw/mode path region/} = ('Replace', Jifty->web->current_region->path, Jifty->web->current_region);
+
+    var current_region = Jifty.Web.current_region;
+    var onclick = _get_onclick(action_hash, current_region.name, current_region.args, current_region.path);
+    outs(
+	 div(function() {
+		 attr(function() { return ['class', 'submit_button'] });
+		 return input(function() { attr(function()
+						{return ['type', 'submit',
+							 'onclick', onclick,
+							 'class', 'widget button',
+							 'id', 'S' + (++SERIAL + SERIAL_postfix),
+							 'value', args.label,
+							 'name', 'J:V-region-__page-signup_widget=_signup|J:ACTIONS=signupnow'] })});
+		     }));
+
+};
+
+function register_action(a) {
+    outs(div(function() {
+		attr(function() { return ['class', 'hidden'] });
+		return input(function() { attr(function() {
+				return ['type', 'hidden',
+					'name', a.register_name(),
+					'id', a.register_name(),
+					'value', a.actionClass] }) } ) } ));
+    /* XXX: fallback values */
+}
+
+function apply_cached_for_action(code, actions) {
+    Jifty.Web.current_actions = actions;
+    this['out_buf'] = '';
+    this['outs'] = function(text) { this.out_buf += text };
+    actions.each(register_action);
+    var foo = code();
+    return foo;
+    alert(foo);
+    throw 'not yet';
+}
+
 /* Actions */
 var Action = Class.create();
 Action.prototype = {
@@ -121,7 +212,11 @@
                     a['fields'][Form.Element.getField(f)] = {};
                 var field = Form.Element.getField(f);
                 var type = Form.Element.getType(f);
-                    
+
+                // XXX: fallback value being an array makes server
+                // upset, we don't think that should happen anyway
+                if (type == 'fallback' && a['fields'][field][type])
+                    continue                    
                 a['fields'][field][type] = this._mergeValues(a['fields'][field][type],
                                                              Form.Element.getValue(f));
             }
@@ -231,10 +326,145 @@
         var enable = function() { arguments[0].disabled = false; };
         this.fields().each( enable );
         this.buttons().each( enable );
-    }
+    },
+
+
+    /* client side logic extracted from Jifty::Action */
+    _action_spec: function() {
+	if (!this.s_a) {
+	    /* XXX: make REST client accessible */
+	    var Todo = new AsynapseRecord('todo');
+	    this.s_a = $H(Todo.eval_ajax_get('/=/action/'+this.actionClass+'.js'));
+	}
+	
+	return this.s_a
+    },
+    argument_names: function() {
+	return this._action_spec().keys();
+    },
+
+    render_param: function(field) {
+	var a_s = this._action_spec();
+	var type = 'text';
+	var f = new ActionField(field, a_s[field], this);
+	return f.render();
+    },
+    register_name: function() { return this.register.id }
+
 };
 
+var SERIAL_postfix = Math.ceil(10000*Math.random());
+var SERIAL = 0;
+ActionField = Class.create();
+ActionField.prototype = {
+ initialize: function(name, args, action) {
+	this.name = name;
+	this.label = args.label;
+	this.hints = args.hints;
+	this.mandatory = args.mandatory;
+	this.ajax_validates = args.ajax_validates;
+	this.current_value = action.data_structure().fields[name].value;
+        this.error = action.result.field_error[name];
+	this.action = action;
+	if (!this.render_mode) this.render_mode = 'update';
+	this.type = 'text';
+    },
+
+ render: function() {
+	if (this.render_mode == 'read')
+	    return this.render_wrapper
+		(this.render_preamble,
+		 this.render_label,
+		 this.render_value);
+	else
+	    return this.render_wrapper
+	    (this.render_preamble,
+	     this.render_label,
+	     this.render_widget,
+	     this.render_autocomplete_div,
+	     this.render_inline_javascript,
+	     this.render_hints,
+	     this.render_errors,
+	     this.render_warnings,
+	     this.render_canonicalization_notes);
+    },
+ render_wrapper: function () {
+	var classes = ['form_field'];
+	if (this.mandatory) classes.push('mandatory');
+	if (this.name) classes.push('argument-'+this.name);
+	var args = arguments;
+	var tthis = this;
+	return div(function() {
+		attr(function(){return ['class', classes.join(' ')]});
+		var buf = new Array;
+		for (var i = 0; i < args.length; ++i) {
+		    buf.push(typeof(args[i]) == 'function' ? args[i].apply(tthis) : args[i]);
+		}
+		return buf.join('');
+	    });
+    },
+    render_preamble: function() {
+	var tthis = this;
+	return span(function(){attr(function(){return ['class', "preamble"]});
+		return tthis.preamble });
+    },
+
+    render_label: function() {
+	var tthis = this;
+	if(this.render_mode == 'update')
+	    return label(function(){attr(function(){return['class', "label", 'for', tthis.element_id()]});
+		    return tthis.label });
+	else
+	    return span(function(){attr(function(){return['class', "label" ]});
+		    return tthis.label });
+    },
+ input_name: function() {
+	return ['J:A:F', this.name, this.action.moniker].join('-');
+    },
+ render_hints: function() {
+	var tthis = this;
+	return span(function(){attr(function(){return ['class', "hints"]});
+		return tthis.hints });
+    },
+
+ render_errors: function() {
+	if (!this.action) return '';
+	var tthis = this;
+	// XXX: post-request handler needs to extract field error messages
+	return span(function(){attr(function(){return ['class', "error", 'id', 'errors-'+tthis.input_name()]});
+		return tthis.error });
+    },
+
+ render_widget: function () {
+	var tthis = this;
+	return input(function(){
+		    attr(function(){
+			    var fields = ['type', tthis.type];
+			    if (tthis.input_name) fields.push('name', tthis.input_name());
+			    fields.push('id', tthis.element_id());
+			    if (tthis.current_value) fields.push('value', tthis.current_value);
+			    fields.push('class', tthis._widget_class().join(' '));
+			    if (tthis.max_length) fields.push('size', tthis.max_length, 'maxlength', tthis.max_length);
+			    if (tthis.disable_autocomplete) fields.push('autocomplete', "off");
+			    //" " .$self->other_widget_properties;
+			    return fields;
+			})});
+    },
+ _widget_class: function() {
+	var classes = ['form_field'];
+	if (this.mandatory)      classes.push('mandatory');
+	if (this.name)           classes.push('argument-'+this.name);
+	if (this.ajax_validates) classes.push('ajaxvalidation');
+	return classes;
+    },
+
+ element_id: function() { if(!this._element_id) this._element_id = this.input_name() + '-S' + (++SERIAL + SERIAL_postfix);
+			  return this._element_id; },
+ __noSuchMethod__: function(name) {
+	return '<!-- '+name+' not implemented yet -->';
+    }
 
+};
 
 /* Forms */
 Object.extend(Form, {
@@ -245,7 +475,7 @@
 
         for (var i = 0; i < possible.length; i++) {
             if (Form.Element.getType(possible[i]) == "registration")
-                elements.push(new Action(Form.Element.getMoniker(possible[i])));
+                elements.push(Form.Element.getAction(possible[i]));
         }
         
         return elements;
@@ -260,6 +490,7 @@
 });
 
 
+var current_actions = $H();
 
 /* Fields */
 Object.extend(Form.Element, {
@@ -283,9 +514,10 @@
     // Takes an element or an element id
     getAction: function (element) {
         element = $(element);    
-
         var moniker = Form.Element.getMoniker(element);
-        return new Action(moniker);
+	if (!current_actions[moniker])
+	    current_actions[moniker] = new Action(moniker);
+	return current_actions[moniker];
     },
 
     // Returns the name of the field
@@ -646,6 +878,42 @@
 
     return f;    
 }
+
+var CACHE = {};
+
+
+var walk_node = function(node, table) {
+    for (var child = node.firstChild;
+         child != null;
+         child = child.nextSibling) {
+        var name = child.nodeName.toLowerCase();
+        if (table[name])
+	    table[name](child);
+    }
+}
+
+var extract_cacheable = function(fragment, f) {
+    walk_node(fragment,
+    { cacheable: function(fragment_bit) {
+            var c_type = fragment_bit.getAttribute("type");
+            var textContent = '';
+            if (fragment_bit.textContent) {
+                textContent = fragment_bit.textContent;
+            } else if (fragment_bit.firstChild) {
+                textContent = fragment_bit.firstChild.nodeValue;
+            } 
+	    try {
+		var cache_func = eval(textContent);
+		CACHE[f['path']] = { 'type': c_type, 'content': cache_func };
+	    }
+	    catch(e) { 
+		alert(e);
+		alert(textContent);
+	    }
+        }
+    });
+};
+
 // applying updates from a fragment
 //   - fragment: the fragment from the server
 //   - f: fragment spec
@@ -653,11 +921,10 @@
     // We found the right fragment
     var dom_fragment = fragments[f['region']];
     var new_dom_args = $H();
+
     var element = f['element'];
-    for (var fragment_bit = fragment.firstChild;
-	 fragment_bit != null;
-	 fragment_bit = fragment_bit.nextSibling) {
-	if (fragment_bit.nodeName == 'argument') {
+    walk_node(fragment,
+    { argument: function(fragment_bit) {
 	    // First, update the fragment's arguments
 	    // with what the server actually used --
 	    // this is needed in case there was
@@ -669,7 +936,8 @@
 		textContent = fragment_bit.firstChild.nodeValue;
 	    }
 	    new_dom_args[fragment_bit.getAttribute("name")] = textContent;
-	} else if (fragment_bit.nodeName.toLowerCase() == 'content') {
+	},
+      content: function(fragment_bit) {
 	    var textContent = '';
 	    if (fragment_bit.textContent) {
 		textContent = fragment_bit.textContent;
@@ -684,11 +952,14 @@
 	    } else {
 		Element.update(element, textContent.stripScripts());
 	    }
-	    // We need to give the browser some "settle" time before we eval scripts in the body
-	    setTimeout((function() { this.evalScripts() }).bind(textContent), 10);
-	    Behaviour.apply(element);
+	    // We need to give the browser some "settle" time before
+	    // we eval scripts in the body
+        YAHOO.util.Event.onAvailable(element.id, function() {
+            (function() { this.evalScripts() }).bind(textContent)();
+        });
+        Behaviour.apply(element);
 	}
-    }
+    });
     dom_fragment.setArgs(new_dom_args);
 
     // Also, set us up the effect
@@ -717,7 +988,6 @@
 //     - 'mode' is one of 'Replace', or the name of a Prototype Insertion
 //     - 'effect' is the name of a Prototype Effect
 function update() {
-    // If we don't have XMLHttpRequest, bail and fallback on full-page
     // loads
     if(!Ajax.getTransport()) return true;
     // XXX: prevent default behavior in IE
@@ -725,7 +995,6 @@
         window.event.returnValue = false;
     }
 
-    show_wait_message();
     var named_args = arguments[0];
     var trigger    = arguments[1];
 
@@ -738,27 +1007,31 @@
     // Grab extra arguments (from a button)
     var button_args = Form.Element.buttonFormElements(trigger);
 
+    var form = Form.Element.getForm(trigger);
     // If the action is null, take all actions
     if (named_args['actions'] == null) {
         named_args['actions'] = {};
         // default to disable fields
-        var form = Form.Element.getForm(trigger);
         if (form)
             Form.getActions(form).map(function(x){
                 named_args['actions'][x.moniker] = 1;
             });
     }
+    var optional_fragments;
+    if (form && form['J:CALL']) 
+	optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': null}) ];
     // Build actions structure
+    var has_request = 0;
     request['actions'] = $H();
-    var optional_fragments;
     for (var moniker in named_args['actions']) {
         var disable = named_args['actions'][moniker];
         var a = new Action(moniker, button_args);
+	current_actions[moniker] = a; // XXX: how do i make this bloody singleton?
         // Special case for Redirect, allow optional, implicit __page
         // from the response to be used.
         if (a.actionClass == 'Jifty::Action::Redirect')
             optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': a.fields().last().value}) ];
-
+        a.result = {}; a.result.field_error = {};
         if (a.register) {
             if (a.hasUpload())
                 return true;
@@ -766,15 +1039,71 @@
                 a.disable_input_fields();
             }
             request['actions'][moniker] = a.data_structure();
+            ++has_request;
         }
+
     }
 
     request['fragments'] = $H();
+    var update_from_cache = new Array;
+
     // Build fragments structure
     for (var i = 0; i < named_args['fragments'].length; i++) {
         var f = named_args['fragments'][i];
         f = prepare_element_for_update(f);
         if (!f) continue;
+
+        var cached = CACHE[f['path']];
+        if (cached && cached['type'] == 'static') {
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+	    var cached_result;
+
+	    Jifty.Web.current_region = fragments[f['region']];
+	    try { cached_result = apply_cached_for_action(cached['content'], []) }
+	    catch (e) { alert(e) }
+
+            content_node.textContent = cached_result;
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+
+            update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f);
+ } );
+            continue;
+        }
+	else if (cached && cached['type'] == 'action') {
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+            update_from_cache.push(function(){
+		    var cached_result;
+		    Jifty.Web.current_region = fragments[f['region']];
+		    try {
+			cached_result = apply_cached_for_action(cached['content'], Form.getActions(form));
+		    }
+		    catch (e) { alert(e); throw e }
+		    content_node.textContent = cached_result;
+		    apply_fragment_updates(my_fragment, f);
+ } );
+            continue;
+	}
+        else if (cached && cached['type'] == 'crudview') {
+	    try { // XXX: get model class etc as metadata in cache 
+		// XXX: kill dup code
+	    var Todo = new AsynapseRecord('todo');
+	    var record = Todo.find(f['args']['id']);
+            var my_fragment = document.createElement('fragment');
+            var content_node = document.createElement('content');
+            content_node.textContent = cached['content'](record);
+            my_fragment.appendChild(content_node);
+            my_fragment.setAttribute('id', f['region']);
+            update_from_cache.push(function(){ apply_fragment_updates(my_fragment, f); } );
+	    } catch (e) { alert(e) };
+	    continue;
+	}
+
         // Update with all new values
         var name = f['region'];
         var fragment_request = fragments[name].data_structure(f['path'], f['args']);
@@ -785,42 +1114,71 @@
 
         // Push it onto the request stack
         request['fragments'][name] = fragment_request;
+        ++has_request;
     }
 
+    if (!has_request) {
+        for (var i = 0; i < update_from_cache.length; i++)
+            update_from_cache[i]();
+        return false;
+    }
+
+    show_wait_message();
+
     // And when we get the result back..
     var onSuccess = function(transport, object) {
         // Grab the XML response
         var response = transport.responseXML.documentElement;
+
+	// Get action results
+        walk_node(response,
+	{ result: function(result) {
+		var moniker = result.getAttribute("moniker");
+		walk_node(result,
+			  { field: function(field) {
+				  var error = field.getElementsByTagName('error')[0];
+				  if (error) {
+				      var text = error.textContent
+					  ? error.textContent
+					  : (error.firstChild ? error.firstChild.nodeValue : '');
+				      var action = current_actions[moniker];
+				      action.result.field_error[field.getAttribute("name")] = text;
+				      }
+			      }});
+	    }});
+	// empty known action. XXX: we should only need to discard actions being submitted
+
         // Loop through the result looking for it
         var expected_fragments = optional_fragments ? optional_fragments : named_args['fragments'];
         for (var response_fragment = response.firstChild;
              response_fragment != null && response_fragment.nodeName == 'fragment';
              response_fragment = response_fragment.nextSibling) {
 
-            var f; 
-            for (var i = 0; i < expected_fragments.length; i++) {
-                f = expected_fragments[i];
-                if (response_fragment.getAttribute("id") == f['region'])
-                    break;
-            }
-            if (response_fragment.getAttribute("id") != f['region'])
+            var exp_id = response_fragment.getAttribute("id");
+            var f = expected_fragments.find(function(f) { return exp_id == f['region'] });
+            if (!f)
                 continue;
 
-	    try {
-            apply_fragment_updates(response_fragment, f);
-	    }catch (e) { alert(e) }
+            try {
+                apply_fragment_updates(response_fragment, f);
+            }catch (e) { alert(e) }
+            extract_cacheable(response_fragment, f);
         }
-        for (var result = response.firstChild;
-             result != null;
-             result = result.nextSibling) {
-            if (result.nodeName == 'result') {
+
+	update_from_cache.each(function(x) { x() });
+
+        walk_node(response,
+	{ result: function(result) {
                 for (var key = result.firstChild;
                      key != null;
                      key = key.nextSibling) {
                     show_action_result(result.getAttribute("moniker"),key);
                 }
-            }
-        }
+            },
+	  redirect: function(redirect) {
+                document.location =  redirect.firstChild.firstChild.nodeValue;
+	}});
+	current_actions = $H();
     };
     var onFailure = function(transport, object) {
         hide_wait_message_now();
@@ -844,6 +1202,9 @@
         request['variables']['region-'+k] = current_args[k];
     }
 
+    // Build continuation structure
+    request['continuation'] = named_args['continuation'];
+
     // Push any state variables which we set into the forms
     for (var i = 0; i < document.forms.length; i++) {
         var form = document.forms[i];

Modified: jifty/branches/js-refactor/share/web/static/js/key_bindings.js
==============================================================================
--- jifty/branches/js-refactor/share/web/static/js/key_bindings.js	(original)
+++ jifty/branches/js-refactor/share/web/static/js/key_bindings.js	Tue Jul 31 17:23:52 2007
@@ -22,6 +22,10 @@
         YAHOO.util.Event.removeListener(Jifty.KeyBindings.listener);
     },
 
+    reset: function() {
+        Jifty.KeyBindings.bindings = new Array();
+    },
+
     doClick: function(e) {
         if (e.target.nodeType == 3) // defeat Safari bug
             e.target = e.target.parentNode;

Added: jifty/branches/js-refactor/share/web/static/js/template_declare.js
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/share/web/static/js/template_declare.js	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,53 @@
+// FIXME: try not to pollute the namespace!
+var tags = ['div', 'h2', 'dl', 'dt', 'dd', 'span', 'label', 'input', 'a'];
+for (var i in tags) {
+    this[tags[i]] = _mk_tag_wrapper(tags[i]);
+}
+this['form'] = _mk_tag_wrapper('form', function(attr) {
+	return '<form method="post" enctype="multipart/form-data" >'; // XXX action: & friends
+    }, null, 1);
+var _ = function(str) { return str };
+var attr = function() {};
+
+function _mk_tag_wrapper(name, pre, post, want_outbuf) {
+    return function() {
+	var buf = new Array;
+	var sp = this['attr'];
+	var attr = {};
+	this['attr'] = function(a) {
+	    var foo;
+	    a = a();
+	    while(foo = a.splice(0, 2)) {
+		if (foo.length == 0)
+		    break;
+		attr[foo[0]] = foo[1];
+	    }
+	};
+
+	var flushed = '';
+	if (this.out_buf) {
+	    flushed = this.out_buf;
+	    this.out_buf = '';
+	}
+
+	for (var i = 0; i < arguments.length; ++i) {
+	    buf.push(typeof(arguments[i]) == 'function' ? arguments[i]() : arguments[i]);
+	}
+	var _mk_attr = function() {
+	    var foo = ' ';
+	    for (var k in attr) {
+		if (k == 'extend') continue;
+		foo += k + '="' + attr[k] + '"';
+	    }
+	    return foo;
+	};
+	var first = buf.splice(0, 1);
+	var _pre = pre ? pre(attr) : '<'+name+_mk_attr(attr)+'>';
+	var _post = post ? post(attr) : '</'+name+'>';
+	if (want_outbuf && this.out_buf) {
+	    first += this.out_buf;
+	    this.out_buf = '';
+	}
+	return flushed + _pre + first + _post + buf.join('');
+    }
+};

Modified: jifty/branches/js-refactor/share/web/static/js/yui/calendar.js
==============================================================================
--- jifty/branches/js-refactor/share/web/static/js/yui/calendar.js	(original)
+++ jifty/branches/js-refactor/share/web/static/js/yui/calendar.js	Tue Jul 31 17:23:52 2007
@@ -1,4 +1,8 @@
 /*
+    This file has been PATCHED by trs to allow selecting of out of month dates.
+    Please do not update it without also applying the patch (oom_select.patch).
+*/
+/*
 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
 Code licensed under the BSD License:
 http://developer.yahoo.net/yui/license.txt
@@ -903,6 +907,7 @@
 	MINDATE : {key:"mindate", value:null},
 	MAXDATE : {key:"maxdate", value:null},
 	MULTI_SELECT : {key:"multi_select",	value:false},
+	OOM_SELECT : {key:"oom_select",	value:false},
 	START_WEEKDAY : {key:"start_weekday", value:0},
 	SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
 	SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
@@ -1505,6 +1510,14 @@
 	*/
 	this.cfg.addProperty(defCfg.MULTI_SELECT.key,	{ value:defCfg.MULTI_SELECT.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
 
+    /**
+    * True if the Calendar should allow selection of out-of-month dates. False by default.
+    * @config OOM_SELECT
+    * @type Boolean
+    * @default false
+    */
+    this.cfg.addProperty(defCfg.OOM_SELECT.key,      { value:defCfg.OOM_SELECT.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
+
 	/**
 	* The weekday the week begins on. Default is 0 (Sunday).
 	* @config START_WEEKDAY
@@ -2176,7 +2189,7 @@
 		weekClass = weekPrefix + weekNum;
 
 		// Local OOM check for performance, since we already have pagedate
-		if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
+		if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth() && !this.cfg.getProperty(defCfg.OOM_SELECT.key)) {
 			break;
 		} else {
 
@@ -2203,7 +2216,7 @@
 				this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
 				
 				// Local OOM check for performance, since we already have pagedate
-				if (workingDate.getMonth() != useDate.getMonth()) {
+				if (workingDate.getMonth() != useDate.getMonth() && !this.cfg.getProperty(defCfg.OOM_SELECT.key)) {
 					cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
 				} else {
 					YAHOO.util.Dom.addClass(cell, workingDayPrefix + workingDate.getDay());

Added: jifty/branches/js-refactor/share/web/static/js/yui/oom_select.patch
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/share/web/static/js/yui/oom_select.patch	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,54 @@
+=== share/web/static/js/yui/calendar.js
+==================================================================
+--- share/web/static/js/yui/calendar.js	(revision 24788)
++++ share/web/static/js/yui/calendar.js	(local)
+@@ -1,4 +1,8 @@
+ /*
++    This file has been PATCHED by trs to allow selecting of out of month dates.
++    Please do not update it without also applying the patch (oom_select.patch).
++*/
++/*
+ Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+ Code licensed under the BSD License:
+ http://developer.yahoo.net/yui/license.txt
+@@ -903,6 +907,7 @@
+ 	MINDATE : {key:"mindate", value:null},
+ 	MAXDATE : {key:"maxdate", value:null},
+ 	MULTI_SELECT : {key:"multi_select",	value:false},
++	OOM_SELECT : {key:"oom_select",	value:false},
+ 	START_WEEKDAY : {key:"start_weekday", value:0},
+ 	SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
+ 	SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
+@@ -1505,6 +1510,14 @@
+ 	*/
+ 	this.cfg.addProperty(defCfg.MULTI_SELECT.key,	{ value:defCfg.MULTI_SELECT.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
+ 
++    /**
++    * True if the Calendar should allow selection of out-of-month dates. False by default.
++    * @config OOM_SELECT
++    * @type Boolean
++    * @default false
++    */
++    this.cfg.addProperty(defCfg.OOM_SELECT.key,      { value:defCfg.OOM_SELECT.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
++
+ 	/**
+ 	* The weekday the week begins on. Default is 0 (Sunday).
+ 	* @config START_WEEKDAY
+@@ -2176,7 +2189,7 @@
+ 		weekClass = weekPrefix + weekNum;
+ 
+ 		// Local OOM check for performance, since we already have pagedate
+-		if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
++		if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth() && !this.cfg.getProperty(defCfg.OOM_SELECT.key)) {
+ 			break;
+ 		} else {
+ 
+@@ -2203,7 +2216,7 @@
+ 				this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
+ 				
+ 				// Local OOM check for performance, since we already have pagedate
+-				if (workingDate.getMonth() != useDate.getMonth()) {
++				if (workingDate.getMonth() != useDate.getMonth() && !this.cfg.getProperty(defCfg.OOM_SELECT.key)) {
+ 					cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
+ 				} else {
+ 					YAHOO.util.Dom.addClass(cell, workingDayPrefix + workingDate.getDay());

Modified: jifty/branches/js-refactor/share/web/templates/__jifty/webservices/xml
==============================================================================
--- jifty/branches/js-refactor/share/web/templates/__jifty/webservices/xml	(original)
+++ jifty/branches/js-refactor/share/web/templates/__jifty/webservices/xml	Tue Jul 31 17:23:52 2007
@@ -4,6 +4,14 @@
 $writer->xmlDecl( "UTF-8", "yes" );
 $writer->startTag("response");
 
+if (my $ext = Jifty->web->request->argument('_webservice_external_redirect')) {
+    $writer->startTag("redirect");
+    $writer->cdataElement(url=> $ext);
+
+    $writer->endTag();
+}
+else {
+
 FRAGMENT:
 for my $f ( Jifty->web->request->fragments ) {
     # Set up the region stack
@@ -27,7 +35,7 @@
 
         $new ||= Jifty::Web::PageRegion->new(
             name           => $f->name,
-            path           => $f->path,
+            path           => URI::Escape::uri_unescape($f->path),
             region_wrapper => $f->wrapper,
             parent         => Jifty->web->current_region,
             defaults       => \%defaults,
@@ -44,12 +52,17 @@
     $writer->startTag( "fragment", id => Jifty->web->current_region->qualified_name );
     my %args = %{ Jifty->web->current_region->arguments };
     $writer->dataElement( "argument", $args{$_}, name => $_) for sort keys %args;
+    if (Jifty->config->framework('ClientTemplate') && Jifty->web->current_region->client_cacheable) {
+        $writer->cdataElement( "cacheable", Jifty->web->current_region->client_cache_content, type => Jifty->web->current_region->client_cacheable );
+    }
     $writer->cdataElement( "content", Jifty->web->current_region->as_string );
     $writer->endTag();
 
     Jifty->web->current_region->exit while Jifty->web->current_region;
 }
 
+}
+
 my %results = Jifty->web->response->results;
 for (keys %results) {
     $writer->startTag("result", moniker => $_, class => $results{$_}->action_class);

Added: jifty/branches/js-refactor/t/13-sessions.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/13-sessions.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,98 @@
+use strict;
+use warnings;
+
+=head1 DESCRIPTION
+
+Tests Jifty::Web::Session
+
+=cut
+
+use Jifty::Test tests => 31;
+
+my ($first_id, $second_id, $third_id);
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    $first_id = $session->id;
+
+    $session->set(hello => 'world');
+    $session->set(number => '1st');
+
+    is($session->get('hello'),  'world', "immediate 'get' works");
+    is($session->get('number'), '1st',   "immediate 'get' works");
+
+    $session->load_by_kv(user => 'first');
+    is($session->id, $first_id, "same session as before");
+    is($session->get('hello'),  'world', "'get' before destroy works");
+    is($session->get('number'), '1st',   "'get' before destroy works");
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "same session as before");
+
+    is($session->get('hello'),  'world', "'set', destroy, 'get' works");
+    is($session->get('number'), '1st',   "'set', destroy, 'get' works");
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'second');
+    ok($session->id, "got a session");
+    isnt($session->id, $first_id, "NOT same session as before");
+    $second_id = $session->id;
+
+    is($session->get('hello'),  undef, "different value gives different session");
+    is($session->get('number'), undef, "different value gives different session");
+
+    $session->set(hello => 'world');
+    $session->set(number => '2nd');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv(user => 'first');
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "first session again");
+
+    is($session->get('hello'), 'world');
+    is($session->get('number'), '1st', "even though the two sessions have some overlapping keys, the one that matters doesn't overlap");
+}
+
+# the three-arg form
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'first', sub { $_[0] =~ /^f/ } );
+    ok($session->id, "got a session");
+    is($session->id, $first_id, "first session again");
+    is($session->get('number'), '1st');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'third', sub { $_[0] =~ /\b(thi|3)rd\b/ } );
+    ok($session->id, "got a session");
+    $third_id = $session->id;
+
+    isnt($session->id, $first_id,  "not first session");
+    isnt($session->id, $second_id, "not second session");
+    is($session->get('number'), undef);
+    $session->set(number => '3rd');
+    is($session->get('number'), '3rd');
+}
+
+{
+    my $session = Jifty::Web::Session->new();
+    $session->load_by_kv('user', 'third', sub { $_[0] =~ /\b(thi|3)rd\b/ } );
+    ok($session->id, "got a session");
+    isnt($session->id, $first_id,  "not first session");
+    isnt($session->id, $second_id, "not second session");
+    is($session->id,   $third_id, "third session again");
+    is($session->get('number'), '3rd');
+}
+

Modified: jifty/branches/js-refactor/t/Mapper/lib/Mapper/Action/CrossBridge.pm
==============================================================================
--- jifty/branches/js-refactor/t/Mapper/lib/Mapper/Action/CrossBridge.pm	(original)
+++ jifty/branches/js-refactor/t/Mapper/lib/Mapper/Action/CrossBridge.pm	Tue Jul 31 17:23:52 2007
@@ -6,6 +6,7 @@
 param name      => default is 'something';
 param 'quest';
 param colour    => valid are ("Blue, I mean greeeeeen!", "Green");
+param 'castle';
 
 };
 

Modified: jifty/branches/js-refactor/t/Mapper/share/web/templates/index.html
==============================================================================
--- jifty/branches/js-refactor/t/Mapper/share/web/templates/index.html	(original)
+++ jifty/branches/js-refactor/t/Mapper/share/web/templates/index.html	Tue Jul 31 17:23:52 2007
@@ -11,6 +11,7 @@
 % my $grail_1  = Jifty->web->form->add_action( class => 'GetGrail', order => 1 );
 % my $bridge_1 = Jifty->web->new_action( class => 'CrossBridge',    order => 2 );
 <% $bridge_1->form_field( 'quest',  default_value => { result_of => $grail_1, name => 'castle' } ) %>
+<% $bridge_1->form_field( 'castle', default_value => { result_of => $grail_1 } ) %>
 <% $bridge_1->form_field( 'colour', default_value => 'Green' ) %>
 <% Jifty->web->form->submit( label => 'Do both') %>
 <% Jifty->web->form->end %>

Modified: jifty/branches/js-refactor/t/Mapper/t/02-api.t
==============================================================================
--- jifty/branches/js-refactor/t/Mapper/t/02-api.t	(original)
+++ jifty/branches/js-refactor/t/Mapper/t/02-api.t	Tue Jul 31 17:23:52 2007
@@ -12,7 +12,7 @@
 use lib 't/lib';
 use Jifty::SubTest;
 
-use Jifty::Test tests => 11;
+use Jifty::Test tests => 13;
 
 use_ok('Jifty::Test::WWW::Mechanize');
 
@@ -38,6 +38,11 @@
 $mech->content_like(qr/got the grail/i, "Got the grail");
 $mech->content_like(qr/crossed the bridge/i, "And crossed the bridge");
 
+# Tests for proper generation of default "name" parameter to
+# argument_of and result_to
+$mech->content_unlike(qr/R`[^']+`J:A:F/, "Doesn't have full argument name");
+$mech->content_like(qr/J:A:F-castle-(\S+): Aaaaaargh/, "Has the right value name");
+
 # And then, the same, but via default_values on the form field
 $mech->form_number(3);
 ok($mech->click_button(value => "Do both"));

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/Makefile.PL	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name        'TestApp::Plugin::AppPluginHasModels';
+version     '0.01';
+requires    'Jifty' => '0.70129';
+
+WriteAll;

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/bin/jifty	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Basename qw(dirname); 
+use UNIVERSAL::require;
+
+use Jifty;
+use Jifty::Script;
+
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/etc/config.yml	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,54 @@
+--- 
+framework: 
+  AdminMode: 1
+  ApplicationClass: TestApp::Plugin::AppPluginHasModels
+  ApplicationName: TestApp-Plugin-AppPluginHasModels
+  ApplicationUUID: 646FD662-32DD-11DC-AD79-2A0157C3B83B
+  ConfigFileVersion: 2
+  Database: 
+    CheckSchema: 1
+    Database: testapp_plugin_apppluginhasmodels
+    Driver: SQLite
+    Host: localhost
+    Password: ''
+    RecordBaseClass: Jifty::DBI::Record::Cachable
+    RecordUUIDs: active
+    User: ''
+    Version: 0.0.1
+  DevelMode: 1
+  L10N: 
+    PoDir: share/po
+  LogLevel: INFO
+  Mailer: Sendmail
+  MailerArgs: []
+
+  Plugins: 
+    - LetMe: {}
+    - SkeletonApp: {}
+    - REST: {}
+    - Halo: {}
+    - ErrorTemplates: {}
+    - OnlineDocs: {}
+    - CompressedCSSandJS: {}
+    - AdminUI: {}
+    - TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin: {}
+
+  PubSub: 
+    Backend: Memcached
+    Enable: ~
+  SkipAccessControl: 0
+  TemplateClass: TestApp::Plugin::AppPluginHasModels::View
+  Web: 
+    BaseURL: http://localhost
+    DataDir: var/mason
+    Globals: []
+
+    MasonConfig: 
+      autoflush: 0
+      default_escape_flags: h
+      error_format: text
+      error_mode: fatal
+    Port: 8888
+    ServeStaticFiles: 1
+    StaticRoot: share/web/static
+    TemplateRoot: share/web/templates

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,7 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin;
+use base qw/ Jifty::Plugin /;
+
+1;

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/Model/Color.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/lib/TestApp/Plugin/AppPluginHasModels/Plugin/MyAppPlugin/Model/Color.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin::Model::Color;
+use Jifty::DBI::Schema;
+
+use TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin::Record schema {
+    column name =>
+        type is 'text';
+
+    column contrasting_color =>
+        type is 'text';
+};
+
+1;

Added: jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/t/plugin-model.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-AppPluginHasModels/t/plugin-model.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,54 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+A basic test harness for the Color model.
+
+=cut
+
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 12;
+
+# Make sure we can load the model
+use_ok('TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin::Model::Color');
+
+# Grab a system user
+my $system_user = TestApp::Plugin::AppPluginHasModels::CurrentUser->superuser;
+ok($system_user, "Found a system user");
+
+# Try testing a create
+my $o = TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin::Model::Color->new(current_user => $system_user);
+my ($id) = $o->create();
+ok($id, "Color create returned success");
+ok($o->id, "New Color has valid id set");
+is($o->id, $id, "Create returned the right id");
+
+# Does it use a prefixed table
+is($o->table, 'testapp_plugin_apppluginhasmodels_plugin_myappplugin_colors', 'plugin table prefix');
+
+# And another
+$o->create();
+ok($o->id, "Color create returned another value");
+isnt($o->id, $id, "And it is different from the previous one");
+
+# Searches in general
+my $collection = TestApp::Plugin::AppPluginHasModels::Plugin::MyAppPlugin::Model::ColorCollection->new(current_user => $system_user);
+$collection->unlimit;
+is($collection->count, 2, "Finds two records");
+
+# Searches in specific
+$collection->limit(column => 'id', value => $o->id);
+is($collection->count, 1, "Finds one record with specific id");
+
+# Delete one of them
+$o->delete;
+$collection->redo_search;
+is($collection->count, 0, "Deleted row is gone");
+
+# And the other one is still there
+$collection->unlimit;
+is($collection->count, 1, "Still one left");
+

Added: jifty/branches/js-refactor/t/TestApp-Plugin-Chart/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-Chart/Makefile.PL	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name        'TestApp::Plugin::Chart';
+version     '0.01';
+requires    'Jifty' => '0.70129';
+
+WriteAll;

Added: jifty/branches/js-refactor/t/TestApp-Plugin-Chart/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-Chart/bin/jifty	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Basename qw(dirname); 
+use UNIVERSAL::require;
+
+use Jifty;
+use Jifty::Script;
+
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();

Added: jifty/branches/js-refactor/t/TestApp-Plugin-Chart/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-Chart/etc/config.yml	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,54 @@
+--- 
+framework: 
+  AdminMode: 1
+  ApplicationClass: TestApp::Plugin::Chart
+  ApplicationName: TestApp-Plugin-Chart
+  ApplicationUUID: D16D885C-3E10-11DC-ABE9-A583E6FF98E1
+  ConfigFileVersion: 2
+  Database: 
+    CheckSchema: 1
+    Database: testapp_plugin_chart
+    Driver: SQLite
+    Host: localhost
+    Password: ''
+    RecordBaseClass: Jifty::DBI::Record::Cachable
+    RecordUUIDs: active
+    User: ''
+    Version: 0.0.1
+  DevelMode: 1
+  L10N: 
+    PoDir: share/po
+  LogLevel: INFO
+  Mailer: Sendmail
+  MailerArgs: []
+
+  Plugins: 
+    - LetMe: {}
+    - SkeletonApp: {}
+    - REST: {}
+    - Halo: {}
+    - ErrorTemplates: {}
+    - OnlineDocs: {}
+    - CompressedCSSandJS: {}
+    - AdminUI: {}
+    - Chart: {}
+
+  PubSub: 
+    Backend: Memcached
+    Enable: ~
+  SkipAccessControl: 0
+  TemplateClass: TestApp::Plugin::Chart::View
+  Web: 
+    BaseURL: http://localhost
+    DataDir: var/mason
+    Globals: []
+
+    MasonConfig: 
+      autoflush: 0
+      default_escape_flags: h
+      error_format: text
+      error_mode: fatal
+    Port: 8888
+    ServeStaticFiles: 1
+    StaticRoot: share/web/static
+    TemplateRoot: share/web/templates

Added: jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-Chart/lib/TestApp/Plugin/Chart/View.pm	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,21 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::Chart::View;
+use Jifty::View::Declare -base;
+
+template '/graphit' => page {
+    Jifty->web->chart(
+        type   => 'Pie',
+        width  => 400,
+        height => 300,
+        data   => sub {
+            [
+                [ 2004, 2005, 2006, 2007 ],
+                [ 26, 37, 12, 42 ]
+            ];
+        },
+    );
+};
+
+1;

Added: jifty/branches/js-refactor/t/TestApp-Plugin-Chart/t/chart.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp-Plugin-Chart/t/chart.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,45 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Jifty::SubTest;
+use Jifty::Test tests => 9;
+use Jifty::Test::WWW::Mechanize;
+
+my $server = Jifty::Test->make_server;
+ok($server, 'got a server');
+
+my $url = $server->started_ok;
+
+my $mech = Jifty::Test::WWW::Mechanize->new;
+
+$mech->get_ok($url . '/graphit', 'try getting /graphit');
+my $img_match = qr{<img src="(/chart/S\d+)" width="400" height="300"/>};
+$mech->content_like($img_match, 'has an img tag');
+my ($chart_path) = $mech->content =~ $img_match;
+
+TODO: {
+local $TODO = "Always fail because eval 'use Chart::*' breaks file handles?";
+
+$mech->get_ok($url . $chart_path, 'try getting ' . $chart_path);
+
+my $response = $mech->response;
+is($response->header('Content-type'), 'image/png', 'content type set to png');
+
+SKIP: {
+    eval "use Image::Info qw/ image_info /";
+    skip "Image::Info is not installed", 3 if $@;
+
+    my $imgdata = $mech->content;
+    my $info = image_info(\$imgdata);
+
+    diag($info->{error}) if $info->{error};
+
+    is($info->{file_ext}, 'png', 'it is a png file');
+    is($info->{width}, 400, 'it is 400 pixels wide');
+    is($info->{height}, 300, 'it is 300 pixels tall');
+};
+
+};

Modified: jifty/branches/js-refactor/t/TestApp/lib/TestApp/Dispatcher.pm
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/lib/TestApp/Dispatcher.pm	(original)
+++ jifty/branches/js-refactor/t/TestApp/lib/TestApp/Dispatcher.pm	Tue Jul 31 17:23:52 2007
@@ -82,4 +82,16 @@
     }
 };
 
+on HTTPS '/dispatch/protocol' => run {
+    set content => 'HTTPS';
+};
+
+on HTTP '/dispatch/protocol' => run {
+    set content => 'NOT HTTPS';
+};
+
+on '/dispatch/protocol' => run {
+    set footer => 'normal';
+};
+
 1;

Modified: jifty/branches/js-refactor/t/TestApp/lib/TestApp/Model/User.pm
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/lib/TestApp/Model/User.pm	(original)
+++ jifty/branches/js-refactor/t/TestApp/lib/TestApp/Model/User.pm	Tue Jul 31 17:23:52 2007
@@ -34,6 +34,7 @@
   is immutable,
   default is defer { DateTime->now },
   filters are 'Jifty::DBI::Filter::DateTime';
+column 'uuid' => is UUID;
 };
 
 

Modified: jifty/branches/js-refactor/t/TestApp/lib/TestApp/View.pm
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/lib/TestApp/View.pm	(original)
+++ jifty/branches/js-refactor/t/TestApp/lib/TestApp/View.pm	Tue Jul 31 17:23:52 2007
@@ -44,8 +44,8 @@
 
 require TestApp::View::base;
 require TestApp::View::instance;
-import_templates TestApp::View::base under '/base';
-import_templates TestApp::View::instance under '/instance';
+alias TestApp::View::base under '/base';
+alias TestApp::View::instance under '/instance';
 
 use Jifty::View::Declare::CRUD;
 
@@ -54,7 +54,7 @@
     if ($model =~ /^.*::(.*?)$/) {
         $bare_model = $1;
     }
-    alias Jifty::View::Declare::CRUD under '/crud/'.$bare_model,  { object_type => $bare_model, base_path => '/crud/'.$bare_model };
+    alias Jifty::View::Declare::CRUD under '/crud/'.$bare_model,  { object_type => $bare_model };
 
 }
 

Modified: jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/base.pm
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/base.pm	(original)
+++ jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/base.pm	Tue Jul 31 17:23:52 2007
@@ -1,11 +1,13 @@
 package TestApp::View::base;
 use Jifty::View::Declare -base;
 
+sub base_path { '/base' }
+
 template 'list_ht' => sub {
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	$self->has_template('view')->();
+	$self->resolve_template('view')->();
     }
 };
 
@@ -13,7 +15,7 @@
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	show('view');
+	show($self->base_path.'/view');
     }
 };
 
@@ -21,7 +23,7 @@
     my $self = shift;
     outs("/base/list=$self");
     for (0..1) {
-	render_region('view-'.$_, path => 'view');
+	render_region('view-'.$_, path => $self->base_path.'/view');
     }
 };
 

Modified: jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/instance.pm
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/instance.pm	(original)
+++ jifty/branches/js-refactor/t/TestApp/lib/TestApp/View/instance.pm	Tue Jul 31 17:23:52 2007
@@ -2,6 +2,8 @@
 use Jifty::View::Declare -base;
 use base 'TestApp::View::base';
 
+sub base_path { '/instance' }
+
 template 'myview' => sub {
     
 };

Added: jifty/branches/js-refactor/t/TestApp/share/web/templates/dispatch/protocol
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp/share/web/templates/dispatch/protocol	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,3 @@
+<&| /_elements/wrapper, title => "Test of protocol adjectives" &>
+<% Jifty::YAML::Dump(\%ARGS) %>
+</&>

Added: jifty/branches/js-refactor/t/TestApp/t/02-dispatch-http.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp/t/02-dispatch-http.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+# Just in case
+BEGIN { delete $ENV{HTTPS}; }
+
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 5;
+use Jifty::Test::WWW::Mechanize;
+
+my $server  = Jifty::Test->make_server;
+
+isa_ok($server, 'Jifty::Server');
+
+my $URL     = $server->started_ok;
+my $mech    = Jifty::Test::WWW::Mechanize->new();
+
+$mech->get_ok("$URL/dispatch/protocol", "Got /dispatch/protocol");
+$mech->content_contains("NOT HTTPS");
+$mech->content_contains("normal");
+

Added: jifty/branches/js-refactor/t/TestApp/t/02-dispatch-https.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/TestApp/t/02-dispatch-https.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,24 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+BEGIN { $ENV{HTTPS} = 1; }
+
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 6;
+use Jifty::Test::WWW::Mechanize;
+
+my $server  = Jifty::Test->make_server;
+
+isa_ok($server, 'Jifty::Server');
+
+my $URL     = $server->started_ok;
+my $mech    = Jifty::Test::WWW::Mechanize->new();
+
+$mech->get_ok("$URL/dispatch/protocol", "Got /dispatch/protocol");
+$mech->content_contains("HTTPS");
+$mech->content_lacks("NOT");
+$mech->content_contains("normal");
+
+

Modified: jifty/branches/js-refactor/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t	(original)
+++ jifty/branches/js-refactor/t/TestApp/t/02-dispatch-show-rule-in-wrong-ruleset.t	Tue Jul 31 17:23:52 2007
@@ -6,6 +6,7 @@
 use Jifty::SubTest;
 use Jifty::Test tests => 8;
 use Jifty::Test::WWW::Mechanize;
+use Test::Log4perl;
 
 my $server  = Jifty::Test->make_server;
 
@@ -14,11 +15,14 @@
 my $URL     = $server->started_ok;
 my $mech    = Jifty::Test::WWW::Mechanize->new();
 
+{
+#    my $log = Test::Log4perl->expect(['', warn => qr/You can't call a 'show' rule in a 'before' or 'after' block in the dispatcher/ ]);
 $mech->get("$URL/before_stage_show", "Got /before_stage_show");
 $mech->content_lacks("This is content");
 is( $mech->status , '404');
-
+}
 $mech->get_ok("$URL/on_stage_show", "Got /on_stage_show");
+#diag $mech->content;
 $mech->content_contains("his is content");
 
 $mech->get("$URL/after_stage_show", "Got /after_stage_show");

Modified: jifty/branches/js-refactor/t/TestApp/t/05-editactions-Record.t
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/t/05-editactions-Record.t	(original)
+++ jifty/branches/js-refactor/t/TestApp/t/05-editactions-Record.t	Tue Jul 31 17:23:52 2007
@@ -7,12 +7,12 @@
 use Jifty::SubTest;
 BEGIN { $ENV{'JIFTY_CONFIG'} = 't/config-Record' }
 
-use Jifty::Test tests => 10;
+use Jifty::Test tests => 11;
 use Jifty::Test::WWW::Mechanize;
-
 # Make sure we can load the model
 use_ok('TestApp::Model::User');
 
+Jifty->new();
 # Grab a system use
 my $system_user = TestApp::CurrentUser->superuser;
 ok($system_user, "Found a system user");
@@ -38,8 +38,9 @@
 $o->load($id);
 ok($id, "Load returned success");
 
+
 is($o->email, 'newemail at example.com', "Email was updated by form");
 is($o->tasty, 1, "User is still tasty (was not updated since immutable)");
-
+ok($o->uuid,  "The user has a uuid ". $o->uuid);
 1;
 

Modified: jifty/branches/js-refactor/t/TestApp/t/08-notifications.t
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/t/08-notifications.t	(original)
+++ jifty/branches/js-refactor/t/TestApp/t/08-notifications.t	Tue Jul 31 17:23:52 2007
@@ -5,9 +5,29 @@
 use lib 't/lib';
 use Jifty::SubTest;
 
-use Jifty::Test tests => 2;
+use Jifty::Test tests => 4;
+
 use_ok('Jifty::Notification');
+use_ok('Email::MIME::CreateHTML');
+use_ok('Email::MIME');
+use_ok('Email::Send');
+
+my $html = "<html><body>This is the HTML portion of the test email</body></html>";
+
+my $text = "This is the text portion of the text email";
+
+my $test_email = Email::MIME->create_html(
+					  header => [
+						     From => 'test at test',
+						     To => 'test2 at test2',
+						     Subject => 'This is a test email',
+						     ],
+					  body => $html,
+					  text_body => $text
+					  );
+
+
 
-TODO: {local $TODO = "Actually write tests"; ok(0, "Test notifications")};
+# TODO: {local $TODO = "Actually write tests"; ok(0, "Test notifications")};
 
 1;

Modified: jifty/branches/js-refactor/t/TestApp/t/15-template-subclass.t
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/t/15-template-subclass.t	(original)
+++ jifty/branches/js-refactor/t/TestApp/t/15-template-subclass.t	Tue Jul 31 17:23:52 2007
@@ -72,14 +72,8 @@
 my $mech = Jifty::Test::WWW::Mechanize->new;
 foreach my $test (@tests) {
     $mech->get_ok( $URL . $test->{url}, "get '$URL: /jifty/jifty/trunk/t/TestApp/t/15-template-subclass.t $test->{url}'" );
-    TODO: { 
-
-    local $TODO = " Template subclassing needs some love";
-    ok($mech->content =~ qr{$test->{text}}, "found the test content");
-    # $mech->content_contains breaks on multiline content
-    #$mech->content_contains( $test->{text}, "found content '$test->{text}'" );
-    };
 
+    $mech->content_contains( $test->{text}, "found content '$test->{text}'" );
 }
 
 1;

Modified: jifty/branches/js-refactor/t/TestApp/t/config-Record
==============================================================================
--- jifty/branches/js-refactor/t/TestApp/t/config-Record	(original)
+++ jifty/branches/js-refactor/t/TestApp/t/config-Record	Tue Jul 31 17:23:52 2007
@@ -2,3 +2,5 @@
 framework:
   Database:
     RecordBaseClass: Jifty::DBI::Record
+  Plugins:
+    - UUID: {}

Added: jifty/branches/js-refactor/t/clientside/td.t
==============================================================================
--- (empty file)
+++ jifty/branches/js-refactor/t/clientside/td.t	Tue Jul 31 17:23:52 2007
@@ -0,0 +1,104 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Jifty;
+
+package Foo;
+use Jifty::View::Declare -base;
+
+template _faq => \&_faq;
+
+sub _faq {
+    div {
+        attr { id => "faq" };
+        h2 { 'Using Yada' }
+        dl {
+            dt { 'Yada Yada Yada!'}
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+template _faq2 => \&_faq2;
+
+sub _faq2 {
+    div {
+        attr { id => "faq" };
+        h2 { 'Using Yada' }
+        dl {
+            dt { 'Yada Yada Yada!'};
+            dd {
+                span {
+                    'are we nearly there yet?'
+                }
+	    }
+	};
+    }
+};
+
+package main;
+
+use Test::More;
+use IPC::Run3;
+eval 'use Jifty::View::Declare::Compile; 1'
+    or plan skip_all => "Can't load Jifty::View::Declare::Compile";
+
+my $jsbin = can_run('js')
+    or plan skip_all => "Can't find spidermonkey js binary";
+
+Template::Declare->init( roots => ['Foo']);
+
+plan tests => 2;
+
+is_compatible('_faq');
+TODO: {
+local $TODO = 'buf handling (non-katamari version) not yet';
+is_compatible('_faq2');
+
+};
+
+
+
+sub is_compatible {
+    my $template = shift;
+    my $js = js_output( js_code( Foo->can($template) ) );
+    my $td = Template::Declare->show($template);
+    $js =~ s/\s*//g;
+    $td =~ s/\s*//g;
+    unshift @_, $js, $td;
+    goto \&is;
+}
+
+sub js_code {
+    my $code = shift;
+    return '(function() '.Jifty::View::Declare::Compile->new->coderef2text($code) . ')()';
+}
+
+sub js_output {
+    my $code = shift;
+    my ($out, $err);
+    run3 [$jsbin],
+	['load("share/web/static/js/template_declare.js");', "print($code);"],
+	    \$out, \$err;
+    diag $err if $err;
+    return $out;
+
+}
+
+use File::Spec::Functions 'catfile';
+sub can_run {
+    my ($_cmd, @path) = @_;
+
+    return $_cmd if -x $_cmd;
+
+    for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), @path, '.') {
+        my $abs = catfile($dir, $_[0]);
+        return $abs if -x $abs;
+    }
+
+    return;
+}


More information about the Jifty-commit mailing list