← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wallyworld/launchpad/use-convoy into lp:launchpad

 

Ian Booth has proposed merging lp:~wallyworld/launchpad/use-convoy into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/use-convoy/+merge/89222

== Implementation ==

Add support for using a YUI combo loader. A feature flag is used so that by default, the legacy launchpad.js is loaded. The combo loader used is lp:convoy

Feature flags:
js.combo_loader.enabled : on/true = use combo loader
js.yui_version : spefic a non-default yui version for the combo loader to serve up

Key changes:
- TAL templates updated to use combo loader
- combo-rootdir script to set up the combo loader
- altered make/build scripts

Three versions of YUI are currently unpacked from tarballs in the download cache:
- 3.3.0
- 3.4.1
- 3.5 pr1

We use 3.3.0 as specified in versions.cfg. To use a different version, change the yui symlink in /var/tmp/convoy to point to a different version.

Because I did a a lot of make testing, I got so pissed off with WADL and mailman taking minutes to build that I added support in the Makefile to make things way quicker for devs:

If LP_MAKE_NO_WADL is defined (can be set as env var), the WADL make step is totally skipped.

If LP_MAKE_KEEP_MAILMAN is defined, make clean does not remove the compiled mailman and so the next make after a clean doesn't need to rebuild it.

The default make behaviour is as per normal. However, setting the above 2 variables causes the make to complete in literally seconds \o/

== Tests ==

Testing included:
- checking launchpad.js was the same on the old and new codebases
- testing all manner of pages with and without combo loader feature flag, including yui2 stuff like calendar
- quick look at js loaded, xhr requests
- running yuitests (as usual for me they thrashed my disk so much that a couple timed out but everything looks ok)

-- 
https://code.launchpad.net/~wallyworld/launchpad/use-convoy/+merge/89222
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/use-convoy into lp:launchpad.
=== modified file 'Makefile'
--- Makefile	2012-01-18 21:46:42 +0000
+++ Makefile	2012-01-19 11:32:28 +0000
@@ -42,6 +42,8 @@
 
 CODEHOSTING_ROOT=/var/tmp/bazaar.launchpad.dev
 
+CONVOY_ROOT=/var/tmp/convoy
+
 BZR_VERSION_INFO = bzr-version-info.py
 
 APIDOC_DIR = lib/canonical/launchpad/apidoc
@@ -86,7 +88,12 @@
 	    --force "$(APIDOC_TMPDIR)"
 	mv $(APIDOC_TMPDIR) $(APIDOC_DIR)
 
-apidoc: compile $(API_INDEX)
+apidoc:
+ifdef LP_MAKE_NO_WADL
+	@echo "Skipping WADL generation."
+else
+	compile $(API_INDEX)
+endif
 
 # Used to generate HTML developer documentation for Launchpad.
 doc:
@@ -180,6 +187,10 @@
 endif
 
 jsbuild: $(PY) $(JS_OUT)
+	mkdir -p $(CONVOY_ROOT)
+	bin/combo-rootdir $(CONVOY_ROOT)
+	rm -f $(ICING)/yui
+	ln -sf $(CONVOY_ROOT)/yui $(ICING)/yui
 
 eggs:
 	# Usually this is linked via link-external-sourcecode, but in
@@ -348,21 +359,31 @@
 
 clean_js:
 	$(RM) $(JS_OUT)
-	$(RM) -r $(LAZR_BUILT_JS_ROOT)
+	$(RM) -r $(ICING)/yui
+	$(RM) -r $(CONVOY_ROOT)
 
 clean_buildout:
 	$(RM) -r bin
 	$(RM) -r parts
 	$(RM) -r develop-eggs
 	$(RM) .installed.cfg
-	$(RM) -r build
 	$(RM) _pythonpath.py
 	$(RM) -r yui/*
 
 clean_logs:
 	$(RM) logs/thread*.request
 
-clean: clean_js clean_buildout clean_logs
+clean_mailman:
+	$(RM) -r \
+			  /var/tmp/mailman \
+			  /var/tmp/mailman-xmlrpc.test
+ifdef LP_MAKE_KEEP_MAILMAN
+	@echo "Keeping previously built mailman."
+else
+	$(RM) -r lib/mailman
+endif
+
+clean: clean_js clean_mailman clean_buildout clean_logs
 	$(MAKE) -C sourcecode/pygettextpo clean
 	# XXX gary 2009-11-16 bug 483782
 	# The pygettextpo Makefile should have this next line in it for its make
@@ -375,11 +396,11 @@
 		-type f \( -name '*.o' -o -name '*.so' -o -name '*.la' -o \
 	    -name '*.lo' -o -name '*.py[co]' -o -name '*.dll' \) \
 	    -print0 | xargs -r0 $(RM)
-	$(RM) -r lib/mailman
 	$(RM) -r $(LP_BUILT_JS_ROOT)/*
 	$(RM) -r $(CODEHOSTING_ROOT)
 	$(RM) -r $(APIDOC_DIR)
 	$(RM) -r $(APIDOC_DIR).tmp
+	$(RM) -r build
 	$(RM) $(BZR_VERSION_INFO)
 	$(RM) +config-overrides.zcml
 	$(RM) -r \

=== added file 'buildout-templates/bin/combo-rootdir.in'
--- buildout-templates/bin/combo-rootdir.in	1970-01-01 00:00:00 +0000
+++ buildout-templates/bin/combo-rootdir.in	2012-01-19 11:32:28 +0000
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+set -e
+
+if [ -z "$1" ]; then
+    echo "$0: Need root combo loader directory as an argument."
+    exit 1
+fi
+
+BUILD_DIR=$1
+rm -rf $BUILD_DIR/*
+
+# Populate YUI.
+mkdir $BUILD_DIR/yui-${versions:yui}
+cp -a ${buildout:yui-directory}/yui-${versions:yui}/* $BUILD_DIR/yui-${versions:yui}
+ln -sf $BUILD_DIR/yui-${versions:yui} $BUILD_DIR/yui
+
+# And YUI 2, for now.
+cp -a lib/canonical/launchpad/icing/yui_2.7.0b/build $BUILD_DIR/yui2
+
+# Copy in our modules.
+mkdir $BUILD_DIR/lp
+for jsdir in lib/lp/*/javascript ; do
+    app=$(echo $jsdir | sed -e 's,lib/lp/\(.*\)/javascript,\1,')
+    cp -a $jsdir $BUILD_DIR/lp/$app
+done
+# ... and delete tests.
+find $BUILD_DIR -name 'tests' -type d | xargs rm -rf
+
+# We have to move some modules around.
+cp lib/lp/contrib/javascript/lp.mustache.js $BUILD_DIR/lp
+rm $BUILD_DIR/lp/contrib/mustache.js
+rm $BUILD_DIR/lp/app/worlddata
+cp lib/lp/services/worlddata/javascript/languages.js $BUILD_DIR/lp
+cp lib/lp/app/longpoll/javascript/longpoll.js $BUILD_DIR/lp
+
+# Build the LP module definition file.
+utilities/js-deps -n LP_MODULES -s $BUILD_DIR/lp -o $BUILD_DIR/lp/meta.js >/dev/null
+
+# Minify our JS.
+bin/py -m lp.scripts.utilities.js.jsmin_all $BUILD_DIR/lp
+
+# Check if any JS modules are missing.
+error=0
+for mod in $(grep '  LP' $BUILD_DIR/lp/meta.js | tr -s ' ' | cut -d\  -f2) ; do
+    if ! grep -q "modules\[$mod\]" $BUILD_DIR/lp/meta.js ; then
+        echo "ERROR: $mod is not mentioned in the meta file"
+        error=1
+    fi
+done
+
+exit $error

=== modified file 'buildout.cfg'
--- buildout.cfg	2012-01-13 08:34:16 +0000
+++ buildout.cfg	2012-01-19 11:32:28 +0000
@@ -3,7 +3,9 @@
 
 [buildout]
 parts =
-    yui
+    yui-default
+    yui-3.4
+    yui-3.5
     scripts
     filetemplates
     tags
@@ -13,7 +15,7 @@
 unzip = true
 eggs-directory = eggs
 download-cache = download-cache
-yui-directory = yui
+yui-directory = build/js/yui
 relative-paths = true
 
 # Disable this option temporarily if you want buildout to find software
@@ -39,12 +41,24 @@
 [yui]
 recipe = plone.recipe.command
 command =
-    mkdir -p ${buildout:yui-directory}/yui-${versions:yui}
-    rm -rf ${buildout:yui-directory}/yui-${versions:yui}/*
-    tar -zxf download-cache/dist/yui-${versions:yui}.tar.gz \
-        -C ${buildout:yui-directory}/yui-${versions:yui}
-    ln -sf ../../../../${buildout:yui-directory}/yui-${versions:yui} \
-        lib/canonical/launchpad/icing/yui
+    mkdir -p ${buildout:yui-directory}/yui-${:yui_version}
+    rm -rf ${buildout:yui-directory}/yui-${:yui_version}/*
+    tar -zxf download-cache/dist/yui-${:yui_version}.tar.gz \
+        --wildcards --strip-components 2 \
+        -C ${buildout:yui-directory}/yui-${:yui_version} \
+        '*/build'
+
+[yui-default]
+<= yui
+yui_version = ${versions:yui}
+
+[yui-3.4]
+<= yui
+yui_version = 3.4.1
+
+[yui-3.5]
+<=yui
+yui_version = 3.5.0pr1
 
 [filetemplates]
 recipe = z3c.recipe.filetemplate

=== modified file 'configs/development/local-launchpad-apache'
--- configs/development/local-launchpad-apache	2011-09-30 11:14:23 +0000
+++ configs/development/local-launchpad-apache	2012-01-19 11:32:28 +0000
@@ -138,6 +138,7 @@
 
   ProxyPreserveHost on
   ProxyPass /+longpoll/ http://localhost:22435/ retry=1
+  ProxyPass /+combo !
   ProxyPass / http://localhost:8086/ retry=1
 
   <Location />
@@ -153,6 +154,8 @@
     SetEnvIfNoCase Request_URI ^/@@/.*\.js$ !no-gzip !dont-vary
   </Location>
 
+  WSGIScriptAlias /+combo /usr/share/convoy/convoy.wsgi
+
 </VirtualHost>
 
 <VirtualHost 127.0.0.88:80>

=== modified file 'lib/lp/answers/templates/question-index.pt'
--- lib/lp/answers/templates/question-index.pt	2011-08-20 14:14:26 +0000
+++ lib/lp/answers/templates/question-index.pt	2012-01-19 11:32:28 +0000
@@ -17,7 +17,7 @@
   }
     </style>
     <script type="text/javascript">
-        LPS.use('base', 'node', 'event',
+        LP_YUI.use('base', 'node', 'event',
                 'lp.comments.hide', 'lp.answers.subscribers',
             function(Y) {
         Y.on('domready', function() {

=== modified file 'lib/lp/answers/templates/questiontarget-portlet-answercontacts.pt'
--- lib/lp/answers/templates/questiontarget-portlet-answercontacts.pt	2011-08-15 03:42:02 +0000
+++ lib/lp/answers/templates/questiontarget-portlet-answercontacts.pt	2012-01-19 11:32:28 +0000
@@ -7,7 +7,7 @@
 <tal:script
     replace="structure
     string:&lt;script id='milestone-script' type='text/javascript'&gt;" />
-    LPS.use('base', 'node', 'event',
+    LP_YUI.use('base', 'node', 'event',
             'lp.comments.hide', 'lp.answers.answercontacts',
         function(Y) {
     Y.on('domready', function() {

=== modified file 'lib/lp/app/doc/lazr-js-widgets.txt'
--- lib/lp/app/doc/lazr-js-widgets.txt	2011-06-28 15:09:26 +0000
+++ lib/lp/app/doc/lazr-js-widgets.txt	2012-01-19 11:32:28 +0000
@@ -331,7 +331,7 @@
       </span>
     </span>
     <script>
-    LPS.use('lp.app.choice', function(Y) {
+    YUI.use('lp.app.choice', function(Y) {
     ...
     </script>
 
@@ -390,7 +390,7 @@
         </span>
     </span>
     <script>
-    LPS.use('lp.app.choice', function(Y) {
+    YUI.use('lp.app.choice', function(Y) {
     ...
     </script>
 
@@ -478,7 +478,7 @@
       <div class="yui3-activator-message-box yui3-activator-hidden" />
       </span>
       <script>
-      LPS.use('lp.app.multicheckbox', function(Y) {
+      YUI.use('lp.app.multicheckbox', function(Y) {
       ...
       </script>
 

=== modified file 'lib/lp/app/javascript/ajax_log.js'
--- lib/lp/app/javascript/ajax_log.js	2011-08-09 14:18:02 +0000
+++ lib/lp/app/javascript/ajax_log.js	2012-01-19 11:32:28 +0000
@@ -5,7 +5,7 @@
     */
     var AJAX_OK_TIME = 1;
 
-    LPS.use('node', 'lp.anim', function(Y) {
+    YUI.use('node', 'lp.anim', function(Y) {
         Y.on('contentready', function() {
             var node = Y.one('#ajax-time-list');
             var ajax_request_times = {};

=== modified file 'lib/lp/app/javascript/beta-notification.js'
--- lib/lp/app/javascript/beta-notification.js	2011-11-22 21:38:36 +0000
+++ lib/lp/app/javascript/beta-notification.js	2012-01-19 11:32:28 +0000
@@ -18,7 +18,7 @@
  * This should be called after the page has loaded e.g. on 'domready'.
  */
 function display_beta_notification() {
-    var notifications = Mustache.to_html([
+    var notifications = Y.lp.mustache.to_html([
         '{{#features}}{{#is_beta}}',
         '<span class="beta-feature"> {{title}}',
         '{{#url}}',
@@ -78,4 +78,4 @@
 }
 namespace.display_beta_notification = display_beta_notification;
 
-}, "0.1", {"requires": ["base", "node", "anim"]});
+}, '0.1', {'requires': ['base', 'node', 'anim', 'lp.mustache']});

=== modified file 'lib/lp/app/javascript/configutils.js'
--- lib/lp/app/javascript/configutils.js	2012-01-13 16:46:46 +0000
+++ lib/lp/app/javascript/configutils.js	2012-01-19 11:32:28 +0000
@@ -1,6 +1,6 @@
 /* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
 
-YUI().add('lp.configutils', function(Y) {
+YUI.add('lp.configutils', function(Y) {
     /**
      * The configutils module provides objects for managing the config
      * or settings of a web page or widget.

=== modified file 'lib/lp/app/javascript/indicator/indicator.js'
--- lib/lp/app/javascript/indicator/indicator.js	2012-01-13 16:46:46 +0000
+++ lib/lp/app/javascript/indicator/indicator.js	2012-01-19 11:32:28 +0000
@@ -3,16 +3,15 @@
  *
  * Large indicator of pending operations.
  *
- * @module lp.indicator
+ * @module lp.app.indicator
  *
  * Usage:
- *     lp.indicator.OverlayIndicator({
+ *     lp.app.indicator.OverlayIndicator({
  *         target: Y.one('#id')
  *     });
  *
  */
-YUI().add('lp.indicator', function (Y) {
-
+YUI.add('lp.app.indicator', function (Y) {
     var props = {
         ATTRS: {
             /**
@@ -201,7 +200,7 @@
         }
     };
 
-    var indicator = Y.namespace('lp.indicator');
+    var indicator = Y.namespace('lp.app.indicator');
     indicator.OverlayIndicator = OverlayIndicator;
     indicator.actions = actions;
 

=== modified file 'lib/lp/app/javascript/indicator/tests/test_indicator.js'
--- lib/lp/app/javascript/indicator/tests/test_indicator.js	2011-12-02 21:54:11 +0000
+++ lib/lp/app/javascript/indicator/tests/test_indicator.js	2012-01-19 11:32:28 +0000
@@ -33,7 +33,7 @@
     test_target_attribute: function () {
         // Constrain attribute should be set from passing in target.
         var test_node = Y.one('#' + this.div_id);
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: test_node
         });
         this.indicator.render();
@@ -47,7 +47,7 @@
         // We need to create some nesting to really ensure
         // the test is good.
         this.div.appendChild(child_div);
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: child_div
         });
         this.indicator.render();
@@ -60,7 +60,7 @@
     test_indicator_has_loading_icon: function () {
         // The indicator should have a loading image added
         // to the contentBox.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -72,7 +72,7 @@
 
     test_indiciator_starts_invisible: function () {
         // Indicator widgets should start hidden.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -83,7 +83,7 @@
 
     test_set_busy_shows_overlay: function() {
         // setBusy should show the overlay.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -95,7 +95,7 @@
 
     test_size_matches_on_set_busy: function() {
         // Indicator should always resize when target changes size.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -121,7 +121,7 @@
 
     test_position_matches_on_set_busy: function() {
         // Indicator should always reposition itself before setBusy.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -140,7 +140,7 @@
 
     test_success_hides_overlay: function() {
         // Calling success should hide the overlay.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -157,7 +157,7 @@
         var callback = function() {
             called = true;
         };
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div,
             success_action: callback
         });
@@ -171,9 +171,9 @@
         var viewport = Y.DOM.viewportRegion();
         this.div.set('offsetWidth', viewport.right + 1000);
         this.div.set('offsetHeight', viewport.bottom + 1000);
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div,
-            success_action: Y.lp.indicator.actions.scroll_to_target
+            success_action: Y.lp.app.indicator.actions.scroll_to_target
         });
         this.indicator.render();
         window.scrollTo(1000, 1000);
@@ -188,7 +188,7 @@
 
     test_error_hides_overlay: function () {
         // Calling error should hide the overlay.
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div
         });
         this.indicator.render();
@@ -205,7 +205,7 @@
         var callback = function() {
             called = true;
         };
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: this.div,
             error_action: callback
         });
@@ -217,4 +217,4 @@
 
 large_indicator.suite = suite;
 
-}, '0.1', {'requires': ['test', 'lp.indicator']});
+}, '0.1', {'requires': ['test', 'lp.app.indicator']});

=== modified file 'lib/lp/app/javascript/inlinehelp/inlinehelp.js'
--- lib/lp/app/javascript/inlinehelp/inlinehelp.js	2012-01-06 12:34:37 +0000
+++ lib/lp/app/javascript/inlinehelp/inlinehelp.js	2012-01-19 11:32:28 +0000
@@ -129,4 +129,4 @@
         }
     );
 
-}, "0.1", { "requires": ['lazr.overlay', 'io', 'log'] });
+}, "0.1", { "requires": ['lazr.overlay', 'io'] });

=== modified file 'lib/lp/app/javascript/listing_navigator.js'
--- lib/lp/app/javascript/listing_navigator.js	2012-01-06 13:17:34 +0000
+++ lib/lp/app/javascript/listing_navigator.js	2012-01-19 11:32:28 +0000
@@ -11,7 +11,6 @@
 
 var module = Y.namespace('lp.app.listing_navigator');
 
-
 function empty_nodelist() {
     return new Y.NodeList([]);
 }
@@ -72,9 +71,9 @@
 
         // init the indicator plugin on the target
         // it defaults to invisible, so safe init
-        this.indicator = new Y.lp.indicator.OverlayIndicator({
+        this.indicator = new Y.lp.app.indicator.OverlayIndicator({
             target: config.target.get('parentNode'),
-            success_action: Y.lp.indicator.actions.scroll_to_target
+            success_action: Y.lp.app.indicator.actions.scroll_to_target
         });
 
         this.indicator.render();
@@ -174,13 +173,13 @@
      */
     render: function() {
         var current_batch = this.get_current_batch();
-        var batch_info = Mustache.to_html(this.get('batch_info_template'), {
+        var batch_info = Y.lp.mustache.to_html(this.get('batch_info_template'), {
             start: current_batch.start + 1,
             end: current_batch.start +
                 current_batch.mustache_model.items.length,
             total: current_batch.total
         });
-        var content = Mustache.to_html(
+        var content = Y.lp.mustache.to_html(
             this.get('template'), this.get_render_model(current_batch));
         this.get('target').setContent(content);
 
@@ -518,6 +517,7 @@
 
 }, "0.1", {
     "requires": [
-        "node", 'lp.client', 'lp.app.errors', 'lp.indicator'
+        "node", 'lp.client', 'lp.app.errors', 'lp.app.indicator',
+        'lp.mustache'
     ]
 });

=== modified file 'lib/lp/app/javascript/multicheckbox.js'
--- lib/lp/app/javascript/multicheckbox.js	2011-04-13 05:56:12 +0000
+++ lib/lp/app/javascript/multicheckbox.js	2012-01-19 11:32:28 +0000
@@ -147,7 +147,7 @@
         var checked_html = '';
         if (data.checked)
             checked_html = 'checked="checked"';
-        var checkbox_html = Y.Lang.substitute(
+        var checkbox_html = Y.Lang.sub(
             CHECKBOX_TEMPLATE,
             {field_name: "field."+attribute_name, field_index:i,
             field_value: data.token, field_text: Y.Escape.html(data.name),

=== modified file 'lib/lp/app/javascript/ordering/ordering.js'
--- lib/lp/app/javascript/ordering/ordering.js	2012-01-13 16:46:46 +0000
+++ lib/lp/app/javascript/ordering/ordering.js	2012-01-19 11:32:28 +0000
@@ -1,6 +1,6 @@
 /* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
 
-YUI().add('lp.ordering', function(Y) {
+YUI.add('lp.ordering', function(Y) {
     /**
      * A menu bar for quickly reordering a list of items on a page.
      * This widget is used for managing the bar, it's buttons, and
@@ -341,7 +341,7 @@
             for (i=0; i<len; i++) {
                 id = keys[i][0];
                 label = keys[i][1];
-                li_html = Y.Lang.substitute(
+                li_html = Y.Lang.sub(
                     this.constructor.LI_TEMPLATE,
                     {li_id: 'sort-' + id, li_label: label});
                 li_node = Y.Node.create(li_html);

=== modified file 'lib/lp/app/javascript/tests/test_ajax_batch_navigator.js'
--- lib/lp/app/javascript/tests/test_ajax_batch_navigator.js	2011-10-14 02:12:06 +0000
+++ lib/lp/app/javascript/tests/test_ajax_batch_navigator.js	2012-01-19 11:32:28 +0000
@@ -33,7 +33,7 @@
 
         makeNode: function(node_type, id, css_class) {
             var node = Y.Node.create(
-                    Y.Lang.substitute(
+                    Y.Lang.sub(
                             '<{node_type}></{node_type}>',
                             {node_type: node_type}));
             if (id !== undefined) {

=== modified file 'lib/lp/app/javascript/tests/test_beta_notification.html'
--- lib/lp/app/javascript/tests/test_beta_notification.html	2011-11-17 20:21:03 +0000
+++ lib/lp/app/javascript/tests/test_beta_notification.html	2012-01-19 11:32:28 +0000
@@ -10,7 +10,7 @@
   <script type="text/javascript"
           src="../../../app/javascript/testing/testrunner.js"></script>
   <script type="text/javascript"
-          src="../../../contrib/javascript/mustache.js"></script>
+          src="../../../contrib/javascript/lp.mustache.js"></script>
 
     <!-- The module under test. -->
     <script type="text/javascript" src="../beta-notification.js"></script>

=== modified file 'lib/lp/app/javascript/tests/test_listing_navigator.html'
--- lib/lp/app/javascript/tests/test_listing_navigator.html	2011-12-15 17:09:48 +0000
+++ lib/lp/app/javascript/tests/test_listing_navigator.html	2012-01-19 11:32:28 +0000
@@ -27,7 +27,7 @@
           src="../../../app/javascript/lp.js"></script>
 
     <script type="text/javascript"
-      src="../../../contrib/javascript/mustache.js"></script>
+      src="../../../contrib/javascript/lp.mustache.js"></script>
   <script type="text/javascript"
       src="../../../app/javascript/overlay/overlay.js"></script>
   <script type="text/javascript"

=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt	2012-01-13 09:17:11 +0000
+++ lib/lp/app/templates/base-layout-macros.pt	2012-01-19 11:32:28 +0000
@@ -38,9 +38,7 @@
       revno modules/lp.app.versioninfo/revno | string:unknown;
       icingroot string:/+icing/rev${revno};
       devmode modules/lp.services.config/config/devmode;
-      yui string:${icingroot}/yui;
-      lazr_js string:${icingroot}/lazr/build;
-      lp_js string:${icingroot}/build"
+      yui_version view/yui_version;"
   >
   <tal:comment replace="nothing">
     This macro just loads javascript files. It doesn't
@@ -62,23 +60,73 @@
         links: {}
     };
   </script>
-  <script type="text/javascript"
+  <tal:js-legacy condition="not: request/features/js.combo_loader.enabled">
+    <script type="text/javascript"
           tal:attributes="src string:${icingroot}/build/launchpad.js"></script>
+    <script type="text/javascript">
+       YUI.GlobalConfig = {
+           fetchCSS: false,
+           timeout: 50
+        }
+    </script>
+  </tal:js-legacy>
 
   <script type="text/javascript"
       tal:content="string:var cookie_scope = '${request/lp:cookie_scope}';"></script>
+
+  <tal:js-loader condition="request/features/js.combo_loader.enabled">
+   <script
+   src="/+combo/?yui/yui/yui-min.js&lp/meta.js&yui/loader/loader-min.js"></script>
+   <script type="text/javascript" tal:content="string:
+        YUI.GlobalConfig = {
+            combine: true,
+            comboBase: '/+combo/?',
+            root: 'yui/',
+            fetchCSS: false,
+            groups: {
+                lp: {
+                    combine: true,
+                    base: '/+combo/?lp/',
+                    comboBase: '/+combo/?',
+                    root: 'lp/',
+                    // comes from including lp/meta.js
+                    modules: LP_MODULES,
+                    fetchCSS: false
+                },
+                yui2: {
+                    combine: true,
+                    base: '/+combo/?yui2/',
+                    comboBase: '/+combo/?',
+                    root: 'yui2/',
+                    fetchCSS: false,
+                    modules: {
+                        'yui2-yahoo': {
+                            path: 'yahoo/yahoo.js'
+                        },
+                        'yui2-event': {
+                            path: 'event/event.js'
+                        },
+                        'yui2-dom': {
+                            path: 'dom/dom.js'
+                        },
+                        'yui2-calendar': {
+                            path: 'calendar/calendar.js'
+                        },
+                        'yui2-dom-event': {
+                            path: 'yahoo-dom-event/yahoo-dom-event.js'
+                        }
+                    }
+                }
+            }
+        }">
+   </script>
+  </tal:js-loader>
   <script type="text/javascript">
     // Define a global YUI sandbox that should be used by everyone.
-    var LPS = YUI({
-      // Don't try to fetch CSS files.
-      fetchCSS: false,
-      // For paranoia, set a low timeout to not wait on loading a resource.
-      timeout: 50
-      });
+    var LP_YUI = YUI();
   </script>
 </metal:load-javascript>
 
-
 <metal:page-javascript define-macro="page-javascript"
   tal:define="
       revno modules/lp.app.versioninfo/revno | string:unknown;
@@ -91,182 +139,181 @@
   <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" />
 
     <script id="base-layout-load-scripts" type="text/javascript">
-      LPS.use('base', 'node', 'oop', 'event', 'lp.app.beta_features',
-              'lp.bugs.bugtask_index', 'lp.bugs.subscribers',
-                'lp.code.branchmergeproposal.diff', 'lp.comments.hide',
-                function(Y) {
-          Y.on("domready", function () {
-              if (Y.one(document.body).hasClass('private')) {
-                  Y.lp.app.privacy.setup_privacy_notification();
-                  Y.lp.app.privacy.display_privacy_notification();
-              }
-              Y.lp.app.beta_features.display_beta_notification();
-          });
-      });
-
-      LPS.use('node', 'event-delegate', 'lp', 'lp.app.foldables', 'lp.app.links',
-          'lp.app.longpoll', 'lp.app.inlinehelp', 'lp.app.sorttable', function(Y) {
-          Y.on('load', function(e) {
-              Y.lp.app.sorttable.SortTable.init();
-              Y.lp.app.inlinehelp.init_help();
-              Y.lp.activate_collapsibles();
-              Y.lp.app.foldables.activate();
-              Y.lp.app.links.check_valid_lp_links();
-              // Longpolling will only start if
-              // LP.cache.longpoll is populated.
-              // We use Y.later to work around a Safari/Chrome 'feature':
-              // The mouse cursor stays 'busy' until all the requests started during
-              // page load are finished.  Hence we want the long poll request to start
-              // right *after* the page has loaded.
-              Y.later(0, Y.lp.app.longpoll, Y.lp.app.longpoll.setupLongPollManager);
-          }, window);
-
-          Y.on('lp:context:web_link:changed', function(e) {
-              window.location = e.new_value;
-          });
-      });
-
-      // This code is pulled from lp.js that needs to be available on every
-      // request. Pulling here to get it outside the scope of the YUI block.
-      // Lint-safe scripting URL.
-      var VOID_URL = '_:void(0);'.replace('_', 'javascript');
-
-      function registerLaunchpadFunction(func) {
-          // registers a function to fire onload.
-          // Use this for initilaizing any javascript that should fire once the page
-          // has been loaded.
-          LPS.use('node', function(Y) {
-              Y.on('load', function(e) {
-                  func();
-              }, window);
-          });
-      }
-
-      // Enable or disable the beta.launchpad.net redirect
-      function setBetaRedirect(enable) {
-          var expire = new Date();
-          if (enable) {
-              expire.setTime(expire.getTime() + 1000);
-              document.cookie = ('inhibit_beta_redirect=0; Expires=' +
-                                 expire.toGMTString() + cookie_scope);
-              alert('Redirection to the beta site has been enabled');
-          } else {
-              expire.setTime(expire.getTime() + 2 * 60 * 60 * 1000);
-              document.cookie = ('inhibit_beta_redirect=1; Expires=' +
-                                 expire.toGMTString() + cookie_scope);
-              alert('You will not be redirected to the beta site for 2 hours');
-          }
-          return false;
-      }
-
-      function setFocusByName(name) {
-          // Focus the first element matching the given name which can be focused.
-          var nodes = document.getElementsByName(name);
-          var i, node;
-          for (i = 0; i < nodes.length; i++) {
-              node = nodes[i];
-              if (node.focus) {
-                  try {
-                      // Trying to focus a hidden element throws an error in IE8.
-                      if (node.offsetHeight !== 0) {
-                          node.focus();
-                      }
-                  } catch (e) {
-                      LPS.use('console', function(Y) {
-                          Y.log('In setFocusByName(<' +
-                              node.tagName + ' type=' + node.type + '>): ' + e);
-                      });
-                  }
-                  break;
-              }
-          }
-      }
-
-      function popup_window(url, name, width, height) {
-          var iframe = document.getElementById('popup_iframe_' + name);
-          if (!iframe.src || iframe.src === VOID_URL) {
-              // The first time this handler runs the window may not have been
-              // set up yet; sort that out.
-              iframe.style.width = width + 'px';
-              iframe.style.height = height + 'px';
-              iframe.style.position = 'absolute';
-              iframe.style.background = 'white';
-              iframe.src = url;
-          }
-          iframe.style.display = 'inline';
-          // I haven't found a way of making the search form focus again when
-          // the popup window is redisplayed. I tried doing an
-          //    iframe.contentDocument.searchform.search.focus()
-          // but nothing happens.. -- kiko, 2007-03-12
-      }
-
-      function selectWidget(widget_name, event) {
-        if (event && (event.keyCode === 9 || event.keyCode === 13)) {
-            // Avoid firing if user is tabbing through or simply pressing
-            // enter to submit the form.
-            return;
-        }
-        document.getElementById(widget_name).checked = true;
-      }
-
-      function switchBugBranchFormAndWhiteboard(id) {
-          var div = document.getElementById('bugbranch' + id);
-          var wb = document.getElementById('bugbranch' + id + '-wb');
-
-          if (div.style.display === "none") {
-              /* Expanding the form */
-              if (wb !== null) {
-                  wb.style.display = "none";
-              }
-              div.style.display = "block";
-              /* Use two focus calls to get the browser to scroll to the end of the
-               * form first, then focus back to the first field of the form.
-               */
-              document.getElementById('field'+id+'.actions.update').focus();
-              document.getElementById('field'+id+'.whiteboard').focus();
-          } else {
-              if (wb !== null) {
-                  wb.style.display = "block";
-              }
-              div.style.display = "none";
-          }
-          return false;
-      }
-
-      function switchSpecBranchFormAndSummary(id) {
-          /* The document has two identifiable elements for each
-           * spec-branch link:
-           *    'specbranchX' which is the div containing the edit form
-           *    'specbranchX-summary' which is the div contining the sumary
-           * where X is the database id of the link.
-           */
-          var div = document.getElementById('specbranch' + id);
-          var wb = document.getElementById('specbranch' + id + '-summary');
-
-          if (div.style.display === "none") {
-              /* Expanding the form */
-              if (wb !== null) {
-                  wb.style.display = "none";
-              }
-              div.style.display = "block";
-              /* Use two focus calls to get the browser to scroll to the end of the
-               * form first, then focus back to the first field of the form.
-               */
-              document.getElementById('field' + id + '.actions.change').focus();
-              document.getElementById('field' + id + '.summary').focus();
-          } else {
-              if (wb !== null) {
-                  wb.style.display = "block";
-              }
-              div.style.display = "none";
-          }
-          return false;
-      }
-
-      function updateField(field, enabled)
-      {
-          field.disabled = !enabled;
-      }
+        LP_YUI.use('base', 'node', 'console', 'event',
+            'oop', 'lp', 'lp.app.privacy',
+            'lp.app.beta_features', 'lp.app.foldables','lp.app.sorttable',
+            'lp.app.inlinehelp', 'lp.app.links', 'lp.app.longpoll',
+            'lp.bugs.bugtask_index', 'lp.bugs.subscribers',
+            'lp.code.branchmergeproposal.diff', 'lp.comments.hide',
+            function(Y) {
+
+            Y.on("domready", function () {
+                if (Y.one(document.body).hasClass('private')) {
+                    Y.lp.app.privacy.setup_privacy_notification();
+                    Y.lp.app.privacy.display_privacy_notification();
+                }
+                Y.lp.app.beta_features.display_beta_notification();
+                Y.lp.app.sorttable.SortTable.init();
+                Y.lp.app.inlinehelp.init_help();
+                Y.lp.activate_collapsibles();
+                Y.lp.app.foldables.activate();
+                Y.lp.app.links.check_valid_lp_links();
+                // Longpolling will only start if
+                // LP.cache.longpoll is populated.
+                // We use Y.later to work around a Safari/Chrome 'feature':
+                // The mouse cursor stays 'busy' until all the requests started during
+                // page load are finished.  Hence we want the long poll request to start
+                // right *after* the page has loaded.
+                Y.later(0, Y.lp.app.longpoll, Y.lp.app.longpoll.setupLongPollManager);
+
+            });
+
+            Y.on('lp:context:web_link:changed', function(e) {
+                  window.location = e.new_value;
+            });
+        });
+
+        // This code is pulled from lp.js that needs to be available on every
+        // request. Pulling here to get it outside the scope of the YUI block.
+        // Lint-safe scripting URL.
+        var VOID_URL = '_:void(0);'.replace('_', 'javascript');
+
+        function registerLaunchpadFunction(func) {
+            // registers a function to fire onload.
+            // Use this for initilaizing any javascript that should fire once the page
+            // has been loaded.
+            LP_YUI.use('node', function(Y) {
+                Y.on('load', function(e) {
+                    func();
+                }, window);
+            });
+        }
+
+        // Enable or disable the beta.launchpad.net redirect
+        function setBetaRedirect(enable) {
+            var expire = new Date();
+            if (enable) {
+                expire.setTime(expire.getTime() + 1000);
+                document.cookie = ('inhibit_beta_redirect=0; Expires=' +
+                                   expire.toGMTString() + cookie_scope);
+                alert('Redirection to the beta site has been enabled');
+            } else {
+                expire.setTime(expire.getTime() + 2 * 60 * 60 * 1000);
+                document.cookie = ('inhibit_beta_redirect=1; Expires=' +
+                                   expire.toGMTString() + cookie_scope);
+                alert('You will not be redirected to the beta site for 2 hours');
+            }
+            return false;
+        }
+
+        function setFocusByName(name) {
+            // Focus the first element matching the given name which can be focused.
+            var nodes = document.getElementsByName(name);
+            var i, node;
+            for (i = 0; i < nodes.length; i++) {
+                node = nodes[i];
+                if (node.focus) {
+                    try {
+                        // Trying to focus a hidden element throws an error in IE8.
+                        if (node.offsetHeight !== 0) {
+                            node.focus();
+                        }
+                    } catch (e) {
+                        LP_YUI.use('console', function(Y) {
+                            Y.log('In setFocusByName(<' +
+                                node.tagName + ' type=' + node.type + '>): ' + e);
+                        });
+                    }
+                    break;
+                }
+            }
+        }
+
+        function popup_window(url, name, width, height) {
+            var iframe = document.getElementById('popup_iframe_' + name);
+            if (!iframe.src || iframe.src === VOID_URL) {
+                // The first time this handler runs the window may not have been
+                // set up yet; sort that out.
+                iframe.style.width = width + 'px';
+                iframe.style.height = height + 'px';
+                iframe.style.position = 'absolute';
+                iframe.style.background = 'white';
+                iframe.src = url;
+            }
+            iframe.style.display = 'inline';
+            // I haven't found a way of making the search form focus again when
+            // the popup window is redisplayed. I tried doing an
+            //    iframe.contentDocument.searchform.search.focus()
+            // but nothing happens.. -- kiko, 2007-03-12
+        }
+
+        function selectWidget(widget_name, event) {
+          if (event && (event.keyCode === 9 || event.keyCode === 13)) {
+              // Avoid firing if user is tabbing through or simply pressing
+              // enter to submit the form.
+              return;
+          }
+          document.getElementById(widget_name).checked = true;
+        }
+
+        function switchBugBranchFormAndWhiteboard(id) {
+            var div = document.getElementById('bugbranch' + id);
+            var wb = document.getElementById('bugbranch' + id + '-wb');
+
+            if (div.style.display === "none") {
+                /* Expanding the form */
+                if (wb !== null) {
+                    wb.style.display = "none";
+                }
+                div.style.display = "block";
+                /* Use two focus calls to get the browser to scroll to the end of the
+                 * form first, then focus back to the first field of the form.
+                 */
+                document.getElementById('field'+id+'.actions.update').focus();
+                document.getElementById('field'+id+'.whiteboard').focus();
+            } else {
+                if (wb !== null) {
+                    wb.style.display = "block";
+                }
+                div.style.display = "none";
+            }
+            return false;
+        }
+
+        function switchSpecBranchFormAndSummary(id) {
+            /* The document has two identifiable elements for each
+             * spec-branch link:
+             *    'specbranchX' which is the div containing the edit form
+             *    'specbranchX-summary' which is the div contining the sumary
+             * where X is the database id of the link.
+             */
+            var div = document.getElementById('specbranch' + id);
+            var wb = document.getElementById('specbranch' + id + '-summary');
+
+            if (div.style.display === "none") {
+                /* Expanding the form */
+                if (wb !== null) {
+                    wb.style.display = "none";
+                }
+                div.style.display = "block";
+                /* Use two focus calls to get the browser to scroll to the end of the
+                 * form first, then focus back to the first field of the form.
+                 */
+                document.getElementById('field' + id + '.actions.change').focus();
+                document.getElementById('field' + id + '.summary').focus();
+            } else {
+                if (wb !== null) {
+                    wb.style.display = "block";
+                }
+                div.style.display = "none";
+            }
+            return false;
+        }
+
+        function updateField(field, enabled)
+        {
+            field.disabled = !enabled;
+        }
     </script>
 </metal:page-javascript>
 

=== modified file 'lib/lp/app/templates/base-layout.pt'
--- lib/lp/app/templates/base-layout.pt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/templates/base-layout.pt	2012-01-19 11:32:28 +0000
@@ -21,10 +21,7 @@
   xmlns:metal="http://xml.zope.org/namespaces/metal";
   xmlns:i18n="http://xml.zope.org/namespaces/i18n";
   xml:lang="en" lang="en" dir="ltr">
-  <head tal:define="
-    yui string:${icingroot}/yui/current/build;
-    lazr_js string:${icingroot}/lazr/build;
-    lp_js string:${icingroot}/build">
+  <head>
     <title tal:content="view/fmt:pagetitle">Page Title</title>
     <link rel="shortcut icon" href="/@@/launchpad.png" />
     <link
@@ -209,7 +206,7 @@
     replace='structure string:&lt;script type="text/javascript"&gt;
   start_ajax_logging();
   var render_time = "${render_time}";
-  LPS.use("node", function(Y) {
+  LP_YUI.use("node", function(Y) {
     Y.on("domready", function() {
       var node = Y.one("#rendertime");
       node.set("innerHTML", render_time);

=== modified file 'lib/lp/app/templates/boolean-choice-widget.pt'
--- lib/lp/app/templates/boolean-choice-widget.pt	2011-03-10 00:40:15 +0000
+++ lib/lp/app/templates/boolean-choice-widget.pt	2012-01-19 11:32:28 +0000
@@ -10,7 +10,7 @@
 
 <script tal:condition="view/can_write"
         tal:content="structure string:
-LPS.use('lp.app.choice', function(Y) {
+LP_YUI.use('lp.app.choice', function(Y) {
     Y.lp.app.choice.addBinaryChoice(
         ${view/json_config},
         ${view/json_resource_uri},

=== modified file 'lib/lp/app/templates/enum-choice-widget.pt'
--- lib/lp/app/templates/enum-choice-widget.pt	2011-03-10 00:40:15 +0000
+++ lib/lp/app/templates/enum-choice-widget.pt	2012-01-19 11:32:28 +0000
@@ -9,7 +9,7 @@
 </span>
 <script tal:condition="view/can_write"
         tal:content="structure string:
-LPS.use('lp.app.choice', function(Y) {
+LP_YUI.use('lp.app.choice', function(Y) {
     Y.lp.app.choice.addEnumChoice(
         ${view/json_config},
         ${view/json_resource_uri},

=== modified file 'lib/lp/app/templates/inline-multicheckbox-widget.pt'
--- lib/lp/app/templates/inline-multicheckbox-widget.pt	2011-04-25 18:16:19 +0000
+++ lib/lp/app/templates/inline-multicheckbox-widget.pt	2012-01-19 11:32:28 +0000
@@ -31,7 +31,7 @@
 </span>
 <script tal:condition="view/can_write"
         tal:content="string:
-LPS.use('lp.app.multicheckbox', function(Y) {
+LP_YUI.use('lp.app.multicheckbox', function(Y) {
     if (Y.UA.ie) {
         return;
     }

=== modified file 'lib/lp/app/templates/inline-picker.pt'
--- lib/lp/app/templates/inline-picker.pt	2011-12-15 16:09:03 +0000
+++ lib/lp/app/templates/inline-picker.pt	2012-01-19 11:32:28 +0000
@@ -29,7 +29,7 @@
 
 <script tal:condition="view/can_write"
         tal:content="structure string:
-LPS.use('lp.app.picker', 'lp.client', function(Y) {
+LP_YUI.use('lp.app.picker', 'lp.client', function(Y) {
     if (Y.UA.ie) {
         return;
     }

=== modified file 'lib/lp/app/templates/launchpad-databaseunavailable.pt'
--- lib/lp/app/templates/launchpad-databaseunavailable.pt	2011-12-07 04:57:36 +0000
+++ lib/lp/app/templates/launchpad-databaseunavailable.pt	2012-01-19 11:32:28 +0000
@@ -159,7 +159,7 @@
 "
 ></script>
     <script id="load-status" type="text/javascript">
-LPS.use('node', 'io-xdr', 'json-parse', 'datatype-date', function(Y) {
+LP_YUI.use('node', 'io-xdr', 'json-parse', 'datatype-date', function(Y) {
   var tag = function(name) {
     return Y.Node.create('<'+name+'/>');
   }

=== modified file 'lib/lp/app/templates/launchpad-widget-macros.pt'
--- lib/lp/app/templates/launchpad-widget-macros.pt	2010-11-10 15:33:47 +0000
+++ lib/lp/app/templates/launchpad-widget-macros.pt	2012-01-19 11:32:28 +0000
@@ -91,6 +91,17 @@
     <tal:yui2resources
       tal:define="yui2 string:${icingroot}/yui_2.7.0b/build;">
 
+      <script type="text/javascript">
+          LP_YUI.use(
+              'lang', 'node', 'lp.app.calendar', 'yui2-yahoo',
+              'yui2-event', 'yui2-dom', 'yui2-calendar', 'yui2-dom-event',
+              function(Y) {
+                  Y.lp.app.calendar.setup_calendar_widgets();
+              }
+          );
+      </script>
+
+      <tal:js-legacy condition="not: features/js.combo_loader.enabled">
         <tal:devmode condition="devmode">
         <script type="text/javascript"
                 tal:attributes="src string:${yui2}/yahoo/yahoo.js"></script>
@@ -100,8 +111,6 @@
                 tal:attributes="src string:${yui2}/dom/dom.js"></script>
         <script type="text/javascript"
                 tal:attributes="src string:${yui2}/calendar/calendar.js"></script>
-        <script type="text/javascript"
-                tal:attributes="src string:${lp_js}/app/calendar.js"></script>
         </tal:devmode>
         <tal:nondevmode condition="not: devmode">
         <script type="text/javascript"
@@ -109,17 +118,12 @@
         <script type="text/javascript"
                 tal:attributes="src string:${yui2}/calendar/calendar-min.js"></script>
         </tal:nondevmode>
-
+      </tal:js-legacy>
         <link rel="stylesheet" type="text/css"
             tal:attributes="href
             string:${yui2}/calendar/assets/skins/sam/calendar.css" />
     </tal:yui2resources>
 
-    <script type="text/javascript">
-        LPS.use('node', 'lp.app.calendar', function(Y) {
-            Y.lp.app.calendar.setup_calendar_widgets();
-        });
-    </script>
 </metal:yui2calendar-dependencies>
 
 </tal:root>

=== modified file 'lib/lp/app/templates/root-index.pt'
--- lib/lp/app/templates/root-index.pt	2011-03-04 00:08:20 +0000
+++ lib/lp/app/templates/root-index.pt	2012-01-19 11:32:28 +0000
@@ -146,9 +146,11 @@
               <input id="text" type="text" name="field.text" size="25%" />
               <input id="search" type="submit" value="Search Launchpad" />
             </form>
-            <script type="text/javascript"><!--
-              setFocusByName('field.text');
-            // --></script>
+            <script type="text/javascript">
+                LP_YUI.use('lp', function () {
+                    setFocusByName('field.text');
+                });
+            </script>
             <div id="homepage-stats" tal:content="cache:public, 1 hour">
               <strong
                 tal:content="view/project_count/fmt:intcomma">123</strong>&nbsp;projects,

=== modified file 'lib/lp/app/templates/text-area-editor.pt'
--- lib/lp/app/templates/text-area-editor.pt	2011-03-10 00:40:15 +0000
+++ lib/lp/app/templates/text-area-editor.pt	2012-01-19 11:32:28 +0000
@@ -16,7 +16,7 @@
        tal:content="structure view/value">some text</div>
 <script tal:condition="view/can_write"
         tal:content="structure string:
-        LPS.use('lazr.editor', 'lp.client.plugins', function (Y) {
+        LP_YUI.use('lazr.editor', 'lp.client.plugins', function (Y) {
             var widget = new Y.EditableText({
                 contentBox: ${view/widget_css_selector},
                 accept_empty: ${view/accept_empty},

=== modified file 'lib/lp/app/templates/text-line-editor.pt'
--- lib/lp/app/templates/text-line-editor.pt	2011-03-10 00:40:15 +0000
+++ lib/lp/app/templates/text-line-editor.pt	2012-01-19 11:32:28 +0000
@@ -8,7 +8,7 @@
 
 <script tal:condition="view/can_write"
         tal:content="structure string:
-        LPS.use('lazr.editor', 'lp.client.plugins', function (Y) {
+        LP_YUI.use('lazr.editor', 'lp.client.plugins', function (Y) {
             var widget = new Y.EditableText({
                 contentBox: ${view/widget_css_selector},
                 accept_empty: ${view/accept_empty},

=== modified file 'lib/lp/app/widgets/doc/location-widget.txt'
--- lib/lp/app/widgets/doc/location-widget.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/widgets/doc/location-widget.txt	2012-01-19 11:32:28 +0000
@@ -166,7 +166,7 @@
     >>> print widget.map_javascript
     <BLANKLINE>
     <script type="text/javascript">
-        LPS.use('node', 'lp.app.mapping', function(Y) {
+        YUI.use('node', 'lp.app.mapping', function(Y) {
             function renderMap() {
                 Y.lp.app.mapping.renderPersonMap(
                     52.2, 0.3, "Colin Watson",

=== modified file 'lib/lp/app/widgets/location.py'
--- lib/lp/app/widgets/location.py	2012-01-01 02:58:52 +0000
+++ lib/lp/app/widgets/location.py	2012-01-19 11:32:28 +0000
@@ -132,7 +132,7 @@
             show_marker=self.show_marker)
         return """
             <script type="text/javascript">
-                LPS.use('node', 'lp.app.mapping', function(Y) {
+                YUI.use('node', 'lp.app.mapping', function(Y) {
                     function renderMap() {
                         Y.lp.app.mapping.renderPersonMap(
                             %(center_lat)s, %(center_lng)s, %(displayname)s,

=== modified file 'lib/lp/app/widgets/templates/bugtracker-picker.pt'
--- lib/lp/app/widgets/templates/bugtracker-picker.pt	2011-04-16 00:52:07 +0000
+++ lib/lp/app/widgets/templates/bugtracker-picker.pt	2012-01-19 11:32:28 +0000
@@ -5,7 +5,7 @@
 
 <metal:form-picker use-macro="context/@@form-picker-macros/form-picker"/>
 <script tal:content="structure string:
-LPS.use('lp.bugs.bugtracker_overlay', function(Y) {
+LP_YUI.use('lp.bugs.bugtracker_overlay', function(Y) {
     if (Y.UA.ie) {
         return;
     }

=== modified file 'lib/lp/app/widgets/templates/form-picker-macros.pt'
--- lib/lp/app/widgets/templates/form-picker-macros.pt	2011-10-31 08:09:56 +0000
+++ lib/lp/app/widgets/templates/form-picker-macros.pt	2012-01-19 11:32:28 +0000
@@ -19,7 +19,7 @@
             />
       </select>
       <script type="text/javascript" tal:content="string:
-          LPS.use('node', 'lp.app.picker', function(Y) {
+          LP_YUI.use('node', 'lp.app.picker', function(Y) {
               var text_input = Y.DOM.byId('${view/name}');
               var select_menu = Y.DOM.byId('${suggestion_id}');
               Y.lp.app.picker.connect_select_menu(
@@ -30,7 +30,7 @@
 
     <tal:chooseLink replace="structure view/chooseLink" />
     <script tal:content="structure string:
-    LPS.use('node', 'lp.app.picker', function(Y) {
+    LP_YUI.use('node', 'lp.app.picker', function(Y) {
         if (Y.UA.ie) {
             return;
         }

=== modified file 'lib/lp/app/widgets/templates/license.pt'
--- lib/lp/app/widgets/templates/license.pt	2011-12-29 05:29:36 +0000
+++ lib/lp/app/widgets/templates/license.pt	2012-01-19 11:32:28 +0000
@@ -26,7 +26,7 @@
 // and breaks with every YUI/lazr-js upgrade.
 // Tread carefully, or else please make this better
 // and add tests, too (preferably in reverse order there.)
-LPS.use('node', 'lazr.effects', function(Y) {
+LP_YUI.use('node', 'lazr.effects', function(Y) {
     Y.on('domready', function() {
         function make_slider(cfg) {
             var table_name = '#' + cfg.which;

=== modified file 'lib/lp/blueprints/templates/specification-index.pt'
--- lib/lp/blueprints/templates/specification-index.pt	2011-08-25 06:32:24 +0000
+++ lib/lp/blueprints/templates/specification-index.pt	2012-01-19 11:32:28 +0000
@@ -320,7 +320,7 @@
     </div>
 
   <script type="text/javascript">
-    LPS.use('lp.anim', 'lp.ui', function(Y) {
+    LP_YUI.use('lp.anim', 'lp.ui', function(Y) {
 
         Y.on('lp:context:implementation_status:changed', function(e) {
             var icon = Y.one('#informational-icon');

=== modified file 'lib/lp/blueprints/templates/specifications-index.pt'
--- lib/lp/blueprints/templates/specifications-index.pt	2011-05-26 22:01:35 +0000
+++ lib/lp/blueprints/templates/specifications-index.pt	2012-01-19 11:32:28 +0000
@@ -64,9 +64,11 @@
           </tbody>
         </table>
       </form>
-      <script type="text/javascript"><!--
-         setFocusByName('field.search_text');
-       // --></script>
+      <script type="text/javascript">
+          LP_YUI.use('lp', function (Y) {
+              setFocusByName('field.search_text');
+          });
+      </script>
 
       <tal:searchresults condition="view/searchrequested"
                          define="specs view/specs" >

=== modified file 'lib/lp/bugs/browser/bugalsoaffects.py'
--- lib/lp/bugs/browser/bugalsoaffects.py	2012-01-03 00:28:55 +0000
+++ lib/lp/bugs/browser/bugalsoaffects.py	2012-01-19 11:32:28 +0000
@@ -193,7 +193,7 @@
             structured("""
                 There is no project in Launchpad named "%s". Please
                 <a href="/projects"
-                onclick="LPS.use('event').Event.simulate(
+                onclick="YUI.use('event').Event.simulate(
                          document.getElementById('%s'), 'click');
                          return false;"
                 >search for it</a> as it may be

=== modified file 'lib/lp/bugs/javascript/bug_tags_entry.js'
--- lib/lp/bugs/javascript/bug_tags_entry.js	2012-01-05 18:25:04 +0000
+++ lib/lp/bugs/javascript/bug_tags_entry.js	2012-01-19 11:32:28 +0000
@@ -97,11 +97,11 @@
             official_tags.sort();
             unofficial_tags.sort();
             var tags_html = Y.Array(official_tags).map(function(tag) {
-                return Y.Lang.substitute(
+                return Y.Lang.sub(
                     '<a href="{tag_url}" class="official-tag">{tag}</a>',
                     {tag_url: base_url + tag, tag: tag});
             }).join(' ') + ' ' + Y.Array(unofficial_tags).map(function(tag) {
-                return Y.Lang.substitute(
+                return Y.Lang.sub(
                     '<a href="{tag_url}" class="unofficial-tag">{tag}</a>',
                     {tag_url: base_url + tag, tag: tag});
             }).join(' ');

=== modified file 'lib/lp/bugs/javascript/buglisting.js'
--- lib/lp/bugs/javascript/buglisting.js	2011-12-23 16:34:48 +0000
+++ lib/lp/bugs/javascript/buglisting.js	2012-01-19 11:32:28 +0000
@@ -224,5 +224,5 @@
 
 }, "0.1", {
     "requires": [
-        "history", "node", 'lp.app.listing_navigator', 'lp.app.inlinehelp']
+        "history", "node", 'lp.app.listing_navigator', 'lp.app.inlinehelp', 'lp.app.indicator']
 });

=== modified file 'lib/lp/bugs/javascript/buglisting_utils.js'
--- lib/lp/bugs/javascript/buglisting_utils.js	2012-01-13 16:46:46 +0000
+++ lib/lp/bugs/javascript/buglisting_utils.js	2012-01-19 11:32:28 +0000
@@ -1,6 +1,6 @@
 /* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
 
-YUI().add('lp.buglisting_utils', function(Y) {
+YUI.add('lp.buglisting_utils', function(Y) {
     /**
      * A utility for configuring the display of bug listings.
      *
@@ -154,7 +154,7 @@
                     } else {
                         checked = '';
                     }
-                    input_html = Y.Lang.substitute(
+                    input_html = Y.Lang.sub(
                         this.constructor.INPUT_TEMPLATE,
                         {name: name, display_name: display_name,
                         checked: checked});

=== modified file 'lib/lp/bugs/javascript/bugtarget_portlet_bugtags.js'
--- lib/lp/bugs/javascript/bugtarget_portlet_bugtags.js	2011-12-21 08:26:19 +0000
+++ lib/lp/bugs/javascript/bugtarget_portlet_bugtags.js	2012-01-19 11:32:28 +0000
@@ -73,4 +73,4 @@
     }
 };
 
-}, "0.1", {"requires": ["node", 'io-base']});
+}, "0.1", {"requires": ["node", 'io-base', 'lp.client']});

=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
--- lib/lp/bugs/javascript/bugtask_index.js	2012-01-10 17:01:38 +0000
+++ lib/lp/bugs/javascript/bugtask_index.js	2012-01-19 11:32:28 +0000
@@ -147,7 +147,10 @@
                     // XXX Abel Deuring 2009-04-23, bug 365462
                     // Y.one('#field.private') returns null.
                     // Seems that YUI does not like IDs containing a '.'
-                    document.getElementById('field.private').focus();
+                    var field_private = document.getElementById('field.private');
+                    if (field_private !== null) {
+                        field_private.focus();
+                    }
                 }
             });
             privacy_link.addClass('js-action');
@@ -740,7 +743,7 @@
         '    <strong>Please confirm you really want to do this.</strong>',
         '</p>'
         ].join('');
-    var delete_text = Y.Lang.substitute(delete_text_template,
+    var delete_text = Y.Lang.sub(delete_text_template,
             {bug: conf.bug_title, target: conf.targetname});
     var co = new Y.lp.app.confirmationoverlay.ConfirmationOverlay({
         submit_fn: function() {
@@ -788,10 +791,10 @@
     // be 404. In this case, we remove the affected row and display a
     // informational message to the user.
     if( response.status === 404) {
-        var message = Y.Lang.substitute(
+        var message = Y.Lang.sub(
             "Bug task affecting {targetname} has already been deleted.",
             {targetname: LP.cache.bugtask_data[bugtask_id].targetname});
-        var notification = Y.Lang.substitute(
+        var notification = Y.Lang.sub(
                 '[[20, "{message}"]]', {message: message});
         Y.lp.client.display_notifications(notification);
         animate_deletion(function() {
@@ -1095,7 +1098,7 @@
                         "assigned bugs in {pillar}.</p>" +
                         "<p>Do you really want to assign them to this bug?"+
                         "</p>";
-                    var yesno_content = Y.Lang.substitute(
+                    var yesno_content = Y.Lang.sub(
                             yesno_content_template,
                             {person_name: person, pillar: pillar});
                     Y.lp.app.picker.yesno_save_confirmation(

=== modified file 'lib/lp/bugs/javascript/official_bug_tags.js'
--- lib/lp/bugs/javascript/official_bug_tags.js	2010-12-09 16:20:20 +0000
+++ lib/lp/bugs/javascript/official_bug_tags.js	2012-01-19 11:32:28 +0000
@@ -164,7 +164,7 @@
       item.count = '';
     }
     item._tag_id = mangle_id(item.tag);
-    var li_html = Y.Lang.substitute([
+    var li_html = Y.Lang.sub([
         '<li id="tag-{_tag_id}">',
         '  <input type="checkbox" id="tag-checkbox-{_tag_id}" />',
         '  <label for="tag-checkbox-{_tag_id}">',
@@ -353,7 +353,7 @@
     var overlay = new Y.lazr.PrettyOverlay({
       headerContent: '<span class="official-tag-error-message-header">' +
                      '<img src="/@@/error" />&nbsp;Invalid Tag</span>',
-      bodyContent: Y.Lang.substitute(ERROR_MSG, {new_tag: new_tag}),
+      bodyContent: Y.Lang.sub(ERROR_MSG, {new_tag: new_tag}),
       align: {
         points: [Y.WidgetPositionAlign.CC, Y.WidgetPositionAlign.CC]
       },

=== modified file 'lib/lp/bugs/javascript/tests/test_buglisting.html'
--- lib/lp/bugs/javascript/tests/test_buglisting.html	2012-01-05 21:13:29 +0000
+++ lib/lp/bugs/javascript/tests/test_buglisting.html	2012-01-19 11:32:28 +0000
@@ -28,7 +28,7 @@
           src="../../../app/javascript/lp.js"></script>
 
     <script type="text/javascript"
-      src="../../../contrib/javascript/mustache.js"></script>
+      src="../../../contrib/javascript/lp.mustache.js"></script>
   <script type="text/javascript"
       src="../../../app/javascript/overlay/overlay.js"></script>
   <script type="text/javascript"

=== modified file 'lib/lp/bugs/templates/bug-portlet-subscription.pt'
--- lib/lp/bugs/templates/bug-portlet-subscription.pt	2011-11-26 04:03:29 +0000
+++ lib/lp/bugs/templates/bug-portlet-subscription.pt	2012-01-19 11:32:28 +0000
@@ -51,7 +51,7 @@
     </ul>
   </div>
   <script type="text/javascript">
-    LPS.use('io-base', 'node',
+    LP_YUI.use('io-base', 'node',
             'lp.bugs.bugtask_index.portlets.subscription', function(Y) {
         // Must be done inline here to ensure the load event fires.
         // This is a work around for a YUI3 issue with event handling.

=== modified file 'lib/lp/bugs/templates/bug-subscription-list.pt'
--- lib/lp/bugs/templates/bug-subscription-list.pt	2011-06-16 13:50:58 +0000
+++ lib/lp/bugs/templates/bug-subscription-list.pt	2012-01-19 11:32:28 +0000
@@ -13,7 +13,7 @@
 <head>
   <tal:head-epilogue metal:fill-slot="head_epilogue">
     <script type="text/javascript">
-      LPS.use('lp.registry.structural_subscription', 'lp.bugs.subscription',
+      LP_YUI.use('lp.registry.structural_subscription', 'lp.bugs.subscription',
               function(Y) {
           var ss_module = Y.lp.registry.structural_subscription;
           var info_module = Y.lp.bugs.subscription;

=== modified file 'lib/lp/bugs/templates/bug-subscription.pt'
--- lib/lp/bugs/templates/bug-subscription.pt	2011-06-01 22:38:01 +0000
+++ lib/lp/bugs/templates/bug-subscription.pt	2012-01-19 11:32:28 +0000
@@ -11,12 +11,8 @@
 >
 
   <metal:block fill-slot="head_epilogue">
-    <script type="text/javascript"
-            tal:condition="devmode"
-            tal:content="string:var yui_base='${yui}';"></script>
-
     <script type="text/javascript">
-        LPS.use('base', 'node', 'oop', 'event',
+        LP_YUI.use('base', 'node', 'oop', 'event',
                 'lp.bugs.bug_notification_level',
             function(Y) {
                 Y.on('domready', function () {

=== modified file 'lib/lp/bugs/templates/bugcomment-macros.pt'
--- lib/lp/bugs/templates/bugcomment-macros.pt	2011-11-14 04:07:15 +0000
+++ lib/lp/bugs/templates/bugcomment-macros.pt	2012-01-19 11:32:28 +0000
@@ -116,7 +116,7 @@
           comment_form/actions/field.actions.save/render" />
     </form>
     <script type="text/javascript">
-      LPS.use('lp.app.comment', function(Y) {
+      LP_YUI.use('lp.app.comment', function(Y) {
           var comment = new Y.lp.app.comment.Comment();
           comment.render();
       });

=== modified file 'lib/lp/bugs/templates/buglisting-default.pt'
--- lib/lp/bugs/templates/buglisting-default.pt	2011-12-08 13:23:00 +0000
+++ lib/lp/bugs/templates/buglisting-default.pt	2012-01-19 11:32:28 +0000
@@ -1,24 +1,24 @@
 <html
-  xmlns="http://www.w3.org/1999/xhtml";
-  xmlns:tal="http://xml.zope.org/namespaces/tal";
-  xmlns:metal="http://xml.zope.org/namespaces/metal";
-  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
-  metal:use-macro="view/macro:page/main_side"
-  i18n:domain="malone"
+xmlns="http://www.w3.org/1999/xhtml";
+xmlns:tal="http://xml.zope.org/namespaces/tal";
+xmlns:metal="http://xml.zope.org/namespaces/metal";
+xmlns:i18n="http://xml.zope.org/namespaces/i18n";
+metal:use-macro="view/macro:page/main_side"
+i18n:domain="malone"
 >
 
 <metal:block fill-slot="head_epilogue">
-  <meta condition="not: view/should_show_bug_information"
-        name="robots" content="noindex,nofollow" />
+<meta condition="not: view/should_show_bug_information"
+    name="robots" content="noindex,nofollow" />
 
-    <tal:comment replace="nothing">
-        The javascript below should only be executed (or indeed even exist)
-        when the bug subscription links exist. They don't exist on the
-        advanced search form, so we don't show the js for that case.
-    </tal:comment>
-    <script type="text/javascript"
-      tal:condition="not: view/shouldShowAdvancedForm">
-    LPS.use('lp.registry.structural_subscription', function(Y) {
+<tal:comment replace="nothing">
+    The javascript below should only be executed (or indeed even exist)
+    when the bug subscription links exist. They don't exist on the
+    advanced search form, so we don't show the js for that case.
+</tal:comment>
+<script type="text/javascript"
+  tal:condition="not: view/shouldShowAdvancedForm">
+    LP_YUI.use('lp.registry.structural_subscription', function(Y) {
         Y.on('domready', function() {
             Y.lp.registry.structural_subscription.setup(
                 {content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/bugs/templates/bugs-listing-table.pt'
--- lib/lp/bugs/templates/bugs-listing-table.pt	2011-11-02 19:15:59 +0000
+++ lib/lp/bugs/templates/bugs-listing-table.pt	2012-01-19 11:32:28 +0000
@@ -33,7 +33,7 @@
   <tal:comment
     tal:condition="request/features/ajax.batch_navigator.enabled"
     replace='structure string:&lt;script type="text/javascript"&gt;
-    LPS.use("lp.app.batchnavigator",
+    LP_YUI.use("lp.app.batchnavigator",
         function(Y) {
             Y.on("domready", function () {
                 var config = {

=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-search.pt'
--- lib/lp/bugs/templates/bugtarget-filebug-search.pt	2011-11-30 16:55:21 +0000
+++ lib/lp/bugs/templates/bugtarget-filebug-search.pt	2012-01-19 11:32:28 +0000
@@ -7,12 +7,8 @@
 
   <metal:block fill-slot="head_epilogue">
     <script type="text/javascript"
-            tal:condition="devmode"
-            tal:content="string:var yui_base='${yui}';"></script>
-
-    <script type="text/javascript"
             tal:condition="context/enable_bugfiling_duplicate_search">
-        LPS.use(
+        LP_YUI.use(
           'base', 'node', 'oop', 'event', 'lp.bugs.filebug_dupefinder',
           function(Y) {
             Y.lp.bugs.filebug_dupefinder.setup_dupe_finder();

=== modified file 'lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt'
--- lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt	2011-11-30 16:55:21 +0000
+++ lib/lp/bugs/templates/bugtarget-filebug-submit-bug.pt	2012-01-19 11:32:28 +0000
@@ -6,11 +6,8 @@
   metal:use-macro="view/macro:page/main_only">
 
   <metal:block fill-slot="head_epilogue">
-    <script type="text/javascript"
-            tal:condition="devmode"
-            tal:content="string:var yui_base='${yui}';"></script>
     <script type="text/javascript">
-      LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.filebug_dupefinder',
+      LP_YUI.use('base', 'node', 'oop', 'event', 'lp.bugs.filebug_dupefinder',
       function(Y) {
           Y.on('domready', function() {
               Y.lp.bugs.filebug_dupefinder.setup_dupes();

=== modified file 'lib/lp/bugs/templates/bugtarget-macros-filebug.pt'
--- lib/lp/bugs/templates/bugtarget-macros-filebug.pt	2011-12-01 16:58:16 +0000
+++ lib/lp/bugs/templates/bugtarget-macros-filebug.pt	2012-01-19 11:32:28 +0000
@@ -330,7 +330,7 @@
 <metal:privacy define-macro="bug-reporting-privacy">
     <tal:private condition="view/isPrivate">
         <script type="text/javascript">
-            LPS.use('lp.app.privacy',  function(Y) {
+            LP_YUI.use('lp.app.privacy',  function(Y) {
                 Y.on('domready', function() {
                    var cfg = {
                         notification_text: "This report will be private. "
@@ -344,7 +344,7 @@
     </tal:private>
     <tal:not-private condition="not:view/isPrivate">
         <script type="text/javascript">
-            LPS.use('lp.app.privacy', 'lp.bugs.filebug_dupefinder', function(Y) {
+            LP_YUI.use('lp.app.privacy', 'lp.bugs.filebug_dupefinder', function(Y) {
                 Y.on('domready', function() {
                    var cfg = {
                         notification_text: "This report will be private "

=== modified file 'lib/lp/bugs/templates/bugtarget-patches.pt'
--- lib/lp/bugs/templates/bugtarget-patches.pt	2011-07-01 11:39:14 +0000
+++ lib/lp/bugs/templates/bugtarget-patches.pt	2012-01-19 11:32:28 +0000
@@ -20,7 +20,7 @@
         tal:attributes="action string:${context/fmt:url}/+patches">
 
     <script type="text/javascript">
-      LPS.use('base', 'node', 'event', function(Y) {
+      LP_YUI.use('base', 'node', 'event', function(Y) {
         Y.on('domready', function(e) {
           Y.one('#sort-button').setStyle('display', 'none');
           Y.one('#orderby').on('change', function(e) {
@@ -87,7 +87,7 @@
                  <p tal:content="string:${patch/title}"></p>
                </div>
                <script type="text/javascript" tal:content="string:
-                 LPS.use('base', 'node', 'event', function(Y) {
+                 LP_YUI.use('base', 'node', 'event', function(Y) {
                    Y.on('domready', function(e) {
                      var cell_id = '#patch-cell-${repeat/patch_task/index}';
                      var target_id = '#patch-popup-${repeat/patch_task/index}';

=== modified file 'lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt'
--- lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt	2011-06-16 13:50:58 +0000
+++ lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt	2012-01-19 11:32:28 +0000
@@ -55,7 +55,7 @@
     </tbody>
   </table>
   <script type="text/javascript">
-    LPS.use('io-base', 'node', function(Y) {
+    LP_YUI.use('io-base', 'node', function(Y) {
         Y.on('domready', function() {
             var url = Y.one('#bugtarget-bugfilters-link').getAttribute('href');
             var handlers = {

=== modified file 'lib/lp/bugs/templates/bugtarget-portlet-bugtags.pt'
--- lib/lp/bugs/templates/bugtarget-portlet-bugtags.pt	2011-11-10 04:10:32 +0000
+++ lib/lp/bugs/templates/bugtarget-portlet-bugtags.pt	2012-01-19 11:32:28 +0000
@@ -9,7 +9,7 @@
   <a id="tags-content-link"
      tal:attributes="href context/fmt:url/+bugtarget-portlet-tags-content"></a>
   <script type="text/javascript">
-    LPS.use('lp.bugs.bugtask.taglist', function(Y) {
+    LP_YUI.use('lp.bugs.bugtask.taglist', function(Y) {
       Y.on('domready', function() {
         Y.lp.bugs.bugtask.taglist.setup_taglist();
       });

=== modified file 'lib/lp/bugs/templates/bugtarget-subscription-list.pt'
--- lib/lp/bugs/templates/bugtarget-subscription-list.pt	2011-06-16 13:50:58 +0000
+++ lib/lp/bugs/templates/bugtarget-subscription-list.pt	2012-01-19 11:32:28 +0000
@@ -13,7 +13,7 @@
 <head>
   <tal:head-epilogue metal:fill-slot="head_epilogue">
     <script type="text/javascript">
-      LPS.use('lp.registry.structural_subscription', function(Y) {
+      LP_YUI.use('lp.registry.structural_subscription', function(Y) {
           var module = Y.lp.registry.structural_subscription;
           var config = {
               content_box: "#structural-subscription-content-box",

=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
--- lib/lp/bugs/templates/bugtask-index.pt	2011-12-08 20:31:53 +0000
+++ lib/lp/bugs/templates/bugtask-index.pt	2012-01-19 11:32:28 +0000
@@ -7,11 +7,10 @@
 >
   <body>
     <metal:block fill-slot="head_epilogue">
-      <script type='text/javascript' tal:content="string:var yui_base='${yui}';" />
       <script type='text/javascript' id='available-official-tags-js'
               tal:content="view/available_official_tags_js" />
       <script type="text/javascript">
-        LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index',
+        LP_YUI.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index',
                   'lp.bugs.subscribers',
                   'lp.code.branchmergeproposal.diff', 'lp.comments.hide',
                   function(Y) {
@@ -159,7 +158,7 @@
                 <img src="/@@/spinner" id="tags-edit-spinner" class="hidden"/>
                 <a href="+edit" title="Edit tags" id="edit-tags-trigger" class="sprite edit"></a>
               <script type="text/javascript">
-                  LPS.use('event', 'node', 'lp.bugs.bug_tags_entry', function(Y) {
+                  LP_YUI.use('event', 'node', 'lp.bugs.bug_tags_entry', function(Y) {
                       // XXX intellectronica 2009-04-16 bug #362309:
                       // The load event fires very late on bug pages that take a
                       // long time to render, but we prefer to use it since the

=== modified file 'lib/lp/bugs/templates/bugtask-macros-tableview.pt'
--- lib/lp/bugs/templates/bugtask-macros-tableview.pt	2011-12-23 03:10:11 +0000
+++ lib/lp/bugs/templates/bugtask-macros-tableview.pt	2012-01-19 11:32:28 +0000
@@ -655,7 +655,7 @@
       show_new_listings request/features/bugs.dynamic_bug_listings.enabled;
       advanced_search view/shouldShowAdvancedForm"
     tal:condition="python: show_new_listings and not advanced_search">
-    LPS.use('lp.bugs.buglisting', 'lp.ordering', 'lp.buglisting_utils',
+    LP_YUI.use('lp.bugs.buglisting', 'lp.ordering', 'lp.buglisting_utils',
       function(Y) {
         Y.on('domready', function() {
             var navigator = Y.lp.bugs.buglisting.BugListingNavigator

=== modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-portal.pt'
--- lib/lp/bugs/templates/bugtasks-and-nominations-portal.pt	2011-11-26 04:03:29 +0000
+++ lib/lp/bugs/templates/bugtasks-and-nominations-portal.pt	2012-01-19 11:32:28 +0000
@@ -37,7 +37,7 @@
           </a>
         </span>
         <script type="text/javascript" tal:content="string:
-          LPS.use('event', 'lp.bugs.bugtask_index', function(Y) {
+          LP_YUI.use('event', 'lp.bugs.bugtask_index', function(Y) {
               Y.on('load', function(e) {
                   Y.lp.bugs.bugtask_index.setup_me_too(
                       ${view/current_user_affected_js_status},

=== modified file 'lib/lp/bugs/templates/bugtracker-configure.pt'
--- lib/lp/bugs/templates/bugtracker-configure.pt	2012-01-11 07:16:10 +0000
+++ lib/lp/bugs/templates/bugtracker-configure.pt	2012-01-19 11:32:28 +0000
@@ -8,7 +8,7 @@
 
     <metal:block fill-slot="head_epilogue">
         <script type="text/javascript">
-            LPS.use('node', 'lang', function(Y) {
+            LP_YUI.use('node', 'lang', function(Y) {
                 // Constrain enable_bug_expiration to the Launchpad Bugs radio input.
                 // The Launchpad bug tracker is either the first item in a product's
                 // bugtracker field, or it is a distribution's official_malone field.

=== modified file 'lib/lp/bugs/templates/official-bug-target-manage-tags.pt'
--- lib/lp/bugs/templates/official-bug-target-manage-tags.pt	2011-11-14 04:07:15 +0000
+++ lib/lp/bugs/templates/official-bug-target-manage-tags.pt	2012-01-19 11:32:28 +0000
@@ -23,7 +23,7 @@
 
         <script tal:replace="structure view/tags_js_data" />
         <script type="text/javascript">
-            LPS.use('event', 'lp.bugs.official_bug_tags', function(Y) {
+            LP_YUI.use('event', 'lp.bugs.official_bug_tags', function(Y) {
                 Y.on('domready', function(e) {
                    Y.lp.bugs.official_bug_tags.setup_official_bug_tag_management();
                 });

=== modified file 'lib/lp/bugs/templates/projectgroup-filebug-search.pt'
--- lib/lp/bugs/templates/projectgroup-filebug-search.pt	2011-11-30 16:55:21 +0000
+++ lib/lp/bugs/templates/projectgroup-filebug-search.pt	2012-01-19 11:32:28 +0000
@@ -6,11 +6,8 @@
         metal:use-macro="view/macro:page/main_only">
 
   <metal:block fill-slot="head_epilogue">
-    <script type="text/javascript"
-            tal:condition="devmode"
-            tal:content="string:var yui_base='${yui}';"></script>
     <script type="text/javascript">
-      LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.filebug_dupefinder',
+      LP_YUI.use('base', 'node', 'oop', 'event', 'lp.bugs.filebug_dupefinder',
       function(Y) {
       Y.on('domready', function() {
       Y.lp.bugs.filebug_dupefinder.setup_dupes();

=== modified file 'lib/lp/code/javascript/requestbuild_overlay.js'
--- lib/lp/code/javascript/requestbuild_overlay.js	2011-08-31 11:03:56 +0000
+++ lib/lp/code/javascript/requestbuild_overlay.js	2012-01-19 11:32:28 +0000
@@ -169,7 +169,7 @@
                                 break;
                             default:
                                 new_builds_message =
-                                        Y.Lang.substitute(
+                                        Y.Lang.sub(
                                                 MANY_BUILDS_MESSAGE,
                                                 {nr_new: nr_new});
                         }
@@ -254,7 +254,7 @@
     }
     nr_new = display_build_records(build_html, current_builds);
     if (nr_new > 1) {
-        return Y.Lang.substitute(MANY_BUILDS_MESSAGE, {nr_new: nr_new});
+        return Y.Lang.sub(MANY_BUILDS_MESSAGE, {nr_new: nr_new});
     }
     return ONE_BUILD_MESSAGE;
 };
@@ -457,7 +457,7 @@
             if (distro === distroarchive[0]) {
                 nr_matches += 1;
                 escaped_distro = Y.Escape.html(distro);
-                disabled_checkbox_html = Y.Lang.substitute(
+                disabled_checkbox_html = Y.Lang.sub(
                     DISABLED_DISTROSERIES_CHECKBOX_HTML,
                     {distro: escaped_distro});
                 distroseries_node.set("innerHTML", disabled_checkbox_html);

=== modified file 'lib/lp/code/javascript/tests/test_productseries-setbranch.html'
--- lib/lp/code/javascript/tests/test_productseries-setbranch.html	2011-07-08 06:21:11 +0000
+++ lib/lp/code/javascript/tests/test_productseries-setbranch.html	2012-01-19 11:32:28 +0000
@@ -17,7 +17,7 @@
   <!-- The module under test -->
   <script type="text/javascript" src="../productseries-setbranch.js"></script>
   <script type="text/javascript">
-    LPS.use('lp.code.productseries_setbranch', function(Y) {
+    YUI.use('lp.code.productseries_setbranch', function(Y) {
       Y.on('domready', Y.lp.code.productseries_setbranch.setup);
     });
   </script>

=== modified file 'lib/lp/code/templates/bazaar-index.pt'
--- lib/lp/code/templates/bazaar-index.pt	2010-12-20 20:17:42 +0000
+++ lib/lp/code/templates/bazaar-index.pt	2012-01-19 11:32:28 +0000
@@ -38,9 +38,11 @@
         <input id="text" type="text" name="text" size="50" />
         <input type="submit" value="Find a Project" />
       </form>
-      <script type="text/javascript"><!--
-         setFocusByName('text');
-       // --></script>
+      <script type="text/javascript">
+          LP_YUI.use('lp', function (Y) {
+              setFocusByName('text');
+          });
+      </script>
 
       <p id="application-summary">
         Launchpad can host your project&#8217;s source code

=== modified file 'lib/lp/code/templates/branch-form-macros.pt'
--- lib/lp/code/templates/branch-form-macros.pt	2012-01-17 07:06:43 +0000
+++ lib/lp/code/templates/branch-form-macros.pt	2012-01-19 11:32:28 +0000
@@ -17,7 +17,7 @@
 
 <script type="text/javascript">
 //<![CDATA[
-LPS.use('lp.code.util', function(Y) {
+LP_YUI.use('lp.code.util', function(Y) {
   Y.on('domready', function(e) {
       Y.lp.code.util.hookUpBranchFieldFunctions(Y);
   }, window);

=== modified file 'lib/lp/code/templates/branch-import-details.pt'
--- lib/lp/code/templates/branch-import-details.pt	2011-12-29 05:29:36 +0000
+++ lib/lp/code/templates/branch-import-details.pt	2012-01-19 11:32:28 +0000
@@ -32,7 +32,7 @@
                 Try again
               </a>
               <script type="text/javascript">
-                  LPS.use('event', 'node', function(Y) {
+                  LP_YUI.use('event', 'node', function(Y) {
                     Y.on("domready", function () { Y.one('#tryagainlink').setStyle('display', 'inline') });
                   });
               </script>

=== modified file 'lib/lp/code/templates/branch-index.pt'
--- lib/lp/code/templates/branch-index.pt	2011-08-24 08:57:25 +0000
+++ lib/lp/code/templates/branch-index.pt	2012-01-19 11:32:28 +0000
@@ -30,9 +30,8 @@
   </style>
   <script type="text/javascript"
           tal:content="string:
-    LPS.use('node', 'event', 'widget', 'plugin', 'overlay',
+    LP_YUI.use('node', 'event', 'widget', 'plugin', 'overlay',
               'lazr.choiceedit',
-              'lp.code.branch.status',
               'lp.code.branchmergeproposal.diff',
               'lp.code.branch.subscription',
               function(Y) {

=== modified file 'lib/lp/code/templates/branch-listing.pt'
--- lib/lp/code/templates/branch-listing.pt	2012-01-17 07:06:43 +0000
+++ lib/lp/code/templates/branch-listing.pt	2012-01-19 11:32:28 +0000
@@ -35,7 +35,7 @@
 <tal:comment
 tal:condition="not: request/features/ajax.batch_navigator.enabled"
 replace='structure string:&lt;script type="text/javascript"&gt;
-    LPS.use("lp.code.util", function(Y) {
+    LP_YUI.use("lp.code.util", function(Y) {
       Y.on("domready", function(e) {
           Y.lp.code.util.hookUpBranchFilterSubmission(Y);
       }, window);
@@ -191,7 +191,7 @@
   <tal:comment
     tal:condition="request/features/ajax.batch_navigator.enabled"
     replace='structure string:&lt;script type="text/javascript"&gt;
-    LPS.use("lp.app.batchnavigator",
+    LP_YUI.use("lp.app.batchnavigator",
         function(Y) {
             Y.on("domready", function () {
                 var config = {

=== modified file 'lib/lp/code/templates/branch-register-merge.pt'
--- lib/lp/code/templates/branch-register-merge.pt	2011-07-07 12:03:52 +0000
+++ lib/lp/code/templates/branch-register-merge.pt	2012-01-19 11:32:28 +0000
@@ -65,7 +65,7 @@
       -->
       <tal:script>
         <script type="text/javascript" tal:content="string:
-          LPS.use('node', 'event', function(Y) {
+          LP_YUI.use('node', 'event', function(Y) {
               function reviewer_changed(value) {
                 reviewer = Y.Lang.trim(value);
                 review_type = document.getElementById('field.review_type');

=== modified file 'lib/lp/code/templates/branch-related-bugs-specs.pt'
--- lib/lp/code/templates/branch-related-bugs-specs.pt	2011-02-27 19:45:44 +0000
+++ lib/lp/code/templates/branch-related-bugs-specs.pt	2012-01-19 11:32:28 +0000
@@ -73,7 +73,7 @@
     string:&lt;script id='branchlink-script' type='text/javascript'&gt;" />
     <!--
 
-    LPS.use('io-base', 'lp.code.branch.bugspeclinks', function(Y) {
+    LP_YUI.use('io-base', 'lp.code.branch.bugspeclinks', function(Y) {
 
     if(Y.UA.ie) {
         return;

=== modified file 'lib/lp/code/templates/branchmergeproposal-generic-listing.pt'
--- lib/lp/code/templates/branchmergeproposal-generic-listing.pt	2009-12-03 18:33:22 +0000
+++ lib/lp/code/templates/branchmergeproposal-generic-listing.pt	2012-01-19 11:32:28 +0000
@@ -24,7 +24,7 @@
   </form>
 <script type="text/javascript">
 
-LPS.use('node', function(Y) {
+LP_YUI.use('node', function(Y) {
 
   function submit_filter() {
     Y.one('#filter_form').submit();

=== modified file 'lib/lp/code/templates/branchmergeproposal-index.pt'
--- lib/lp/code/templates/branchmergeproposal-index.pt	2012-01-03 15:38:19 +0000
+++ lib/lp/code/templates/branchmergeproposal-index.pt	2012-01-19 11:32:28 +0000
@@ -206,7 +206,7 @@
   string:&lt;script id='codereview-script' type='text/javascript'&gt;" />
   conf = <tal:status-config replace="view/status_config" />
   <!--
-  LPS.use('io-base', 'lp.code.branchmergeproposal.reviewcomment',
+  LP_YUI.use('io-base', 'lp.code.branchmergeproposal.reviewcomment',
           'lp.code.branchmergeproposal.status', 'lp.app.comment',
           'lp.code.branchmergeproposal.updater', 'lp.app.widgets.expander',
           'lp.code.branch.revisionexpander', function(Y) {

=== modified file 'lib/lp/code/templates/daily-builds-listing.pt'
--- lib/lp/code/templates/daily-builds-listing.pt	2012-01-17 07:06:43 +0000
+++ lib/lp/code/templates/daily-builds-listing.pt	2012-01-19 11:32:28 +0000
@@ -29,7 +29,7 @@
     </form>
 
     <script type="text/javascript">
-        LPS.use("lp.code.util", function(Y) {
+        LP_YUI.use("lp.code.util", function(Y) {
           Y.on("domready", function(e) {
               Y.lp.code.util.hookUpDailyBuildsFilterSubmission(Y);
           }, window);

=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
--- lib/lp/code/templates/sourcepackagerecipe-index.pt	2011-12-07 04:57:36 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-index.pt	2012-01-19 11:32:28 +0000
@@ -149,7 +149,7 @@
   </div>
 
   <script type="text/javascript">
-    LPS.use('io-base', 'lp.ui', 'lp.code.requestbuild_overlay', function(Y) {
+    LP_YUI.use('io-base', 'lp.ui', 'lp.code.requestbuild_overlay', function(Y) {
 
         Y.on('lp:context:deb_version_template:changed', function(e) {
             Y.lp.ui.update_field('#debian-version dd', e.new_value);

=== modified file 'lib/lp/code/templates/sourcepackagerecipe-new.pt'
--- lib/lp/code/templates/sourcepackagerecipe-new.pt	2011-11-26 04:03:29 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-new.pt	2012-01-19 11:32:28 +0000
@@ -86,7 +86,7 @@
               </tr>
 
               <script type="text/javascript">
-                LPS.use('lp.code.sourcepackagerecipe.new', function(Y) {
+                LP_YUI.use('lp.code.sourcepackagerecipe.new', function(Y) {
                   Y.on('domready', Y.lp.code.sourcepackagerecipe.new.setup);
                 });
               </script>

=== added file 'lib/lp/contrib/javascript/lp.mustache.js'
--- lib/lp/contrib/javascript/lp.mustache.js	1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/lp.mustache.js	2012-01-19 11:32:28 +0000
@@ -0,0 +1,418 @@
+/* Copyright 2011 Canonical Ltd.  This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Mustache.js pulled into the lp namespace for our purposes.
+ *
+ * @module lp.mustache
+ */
+
+YUI.add('lp.mustache', function(Y) {
+
+    /**
+     * The Mustache code is simply copied and dropped into here. At the
+     * bottom of the file we'll create the lp.mustache namespace and set
+     * things up to work from there.
+     */
+    var Mustache = function() {
+        var regexCache = {};
+        var Renderer = function() {};
+
+        Renderer.prototype = {
+          otag: "{{",
+          ctag: "}}",
+          pragmas: {},
+          buffer: [],
+          pragmas_implemented: {
+            "IMPLICIT-ITERATOR": true
+          },
+          context: {},
+
+          render: function(template, context, partials, in_recursion) {
+            // reset buffer & set context
+            if(!in_recursion) {
+              this.context = context;
+              this.buffer = []; // TODO: make this non-lazy
+            }
+
+            // fail fast
+            if(!this.includes("", template)) {
+              if(in_recursion) {
+                return template;
+              } else {
+                this.send(template);
+                return;
+              }
+            }
+
+            // get the pragmas together
+            template = this.render_pragmas(template);
+
+            // render the template
+            var html = this.render_section(template, context, partials);
+
+            // render_section did not find any sections, we still need to render the tags
+            if (html === false) {
+              html = this.render_tags(template, context, partials, in_recursion);
+            }
+
+            if (in_recursion) {
+              return html;
+            } else {
+              this.sendLines(html);
+            }
+          },
+
+          /*
+            Sends parsed lines
+          */
+          send: function(line) {
+            if(line !== "") {
+              this.buffer.push(line);
+            }
+          },
+
+          sendLines: function(text) {
+            if (text) {
+              var lines = text.split("\n");
+              for (var i = 0; i < lines.length; i++) {
+                this.send(lines[i]);
+              }
+            }
+          },
+
+          /*
+            Looks for %PRAGMAS
+          */
+          render_pragmas: function(template) {
+            // no pragmas
+            if(!this.includes("%", template)) {
+              return template;
+            }
+
+            var that = this;
+            var regex = this.getCachedRegex("render_pragmas", function(otag, ctag) {
+              return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
+            });
+
+            return template.replace(regex, function(match, pragma, options) {
+              if(!that.pragmas_implemented[pragma]) {
+                throw({message:
+                  "This implementation of mustache doesn't understand the '" +
+                  pragma + "' pragma"});
+              }
+              that.pragmas[pragma] = {};
+              if(options) {
+                var opts = options.split("=");
+                that.pragmas[pragma][opts[0]] = opts[1];
+              }
+              return "";
+              // ignore unknown pragmas silently
+            });
+          },
+
+          /*
+            Tries to find a partial in the curent scope and render it
+          */
+          render_partial: function(name, context, partials) {
+            name = this.trim(name);
+            if(!partials || partials[name] === undefined) {
+              throw({message: "unknown_partial '" + name + "'"});
+            }
+            if(typeof(context[name]) != "object") {
+              return this.render(partials[name], context, partials, true);
+            }
+            return this.render(partials[name], context[name], partials, true);
+          },
+
+          /*
+            Renders inverted (^) and normal (#) sections
+          */
+          render_section: function(template, context, partials) {
+            if(!this.includes("#", template) && !this.includes("^", template)) {
+              // did not render anything, there were no sections
+              return false;
+            }
+
+            var that = this;
+
+            var regex = this.getCachedRegex("render_section", function(otag, ctag) {
+              // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
+              return new RegExp(
+                "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)
+
+                otag +                    // {{
+                "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
+                ctag +                    // }}
+
+                "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped
+
+                otag +                    // {{
+                "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
+                ctag +                    // }}
+
+                "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.
+
+              "g");
+            });
+
+
+            // for each {{#foo}}{{/foo}} section do...
+            return template.replace(regex, function(match, before, type, name, content, after) {
+              // before contains only tags, no sections
+              var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
+
+              // after may contain both sections and tags, so use full rendering function
+                  renderedAfter = after ? that.render(after, context, partials, true) : "",
+
+              // will be computed below
+                  renderedContent,
+
+                  value = that.find(name, context);
+
+              if (type === "^") { // inverted section
+                if (!value || that.is_array(value) && value.length === 0) {
+                  // false or empty list, render it
+                  renderedContent = that.render(content, context, partials, true);
+                } else {
+                  renderedContent = "";
+                }
+              } else if (type === "#") { // normal section
+                if (that.is_array(value)) { // Enumerable, Let's loop!
+                  renderedContent = that.map(value, function(row) {
+                    return that.render(content, that.create_context(row), partials, true);
+                  }).join("");
+                } else if (that.is_object(value)) { // Object, Use it as subcontext!
+                  renderedContent = that.render(content, that.create_context(value),
+                    partials, true);
+                } else if (typeof value === "function") {
+                  // higher order section
+                  renderedContent = value.call(context, content, function(text) {
+                    return that.render(text, context, partials, true);
+                  });
+                } else if (value) { // boolean section
+                  renderedContent = that.render(content, context, partials, true);
+                } else {
+                  renderedContent = "";
+                }
+              }
+
+              return renderedBefore + renderedContent + renderedAfter;
+            });
+          },
+
+          /*
+            Replace {{foo}} and friends with values from our view
+          */
+          render_tags: function(template, context, partials, in_recursion) {
+            // tit for tat
+            var that = this;
+
+
+
+            var new_regex = function() {
+              return that.getCachedRegex("render_tags", function(otag, ctag) {
+                return new RegExp(otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + ctag + "+", "g");
+              });
+            };
+
+            var regex = new_regex();
+            var tag_replace_callback = function(match, operator, name) {
+              switch(operator) {
+              case "!": // ignore comments
+                return "";
+              case "=": // set new delimiters, rebuild the replace regexp
+                that.set_delimiters(name);
+                regex = new_regex();
+                return "";
+              case ">": // render partial
+                return that.render_partial(name, context, partials);
+              case "{": // the triple mustache is unescaped
+                return that.find(name, context);
+              default: // escape the value
+                return that.escape(that.find(name, context));
+              }
+            };
+            var lines = template.split("\n");
+            for(var i = 0; i < lines.length; i++) {
+              lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+              if(!in_recursion) {
+                this.send(lines[i]);
+              }
+            }
+
+            if(in_recursion) {
+              return lines.join("\n");
+            }
+          },
+
+          set_delimiters: function(delimiters) {
+            var dels = delimiters.split(" ");
+            this.otag = this.escape_regex(dels[0]);
+            this.ctag = this.escape_regex(dels[1]);
+          },
+
+          escape_regex: function(text) {
+            // thank you Simon Willison
+            if(!arguments.callee.sRE) {
+              var specials = [
+                '/', '.', '*', '+', '?', '|',
+                '(', ')', '[', ']', '{', '}', '\\'
+              ];
+              arguments.callee.sRE = new RegExp(
+                '(\\' + specials.join('|\\') + ')', 'g'
+              );
+            }
+            return text.replace(arguments.callee.sRE, '\\$1');
+          },
+
+          /*
+            find `name` in current `context`. That is find me a value
+            from the view object
+          */
+          find: function(name, context) {
+            name = this.trim(name);
+
+            // Checks whether a value is thruthy or false or 0
+            function is_kinda_truthy(bool) {
+              return bool === false || bool === 0 || bool;
+            }
+
+            var value;
+            if(is_kinda_truthy(context[name])) {
+              value = context[name];
+            } else if(is_kinda_truthy(this.context[name])) {
+              value = this.context[name];
+            }
+
+            if(typeof value === "function") {
+              return value.apply(context);
+            }
+            if(value !== undefined) {
+              return value;
+            }
+            // silently ignore unkown variables
+            return "";
+          },
+
+          // Utility methods
+
+          /* includes tag */
+          includes: function(needle, haystack) {
+            return haystack.indexOf(this.otag + needle) != -1;
+          },
+
+          /*
+            Does away with nasty characters
+          */
+          escape: function(s) {
+            s = String(s === null ? "" : s);
+            return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
+              switch(s) {
+              case "&": return "&amp;";
+              case '"': return '&quot;';
+              case "'": return '&#39;';
+              case "<": return "&lt;";
+              case ">": return "&gt;";
+              default: return s;
+              }
+            });
+          },
+
+          // by @langalex, support for arrays of strings
+          create_context: function(_context) {
+            if(this.is_object(_context)) {
+              return _context;
+            } else {
+              var iterator = ".";
+              if(this.pragmas["IMPLICIT-ITERATOR"]) {
+                iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+              }
+              var ctx = {};
+              ctx[iterator] = _context;
+              return ctx;
+            }
+          },
+
+          is_object: function(a) {
+            return a && typeof a == "object";
+          },
+
+          is_array: function(a) {
+            return Object.prototype.toString.call(a) === '[object Array]';
+          },
+
+          /*
+            Gets rid of leading and trailing whitespace
+          */
+          trim: function(s) {
+            return s.replace(/^\s*|\s*$/g, "");
+          },
+
+          /*
+            Why, why, why? Because IE. Cry, cry cry.
+          */
+          map: function(array, fn) {
+            if (typeof array.map == "function") {
+              return array.map(fn);
+            } else {
+              var r = [];
+              var l = array.length;
+              for(var i = 0; i < l; i++) {
+                r.push(fn(array[i]));
+              }
+              return r;
+            }
+          },
+
+          getCachedRegex: function(name, generator) {
+            var byOtag = regexCache[this.otag];
+            if (!byOtag) {
+              byOtag = regexCache[this.otag] = {};
+            }
+
+            var byCtag = byOtag[this.ctag];
+            if (!byCtag) {
+              byCtag = byOtag[this.ctag] = {};
+            }
+
+            var regex = byCtag[name];
+            if (!regex) {
+              regex = byCtag[name] = generator(this.otag, this.ctag);
+            }
+
+            return regex;
+          }
+        };
+
+        return({
+          name: "mustache.js",
+          version: "0.4.0-dev",
+
+          /*
+            Turns a template and view into HTML
+          */
+          to_html: function(template, view, partials, send_fun) {
+            var renderer = new Renderer();
+            if(send_fun) {
+              renderer.send = send_fun;
+            }
+            renderer.render(template, view || {}, partials);
+            if(!send_fun) {
+              return renderer.buffer.join("\n");
+            }
+          }
+        });
+    }();
+
+
+    /**
+     * END COPY OF MUSTACHE INTO MODULE.
+     */
+
+    var module = Y.namespace('lp.mustache');
+    module.name = Mustache.name;
+    module.version = Mustache.version;
+    module.to_html = Mustache.to_html;
+
+}, "0.4-dev", { "requires": [] });
+

=== modified file 'lib/lp/registry/browser/__init__.py'
--- lib/lp/registry/browser/__init__.py	2012-01-05 20:11:40 +0000
+++ lib/lp/registry/browser/__init__.py	2012-01-19 11:32:28 +0000
@@ -118,7 +118,7 @@
             'milestone_row_uri': self.milestone_row_uri_template,
             }
         return """
-            LPS.use(
+            YUI.use(
                 'node', 'lp.registry.milestoneoverlay',
                 'lp.registry.milestonetable',
                 function (Y) {

=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2012-01-17 23:55:44 +0000
+++ lib/lp/registry/browser/person.py	2012-01-19 11:32:28 +0000
@@ -3005,7 +3005,7 @@
                         'center_lng': self.context.longitude}
         return u"""
             <script type="text/javascript">
-                LPS.use('node', 'lp.app.mapping', function(Y) {
+                YUI.use('node', 'lp.app.mapping', function(Y) {
                     function renderMap() {
                         Y.lp.app.mapping.renderPersonMapSmall(
                             %(center_lat)s, %(center_lng)s);

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2012-01-18 00:15:38 +0000
+++ lib/lp/registry/browser/team.py	2012-01-19 11:32:28 +0000
@@ -1215,7 +1215,7 @@
         """HTML which shows the map with location of the team's members."""
         return """
             <script type="text/javascript">
-                LPS.use('node', 'lp.app.mapping', function(Y) {
+                YUI.use('node', 'lp.app.mapping', function(Y) {
                     function renderMap() {
                         Y.lp.app.mapping.renderTeamMap(
                             %(min_lat)s, %(max_lat)s, %(min_lng)s,
@@ -1230,7 +1230,7 @@
         """The HTML which shows a small version of the team's map."""
         return """
             <script type="text/javascript">
-                LPS.use('node', 'lp.app.mapping', function(Y) {
+                YUI.use('node', 'lp.app.mapping', function(Y) {
                     function renderMap() {
                         Y.lp.app.mapping.renderTeamMapSmall(
                             %(center_lat)s, %(center_lng)s);

=== modified file 'lib/lp/registry/browser/tests/productrelease-views.txt'
--- lib/lp/registry/browser/tests/productrelease-views.txt	2011-12-22 05:09:10 +0000
+++ lib/lp/registry/browser/tests/productrelease-views.txt	2012-01-19 11:32:28 +0000
@@ -125,7 +125,7 @@
     >>> script = find_tag_by_id(view.render(), 'milestone-script')
     >>> print script
     <script id="milestone-script" type="text/javascript">
-        LPS.use(... 'lp.registry.milestoneoverlay'...
+        YUI.use(... 'lp.registry.milestoneoverlay'...
             var milestone_form_uri = '.../app/simple/+addmilestone/++form++';
             var series_uri = '/app/simple';
             ...

=== modified file 'lib/lp/registry/browser/tests/productseries-views.txt'
--- lib/lp/registry/browser/tests/productseries-views.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/registry/browser/tests/productseries-views.txt	2012-01-19 11:32:28 +0000
@@ -82,7 +82,7 @@
     >>> script = find_tag_by_id(view.render(), 'milestone-script')
     >>> print script
     <script id="milestone-script" type="text/javascript">
-        LPS.use(... 'lp.registry.milestoneoverlay',
+        YUI.use(... 'lp.registry.milestoneoverlay',
                       'lp.registry.milestonetable'...
             var series_uri = '/app/simple';
             var milestone_form_uri = '.../app/simple/+addmilestone/++form++';

=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt	2011-11-29 20:50:18 +0000
+++ lib/lp/registry/templates/distribution-index.pt	2012-01-19 11:32:28 +0000
@@ -11,7 +11,7 @@
       <tal:uses_launchpad_bugtracker
          condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
         <script type="text/javascript">
-            LPS.use('lp.registry.structural_subscription', function(Y) {
+            LP_YUI.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {
                   module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt'
--- lib/lp/registry/templates/distributionsourcepackage-index.pt	2011-08-03 04:49:15 +0000
+++ lib/lp/registry/templates/distributionsourcepackage-index.pt	2012-01-19 11:32:28 +0000
@@ -13,7 +13,7 @@
   <tal:uses_launchpad_bugtracker
      condition="context/distribution/bug_tracking_usage/enumvalue:LAUNCHPAD">
     <script type="text/javascript">
-      LPS.use('lp.registry.structural_subscription', function(Y) {
+      LP_YUI.use('lp.registry.structural_subscription', function(Y) {
           var module = Y.lp.registry.structural_subscription;
           Y.on('domready', function() {
             module.setup({content_box: "#structural-subscription-content-box"});
@@ -236,7 +236,7 @@
       </p>
     </div>
 <script type="text/javascript">
-LPS.use('node', 'event', 'lp.app.widgets.expander', function(Y) {
+LP_YUI.use('node', 'event', 'lp.app.widgets.expander', function(Y) {
 
     // XXX Michael Nelson 20090702 bug=340497 This slider
     // needs an integration test.

=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
--- lib/lp/registry/templates/distroseries-index.pt	2011-07-06 14:42:11 +0000
+++ lib/lp/registry/templates/distroseries-index.pt	2012-01-19 11:32:28 +0000
@@ -16,7 +16,7 @@
       <tal:uses_launchpad_bugtracker
          condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
         <script type="text/javascript">
-            LPS.use('lp.registry.structural_subscription', function(Y) {
+            LP_YUI.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {
                   module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/distroseries-initialize.pt'
--- lib/lp/registry/templates/distroseries-initialize.pt	2011-11-26 04:03:29 +0000
+++ lib/lp/registry/templates/distroseries-initialize.pt	2012-01-19 11:32:28 +0000
@@ -30,7 +30,7 @@
         <metal:form use-macro="context/@@launchpad_form/form" />
       </div>
       <script type="text/javascript">
-        LPS.use('lp.registry.distroseries.initseries', function(Y) {
+        LP_YUI.use('lp.registry.distroseries.initseries', function(Y) {
           Y.on('domready', Y.lp.registry.distroseries.initseries.setup);
         });
       </script>

=== modified file 'lib/lp/registry/templates/distroseries-localdifferences.pt'
--- lib/lp/registry/templates/distroseries-localdifferences.pt	2011-12-19 15:31:31 +0000
+++ lib/lp/registry/templates/distroseries-localdifferences.pt	2012-01-19 11:32:28 +0000
@@ -60,7 +60,7 @@
       </span>
       <div metal:fill-slot="buttons">
         <script type="text/javascript">
-          LPS.use(
+          LP_YUI.use(
             'node', 'event', 'lp.registry.distroseriesdifferences_details',
             'lp.app.confirmationoverlay',function(Y) {
             Y.on('domready', function() {
@@ -295,14 +295,14 @@
       </div>
     </div>
 <script type="text/javascript">
-LPS.use('lp.registry.distroseriesdifferences_details', function(Y) {
+LP_YUI.use('lp.registry.distroseriesdifferences_details', function(Y) {
   Y.on('domready', function() {
     Y.lp.registry.distroseriesdifferences_details.setup();
   });
 });
 </script>
 <script type="text/javascript">
-  LPS.use("lp.registry.distroseries.differences", function(Y) {
+  LP_YUI.use("lp.registry.distroseries.differences", function(Y) {
     Y.on("domready", function() {
       var form = Y.one("form#distroseries-localdiff-search-filter");
       var differences = Y.lp.registry.distroseries.differences;

=== modified file 'lib/lp/registry/templates/milestone-index.pt'
--- lib/lp/registry/templates/milestone-index.pt	2011-12-23 16:40:51 +0000
+++ lib/lp/registry/templates/milestone-index.pt	2012-01-19 11:32:28 +0000
@@ -17,7 +17,7 @@
     <tal:uses_launchpad_bugtracker
        condition="context/target/bug_tracking_usage/enumvalue:LAUNCHPAD">
       <script type="text/javascript">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
+          LP_YUI.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {
                 module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/object-timeline-graph.pt'
--- lib/lp/registry/templates/object-timeline-graph.pt	2011-02-24 00:23:04 +0000
+++ lib/lp/registry/templates/object-timeline-graph.pt	2012-01-19 11:32:28 +0000
@@ -47,7 +47,7 @@
           get_timeline_config.size = size;
       }
 
-      LPS.use('lp.registry.timeline', 'node', 'lp.app.dragscroll', 'lp.client',
+      LP_YUI.use('lp.registry.timeline', 'node', 'lp.app.dragscroll', 'lp.client',
         function(Y) {
           Y.on('domready', function(e) {
               if (Y.UA.ie) {

=== modified file 'lib/lp/registry/templates/people-index.pt'
--- lib/lp/registry/templates/people-index.pt	2011-03-04 00:08:20 +0000
+++ lib/lp/registry/templates/people-index.pt	2012-01-19 11:32:28 +0000
@@ -105,9 +105,12 @@
               </tr>
             </table>
           </form>
-          <script type="text/javascript"><!--
-            setFocusByName('name');
-          // --></script>
+          <script type="text/javascript">
+              LP_YUI.use('lp', function (Y) {
+                  setFocusByName('name');
+              });
+          </script>
+
         </div>
 
         <tal:block condition="not: batch">

=== modified file 'lib/lp/registry/templates/person-macros.pt'
--- lib/lp/registry/templates/person-macros.pt	2011-12-13 04:32:49 +0000
+++ lib/lp/registry/templates/person-macros.pt	2012-01-19 11:32:28 +0000
@@ -253,7 +253,7 @@
               condition="private_prefix">
   <script type="text/javascript"
           tal:content="string:
-    LPS.use('node', 'event', function(Y) {
+    LP_YUI.use('node', 'event', function(Y) {
       // Prepend/remove 'private-' from team name based on visibility
       // setting.  User can choose to edit it back out, if they wish.
       function visibility_on_change(e) {

=== modified file 'lib/lp/registry/templates/product-index.pt'
--- lib/lp/registry/templates/product-index.pt	2011-12-09 15:04:53 +0000
+++ lib/lp/registry/templates/product-index.pt	2012-01-19 11:32:28 +0000
@@ -34,7 +34,7 @@
     <tal:uses_launchpad_bugtracker
        condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
       <script type="text/javascript">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
+          LP_YUI.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {
                 module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/product-new.pt'
--- lib/lp/registry/templates/product-new.pt	2010-11-10 22:04:20 +0000
+++ lib/lp/registry/templates/product-new.pt	2012-01-19 11:32:28 +0000
@@ -14,7 +14,7 @@
  * details widgets until the user states that the project they are
  * registering is not a duplicate.
  */
-LPS.use('node', 'lazr.effects', function(Y) {
+LP_YUI.use('node', 'lazr.effects', function(Y) {
     Y.on('domready', function() {
         /* These two regexps serve slightly different purposes.  The first
          * finds the leftmost run of valid url characters for the autofill

=== modified file 'lib/lp/registry/templates/productrelease-add-from-series.pt'
--- lib/lp/registry/templates/productrelease-add-from-series.pt	2011-08-09 14:18:02 +0000
+++ lib/lp/registry/templates/productrelease-add-from-series.pt	2012-01-19 11:32:28 +0000
@@ -14,7 +14,7 @@
   <tal:script
     replace="structure
     string:&lt;script id='milestone-script' type='text/javascript'&gt;" />
-    LPS.use('node', 'lp.registry.milestoneoverlay', function (Y) {
+    LP_YUI.use('node', 'lp.registry.milestoneoverlay', function (Y) {
 
         // This is a value for the SELECT OPTION which is passed with
         // the SELECT's "change" event. It includes some symbols that are not

=== modified file 'lib/lp/registry/templates/products-index.pt'
--- lib/lp/registry/templates/products-index.pt	2011-03-04 00:08:20 +0000
+++ lib/lp/registry/templates/products-index.pt	2012-01-19 11:32:28 +0000
@@ -46,9 +46,11 @@
         value="Search projects"
         />
     </form>
-    <script type="text/javascript"><!--
-        setFocusByName('text');
-        // --></script>
+    <script type="text/javascript">
+        LP_YUI.use('lp', function (Y) {
+            setFocusByName('text');
+        });
+    </script>
 
   <tal:searching condition="view/search_requested">
 

=== modified file 'lib/lp/registry/templates/productseries-index.pt'
--- lib/lp/registry/templates/productseries-index.pt	2011-06-16 13:50:58 +0000
+++ lib/lp/registry/templates/productseries-index.pt	2012-01-19 11:32:28 +0000
@@ -17,7 +17,7 @@
     <tal:uses_launchpad_bugtracker
        condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
       <script type="text/javascript">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
+          LP_YUI.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {
                 module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/productseries-setbranch.pt'
--- lib/lp/registry/templates/productseries-setbranch.pt	2010-11-10 15:33:47 +0000
+++ lib/lp/registry/templates/productseries-setbranch.pt	2012-01-19 11:32:28 +0000
@@ -119,7 +119,7 @@
   </div>
 
   <script type="text/javascript">
-    LPS.use('lp.code.productseries_setbranch', function(Y) {
+    LP_YUI.use('lp.code.productseries_setbranch', function(Y) {
       Y.on('domready', Y.lp.code.productseries_setbranch.setup);
     });
   </script>

=== modified file 'lib/lp/registry/templates/project-index.pt'
--- lib/lp/registry/templates/project-index.pt	2012-01-05 18:00:32 +0000
+++ lib/lp/registry/templates/project-index.pt	2012-01-19 11:32:28 +0000
@@ -12,7 +12,7 @@
       <tal:uses_launchpad_bugtracker
          condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
         <script type="text/javascript">
-            LPS.use('lp.registry.structural_subscription', function(Y) {
+            LP_YUI.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {
                   module.setup({content_box: "#structural-subscription-content-box"});

=== modified file 'lib/lp/registry/templates/projects-index.pt'
--- lib/lp/registry/templates/projects-index.pt	2011-03-04 00:08:20 +0000
+++ lib/lp/registry/templates/projects-index.pt	2012-01-19 11:32:28 +0000
@@ -32,9 +32,11 @@
           tal:condition="not: view/search_requested"
           type="submit" value="Search project groups" />
       </form>
-      <script type="text/javascript"><!--
-         setFocusByName('text');
-       // --></script>
+      <script type="text/javascript">
+          LP_YUI.use('lp', function (Y) {
+              setFocusByName('text');
+          });
+      </script>
 
       <div tal:condition="view/search_requested">
           <tal:searching condition="view/search_results">

=== modified file 'lib/lp/registry/templates/team-portlet-membership.pt'
--- lib/lp/registry/templates/team-portlet-membership.pt	2011-03-04 00:55:49 +0000
+++ lib/lp/registry/templates/team-portlet-membership.pt	2012-01-19 11:32:28 +0000
@@ -105,7 +105,7 @@
           tal:condition="link/enabled">
         <script type="text/javascript"
                 tal:content="string:
-          LPS.use('lp.registry.team', function(Y) {
+          LP_YUI.use('lp.registry.team', function(Y) {
               Y.on('load',
                   function(e) {
                       Y.lp.registry.team.setup_add_member_handler(

=== modified file 'lib/lp/registry/templates/teammembership-index.pt'
--- lib/lp/registry/templates/teammembership-index.pt	2011-12-06 05:52:07 +0000
+++ lib/lp/registry/templates/teammembership-index.pt	2012-01-19 11:32:28 +0000
@@ -20,7 +20,7 @@
       use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />
 
     <script type="text/javascript">
-      LPS.use('node', 'lp.app.calendar', function(Y) {
+      LP_YUI.use('node', 'lp.app.calendar', function(Y) {
           // Ensure that when the picker is used the radio button switches
           // from 'Never' to 'On' and the expiry field is enabled.
           Y.on("available", function(e) {

=== modified file 'lib/lp/registry/templates/timeline-macros.pt'
--- lib/lp/registry/templates/timeline-macros.pt	2011-11-14 04:07:15 +0000
+++ lib/lp/registry/templates/timeline-macros.pt	2012-01-19 11:32:28 +0000
@@ -45,7 +45,7 @@
       if (size != NaN && size >= 1) {
           timeline_url += "size=" + size + "&";
       }
-      LPS.use('node', function(Y) {
+      LP_YUI.use('node', function(Y) {
           if (Y.UA.ie) {
               return;
           }

=== modified file 'lib/lp/scripts/runlaunchpad.py'
--- lib/lp/scripts/runlaunchpad.py	2011-12-30 08:13:14 +0000
+++ lib/lp/scripts/runlaunchpad.py	2012-01-19 11:32:28 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=W0603

=== added file 'lib/lp/scripts/utilities/js/jsmin_all.py'
--- lib/lp/scripts/utilities/js/jsmin_all.py	1970-01-01 00:00:00 +0000
+++ lib/lp/scripts/utilities/js/jsmin_all.py	2012-01-19 11:32:28 +0000
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+"""Handle minifying all javascript files in the build directory by walking
+
+$ jsmin_all.py $lp_js_root
+
+"""
+import os
+import re
+import sys
+from jsmin import JavascriptMinify
+
+
+def dirwalk(dir):
+    "walk a directory tree, using a generator"
+    for f in os.listdir(dir):
+        fullpath = os.path.join(dir,f)
+        if os.path.isdir(fullpath) and not os.path.islink(fullpath):
+            for x in dirwalk(fullpath):  # recurse into subdir
+                yield x
+        else:
+            yield fullpath
+
+
+def is_min(filename):
+    """Check if this file is alrady a minified file"""
+    return re.search("^(min).js$", filename)
+
+def minify(filename):
+    """Given a filename, handle minifying it as -min.js"""
+    if not is_min(filename):
+        new_filename = re.sub(".js$", "-min.js", filename)
+
+        with open(filename) as shrink_me:
+            with open(new_filename, 'w') as tobemin:
+                jsm = JavascriptMinify()
+                jsm.minify(shrink_me, tobemin)
+
+
+if __name__ == '__main__':
+    root = sys.argv[1]
+
+    if os.path.isfile(root):
+        minify(root)
+    else:
+        [minify(f) for f in dirwalk(root)]

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2011-12-22 22:20:19 +0000
+++ lib/lp/services/features/flags.py	2012-01-19 11:32:28 +0000
@@ -37,8 +37,12 @@
 # Data for generating web-visible feature flag documentation.
 #
 # Entries for each flag are:
-# flag name, value domain, prose documentation, default behaviour, title,
-# URL to a page with more information about the feature.
+# 1. flag name
+# 2. value domain
+# 3. prose documentation
+# 4. default behaviour
+# 5. title
+# 6. URL to a page with more information about the feature.
 #
 # Value domain as in value_domain_info above.
 #
@@ -105,6 +109,18 @@
      '',
      '',
      ''),
+    ('js.combo_loader.enabled',
+     'boolean',
+     'Determines if we use a js combo loader or not.',
+     '',
+     '',
+     ''),
+    ('js.yui-version',
+     'space delimited',
+     'Allows us to change the YUI version we run against, e.g. yui-3.4.',
+     'As speficied in versions.cfg',
+     '',
+     ''),
     ('mail.dkim_authentication.disabled',
      'boolean',
      'Disable DKIM authentication checks on incoming mail.',

=== modified file 'lib/lp/services/profile/profile.pt'
--- lib/lp/services/profile/profile.pt	2011-09-01 22:13:33 +0000
+++ lib/lp/services/profile/profile.pt	2012-01-19 11:32:28 +0000
@@ -347,7 +347,7 @@
   </tal:block>
 </div>
 <script>
-LPS.use('node', 'lp', 'transition', function (Y) {
+LP_YUI.use('node', 'lp', 'transition', function (Y) {
     Y.one('div#reveal_profiling').on('click', function (e) {
         Y.one('div#profiling_info').setStyle('display', 'block');
         e.preventDefault();
@@ -359,7 +359,7 @@
 });
 </script>
 <script tal:condition="not:options/sql_traceback_none|nothing">
-LPS.use('node', 'lp', 'transition', function (Y) {
+LP_YUI.use('node', 'lp', 'transition', function (Y) {
     function slideIn(target) {
         target.transition({
             easing: 'ease-in',

=== modified file 'lib/lp/services/webapp/publisher.py'
--- lib/lp/services/webapp/publisher.py	2012-01-01 02:58:52 +0000
+++ lib/lp/services/webapp/publisher.py	2012-01-19 11:32:28 +0000
@@ -311,6 +311,15 @@
         """The page's template, if configured in zcml."""
         return self.index
 
+    @property
+    def yui_version(self):
+        """The version of YUI we are using."""
+        value = getFeatureFlag('js.yui_version')
+        if not value:
+            return 'yui'
+        else:
+            return value
+
     def render(self):
         """Return the body of the response.
 

=== modified file 'lib/lp/services/worlddata/javascript/languages.js'
--- lib/lp/services/worlddata/javascript/languages.js	2011-02-03 20:55:58 +0000
+++ lib/lp/services/worlddata/javascript/languages.js	2012-01-19 11:32:28 +0000
@@ -5,9 +5,9 @@
  * @requires oop, event, node
  */
 
-YUI.add('languages', function(Y) {
+YUI.add('lp.languages', function(Y) {
 
-var languages = Y.namespace('languages');
+var languages = Y.namespace('lp.languages');
 
 /* Prefilled in initialize_language_page. */
 var all_languages;
@@ -57,7 +57,6 @@
 
 languages.initialize_languages_page = function(Y) {
     init_filter_form();
-
 };
 
 

=== modified file 'lib/lp/soyuz/templates/archive-edit-dependencies.pt'
--- lib/lp/soyuz/templates/archive-edit-dependencies.pt	2009-12-03 18:33:22 +0000
+++ lib/lp/soyuz/templates/archive-edit-dependencies.pt	2012-01-19 11:32:28 +0000
@@ -62,7 +62,7 @@
   </div> <!-- launchpad_form -->
 
 <script type="text/javascript">
-   LPS.use("node", function(Y) {
+   LP_YUI.use("node", function(Y) {
 
        // Highlight (setting bold font-weight) the label for the
        // selected option in a given NodesList. Assumes the input is

=== modified file 'lib/lp/soyuz/templates/archive-index.pt'
--- lib/lp/soyuz/templates/archive-index.pt	2011-11-29 20:49:15 +0000
+++ lib/lp/soyuz/templates/archive-index.pt	2012-01-19 11:32:28 +0000
@@ -148,7 +148,7 @@
             >Celso Providelo</a>.</p>
 
         <script type="text/javascript">
-          LPS.use('lp.app.widgets.expander', function(Y) {
+          LP_YUI.use('lp.app.widgets.expander', function(Y) {
               var widget_header = Y.one('#ppa-install .widget-header');
               var content = Y.one('.widget-body');
               var expander = new Y.lp.app.widgets.expander.Expander(

=== modified file 'lib/lp/soyuz/templates/archive-macros.pt'
--- lib/lp/soyuz/templates/archive-macros.pt	2011-12-08 22:41:00 +0000
+++ lib/lp/soyuz/templates/archive-macros.pt	2012-01-19 11:32:28 +0000
@@ -10,7 +10,7 @@
   </tal:comment>
 
 <script type="text/javascript">
-LPS.use('node', 'io-base', 'lp.anim', 'lp.soyuz.base',
+LP_YUI.use('node', 'io-base', 'lp.anim', 'lp.soyuz.base',
         'lp.app.widgets.expander', function(Y) {
 
 

=== modified file 'lib/lp/soyuz/templates/archive-packages.pt'
--- lib/lp/soyuz/templates/archive-packages.pt	2012-01-10 10:32:59 +0000
+++ lib/lp/soyuz/templates/archive-packages.pt	2012-01-19 11:32:28 +0000
@@ -27,7 +27,7 @@
 
       <script type="text/javascript" id="repository-size-update"
               tal:condition="view/archive_url">
-LPS.use('io-base', 'lp.anim', 'node', 'lp.soyuz.base',
+LP_YUI.use('io-base', 'lp.anim', 'node', 'lp.soyuz.base',
           'lp.soyuz.update_archive_build_statuses', function(Y) {
 
 
@@ -92,7 +92,7 @@
 });
       </script>
       <script type="text/javascript" id="repository-size-update">
-LPS.use('lp.soyuz.archive_packages',
+LP_YUI.use('lp.soyuz.archive_packages',
           'lp.app.widgets.expander.Expander', function(Y) {
 
 Y.on('domready', function() {

=== modified file 'lib/lp/soyuz/templates/archive-subscribers.pt'
--- lib/lp/soyuz/templates/archive-subscribers.pt	2010-10-10 21:54:16 +0000
+++ lib/lp/soyuz/templates/archive-subscribers.pt	2012-01-19 11:32:28 +0000
@@ -93,7 +93,7 @@
         </form>
       </div><!-- class="portlet" -->
       <script type="text/javascript" id="setup-archivesubscribers-index">
-          LPS.use('lp.soyuz.archivesubscribers_index', function(Y) {
+          LP_YUI.use('lp.soyuz.archivesubscribers_index', function(Y) {
               Y.lp.soyuz.archivesubscribers_index.setup_archivesubscribers_index();
           });
       </script>

=== modified file 'lib/lp/soyuz/templates/distributionsourcepackage-publishinghistory.pt'
--- lib/lp/soyuz/templates/distributionsourcepackage-publishinghistory.pt	2011-07-18 09:23:10 +0000
+++ lib/lp/soyuz/templates/distributionsourcepackage-publishinghistory.pt	2012-01-19 11:32:28 +0000
@@ -11,7 +11,7 @@
 
 <div metal:fill-slot="head_epilogue">
   <script type="text/javascript">
-    LPS.use('node', 'lp.app.widgets.expander', function(Y) {
+    LP_YUI.use('node', 'lp.app.widgets.expander', function(Y) {
       Y.on('domready', function() {
         var all_expanders = Y.all('.expander-icon');
         all_expanders.each(function(icon) {

=== modified file 'lib/lp/soyuz/templates/distroseries-queue.pt'
--- lib/lp/soyuz/templates/distroseries-queue.pt	2011-07-28 07:59:23 +0000
+++ lib/lp/soyuz/templates/distroseries-queue.pt	2012-01-19 11:32:28 +0000
@@ -11,7 +11,7 @@
 
 <div metal:fill-slot="head_epilogue">
   <script type="text/javascript">
-    LPS.use('node', 'lp.app.widgets.expander', function(Y) {
+    LP_YUI.use('node', 'lp.app.widgets.expander', function(Y) {
         Y.on('domready', function() {
             var all_expanders = Y.all('.expander-link');
             all_expanders.each(function(link) {

=== modified file 'lib/lp/translations/templates/languageset-index.pt'
--- lib/lp/translations/templates/languageset-index.pt	2010-11-10 15:33:47 +0000
+++ lib/lp/translations/templates/languageset-index.pt	2012-01-19 11:32:28 +0000
@@ -8,15 +8,10 @@
 
   <body>
     <div metal:fill-slot="head_epilogue">
-      <script
-        type="text/javascript"
-        tal:condition="devmode"
-        tal:attributes="src string:${icingroot}/build/worlddata/languages.js">
-      </script>
       <script type="text/javascript">
-          LPS.use('languages', 'event', function(Y) {
+          LP_YUI.use('lp.languages', 'event', function(Y) {
               Y.on('domready', function(e) {
-                  Y.languages.initialize_languages_page(Y);
+                  Y.lp.languages.initialize_languages_page(Y);
               });
           });
       </script>

=== modified file 'lib/lp/translations/templates/object-templates.pt'
--- lib/lp/translations/templates/object-templates.pt	2011-07-26 18:29:40 +0000
+++ lib/lp/translations/templates/object-templates.pt	2012-01-19 11:32:28 +0000
@@ -7,7 +7,7 @@
   <body>
     <tal:head_epilogue metal:fill-slot="head_epilogue">
       <script type="text/javascript">
-        LPS.use('lp.translations.sourcepackage_sharing_details', function(Y) {
+        LP_YUI.use('lp.translations.sourcepackage_sharing_details', function(Y) {
           Y.on('domready', function() {
               Y.lp.translations.sourcepackage_sharing_details.prepare(
                 LP.cache);
@@ -45,7 +45,7 @@
         }
       </style>
       <script language="JavaScript" type="text/javascript">
-      LPS.use('node-base', 'event-delegate', function(Y) {
+      LP_YUI.use('node-base', 'event-delegate', function(Y) {
           Y.on('domready', function(e) {
               Y.all('#templates_table .template_links').addClass(
                     'inactive_links');

=== modified file 'lib/lp/translations/templates/pofile-export.pt'
--- lib/lp/translations/templates/pofile-export.pt	2011-06-09 10:50:25 +0000
+++ lib/lp/translations/templates/pofile-export.pt	2012-01-19 11:32:28 +0000
@@ -13,7 +13,7 @@
     }
     </style>
     <script type="text/javascript">
-    LPS.use('node', 'event', function(Y){
+    LP_YUI.use('node', 'event', function(Y){
         Y.on('domready', function(){
             // The pochanged option is only available for the PO format.
             var formatlist = Y.one('#div_format select');

=== modified file 'lib/lp/translations/templates/pofile-translate.pt'
--- lib/lp/translations/templates/pofile-translate.pt	2012-01-10 21:11:08 +0000
+++ lib/lp/translations/templates/pofile-translate.pt	2012-01-19 11:32:28 +0000
@@ -15,7 +15,7 @@
     </style>
 
     <script type="text/javascript">
-      LPS.use('lp.translations.pofile', function(Y) {
+      LP_YUI.use('lp.translations.pofile', function(Y) {
         Y.on('domready', Y.lp.translations.pofile.initializePOFile);
       });
 

=== modified file 'lib/lp/translations/templates/sourcepackage-sharing-details.pt'
--- lib/lp/translations/templates/sourcepackage-sharing-details.pt	2011-04-14 20:33:00 +0000
+++ lib/lp/translations/templates/sourcepackage-sharing-details.pt	2012-01-19 11:32:28 +0000
@@ -9,7 +9,7 @@
   <body>
     <metal:block fill-slot="head_epilogue">
       <script type="text/javascript">
-        LPS.use('lp.translations.sourcepackage_sharing_details', function(Y) {
+        LP_YUI.use('lp.translations.sourcepackage_sharing_details', function(Y) {
           Y.on('domready', function() {
               Y.lp.translations.sourcepackage_sharing_details.prepare(
                 LP.cache);

=== modified file 'lib/lp/translations/templates/translation-import-queue-macros.pt'
--- lib/lp/translations/templates/translation-import-queue-macros.pt	2011-11-14 04:07:15 +0000
+++ lib/lp/translations/templates/translation-import-queue-macros.pt	2012-01-19 11:32:28 +0000
@@ -13,7 +13,7 @@
       <script type="text/javascript" tal:content="view/choice_confs_js" />
 
       <script type="text/javascript">
-          LPS.use('lp.translations.importqueue', 'event', function(Y) {
+          LP_YUI.use('lp.translations.importqueue', 'event', function(Y) {
               Y.on('domready', function(e) {
                   Y.lp.translations.importqueue.initialize_import_queue_page(Y);
               });

=== modified file 'lib/lp/translations/templates/translationimportqueueentry-index.pt'
--- lib/lp/translations/templates/translationimportqueueentry-index.pt	2010-07-08 14:29:36 +0000
+++ lib/lp/translations/templates/translationimportqueueentry-index.pt	2012-01-19 11:32:28 +0000
@@ -19,7 +19,7 @@
       var template_domains = {'name1': 'domain1', 'name2': 'domain2'};
     </script>
     <script type="text/javascript">
-      LPS.use('node', 'lp.translations.importqueueentry',
+      LP_YUI.use('node', 'lp.translations.importqueueentry',
       function (Y) {
         Y.on('domready', Y.lp.translations.importqueueentry.setup_page);
       });

=== modified file 'lib/lp/translations/templates/translationmessage-translate.pt'
--- lib/lp/translations/templates/translationmessage-translate.pt	2010-08-31 12:42:29 +0000
+++ lib/lp/translations/templates/translationmessage-translate.pt	2012-01-19 11:32:28 +0000
@@ -14,7 +14,7 @@
     }
     </style>
     <script type="text/javascript">
-      LPS.use('lp.translations.pofile', function(Y) {
+      LP_YUI.use('lp.translations.pofile', function(Y) {
         Y.on('domready', Y.lp.translations.pofile.initializeTranslationMessage);
       });
     </script>

=== modified file 'lib/lp/translations/templates/translations-macros.pt'
--- lib/lp/translations/templates/translations-macros.pt	2012-01-10 20:52:40 +0000
+++ lib/lp/translations/templates/translations-macros.pt	2012-01-19 11:32:28 +0000
@@ -150,7 +150,7 @@
 
 <metal:languages-table-js define-macro="languages-table-js">
       <script type="text/javascript">
-      LPS.use('lp.translations.languages', 'event', function(Y) {
+      LP_YUI.use('lp.translations.languages', 'event', function(Y) {
           Y.on("click", function(e) {
             Y.lp.translations.languages.toggle_languages_visibility(e);
           }, "#toggle-languages-visibility");

=== added file 'utilities/js-deps'
--- utilities/js-deps	1970-01-01 00:00:00 +0000
+++ utilities/js-deps	2012-01-19 11:32:28 +0000
@@ -0,0 +1,7 @@
+#!bin/py
+
+import _pythonpath
+from convoy.meta import main
+
+main()
+

=== modified file 'utilities/yui-deps.py'
--- utilities/yui-deps.py	2011-03-11 11:15:31 +0000
+++ utilities/yui-deps.py	2012-01-19 11:32:28 +0000
@@ -8,7 +8,7 @@
 from sys import argv
 
 
-yui_root = 'lib/canonical/launchpad/icing/yui'
+yui_root = 'build/js/yui/yui-3.3.0'
 yui_deps = [
     'yui/yui',
     'oop/oop',

=== modified file 'versions.cfg'
--- versions.cfg	2012-01-11 05:54:50 +0000
+++ versions.cfg	2012-01-19 11:32:28 +0000
@@ -114,7 +114,7 @@
 wsgi-jsonrpc = 0.2.8
 wsgi-xmlrpc = 0.2.7
 wsgiref = 0.1.2
-yui = 3.3
+yui = 3.3.0
 z3c.coverage = 1.1.2
 z3c.csvvocabulary = 1.0.0
 z3c.etestbrowser = 1.0.4


Follow ups